diff options
1186 files changed, 32505 insertions, 14146 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index a3c5a66..ab8d76d 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -117,7 +117,7 @@ Description: What: /sys/bus/pci/devices/.../vpd Date: February 2008 -Contact: Ben Hutchings <bhutchings@solarflare.com> +Contact: Ben Hutchings <bwh@kernel.org> Description: A file named vpd in a device directory will be a binary file containing the Vital Product Data for the diff --git a/Documentation/ABI/testing/sysfs-class-net b/Documentation/ABI/testing/sysfs-class-net index d922060e..416c5d5 100644 --- a/Documentation/ABI/testing/sysfs-class-net +++ b/Documentation/ABI/testing/sysfs-class-net @@ -169,6 +169,14 @@ Description: "unknown", "notpresent", "down", "lowerlayerdown", "testing", "dormant", "up". +What: /sys/class/net/<iface>/phys_port_id +Date: July 2013 +KernelVersion: 3.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the interface unique physical port identifier within + the NIC, as a string. + What: /sys/class/net/<iface>/speed Date: October 2009 KernelVersion: 2.6.33 diff --git a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm new file mode 100644 index 0000000..5cedf72d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm @@ -0,0 +1,149 @@ +What: /sys/class/net/<iface>/cdc_ncm/min_tx_pkt +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + The driver will pad NCM Transfer Blocks (NTBs) longer + than this to tx_max, allowing the device to receive + tx_max sized frames with no terminating short + packet. NTBs shorter than this limit are transmitted + as-is, without any padding, and are terminated with a + short USB packet. + + Padding to tx_max allows the driver to transmit NTBs + back-to-back without any interleaving short USB + packets. This reduces the number of short packet + interrupts in the device, and represents a tradeoff + between USB bus bandwidth and device DMA optimization. + + Set to 0 to pad all frames. Set greater than tx_max to + disable all padding. + +What: /sys/class/net/<iface>/cdc_ncm/rx_max +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + The maximum NTB size for RX. Cannot exceed the + maximum value supported by the device. Must allow at + least one max sized datagram plus headers. + + The actual limits are device dependent. See + dwNtbInMaxSize. + + Note: Some devices will silently ignore changes to + this value, resulting in oversized NTBs and + corresponding framing errors. + +What: /sys/class/net/<iface>/cdc_ncm/tx_max +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + The maximum NTB size for TX. Cannot exceed the + maximum value supported by the device. Must allow at + least one max sized datagram plus headers. + + The actual limits are device dependent. See + dwNtbOutMaxSize. + +What: /sys/class/net/<iface>/cdc_ncm/tx_timer_usecs +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + Datagram aggregation timeout in µs. The driver will + wait up to 3 times this timeout for more datagrams to + aggregate before transmitting an NTB frame. + + Valid range: 5 to 4000000 + + Set to 0 to disable aggregation. + +The following read-only attributes all represent fields of the +structure defined in section 6.2.1 "GetNtbParameters" of "Universal +Serial Bus Communications Class Subclass Specifications for Network +Control Model Devices" (CDC NCM), Revision 1.0 (Errata 1), November +24, 2010 from USB Implementers Forum, Inc. The descriptions are +quoted from table 6-3 of CDC NCM: "NTB Parameter Structure". + +What: /sys/class/net/<iface>/cdc_ncm/bmNtbFormatsSupported +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + Bit 0: 16-bit NTB supported (set to 1) + Bit 1: 32-bit NTB supported + Bits 2 – 15: reserved (reset to zero; must be ignored by host) + +What: /sys/class/net/<iface>/cdc_ncm/dwNtbInMaxSize +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + IN NTB Maximum Size in bytes + +What: /sys/class/net/<iface>/cdc_ncm/wNdpInDivisor +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + Divisor used for IN NTB Datagram payload alignment + +What: /sys/class/net/<iface>/cdc_ncm/wNdpInPayloadRemainder +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + Remainder used to align input datagram payload within + the NTB: (Payload Offset) mod (wNdpInDivisor) = + wNdpInPayloadRemainder + +What: /sys/class/net/<iface>/cdc_ncm/wNdpInAlignment +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + NDP alignment modulus for NTBs on the IN pipe. Shall + be a power of 2, and shall be at least 4. + +What: /sys/class/net/<iface>/cdc_ncm/dwNtbOutMaxSize +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + OUT NTB Maximum Size + +What: /sys/class/net/<iface>/cdc_ncm/wNdpOutDivisor +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + OUT NTB Datagram alignment modulus + +What: /sys/class/net/<iface>/cdc_ncm/wNdpOutPayloadRemainder +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + Remainder used to align output datagram payload + offsets within the NTB: Padding, shall be transmitted + as zero by function, and ignored by host. (Payload + Offset) mod (wNdpOutDivisor) = wNdpOutPayloadRemainder + +What: /sys/class/net/<iface>/cdc_ncm/wNdpOutAlignment +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + NDP alignment modulus for use in NTBs on the OUT + pipe. Shall be a power of 2, and shall be at least 4. + +What: /sys/class/net/<iface>/cdc_ncm/wNtbOutMaxDatagrams +Date: May 2014 +KernelVersion: 3.16 +Contact: Bjørn Mork <bjorn@mork.no> +Description: + Maximum number of datagrams that the host may pack + into a single OUT NTB. Zero means that the device + imposes no limit. diff --git a/Documentation/ABI/testing/sysfs-class-net-queues b/Documentation/ABI/testing/sysfs-class-net-queues new file mode 100644 index 0000000..5e9aeb9 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-net-queues @@ -0,0 +1,79 @@ +What: /sys/class/<iface>/queues/rx-<queue>/rps_cpus +Date: March 2010 +KernelVersion: 2.6.35 +Contact: netdev@vger.kernel.org +Description: + Mask of the CPU(s) currently enabled to participate into the + Receive Packet Steering packet processing flow for this + network device queue. Possible values depend on the number + of available CPU(s) in the system. + +What: /sys/class/<iface>/queues/rx-<queue>/rps_flow_cnt +Date: April 2010 +KernelVersion: 2.6.35 +Contact: netdev@vger.kernel.org +Description: + Number of Receive Packet Steering flows being currently + processed by this particular network device receive queue. + +What: /sys/class/<iface>/queues/tx-<queue>/tx_timeout +Date: November 2011 +KernelVersion: 3.3 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of transmit timeout events seen by this + network interface transmit queue. + +What: /sys/class/<iface>/queues/tx-<queue>/xps_cpus +Date: November 2010 +KernelVersion: 2.6.38 +Contact: netdev@vger.kernel.org +Description: + Mask of the CPU(s) currently enabled to participate into the + Transmit Packet Steering packet processing flow for this + network device transmit queue. Possible vaules depend on the + number of available CPU(s) in the system. + +What: /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/hold_time +Date: November 2011 +KernelVersion: 3.3 +Contact: netdev@vger.kernel.org +Description: + Indicates the hold time in milliseconds to measure the slack + of this particular network device transmit queue. + Default value is 1000. + +What: /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/inflight +Date: November 2011 +KernelVersion: 3.3 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of bytes (objects) in flight on this + network device transmit queue. + +What: /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit +Date: November 2011 +KernelVersion: 3.3 +Contact: netdev@vger.kernel.org +Description: + Indicates the current limit of bytes allowed to be queued + on this network device transmit queue. This value is clamped + to be within the bounds defined by limit_max and limit_min. + +What: /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit_max +Date: November 2011 +KernelVersion: 3.3 +Contact: netdev@vger.kernel.org +Description: + Indicates the absolute maximum limit of bytes allowed to be + queued on this network device transmit queue. See + include/linux/dynamic_queue_limits.h for the default value. + +What: /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit_min +Date: November 2011 +KernelVersion: 3.3 +Contact: netdev@vger.kernel.org +Description: + Indicates the absolute minimum limit of bytes allowed to be + queued on this network device transmit queue. Default value is + 0. diff --git a/Documentation/ABI/testing/sysfs-class-net-statistics b/Documentation/ABI/testing/sysfs-class-net-statistics new file mode 100644 index 0000000..397118d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-net-statistics @@ -0,0 +1,201 @@ +What: /sys/class/<iface>/statistics/collisions +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of collisions seen by this network device. + This value might not be relevant with all MAC layers. + +What: /sys/class/<iface>/statistics/multicast +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of multicast packets received by this + network device. + +What: /sys/class/<iface>/statistics/rx_bytes +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of bytes received by this network device. + See the network driver for the exact meaning of when this + value is incremented. + +What: /sys/class/<iface>/statistics/rx_compressed +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of compressed packets received by this + network device. This value might only be relevant for interfaces + that support packet compression (e.g: PPP). + +What: /sys/class/<iface>/statistics/rx_crc_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets received with a CRC (FCS) error + by this network device. Note that the specific meaning might + depend on the MAC layer used by the interface. + +What: /sys/class/<iface>/statistics/rx_dropped +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets received by the network device + but dropped, that are not forwarded to the upper layers for + packet processing. See the network driver for the exact + meaning of this value. + +What: /sys/class/<iface>/statistics/rx_fifo_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of receive FIFO errors seen by this + network device. See the network driver for the exact + meaning of this value. + +What: /sys/class/<iface>/statistics/rx_frame_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of received frames with error, such as + alignment errors. Note that the specific meaning depends on + on the MAC layer protocol used. See the network driver for + the exact meaning of this value. + +What: /sys/class/<iface>/statistics/rx_length_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of received error packet with a length + error, oversized or undersized. See the network driver for the + exact meaning of this value. + +What: /sys/class/<iface>/statistics/rx_missed_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of received packets that have been missed + due to lack of capacity in the receive side. See the network + driver for the exact meaning of this value. + +What: /sys/class/<iface>/statistics/rx_over_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of received packets that are oversized + compared to what the network device is configured to accept + (e.g: larger than MTU). See the network driver for the exact + meaning of this value. + +What: /sys/class/<iface>/statistics/rx_packets +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the total number of good packets received by this + network device. + +What: /sys/class/<iface>/statistics/tx_aborted_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets that have been aborted + during transmission by a network device (e.g: because of + a medium collision). See the network driver for the exact + meaning of this value. + +What: /sys/class/<iface>/statistics/tx_bytes +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of bytes transmitted by a network + device. See the network driver for the exact meaning of this + value, in particular whether this accounts for all successfully + transmitted packets or all packets that have been queued for + transmission. + +What: /sys/class/<iface>/statistics/tx_carrier_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets that could not be transmitted + because of carrier errors (e.g: physical link down). See the + network driver for the exact meaning of this value. + +What: /sys/class/<iface>/statistics/tx_compressed +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of transmitted compressed packets. Note + this might only be relevant for devices that support + compression (e.g: PPP). + +What: /sys/class/<iface>/statistics/tx_dropped +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets dropped during transmission. + See the driver for the exact reasons as to why the packets were + dropped. + +What: /sys/class/<iface>/statistics/tx_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets in error during transmission by + a network device. See the driver for the exact reasons as to + why the packets were dropped. + +What: /sys/class/<iface>/statistics/tx_fifo_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets having caused a transmit + FIFO error. See the driver for the exact reasons as to why the + packets were dropped. + +What: /sys/class/<iface>/statistics/tx_heartbeat_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets transmitted that have been + reported as heartbeat errors. See the driver for the exact + reasons as to why the packets were dropped. + +What: /sys/class/<iface>/statistics/tx_packets +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets transmitted by a network + device. See the driver for whether this reports the number of all + attempted or successful transmissions. + +What: /sys/class/<iface>/statistics/tx_window_errors +Date: April 2005 +KernelVersion: 2.6.12 +Contact: netdev@vger.kernel.org +Description: + Indicates the number of packets not successfully transmitted + due to a window collision. The specific meaning depends on the + MAC layer used. On Ethernet this is usually used to report + late collisions errors. diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index f9fd615..1d27f0a 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -195,7 +195,7 @@ DVB_DOCUMENTED = \ # install_media_images = \ - $(Q)cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api + $(Q)-cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%.b64 $(Q)base64 -d $< >$@ diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt index cd5e239..6794cdc 100644 --- a/Documentation/devicetree/bindings/clock/at91-clock.txt +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt @@ -62,7 +62,7 @@ Required properties for PMC node: - interrupt-controller : tell that the PMC is an interrupt controller. - #interrupt-cells : must be set to 1. The first cell encodes the interrupt id, and reflect the bit position in the PMC_ER/DR/SR registers. - You can use the dt macros defined in dt-bindings/clk/at91.h. + You can use the dt macros defined in dt-bindings/clock/at91.h. 0 (AT91_PMC_MOSCS) -> main oscillator ready 1 (AT91_PMC_LOCKA) -> PLL A ready 2 (AT91_PMC_LOCKB) -> PLL B ready diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index 5992dce..02a25d9 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -43,7 +43,7 @@ Example clock-output-names = "tpu0", "mmcif1", "sdhi3", "sdhi2", "sdhi1", "sdhi0", "mmcif0"; - renesas,clock-indices = < + clock-indices = < R8A7790_CLK_TPU0 R8A7790_CLK_MMCIF1 R8A7790_CLK_SDHI3 R8A7790_CLK_SDHI2 R8A7790_CLK_SDHI1 R8A7790_CLK_SDHI0 R8A7790_CLK_MMCIF0 diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt index 9fbbdb7..68ff213 100644 --- a/Documentation/devicetree/bindings/dma/ti-edma.txt +++ b/Documentation/devicetree/bindings/dma/ti-edma.txt @@ -29,6 +29,6 @@ edma: edma@49000000 { dma-channels = <64>; ti,edma-regions = <4>; ti,edma-slots = <256>; - ti,edma-xbar-event-map = <1 12 - 2 13>; + ti,edma-xbar-event-map = /bits/ 16 <1 12 + 2 13>; }; diff --git a/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt b/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt index f2febb9..451fef2 100644 --- a/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt +++ b/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt @@ -24,7 +24,7 @@ Optional properties: - fixed-link: When the GENET interface is connected to a MoCA hardware block or when operating in a RGMII to RGMII type of connection, or when the MDIO bus is voluntarily disabled, this property should be used to describe the "fixed link". - See Documentation/devicetree/bindings/net/fsl-tsec-phy.txt for information on + See Documentation/devicetree/bindings/net/fixed-link.txt for information on the property specifics Required child nodes: diff --git a/Documentation/devicetree/bindings/net/broadcom-systemport.txt b/Documentation/devicetree/bindings/net/broadcom-systemport.txt index 1b7600e..c183ea9 100644 --- a/Documentation/devicetree/bindings/net/broadcom-systemport.txt +++ b/Documentation/devicetree/bindings/net/broadcom-systemport.txt @@ -8,7 +8,7 @@ Required properties: - local-mac-address: Ethernet MAC address (48 bits) of this adapter - phy-mode: Should be a string describing the PHY interface to the Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt -- fixed-link: see Documentation/devicetree/bindings/net/fsl-tsec-phy.txt for +- fixed-link: see Documentation/devicetree/bindings/net/fixed-link.txt for the property specific details Optional properties: diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt new file mode 100644 index 0000000..fe38847 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/xilinx_can.txt @@ -0,0 +1,44 @@ +Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings +--------------------------------------------------------- + +Required properties: +- compatible : Should be "xlnx,zynq-can-1.0" for Zynq CAN + controllers and "xlnx,axi-can-1.00.a" for Axi CAN + controllers. +- reg : Physical base address and size of the Axi CAN/Zynq + CANPS registers map. +- interrupts : Property with a value describing the interrupt + number. +- interrupt-parent : Must be core interrupt controller +- clock-names : List of input clock names - "can_clk", "pclk" + (For CANPS), "can_clk" , "s_axi_aclk"(For AXI CAN) + (See clock bindings for details). +- clocks : Clock phandles (see clock bindings for details). +- tx-fifo-depth : Can Tx fifo depth. +- rx-fifo-depth : Can Rx fifo depth. + + +Example: + +For Zynq CANPS Dts file: + zynq_can_0: can@e0008000 { + compatible = "xlnx,zynq-can-1.0"; + clocks = <&clkc 19>, <&clkc 36>; + clock-names = "can_clk", "pclk"; + reg = <0xe0008000 0x1000>; + interrupts = <0 28 4>; + interrupt-parent = <&intc>; + tx-fifo-depth = <0x40>; + rx-fifo-depth = <0x40>; + }; +For Axi CAN Dts file: + axi_can_0: axi-can@40000000 { + compatible = "xlnx,axi-can-1.00.a"; + clocks = <&clkc 0>, <&clkc 1>; + clock-names = "can_clk","s_axi_aclk" ; + reg = <0x40000000 0x10000>; + interrupt-parent = <&intc>; + interrupts = <0 59 1>; + tx-fifo-depth = <0x40>; + rx-fifo-depth = <0x40>; + }; diff --git a/Documentation/devicetree/bindings/net/fixed-link.txt b/Documentation/devicetree/bindings/net/fixed-link.txt new file mode 100644 index 0000000..82bf7e0 --- /dev/null +++ b/Documentation/devicetree/bindings/net/fixed-link.txt @@ -0,0 +1,42 @@ +Fixed link Device Tree binding +------------------------------ + +Some Ethernet MACs have a "fixed link", and are not connected to a +normal MDIO-managed PHY device. For those situations, a Device Tree +binding allows to describe a "fixed link". + +Such a fixed link situation is described by creating a 'fixed-link' +sub-node of the Ethernet MAC device node, with the following +properties: + +* 'speed' (integer, mandatory), to indicate the link speed. Accepted + values are 10, 100 and 1000 +* 'full-duplex' (boolean, optional), to indicate that full duplex is + used. When absent, half duplex is assumed. +* 'pause' (boolean, optional), to indicate that pause should be + enabled. +* 'asym-pause' (boolean, optional), to indicate that asym_pause should + be enabled. + +Old, deprecated 'fixed-link' binding: + +* A 'fixed-link' property in the Ethernet MAC node, with 5 cells, of the + form <a b c d e> with the following accepted values: + - a: emulated PHY ID, choose any but but unique to the all specified + fixed-links, from 0 to 31 + - b: duplex configuration: 0 for half duplex, 1 for full duplex + - c: link speed in Mbits/sec, accepted values are: 10, 100 and 1000 + - d: pause configuration: 0 for no pause, 1 for pause + - e: asymmetric pause configuration: 0 for no asymmetric pause, 1 for + asymmetric pause + +Example: + +ethernet@0 { + ... + fixed-link { + speed = <1000>; + full-duplex; + }; + ... +}; diff --git a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt index 737cdef..be6ea89 100644 --- a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt +++ b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt @@ -42,10 +42,7 @@ Properties: interrupt. For TSEC and eTSEC devices, the first interrupt is transmit, the second is receive, and the third is error. - phy-handle : See ethernet.txt file in the same directory. - - fixed-link : <a b c d e> where a is emulated phy id - choose any, - but unique to the all specified fixed-links, b is duplex - 0 half, - 1 full, c is link speed - d#10/d#100/d#1000, d is pause - 0 no - pause, 1 pause, e is asym_pause - 0 no asym_pause, 1 asym_pause. + - fixed-link : See fixed-link.txt in the same directory. - phy-connection-type : See ethernet.txt file in the same directory. This property is only really needed if the connection is of type "rgmii-id", as all other connection types are detected by hardware. diff --git a/Documentation/devicetree/bindings/net/micrel-ks8851.txt b/Documentation/devicetree/bindings/net/micrel-ks8851.txt index d54d0cc..bbdf9a7 100644 --- a/Documentation/devicetree/bindings/net/micrel-ks8851.txt +++ b/Documentation/devicetree/bindings/net/micrel-ks8851.txt @@ -1,9 +1,18 @@ -Micrel KS8851 Ethernet mac +Micrel KS8851 Ethernet mac (MLL) Required properties: -- compatible = "micrel,ks8851-ml" of parallel interface +- compatible = "micrel,ks8851-mll" of parallel interface - reg : 2 physical address and size of registers for data and command - interrupts : interrupt connection +Micrel KS8851 Ethernet mac (SPI) + +Required properties: +- compatible = "micrel,ks8851" or the deprecated "ks8851" +- reg : chip select number +- interrupts : interrupt connection + Optional properties: -- vdd-supply: supply for Ethernet mac +- vdd-supply: analog 3.3V supply for Ethernet mac +- vdd-io-supply: digital 1.8V IO supply for Ethernet mac +- reset-gpios: reset_n input pin diff --git a/Documentation/devicetree/bindings/net/nfc/pn544.txt b/Documentation/devicetree/bindings/net/nfc/pn544.txt new file mode 100644 index 0000000..dab69f3 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nfc/pn544.txt @@ -0,0 +1,35 @@ +* NXP Semiconductors PN544 NFC Controller + +Required properties: +- compatible: Should be "nxp,pn544-i2c". +- clock-frequency: I²C work frequency. +- reg: address on the bus +- interrupt-parent: phandle for the interrupt gpio controller +- interrupts: GPIO interrupt to which the chip is connected +- enable-gpios: Output GPIO pin used for enabling/disabling the PN544 +- firmware-gpios: Output GPIO pin used to enter firmware download mode + +Optional SoC Specific Properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. + +Example (for ARM-based BeagleBone with PN544 on I2C2): + +&i2c2 { + + status = "okay"; + + pn544: pn544@28 { + + compatible = "nxp,pn544-i2c"; + + reg = <0x28>; + clock-frequency = <400000>; + + interrupt-parent = <&gpio1>; + interrupts = <17 GPIO_ACTIVE_HIGH>; + + enable-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>; + firmware-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/Documentation/devicetree/bindings/net/nfc/st21nfca.txt b/Documentation/devicetree/bindings/net/nfc/st21nfca.txt new file mode 100644 index 0000000..e4faa2e --- /dev/null +++ b/Documentation/devicetree/bindings/net/nfc/st21nfca.txt @@ -0,0 +1,33 @@ +* STMicroelectronics SAS. ST21NFCA NFC Controller + +Required properties: +- compatible: Should be "st,st21nfca_i2c". +- clock-frequency: I²C work frequency. +- reg: address on the bus +- interrupt-parent: phandle for the interrupt gpio controller +- interrupts: GPIO interrupt to which the chip is connected +- enable-gpios: Output GPIO pin used for enabling/disabling the ST21NFCA + +Optional SoC Specific Properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. + +Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2): + +&i2c2 { + + status = "okay"; + + st21nfca: st21nfca@1 { + + compatible = "st,st21nfca_i2c"; + + reg = <0x01>; + clock-frequency = <400000>; + + interrupt-parent = <&gpio5>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + enable-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt index 8dd3ef7..1e43613 100644 --- a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt +++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt @@ -12,6 +12,7 @@ Required properties: Optional SoC Specific Properties: - pinctrl-names: Contains only one value - "default". - pintctrl-0: Specifies the pin control groups used for this controller. +- autosuspend-delay: Specify autosuspend delay in milliseconds. Example (for ARM-based BeagleBone with TRF7970A on SPI1): @@ -29,6 +30,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1): ti,enable-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>, <&gpio2 5 GPIO_ACTIVE_LOW>; vin-supply = <&ldo3_reg>; + autosuspend-delay = <30000>; status = "okay"; }; }; diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt index 5602eb7..e1ae127 100644 --- a/Documentation/input/elantech.txt +++ b/Documentation/input/elantech.txt @@ -504,9 +504,12 @@ byte 5: * reg_10 bit 7 6 5 4 3 2 1 0 - 0 0 0 0 0 0 0 A + 0 0 0 0 R F T A A: 1 = enable absolute tracking + T: 1 = enable two finger mode auto correct + F: 1 = disable ABS Position Filter + R: 1 = enable real hardware resolution 6.2 Native absolute mode 6 byte packet format ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4384217..30a8ad0d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2218,10 +2218,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. noreplace-smp [X86-32,SMP] Don't replace SMP instructions with UP alternatives - nordrand [X86] Disable the direct use of the RDRAND - instruction even if it is supported by the - processor. RDRAND is still available to user - space applications. + nordrand [X86] Disable kernel use of the RDRAND and + RDSEED instructions even if they are supported + by the processor. RDRAND and RDSEED are still + available to user space applications. noresume [SWSUSP] Disables resume and restores original swap space. diff --git a/Documentation/networking/can.txt b/Documentation/networking/can.txt index 2fa44cb..cdd381c 100644 --- a/Documentation/networking/can.txt +++ b/Documentation/networking/can.txt @@ -469,6 +469,41 @@ solution for a couple of reasons: having this 'send only' use-case we may remove the receive list in the Kernel to save a little (really a very little!) CPU usage. + 4.1.1.1 CAN filter usage optimisation + + The CAN filters are processed in per-device filter lists at CAN frame + reception time. To reduce the number of checks that need to be performed + while walking through the filter lists the CAN core provides an optimized + filter handling when the filter subscription focusses on a single CAN ID. + + For the possible 2048 SFF CAN identifiers the identifier is used as an index + to access the corresponding subscription list without any further checks. + For the 2^29 possible EFF CAN identifiers a 10 bit XOR folding is used as + hash function to retrieve the EFF table index. + + To benefit from the optimized filters for single CAN identifiers the + CAN_SFF_MASK or CAN_EFF_MASK have to be set into can_filter.mask together + with set CAN_EFF_FLAG and CAN_RTR_FLAG bits. A set CAN_EFF_FLAG bit in the + can_filter.mask makes clear that it matters whether a SFF or EFF CAN ID is + subscribed. E.g. in the example from above + + rfilter[0].can_id = 0x123; + rfilter[0].can_mask = CAN_SFF_MASK; + + both SFF frames with CAN ID 0x123 and EFF frames with 0xXXXXX123 can pass. + + To filter for only 0x123 (SFF) and 0x12345678 (EFF) CAN identifiers the + filter has to be defined in this way to benefit from the optimized filters: + + struct can_filter rfilter[2]; + + rfilter[0].can_id = 0x123; + rfilter[0].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_SFF_MASK); + rfilter[1].can_id = 0x12345678 | CAN_EFF_FLAG; + rfilter[1].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_EFF_MASK); + + setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); + 4.1.2 RAW socket option CAN_RAW_ERR_FILTER As described in chapter 3.4 the CAN interface driver can generate so diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index 748fd38..58c4439 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -277,7 +277,7 @@ Possible BPF extensions are shown in the following table: mark skb->mark queue skb->queue_mapping hatype skb->dev->type - rxhash skb->rxhash + rxhash skb->hash cpu raw_smp_processor_id() vlan_tci vlan_tx_tag_get(skb) vlan_pr vlan_tx_tag_present(skb) @@ -833,6 +833,20 @@ loops and other CFG validation; second step starts from the first insn and descends all possible paths. It simulates execution of every insn and observes the state change of registers and stack. +Testing +------- + +Next to the BPF toolchain, the kernel also ships a test module that contains +various test cases for classic and internal BPF that can be executed against +the BPF interpreter and JIT compiler. It can be found in lib/test_bpf.c and +enabled via Kconfig: + + CONFIG_TEST_BPF=m + +After the module has been built and installed, the test suite can be executed +via insmod or modprobe against 'test_bpf' module. Results of the test cases +including timings in nsec can be found in the kernel log (dmesg). + Misc ---- diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 6fea79e..38112d5 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -578,7 +578,7 @@ processes. This also works in combination with mmap(2) on packet sockets. Currently implemented fanout policies are: - - PACKET_FANOUT_HASH: schedule to socket by skb's rxhash + - PACKET_FANOUT_HASH: schedule to socket by skb's packet hash - PACKET_FANOUT_LB: schedule to socket by round-robin - PACKET_FANOUT_CPU: schedule to socket by CPU packet arrives on - PACKET_FANOUT_RND: schedule to socket by random selection diff --git a/MAINTAINERS b/MAINTAINERS index bde15ff..dd33abf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -537,7 +537,7 @@ L: linux-alpha@vger.kernel.org F: arch/alpha/ ALTERA TRIPLE SPEED ETHERNET DRIVER -M: Vince Bridgers <vbridgers2013@gmail.com +M: Vince Bridgers <vbridgers2013@gmail.com> L: netdev@vger.kernel.org L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers) S: Maintained @@ -1888,19 +1888,20 @@ F: drivers/net/ethernet/broadcom/bnx2.* F: drivers/net/ethernet/broadcom/bnx2_* BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER -M: Ariel Elior <ariele@broadcom.com> +M: Ariel Elior <ariel.elior@qlogic.com> L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/broadcom/bnx2x/ -BROADCOM BCM281XX/BCM11XXX ARM ARCHITECTURE +BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITECTURE M: Christian Daudt <bcm@fixthebug.org> M: Matt Porter <mporter@linaro.org> L: bcm-kernel-feedback-list@broadcom.com -T: git git://git.github.com/broadcom/bcm11351 +T: git git://github.com/broadcom/mach-bcm S: Maintained F: arch/arm/mach-bcm/ F: arch/arm/boot/dts/bcm113* +F: arch/arm/boot/dts/bcm216* F: arch/arm/boot/dts/bcm281* F: arch/arm/configs/bcm_defconfig F: drivers/mmc/host/sdhci_bcm_kona.c @@ -2228,9 +2229,8 @@ F: drivers/platform/chrome/ CISCO VIC ETHERNET NIC DRIVER M: Christian Benvenuti <benve@cisco.com> M: Sujith Sankar <ssujith@cisco.com> -M: Govindarajulu Varadarajan <govindarajulu90@gmail.com> +M: Govindarajulu Varadarajan <_govind@gmx.com> M: Neel Patel <neepatel@cisco.com> -M: Nishank Trivedi <nistrive@cisco.com> S: Supported F: drivers/net/ethernet/cisco/enic/ @@ -2251,12 +2251,6 @@ L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/host/ohci-ep93xx.c -CIRRUS LOGIC CS4270 SOUND DRIVER -M: Timur Tabi <timur@tabi.org> -L: alsa-devel@alsa-project.org (moderated for non-subscribers) -S: Odd Fixes -F: sound/soc/codecs/cs4270* - CIRRUS LOGIC AUDIO CODEC DRIVERS M: Brian Austin <brian.austin@cirrus.com> M: Paul Handrigan <Paul.Handrigan@cirrus.com> @@ -4824,6 +4818,14 @@ L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core F: kernel/irq/ + +IRQCHIP DRIVERS +M: Thomas Gleixner <tglx@linutronix.de> +M: Jason Cooper <jason@lakedaemon.net> +L: linux-kernel@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core +T: git git://git.infradead.org/users/jcooper/linux.git irqchip/core F: drivers/irqchip/ IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) @@ -5496,15 +5498,15 @@ F: Documentation/hwmon/ltc4261 F: drivers/hwmon/ltc4261.c LTP (Linux Test Project) -M: Shubham Goyal <shubham@linux.vnet.ibm.com> M: Mike Frysinger <vapier@gentoo.org> M: Cyril Hrubis <chrubis@suse.cz> -M: Caspar Zhang <caspar@casparzhang.com> M: Wanlong Gao <gaowanlong@cn.fujitsu.com> +M: Jan Stancek <jstancek@redhat.com> +M: Stanislav Kholmanskikh <stanislav.kholmanskikh@oracle.com> +M: Alexey Kodanev <alexey.kodanev@oracle.com> L: ltp-list@lists.sourceforge.net (subscribers-only) -W: http://ltp.sourceforge.net/ +W: http://linux-test-project.github.io/ T: git git://github.com/linux-test-project/ltp.git -T: git git://ltp.git.sourceforge.net/gitroot/ltp/ltp-dev S: Maintained M32R ARCHITECTURE @@ -6517,10 +6519,10 @@ T: git git://openrisc.net/~jonas/linux F: arch/openrisc/ OPENVSWITCH -M: Jesse Gross <jesse@nicira.com> +M: Pravin Shelar <pshelar@nicira.com> L: dev@openvswitch.org W: http://openvswitch.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/pshelar/openvswitch.git S: Maintained F: net/openvswitch/ @@ -9113,6 +9115,9 @@ F: arch/um/os-Linux/drivers/ TURBOCHANNEL SUBSYSTEM M: "Maciej W. Rozycki" <macro@linux-mips.org> +M: Ralf Baechle <ralf@linux-mips.org> +L: linux-mips@linux-mips.org +Q: http://patchwork.linux-mips.org/project/linux-mips/list/ S: Maintained F: drivers/tc/ F: include/linux/tc.h @@ -9966,7 +9971,7 @@ F: drivers/net/hamradio/*scc.c F: drivers/net/hamradio/z8530.h ZBUD COMPRESSED PAGE ALLOCATOR -M: Seth Jennings <sjenning@linux.vnet.ibm.com> +M: Seth Jennings <sjennings@variantweb.net> L: linux-mm@kvack.org S: Maintained F: mm/zbud.c @@ -10011,7 +10016,7 @@ F: mm/zsmalloc.c F: include/linux/zsmalloc.h ZSWAP COMPRESSED SWAP CACHING -M: Seth Jennings <sjenning@linux.vnet.ibm.com> +M: Seth Jennings <sjennings@variantweb.net> L: linux-mm@kvack.org S: Maintained F: mm/zswap.c @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 15 SUBLEVEL = 0 -EXTRAVERSION = -rc4 +EXTRAVERSION = -rc6 NAME = Shuffling Zombie Juror # *DOCUMENTATION* diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index baf56cc..f1eea4a 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -144,7 +144,7 @@ compatible = "ti,edma3"; ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2"; reg = <0x49000000 0x10000>, - <0x44e10f90 0x10>; + <0x44e10f90 0x40>; interrupts = <12 13 14>; #dma-cells = <1>; dma-channels = <64>; diff --git a/arch/arm/boot/dts/am3517.dtsi b/arch/arm/boot/dts/am3517.dtsi index 788391f..5a452fd 100644 --- a/arch/arm/boot/dts/am3517.dtsi +++ b/arch/arm/boot/dts/am3517.dtsi @@ -62,5 +62,21 @@ }; }; +&iva { + status = "disabled"; +}; + +&mailbox { + status = "disabled"; +}; + +&mmu_isp { + status = "disabled"; +}; + +&smartreflex_mpu_iva { + status = "disabled"; +}; + /include/ "am35xx-clocks.dtsi" /include/ "omap36xx-am35xx-omap3430es2plus-clocks.dtsi" diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts index df8798e..a055f7f 100644 --- a/arch/arm/boot/dts/am437x-gp-evm.dts +++ b/arch/arm/boot/dts/am437x-gp-evm.dts @@ -117,6 +117,11 @@ status = "okay"; }; +&gpio5 { + status = "okay"; + ti,no-reset-on-init; +}; + &mmc1 { status = "okay"; vmmc-supply = <&vmmcsd_fixed>; diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts index 82f238a..3383c4b 100644 --- a/arch/arm/boot/dts/armada-370-db.dts +++ b/arch/arm/boot/dts/armada-370-db.dts @@ -67,6 +67,7 @@ i2c@11000 { pinctrl-0 = <&i2c0_pins>; pinctrl-names = "default"; + clock-frequency = <100000>; status = "okay"; audio_codec: audio-codec@4a { compatible = "cirrus,cs42l51"; diff --git a/arch/arm/boot/dts/armada-375-db.dts b/arch/arm/boot/dts/armada-375-db.dts index 9378d31..0451124 100644 --- a/arch/arm/boot/dts/armada-375-db.dts +++ b/arch/arm/boot/dts/armada-375-db.dts @@ -79,6 +79,11 @@ }; }; + sata@a0000 { + status = "okay"; + nr-ports = <2>; + }; + nand: nand@d0000 { pinctrl-0 = <&nand_pins>; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/armada-xp-db.dts b/arch/arm/boot/dts/armada-xp-db.dts index 448373c..90f0bf6 100644 --- a/arch/arm/boot/dts/armada-xp-db.dts +++ b/arch/arm/boot/dts/armada-xp-db.dts @@ -49,7 +49,7 @@ /* Device Bus parameters are required */ /* Read parameters */ - devbus,bus-width = <8>; + devbus,bus-width = <16>; devbus,turn-off-ps = <60000>; devbus,badr-skew-ps = <0>; devbus,acc-first-ps = <124000>; diff --git a/arch/arm/boot/dts/armada-xp-gp.dts b/arch/arm/boot/dts/armada-xp-gp.dts index 61bda68..0c75642 100644 --- a/arch/arm/boot/dts/armada-xp-gp.dts +++ b/arch/arm/boot/dts/armada-xp-gp.dts @@ -59,7 +59,7 @@ /* Device Bus parameters are required */ /* Read parameters */ - devbus,bus-width = <8>; + devbus,bus-width = <16>; devbus,turn-off-ps = <60000>; devbus,badr-skew-ps = <0>; devbus,acc-first-ps = <124000>; @@ -146,22 +146,22 @@ ethernet@70000 { status = "okay"; phy = <&phy0>; - phy-mode = "rgmii-id"; + phy-mode = "qsgmii"; }; ethernet@74000 { status = "okay"; phy = <&phy1>; - phy-mode = "rgmii-id"; + phy-mode = "qsgmii"; }; ethernet@30000 { status = "okay"; phy = <&phy2>; - phy-mode = "rgmii-id"; + phy-mode = "qsgmii"; }; ethernet@34000 { status = "okay"; phy = <&phy3>; - phy-mode = "rgmii-id"; + phy-mode = "qsgmii"; }; /* Front-side USB slot */ diff --git a/arch/arm/boot/dts/armada-xp-matrix.dts b/arch/arm/boot/dts/armada-xp-matrix.dts index c224274..3bb8c00 100644 --- a/arch/arm/boot/dts/armada-xp-matrix.dts +++ b/arch/arm/boot/dts/armada-xp-matrix.dts @@ -61,6 +61,10 @@ ethernet@30000 { status = "okay"; phy-mode = "sgmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; }; pcie-controller { diff --git a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts index 985948c..5d42feb 100644 --- a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts +++ b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts @@ -39,7 +39,7 @@ /* Device Bus parameters are required */ /* Read parameters */ - devbus,bus-width = <8>; + devbus,bus-width = <16>; devbus,turn-off-ps = <60000>; devbus,badr-skew-ps = <0>; devbus,acc-first-ps = <124000>; diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts index ce13755..4537259 100644 --- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts @@ -34,7 +34,7 @@ }; spi0: spi@f0004000 { - cs-gpios = <&pioD 13 0>; + cs-gpios = <&pioD 13 0>, <0>, <0>, <&pioD 16 0>; status = "okay"; }; @@ -79,7 +79,7 @@ }; spi1: spi@f8008000 { - cs-gpios = <&pioC 25 0>, <0>, <0>, <&pioD 16 0>; + cs-gpios = <&pioC 25 0>; status = "okay"; }; diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi index e21dda0e..3be973e 100644 --- a/arch/arm/boot/dts/at91sam9261.dtsi +++ b/arch/arm/boot/dts/at91sam9261.dtsi @@ -10,7 +10,7 @@ #include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/gpio/gpio.h> -#include <dt-bindings/clk/at91.h> +#include <dt-bindings/clock/at91.h> / { model = "Atmel AT91SAM9261 family SoC"; diff --git a/arch/arm/boot/dts/at91sam9rl.dtsi b/arch/arm/boot/dts/at91sam9rl.dtsi index 63e1784..92a52fa 100644 --- a/arch/arm/boot/dts/at91sam9rl.dtsi +++ b/arch/arm/boot/dts/at91sam9rl.dtsi @@ -8,7 +8,7 @@ #include "skeleton.dtsi" #include <dt-bindings/pinctrl/at91.h> -#include <dt-bindings/clk/at91.h> +#include <dt-bindings/clock/at91.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/gpio/gpio.h> diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts index 7c8c129..a3431d7 100644 --- a/arch/arm/boot/dts/imx53-mba53.dts +++ b/arch/arm/boot/dts/imx53-mba53.dts @@ -244,7 +244,7 @@ &tve { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_vga_sync_1>; - i2c-ddc-bus = <&i2c3>; + ddc-i2c-bus = <&i2c3>; fsl,tve-mode = "vga"; fsl,hsync-pin = <4>; fsl,vsync-pin = <6>; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 9c2bff2..6a1bf4f 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -115,7 +115,7 @@ #address-cells = <1>; #size-cells = <0>; compatible = "fsl,imx53-ipu"; - reg = <0x18000000 0x080000000>; + reg = <0x18000000 0x08000000>; interrupts = <11 10>; clocks = <&clks IMX5_CLK_IPU_GATE>, <&clks IMX5_CLK_IPU_DI0_GATE>, diff --git a/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts b/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts index 32c6fb4..b939f4f5 100644 --- a/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts +++ b/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts @@ -30,6 +30,16 @@ bootargs = "console=ttyS0,115200n8 earlyprintk"; }; + mbus { + pcie-controller { + status = "okay"; + + pcie@1,0 { + status = "okay"; + }; + }; + }; + ocp@f1000000 { pinctrl@10000 { pmx_usb_led: pmx-usb-led { @@ -73,14 +83,6 @@ ehci@50000 { status = "okay"; }; - - pcie-controller { - status = "okay"; - - pcie@1,0 { - status = "okay"; - }; - }; }; gpio-leds { diff --git a/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi b/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi index aa78c2d..e2cc85c 100644 --- a/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi +++ b/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi @@ -4,6 +4,16 @@ / { model = "ZyXEL NSA310"; + mbus { + pcie-controller { + status = "okay"; + + pcie@1,0 { + status = "okay"; + }; + }; + }; + ocp@f1000000 { pinctrl: pinctrl@10000 { @@ -26,14 +36,6 @@ status = "okay"; nr-ports = <2>; }; - - pcie-controller { - status = "okay"; - - pcie@1,0 { - status = "okay"; - }; - }; }; gpio_poweroff { diff --git a/arch/arm/boot/dts/kirkwood-t5325.dts b/arch/arm/boot/dts/kirkwood-t5325.dts index 7d1c767..0bd70d9 100644 --- a/arch/arm/boot/dts/kirkwood-t5325.dts +++ b/arch/arm/boot/dts/kirkwood-t5325.dts @@ -127,11 +127,6 @@ i2c@11000 { status = "okay"; - - alc5621: alc5621@1a { - compatible = "realtek,alc5621"; - reg = <0x1a>; - }; }; serial@12000 { diff --git a/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi b/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi index f577b7d..521c587 100644 --- a/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi +++ b/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi @@ -24,11 +24,10 @@ compatible = "smsc,lan9221", "smsc,lan9115"; bank-width = <2>; gpmc,mux-add-data; - gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <186>; - gpmc,cs-wr-off-ns = <186>; - gpmc,adv-on-ns = <12>; - gpmc,adv-rd-off-ns = <48>; + gpmc,cs-on-ns = <1>; + gpmc,cs-rd-off-ns = <180>; + gpmc,cs-wr-off-ns = <180>; + gpmc,adv-rd-off-ns = <18>; gpmc,adv-wr-off-ns = <48>; gpmc,oe-on-ns = <54>; gpmc,oe-off-ns = <168>; @@ -36,12 +35,10 @@ gpmc,we-off-ns = <168>; gpmc,rd-cycle-ns = <186>; gpmc,wr-cycle-ns = <186>; - gpmc,access-ns = <114>; - gpmc,page-burst-access-ns = <6>; - gpmc,bus-turnaround-ns = <12>; - gpmc,cycle2cycle-delay-ns = <18>; - gpmc,wr-data-mux-bus-ns = <90>; - gpmc,wr-access-ns = <186>; + gpmc,access-ns = <144>; + gpmc,page-burst-access-ns = <24>; + gpmc,bus-turnaround-ns = <90>; + gpmc,cycle2cycle-delay-ns = <90>; gpmc,cycle2cycle-samecsen; gpmc,cycle2cycle-diffcsen; vddvario-supply = <&vddvario>; diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi index 22f35ea..8f8c07d 100644 --- a/arch/arm/boot/dts/omap2.dtsi +++ b/arch/arm/boot/dts/omap2.dtsi @@ -71,13 +71,6 @@ interrupts = <58>; }; - mailbox: mailbox@48094000 { - compatible = "ti,omap2-mailbox"; - ti,hwmods = "mailbox"; - reg = <0x48094000 0x200>; - interrupts = <26>; - }; - intc: interrupt-controller@1 { compatible = "ti,omap2-intc"; interrupt-controller; diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi index 85b1fb0..2d99798 100644 --- a/arch/arm/boot/dts/omap2420.dtsi +++ b/arch/arm/boot/dts/omap2420.dtsi @@ -125,6 +125,14 @@ dma-names = "tx", "rx"; }; + mailbox: mailbox@48094000 { + compatible = "ti,omap2-mailbox"; + reg = <0x48094000 0x200>; + interrupts = <26>, <34>; + interrupt-names = "dsp", "iva"; + ti,hwmods = "mailbox"; + }; + timer1: timer@48028000 { compatible = "ti,omap2420-timer"; reg = <0x48028000 0x400>; diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index d09697d..42d2c61 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -216,6 +216,13 @@ dma-names = "tx", "rx"; }; + mailbox: mailbox@48094000 { + compatible = "ti,omap2-mailbox"; + reg = <0x48094000 0x200>; + interrupts = <26>; + ti,hwmods = "mailbox"; + }; + timer1: timer@49018000 { compatible = "ti,omap2420-timer"; reg = <0x49018000 0x400>; diff --git a/arch/arm/boot/dts/omap3-cm-t3x30.dtsi b/arch/arm/boot/dts/omap3-cm-t3x30.dtsi index d000558..25ba083 100644 --- a/arch/arm/boot/dts/omap3-cm-t3x30.dtsi +++ b/arch/arm/boot/dts/omap3-cm-t3x30.dtsi @@ -10,18 +10,6 @@ cpu0-supply = <&vcc>; }; }; - - vddvario: regulator-vddvario { - compatible = "regulator-fixed"; - regulator-name = "vddvario"; - regulator-always-on; - }; - - vdd33a: regulator-vdd33a { - compatible = "regulator-fixed"; - regulator-name = "vdd33a"; - regulator-always-on; - }; }; &omap3_pmx_core { @@ -35,58 +23,34 @@ hsusb0_pins: pinmux_hsusb0_pins { pinctrl-single,pins = < - OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* hsusb0_clk.hsusb0_clk */ - OMAP3_CORE1_IOPAD(0x21a2, PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */ - OMAP3_CORE1_IOPAD(0x21a4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_dir.hsusb0_dir */ - OMAP3_CORE1_IOPAD(0x21a6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_nxt.hsusb0_nxt */ - OMAP3_CORE1_IOPAD(0x21a8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data0.hsusb2_data0 */ - OMAP3_CORE1_IOPAD(0x21aa, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data1.hsusb0_data1 */ - OMAP3_CORE1_IOPAD(0x21ac, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data2.hsusb0_data2 */ - OMAP3_CORE1_IOPAD(0x21ae, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data3 */ - OMAP3_CORE1_IOPAD(0x21b0, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data4 */ - OMAP3_CORE1_IOPAD(0x21b2, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data5 */ - OMAP3_CORE1_IOPAD(0x21b4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data6 */ - OMAP3_CORE1_IOPAD(0x21b6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */ + OMAP3_CORE1_IOPAD(0x21a2, PIN_OUTPUT | MUX_MODE0) /* hsusb0_clk.hsusb0_clk */ + OMAP3_CORE1_IOPAD(0x21a4, PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */ + OMAP3_CORE1_IOPAD(0x21a6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_dir.hsusb0_dir */ + OMAP3_CORE1_IOPAD(0x21a8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_nxt.hsusb0_nxt */ + OMAP3_CORE1_IOPAD(0x21aa, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data0.hsusb2_data0 */ + OMAP3_CORE1_IOPAD(0x21ac, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data1.hsusb0_data1 */ + OMAP3_CORE1_IOPAD(0x21ae, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data2.hsusb0_data2 */ + OMAP3_CORE1_IOPAD(0x21b0, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data3 */ + OMAP3_CORE1_IOPAD(0x21b2, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data4 */ + OMAP3_CORE1_IOPAD(0x21b4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data5 */ + OMAP3_CORE1_IOPAD(0x21b6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data6 */ + OMAP3_CORE1_IOPAD(0x21b8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */ >; }; }; +#include "omap-gpmc-smsc911x.dtsi" + &gpmc { ranges = <5 0 0x2c000000 0x01000000>; - smsc1: ethernet@5,0 { + smsc1: ethernet@gpmc { compatible = "smsc,lan9221", "smsc,lan9115"; pinctrl-names = "default"; pinctrl-0 = <&smsc1_pins>; interrupt-parent = <&gpio6>; interrupts = <3 IRQ_TYPE_LEVEL_LOW>; reg = <5 0 0xff>; - bank-width = <2>; - gpmc,mux-add-data; - gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <186>; - gpmc,cs-wr-off-ns = <186>; - gpmc,adv-on-ns = <12>; - gpmc,adv-rd-off-ns = <48>; - gpmc,adv-wr-off-ns = <48>; - gpmc,oe-on-ns = <54>; - gpmc,oe-off-ns = <168>; - gpmc,we-on-ns = <54>; - gpmc,we-off-ns = <168>; - gpmc,rd-cycle-ns = <186>; - gpmc,wr-cycle-ns = <186>; - gpmc,access-ns = <114>; - gpmc,page-burst-access-ns = <6>; - gpmc,bus-turnaround-ns = <12>; - gpmc,cycle2cycle-delay-ns = <18>; - gpmc,wr-data-mux-bus-ns = <90>; - gpmc,wr-access-ns = <186>; - gpmc,cycle2cycle-samecsen; - gpmc,cycle2cycle-diffcsen; - vddvario-supply = <&vddvario>; - vdd33a-supply = <&vdd33a>; - reg-io-width = <4>; - smsc,save-mac-address; }; }; diff --git a/arch/arm/boot/dts/omap3-igep.dtsi b/arch/arm/boot/dts/omap3-igep.dtsi index b97736d..e2d163b 100644 --- a/arch/arm/boot/dts/omap3-igep.dtsi +++ b/arch/arm/boot/dts/omap3-igep.dtsi @@ -107,7 +107,7 @@ >; }; - smsc911x_pins: pinmux_smsc911x_pins { + smsc9221_pins: pinmux_smsc9221_pins { pinctrl-single,pins = < 0x1a2 (PIN_INPUT | MUX_MODE4) /* mcspi1_cs2.gpio_176 */ >; diff --git a/arch/arm/boot/dts/omap3-igep0020.dts b/arch/arm/boot/dts/omap3-igep0020.dts index 7abd64f..b22caaa 100644 --- a/arch/arm/boot/dts/omap3-igep0020.dts +++ b/arch/arm/boot/dts/omap3-igep0020.dts @@ -10,7 +10,7 @@ */ #include "omap3-igep.dtsi" -#include "omap-gpmc-smsc911x.dtsi" +#include "omap-gpmc-smsc9221.dtsi" / { model = "IGEPv2 (TI OMAP AM/DM37x)"; @@ -248,7 +248,7 @@ ethernet@gpmc { pinctrl-names = "default"; - pinctrl-0 = <&smsc911x_pins>; + pinctrl-0 = <&smsc9221_pins>; reg = <5 0 0xff>; interrupt-parent = <&gpio6>; interrupts = <16 IRQ_TYPE_LEVEL_LOW>; diff --git a/arch/arm/boot/dts/omap3-sb-t35.dtsi b/arch/arm/boot/dts/omap3-sb-t35.dtsi index 7909c51..d59e3de 100644 --- a/arch/arm/boot/dts/omap3-sb-t35.dtsi +++ b/arch/arm/boot/dts/omap3-sb-t35.dtsi @@ -2,20 +2,6 @@ * Common support for CompuLab SB-T35 used on SBC-T3530, SBC-T3517 and SBC-T3730 */ -/ { - vddvario_sb_t35: regulator-vddvario-sb-t35 { - compatible = "regulator-fixed"; - regulator-name = "vddvario"; - regulator-always-on; - }; - - vdd33a_sb_t35: regulator-vdd33a-sb-t35 { - compatible = "regulator-fixed"; - regulator-name = "vdd33a"; - regulator-always-on; - }; -}; - &omap3_pmx_core { smsc2_pins: pinmux_smsc2_pins { pinctrl-single,pins = < @@ -37,11 +23,10 @@ reg = <4 0 0xff>; bank-width = <2>; gpmc,mux-add-data; - gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <186>; - gpmc,cs-wr-off-ns = <186>; - gpmc,adv-on-ns = <12>; - gpmc,adv-rd-off-ns = <48>; + gpmc,cs-on-ns = <1>; + gpmc,cs-rd-off-ns = <180>; + gpmc,cs-wr-off-ns = <180>; + gpmc,adv-rd-off-ns = <18>; gpmc,adv-wr-off-ns = <48>; gpmc,oe-on-ns = <54>; gpmc,oe-off-ns = <168>; @@ -49,16 +34,14 @@ gpmc,we-off-ns = <168>; gpmc,rd-cycle-ns = <186>; gpmc,wr-cycle-ns = <186>; - gpmc,access-ns = <114>; - gpmc,page-burst-access-ns = <6>; - gpmc,bus-turnaround-ns = <12>; - gpmc,cycle2cycle-delay-ns = <18>; - gpmc,wr-data-mux-bus-ns = <90>; - gpmc,wr-access-ns = <186>; + gpmc,access-ns = <144>; + gpmc,page-burst-access-ns = <24>; + gpmc,bus-turnaround-ns = <90>; + gpmc,cycle2cycle-delay-ns = <90>; gpmc,cycle2cycle-samecsen; gpmc,cycle2cycle-diffcsen; - vddvario-supply = <&vddvario_sb_t35>; - vdd33a-supply = <&vdd33a_sb_t35>; + vddvario-supply = <&vddvario>; + vdd33a-supply = <&vdd33a>; reg-io-width = <4>; smsc,save-mac-address; }; diff --git a/arch/arm/boot/dts/omap3-sbc-t3517.dts b/arch/arm/boot/dts/omap3-sbc-t3517.dts index 024c9c6..42189b6 100644 --- a/arch/arm/boot/dts/omap3-sbc-t3517.dts +++ b/arch/arm/boot/dts/omap3-sbc-t3517.dts @@ -8,6 +8,19 @@ / { model = "CompuLab SBC-T3517 with CM-T3517"; compatible = "compulab,omap3-sbc-t3517", "compulab,omap3-cm-t3517", "ti,am3517", "ti,omap3"; + + /* Only one GPMC smsc9220 on SBC-T3517, CM-T3517 uses am35x Ethernet */ + vddvario: regulator-vddvario-sb-t35 { + compatible = "regulator-fixed"; + regulator-name = "vddvario"; + regulator-always-on; + }; + + vdd33a: regulator-vdd33a-sb-t35 { + compatible = "regulator-fixed"; + regulator-name = "vdd33a"; + regulator-always-on; + }; }; &omap3_pmx_core { diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index acb9019..4231191 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -61,7 +61,7 @@ ti,hwmods = "mpu"; }; - iva { + iva: iva { compatible = "ti,iva2.2"; ti,hwmods = "iva"; diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index f8c9855..36b4312 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -630,6 +630,13 @@ status = "disabled"; }; + mailbox: mailbox@4a0f4000 { + compatible = "ti,omap4-mailbox"; + reg = <0x4a0f4000 0x200>; + interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; + ti,hwmods = "mailbox"; + }; + timer1: timer@4ae18000 { compatible = "ti,omap5430-timer"; reg = <0x4ae18000 0x80>; diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi index eabcfdb..a106b08 100644 --- a/arch/arm/boot/dts/sama5d3.dtsi +++ b/arch/arm/boot/dts/sama5d3.dtsi @@ -13,7 +13,7 @@ #include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/gpio/gpio.h> -#include <dt-bindings/clk/at91.h> +#include <dt-bindings/clock/at91.h> / { model = "Atmel SAMA5D3 family SoC"; diff --git a/arch/arm/boot/dts/sama5d3_mci2.dtsi b/arch/arm/boot/dts/sama5d3_mci2.dtsi index b029fe7..1b02208 100644 --- a/arch/arm/boot/dts/sama5d3_mci2.dtsi +++ b/arch/arm/boot/dts/sama5d3_mci2.dtsi @@ -9,7 +9,7 @@ #include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/interrupt-controller/irq.h> -#include <dt-bindings/clk/at91.h> +#include <dt-bindings/clock/at91.h> / { ahb { diff --git a/arch/arm/boot/dts/sama5d3_tcb1.dtsi b/arch/arm/boot/dts/sama5d3_tcb1.dtsi index 382b044..0284845 100644 --- a/arch/arm/boot/dts/sama5d3_tcb1.dtsi +++ b/arch/arm/boot/dts/sama5d3_tcb1.dtsi @@ -9,7 +9,7 @@ #include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/interrupt-controller/irq.h> -#include <dt-bindings/clk/at91.h> +#include <dt-bindings/clock/at91.h> / { aliases { diff --git a/arch/arm/boot/dts/sama5d3_uart.dtsi b/arch/arm/boot/dts/sama5d3_uart.dtsi index a9fa75e..7a8d4c6 100644 --- a/arch/arm/boot/dts/sama5d3_uart.dtsi +++ b/arch/arm/boot/dts/sama5d3_uart.dtsi @@ -9,7 +9,7 @@ #include <dt-bindings/pinctrl/at91.h> #include <dt-bindings/interrupt-controller/irq.h> -#include <dt-bindings/clk/at91.h> +#include <dt-bindings/clock/at91.h> / { aliases { diff --git a/arch/arm/boot/dts/ste-ccu8540.dts b/arch/arm/boot/dts/ste-ccu8540.dts index 7f3baf5..32dd55e 100644 --- a/arch/arm/boot/dts/ste-ccu8540.dts +++ b/arch/arm/boot/dts/ste-ccu8540.dts @@ -18,6 +18,7 @@ compatible = "st-ericsson,ccu8540", "st-ericsson,u8540"; memory@0 { + device_type = "memory"; reg = <0x20000000 0x1f000000>, <0xc0000000 0x3f000000>; }; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 32efc10..aba1c8a 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -87,7 +87,7 @@ pll4: clk@01c20018 { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-pll1-clk"; + compatible = "allwinner,sun7i-a20-pll4-clk"; reg = <0x01c20018 0x4>; clocks = <&osc24M>; clock-output-names = "pll4"; @@ -109,6 +109,14 @@ clock-output-names = "pll6_sata", "pll6_other", "pll6"; }; + pll8: clk@01c20040 { + #clock-cells = <0>; + compatible = "allwinner,sun7i-a20-pll4-clk"; + reg = <0x01c20040 0x4>; + clocks = <&osc24M>; + clock-output-names = "pll8"; + }; + cpu: cpu@01c20054 { #clock-cells = <0>; compatible = "allwinner,sun4i-a10-cpu-clk"; @@ -805,9 +813,9 @@ status = "disabled"; }; - i2c4: i2c@01c2bc00 { + i2c4: i2c@01c2c000 { compatible = "allwinner,sun4i-i2c"; - reg = <0x01c2bc00 0x400>; + reg = <0x01c2c000 0x400>; interrupts = <0 89 4>; clocks = <&apb1_gates 15>; clock-frequency = <100000>; diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 41bca32..5339009 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1423,55 +1423,38 @@ EXPORT_SYMBOL(edma_clear_event); #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) -static int edma_of_read_u32_to_s16_array(const struct device_node *np, - const char *propname, s16 *out_values, - size_t sz) +static int edma_xbar_event_map(struct device *dev, struct device_node *node, + struct edma_soc_info *pdata, size_t sz) { - int ret; - - ret = of_property_read_u16_array(np, propname, out_values, sz); - if (ret) - return ret; - - /* Terminate it */ - *out_values++ = -1; - *out_values++ = -1; - - return 0; -} - -static int edma_xbar_event_map(struct device *dev, - struct device_node *node, - struct edma_soc_info *pdata, int len) -{ - int ret, i; + const char pname[] = "ti,edma-xbar-event-map"; struct resource res; void __iomem *xbar; - const s16 (*xbar_chans)[2]; + s16 (*xbar_chans)[2]; + size_t nelm = sz / sizeof(s16); u32 shift, offset, mux; + int ret, i; - xbar_chans = devm_kzalloc(dev, - len/sizeof(s16) + 2*sizeof(s16), - GFP_KERNEL); + xbar_chans = devm_kzalloc(dev, (nelm + 2) * sizeof(s16), GFP_KERNEL); if (!xbar_chans) return -ENOMEM; ret = of_address_to_resource(node, 1, &res); if (ret) - return -EIO; + return -ENOMEM; xbar = devm_ioremap(dev, res.start, resource_size(&res)); if (!xbar) return -ENOMEM; - ret = edma_of_read_u32_to_s16_array(node, - "ti,edma-xbar-event-map", - (s16 *)xbar_chans, - len/sizeof(u32)); + ret = of_property_read_u16_array(node, pname, (u16 *)xbar_chans, nelm); if (ret) return -EIO; - for (i = 0; xbar_chans[i][0] != -1; i++) { + /* Invalidate last entry for the other user of this mess */ + nelm >>= 1; + xbar_chans[nelm][0] = xbar_chans[nelm][1] = -1; + + for (i = 0; i < nelm; i++) { shift = (xbar_chans[i][1] & 0x03) << 3; offset = xbar_chans[i][1] & 0xfffffffc; mux = readl(xbar + offset); @@ -1480,8 +1463,7 @@ static int edma_xbar_event_map(struct device *dev, writel(mux, (xbar + offset)); } - pdata->xbar_chans = xbar_chans; - + pdata->xbar_chans = (const s16 (*)[2]) xbar_chans; return 0; } diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig index b5df4a5..81ba78e 100644 --- a/arch/arm/configs/sunxi_defconfig +++ b/arch/arm/configs/sunxi_defconfig @@ -37,7 +37,7 @@ CONFIG_SUN4I_EMAC=y # CONFIG_NET_VENDOR_NATSEMI is not set # CONFIG_NET_VENDOR_SEEQ is not set # CONFIG_NET_VENDOR_SMSC is not set -# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_STMMAC_ETH=y # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_WLAN is not set CONFIG_SERIAL_8250=y diff --git a/arch/arm/include/asm/xen/page.h b/arch/arm/include/asm/xen/page.h index cf4f3e8..ded062f 100644 --- a/arch/arm/include/asm/xen/page.h +++ b/arch/arm/include/asm/xen/page.h @@ -77,7 +77,6 @@ static inline xpaddr_t machine_to_phys(xmaddr_t machine) } /* VIRT <-> MACHINE conversion */ #define virt_to_machine(v) (phys_to_machine(XPADDR(__pa(v)))) -#define virt_to_pfn(v) (PFN_DOWN(__pa(v))) #define virt_to_mfn(v) (pfn_to_mfn(virt_to_pfn(v))) #define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT)) diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S index 75e9295..40c5d5f 100644 --- a/arch/arm/mach-omap2/omap-headsmp.S +++ b/arch/arm/mach-omap2/omap-headsmp.S @@ -1,7 +1,7 @@ /* * Secondary CPU startup routine source file. * - * Copyright (C) 2009 Texas Instruments, Inc. + * Copyright (C) 2009-2014 Texas Instruments, Inc. * * Author: * Santosh Shilimkar <santosh.shilimkar@ti.com> @@ -28,9 +28,13 @@ * code. This routine also provides a holding flag into which * secondary core is held until we're ready for it to initialise. * The primary core will update this flag using a hardware -+ * register AuxCoreBoot0. + * register AuxCoreBoot0. */ ENTRY(omap5_secondary_startup) +.arm +THUMB( adr r9, BSYM(wait) ) @ CPU may be entered in ARM mode. +THUMB( bx r9 ) @ If this is a Thumb-2 kernel, +THUMB( .thumb ) @ switch to Thumb now. wait: ldr r2, =AUX_CORE_BOOT0_PA @ read from AuxCoreBoot0 ldr r0, [r2] mov r0, r0, lsr #5 diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h index f565f99..7548db2 100644 --- a/arch/arm/mach-orion5x/common.h +++ b/arch/arm/mach-orion5x/common.h @@ -21,7 +21,7 @@ struct mv_sata_platform_data; #define ORION_MBUS_DEVBUS_BOOT_ATTR 0x0f #define ORION_MBUS_DEVBUS_TARGET(cs) 0x01 #define ORION_MBUS_DEVBUS_ATTR(cs) (~(1 << cs)) -#define ORION_MBUS_SRAM_TARGET 0x00 +#define ORION_MBUS_SRAM_TARGET 0x09 #define ORION_MBUS_SRAM_ATTR 0x00 /* diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index e4dec9f..9c6029b 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -23,9 +23,7 @@ #include "board.h" static struct rfkill_gpio_platform_data wifi_rfkill_platform_data = { - .name = "wifi_rfkill", - .reset_gpio = 25, /* PD1 */ - .shutdown_gpio = 85, /* PK5 */ + .name = "wifi_rfkill", .type = RFKILL_TYPE_WLAN, }; diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 6f879c3..fb5503c 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -136,7 +136,7 @@ static u16 saved_regs(struct jit_ctx *ctx) u16 ret = 0; if ((ctx->skf->len > 1) || - (ctx->skf->insns[0].code == BPF_S_RET_A)) + (ctx->skf->insns[0].code == (BPF_RET | BPF_A))) ret |= 1 << r_A; #ifdef CONFIG_FRAME_POINTER @@ -164,18 +164,10 @@ static inline int mem_words_used(struct jit_ctx *ctx) static inline bool is_load_to_a(u16 inst) { switch (inst) { - case BPF_S_LD_W_LEN: - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: - case BPF_S_ANC_CPU: - case BPF_S_ANC_IFINDEX: - case BPF_S_ANC_MARK: - case BPF_S_ANC_PROTOCOL: - case BPF_S_ANC_RXHASH: - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - case BPF_S_ANC_QUEUE: + case BPF_LD | BPF_W | BPF_LEN: + case BPF_LD | BPF_W | BPF_ABS: + case BPF_LD | BPF_H | BPF_ABS: + case BPF_LD | BPF_B | BPF_ABS: return true; default: return false; @@ -215,7 +207,7 @@ static void build_prologue(struct jit_ctx *ctx) emit(ARM_MOV_I(r_X, 0), ctx); /* do not leak kernel data to userspace */ - if ((first_inst != BPF_S_RET_K) && !(is_load_to_a(first_inst))) + if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst))) emit(ARM_MOV_I(r_A, 0), ctx); /* stack space for the BPF_MEM words */ @@ -480,36 +472,39 @@ static int build_body(struct jit_ctx *ctx) u32 k; for (i = 0; i < prog->len; i++) { + u16 code; + inst = &(prog->insns[i]); /* K as an immediate value operand */ k = inst->k; + code = bpf_anc_helper(inst); /* compute offsets only in the fake pass */ if (ctx->target == NULL) ctx->offsets[i] = ctx->idx * 4; - switch (inst->code) { - case BPF_S_LD_IMM: + switch (code) { + case BPF_LD | BPF_IMM: emit_mov_i(r_A, k, ctx); break; - case BPF_S_LD_W_LEN: + case BPF_LD | BPF_W | BPF_LEN: ctx->seen |= SEEN_SKB; BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); emit(ARM_LDR_I(r_A, r_skb, offsetof(struct sk_buff, len)), ctx); break; - case BPF_S_LD_MEM: + case BPF_LD | BPF_MEM: /* A = scratch[k] */ ctx->seen |= SEEN_MEM_WORD(k); emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx); break; - case BPF_S_LD_W_ABS: + case BPF_LD | BPF_W | BPF_ABS: load_order = 2; goto load; - case BPF_S_LD_H_ABS: + case BPF_LD | BPF_H | BPF_ABS: load_order = 1; goto load; - case BPF_S_LD_B_ABS: + case BPF_LD | BPF_B | BPF_ABS: load_order = 0; load: /* the interpreter will deal with the negative K */ @@ -552,31 +547,31 @@ load_common: emit_err_ret(ARM_COND_NE, ctx); emit(ARM_MOV_R(r_A, ARM_R0), ctx); break; - case BPF_S_LD_W_IND: + case BPF_LD | BPF_W | BPF_IND: load_order = 2; goto load_ind; - case BPF_S_LD_H_IND: + case BPF_LD | BPF_H | BPF_IND: load_order = 1; goto load_ind; - case BPF_S_LD_B_IND: + case BPF_LD | BPF_B | BPF_IND: load_order = 0; load_ind: OP_IMM3(ARM_ADD, r_off, r_X, k, ctx); goto load_common; - case BPF_S_LDX_IMM: + case BPF_LDX | BPF_IMM: ctx->seen |= SEEN_X; emit_mov_i(r_X, k, ctx); break; - case BPF_S_LDX_W_LEN: + case BPF_LDX | BPF_W | BPF_LEN: ctx->seen |= SEEN_X | SEEN_SKB; emit(ARM_LDR_I(r_X, r_skb, offsetof(struct sk_buff, len)), ctx); break; - case BPF_S_LDX_MEM: + case BPF_LDX | BPF_MEM: ctx->seen |= SEEN_X | SEEN_MEM_WORD(k); emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx); break; - case BPF_S_LDX_B_MSH: + case BPF_LDX | BPF_B | BPF_MSH: /* x = ((*(frame + k)) & 0xf) << 2; */ ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL; /* the interpreter should deal with the negative K */ @@ -606,113 +601,113 @@ load_ind: emit(ARM_AND_I(r_X, ARM_R0, 0x00f), ctx); emit(ARM_LSL_I(r_X, r_X, 2), ctx); break; - case BPF_S_ST: + case BPF_ST: ctx->seen |= SEEN_MEM_WORD(k); emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx); break; - case BPF_S_STX: + case BPF_STX: update_on_xread(ctx); ctx->seen |= SEEN_MEM_WORD(k); emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx); break; - case BPF_S_ALU_ADD_K: + case BPF_ALU | BPF_ADD | BPF_K: /* A += K */ OP_IMM3(ARM_ADD, r_A, r_A, k, ctx); break; - case BPF_S_ALU_ADD_X: + case BPF_ALU | BPF_ADD | BPF_X: update_on_xread(ctx); emit(ARM_ADD_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_SUB_K: + case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ OP_IMM3(ARM_SUB, r_A, r_A, k, ctx); break; - case BPF_S_ALU_SUB_X: + case BPF_ALU | BPF_SUB | BPF_X: update_on_xread(ctx); emit(ARM_SUB_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_MUL_K: + case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ emit_mov_i(r_scratch, k, ctx); emit(ARM_MUL(r_A, r_A, r_scratch), ctx); break; - case BPF_S_ALU_MUL_X: + case BPF_ALU | BPF_MUL | BPF_X: update_on_xread(ctx); emit(ARM_MUL(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_DIV_K: + case BPF_ALU | BPF_DIV | BPF_K: if (k == 1) break; emit_mov_i(r_scratch, k, ctx); emit_udiv(r_A, r_A, r_scratch, ctx); break; - case BPF_S_ALU_DIV_X: + case BPF_ALU | BPF_DIV | BPF_X: update_on_xread(ctx); emit(ARM_CMP_I(r_X, 0), ctx); emit_err_ret(ARM_COND_EQ, ctx); emit_udiv(r_A, r_A, r_X, ctx); break; - case BPF_S_ALU_OR_K: + case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ OP_IMM3(ARM_ORR, r_A, r_A, k, ctx); break; - case BPF_S_ALU_OR_X: + case BPF_ALU | BPF_OR | BPF_X: update_on_xread(ctx); emit(ARM_ORR_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_XOR_K: + case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K; */ OP_IMM3(ARM_EOR, r_A, r_A, k, ctx); break; - case BPF_S_ANC_ALU_XOR_X: - case BPF_S_ALU_XOR_X: + case BPF_ANC | SKF_AD_ALU_XOR_X: + case BPF_ALU | BPF_XOR | BPF_X: /* A ^= X */ update_on_xread(ctx); emit(ARM_EOR_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_AND_K: + case BPF_ALU | BPF_AND | BPF_K: /* A &= K */ OP_IMM3(ARM_AND, r_A, r_A, k, ctx); break; - case BPF_S_ALU_AND_X: + case BPF_ALU | BPF_AND | BPF_X: update_on_xread(ctx); emit(ARM_AND_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_LSH_K: + case BPF_ALU | BPF_LSH | BPF_K: if (unlikely(k > 31)) return -1; emit(ARM_LSL_I(r_A, r_A, k), ctx); break; - case BPF_S_ALU_LSH_X: + case BPF_ALU | BPF_LSH | BPF_X: update_on_xread(ctx); emit(ARM_LSL_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_RSH_K: + case BPF_ALU | BPF_RSH | BPF_K: if (unlikely(k > 31)) return -1; emit(ARM_LSR_I(r_A, r_A, k), ctx); break; - case BPF_S_ALU_RSH_X: + case BPF_ALU | BPF_RSH | BPF_X: update_on_xread(ctx); emit(ARM_LSR_R(r_A, r_A, r_X), ctx); break; - case BPF_S_ALU_NEG: + case BPF_ALU | BPF_NEG: /* A = -A */ emit(ARM_RSB_I(r_A, r_A, 0), ctx); break; - case BPF_S_JMP_JA: + case BPF_JMP | BPF_JA: /* pc += K */ emit(ARM_B(b_imm(i + k + 1, ctx)), ctx); break; - case BPF_S_JMP_JEQ_K: + case BPF_JMP | BPF_JEQ | BPF_K: /* pc += (A == K) ? pc->jt : pc->jf */ condt = ARM_COND_EQ; goto cmp_imm; - case BPF_S_JMP_JGT_K: + case BPF_JMP | BPF_JGT | BPF_K: /* pc += (A > K) ? pc->jt : pc->jf */ condt = ARM_COND_HI; goto cmp_imm; - case BPF_S_JMP_JGE_K: + case BPF_JMP | BPF_JGE | BPF_K: /* pc += (A >= K) ? pc->jt : pc->jf */ condt = ARM_COND_HS; cmp_imm: @@ -731,22 +726,22 @@ cond_jump: _emit(condt ^ 1, ARM_B(b_imm(i + inst->jf + 1, ctx)), ctx); break; - case BPF_S_JMP_JEQ_X: + case BPF_JMP | BPF_JEQ | BPF_X: /* pc += (A == X) ? pc->jt : pc->jf */ condt = ARM_COND_EQ; goto cmp_x; - case BPF_S_JMP_JGT_X: + case BPF_JMP | BPF_JGT | BPF_X: /* pc += (A > X) ? pc->jt : pc->jf */ condt = ARM_COND_HI; goto cmp_x; - case BPF_S_JMP_JGE_X: + case BPF_JMP | BPF_JGE | BPF_X: /* pc += (A >= X) ? pc->jt : pc->jf */ condt = ARM_COND_CS; cmp_x: update_on_xread(ctx); emit(ARM_CMP_R(r_A, r_X), ctx); goto cond_jump; - case BPF_S_JMP_JSET_K: + case BPF_JMP | BPF_JSET | BPF_K: /* pc += (A & K) ? pc->jt : pc->jf */ condt = ARM_COND_NE; /* not set iff all zeroes iff Z==1 iff EQ */ @@ -759,16 +754,16 @@ cmp_x: emit(ARM_TST_I(r_A, imm12), ctx); } goto cond_jump; - case BPF_S_JMP_JSET_X: + case BPF_JMP | BPF_JSET | BPF_X: /* pc += (A & X) ? pc->jt : pc->jf */ update_on_xread(ctx); condt = ARM_COND_NE; emit(ARM_TST_R(r_A, r_X), ctx); goto cond_jump; - case BPF_S_RET_A: + case BPF_RET | BPF_A: emit(ARM_MOV_R(ARM_R0, r_A), ctx); goto b_epilogue; - case BPF_S_RET_K: + case BPF_RET | BPF_K: if ((k == 0) && (ctx->ret0_fp_idx < 0)) ctx->ret0_fp_idx = i; emit_mov_i(ARM_R0, k, ctx); @@ -776,17 +771,17 @@ b_epilogue: if (i != ctx->skf->len - 1) emit(ARM_B(b_imm(prog->len, ctx)), ctx); break; - case BPF_S_MISC_TAX: + case BPF_MISC | BPF_TAX: /* X = A */ ctx->seen |= SEEN_X; emit(ARM_MOV_R(r_X, r_A), ctx); break; - case BPF_S_MISC_TXA: + case BPF_MISC | BPF_TXA: /* A = X */ update_on_xread(ctx); emit(ARM_MOV_R(r_A, r_X), ctx); break; - case BPF_S_ANC_PROTOCOL: + case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol) */ ctx->seen |= SEEN_SKB; BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, @@ -795,7 +790,7 @@ b_epilogue: emit(ARM_LDRH_I(r_scratch, r_skb, off), ctx); emit_swap16(r_A, r_scratch, ctx); break; - case BPF_S_ANC_CPU: + case BPF_ANC | SKF_AD_CPU: /* r_scratch = current_thread_info() */ OP_IMM3(ARM_BIC, r_scratch, ARM_SP, THREAD_SIZE - 1, ctx); /* A = current_thread_info()->cpu */ @@ -803,7 +798,7 @@ b_epilogue: off = offsetof(struct thread_info, cpu); emit(ARM_LDR_I(r_A, r_scratch, off), ctx); break; - case BPF_S_ANC_IFINDEX: + case BPF_ANC | SKF_AD_IFINDEX: /* A = skb->dev->ifindex */ ctx->seen |= SEEN_SKB; off = offsetof(struct sk_buff, dev); @@ -817,30 +812,30 @@ b_epilogue: off = offsetof(struct net_device, ifindex); emit(ARM_LDR_I(r_A, r_scratch, off), ctx); break; - case BPF_S_ANC_MARK: + case BPF_ANC | SKF_AD_MARK: ctx->seen |= SEEN_SKB; BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); off = offsetof(struct sk_buff, mark); emit(ARM_LDR_I(r_A, r_skb, off), ctx); break; - case BPF_S_ANC_RXHASH: + case BPF_ANC | SKF_AD_RXHASH: ctx->seen |= SEEN_SKB; BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); off = offsetof(struct sk_buff, hash); emit(ARM_LDR_I(r_A, r_skb, off), ctx); break; - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: + case BPF_ANC | SKF_AD_VLAN_TAG: + case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: ctx->seen |= SEEN_SKB; BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); off = offsetof(struct sk_buff, vlan_tci); emit(ARM_LDRH_I(r_A, r_skb, off), ctx); - if (inst->code == BPF_S_ANC_VLAN_TAG) + if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) OP_IMM3(ARM_AND, r_A, r_A, VLAN_VID_MASK, ctx); else OP_IMM3(ARM_AND, r_A, r_A, VLAN_TAG_PRESENT, ctx); break; - case BPF_S_ANC_QUEUE: + case BPF_ANC | SKF_AD_QUEUE: ctx->seen |= SEEN_SKB; BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index e94f945..993bce5 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -138,6 +138,7 @@ static inline void *phys_to_virt(phys_addr_t x) #define __pa(x) __virt_to_phys((unsigned long)(x)) #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) +#define virt_to_pfn(x) __phys_to_pfn(__virt_to_phys(x)) /* * virt_to_page(k) convert a _valid_ virtual address to struct page * diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index 473e5db..0f08dfd 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -97,11 +97,15 @@ static bool migrate_one_irq(struct irq_desc *desc) if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity)) return false; - if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) { - affinity = cpu_online_mask; + if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) ret = true; - } + /* + * when using forced irq_set_affinity we must ensure that the cpu + * being offlined is not present in the affinity mask, it may be + * selected as the target CPU otherwise + */ + affinity = cpu_online_mask; c = irq_data_get_irq_chip(d); if (!c->irq_set_affinity) pr_debug("IRQ%u: unable to set affinity\n", d->irq); diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 5e9aec3..31eb959 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -51,7 +51,11 @@ int pmd_huge(pmd_t pmd) int pud_huge(pud_t pud) { +#ifndef __PAGETABLE_PMD_FOLDED return !(pud_val(pud) & PUD_TABLE_BIT); +#else + return 0; +#endif } int pmd_huge_support(void) diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index ae763d8b..fb13dc5 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -11,7 +11,7 @@ -#define NR_syscalls 314 /* length of syscall table */ +#define NR_syscalls 315 /* length of syscall table */ /* * The following defines stop scripts/checksyscalls.sh from complaining about diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h index 715e85f..7de0a2d 100644 --- a/arch/ia64/include/uapi/asm/unistd.h +++ b/arch/ia64/include/uapi/asm/unistd.h @@ -327,5 +327,6 @@ #define __NR_finit_module 1335 #define __NR_sched_setattr 1336 #define __NR_sched_getattr 1337 +#define __NR_renameat2 1338 #endif /* _UAPI_ASM_IA64_UNISTD_H */ diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index fa8d61a..ba3d03503 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1775,6 +1775,7 @@ sys_call_table: data8 sys_finit_module // 1335 data8 sys_sched_setattr data8 sys_sched_getattr + data8 sys_renameat2 .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls #endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */ diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index 9d38b73..33afa56 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h @@ -4,7 +4,7 @@ #include <uapi/asm/unistd.h> -#define NR_syscalls 351 +#define NR_syscalls 352 #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT diff --git a/arch/m68k/include/uapi/asm/unistd.h b/arch/m68k/include/uapi/asm/unistd.h index b932dd4..9cd82fb 100644 --- a/arch/m68k/include/uapi/asm/unistd.h +++ b/arch/m68k/include/uapi/asm/unistd.h @@ -356,5 +356,6 @@ #define __NR_finit_module 348 #define __NR_sched_setattr 349 #define __NR_sched_getattr 350 +#define __NR_renameat2 351 #endif /* _UAPI_ASM_M68K_UNISTD_H_ */ diff --git a/arch/m68k/kernel/syscalltable.S b/arch/m68k/kernel/syscalltable.S index b6223dc4..501e102 100644 --- a/arch/m68k/kernel/syscalltable.S +++ b/arch/m68k/kernel/syscalltable.S @@ -371,4 +371,5 @@ ENTRY(sys_call_table) .long sys_finit_module .long sys_sched_setattr .long sys_sched_getattr /* 350 */ + .long sys_renameat2 diff --git a/arch/metag/include/asm/barrier.h b/arch/metag/include/asm/barrier.h index 5d6b4b4..2d6f0de 100644 --- a/arch/metag/include/asm/barrier.h +++ b/arch/metag/include/asm/barrier.h @@ -15,6 +15,7 @@ static inline void wr_fence(void) volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_FENCE; barrier(); *flushptr = 0; + barrier(); } #else /* CONFIG_METAG_META21 */ @@ -35,6 +36,7 @@ static inline void wr_fence(void) *flushptr = 0; *flushptr = 0; *flushptr = 0; + barrier(); } #endif /* !CONFIG_METAG_META21 */ @@ -68,6 +70,7 @@ static inline void fence(void) volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_ATOMIC_UNLOCK; barrier(); *flushptr = 0; + barrier(); } #define smp_mb() fence() #define smp_rmb() fence() diff --git a/arch/metag/include/asm/processor.h b/arch/metag/include/asm/processor.h index f16477d..a8a3747 100644 --- a/arch/metag/include/asm/processor.h +++ b/arch/metag/include/asm/processor.h @@ -22,6 +22,8 @@ /* Add an extra page of padding at the top of the stack for the guard page. */ #define STACK_TOP (TASK_SIZE - PAGE_SIZE) #define STACK_TOP_MAX STACK_TOP +/* Maximum virtual space for stack */ +#define STACK_SIZE_MAX (CONFIG_MAX_STACK_SIZE_MB*1024*1024) /* This decides where the kernel will search for a free chunk of vm * space during mmap's. diff --git a/arch/metag/include/uapi/asm/Kbuild b/arch/metag/include/uapi/asm/Kbuild index 84e09fe..ab78be2 100644 --- a/arch/metag/include/uapi/asm/Kbuild +++ b/arch/metag/include/uapi/asm/Kbuild @@ -4,11 +4,11 @@ include include/uapi/asm-generic/Kbuild.asm header-y += byteorder.h header-y += ech.h header-y += ptrace.h -header-y += resource.h header-y += sigcontext.h header-y += siginfo.h header-y += swab.h header-y += unistd.h generic-y += mman.h +generic-y += resource.h generic-y += setup.h diff --git a/arch/metag/include/uapi/asm/resource.h b/arch/metag/include/uapi/asm/resource.h deleted file mode 100644 index 526d23c..0000000 --- a/arch/metag/include/uapi/asm/resource.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _UAPI_METAG_RESOURCE_H -#define _UAPI_METAG_RESOURCE_H - -#define _STK_LIM_MAX (1 << 28) -#include <asm-generic/resource.h> - -#endif /* _UAPI_METAG_RESOURCE_H */ diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index a8b5408..da4cdb1 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -168,6 +168,7 @@ static void nvram_read_alpha2(const char *prefix, const char *name, static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom, const char *prefix, bool fallback) { + nvram_read_u16(prefix, NULL, "devid", &sprom->dev_id, 0, fallback); nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff, fallback); nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff, fallback); nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff, fallback); diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c index 5abf4e8..2a66e90 100644 --- a/arch/mips/dec/ecc-berr.c +++ b/arch/mips/dec/ecc-berr.c @@ -21,6 +21,7 @@ #include <asm/addrspace.h> #include <asm/bootinfo.h> #include <asm/cpu.h> +#include <asm/cpu-type.h> #include <asm/irq_regs.h> #include <asm/processor.h> #include <asm/ptrace.h> diff --git a/arch/mips/dec/kn02xa-berr.c b/arch/mips/dec/kn02xa-berr.c index f434b75..ec60636 100644 --- a/arch/mips/dec/kn02xa-berr.c +++ b/arch/mips/dec/kn02xa-berr.c @@ -19,6 +19,7 @@ #include <linux/types.h> #include <asm/addrspace.h> +#include <asm/cpu-type.h> #include <asm/irq_regs.h> #include <asm/ptrace.h> #include <asm/traps.h> diff --git a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile index 064ae7a..ae73e42 100644 --- a/arch/mips/dec/prom/Makefile +++ b/arch/mips/dec/prom/Makefile @@ -6,4 +6,3 @@ lib-y += init.o memory.o cmdline.o identify.o console.o lib-$(CONFIG_32BIT) += locore.o -lib-$(CONFIG_64BIT) += call_o32.o diff --git a/arch/mips/dec/prom/call_o32.S b/arch/mips/dec/prom/call_o32.S deleted file mode 100644 index 8c84981..0000000 --- a/arch/mips/dec/prom/call_o32.S +++ /dev/null @@ -1,89 +0,0 @@ -/* - * O32 interface for the 64 (or N32) ABI. - * - * Copyright (C) 2002 Maciej W. Rozycki - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <asm/asm.h> -#include <asm/regdef.h> - -/* Maximum number of arguments supported. Must be even! */ -#define O32_ARGC 32 -/* Number of static registers we save. */ -#define O32_STATC 11 -/* Frame size for both of the above. */ -#define O32_FRAMESZ (4 * O32_ARGC + SZREG * O32_STATC) - - .text - -/* - * O32 function call dispatcher, for interfacing 32-bit ROM routines. - * - * The standard 64 (N32) calling sequence is supported, with a0 - * holding a function pointer, a1-a7 -- its first seven arguments - * and the stack -- remaining ones (up to O32_ARGC, including a1-a7). - * Static registers, gp and fp are preserved, v0 holds a result. - * This code relies on the called o32 function for sp and ra - * restoration and thus both this dispatcher and the current stack - * have to be placed in a KSEGx (or KUSEG) address space. Any - * pointers passed have to point to addresses within one of these - * spaces as well. - */ -NESTED(call_o32, O32_FRAMESZ, ra) - REG_SUBU sp,O32_FRAMESZ - - REG_S ra,O32_FRAMESZ-1*SZREG(sp) - REG_S fp,O32_FRAMESZ-2*SZREG(sp) - REG_S gp,O32_FRAMESZ-3*SZREG(sp) - REG_S s7,O32_FRAMESZ-4*SZREG(sp) - REG_S s6,O32_FRAMESZ-5*SZREG(sp) - REG_S s5,O32_FRAMESZ-6*SZREG(sp) - REG_S s4,O32_FRAMESZ-7*SZREG(sp) - REG_S s3,O32_FRAMESZ-8*SZREG(sp) - REG_S s2,O32_FRAMESZ-9*SZREG(sp) - REG_S s1,O32_FRAMESZ-10*SZREG(sp) - REG_S s0,O32_FRAMESZ-11*SZREG(sp) - - move jp,a0 - - sll a0,a1,zero - sll a1,a2,zero - sll a2,a3,zero - sll a3,a4,zero - sw a5,0x10(sp) - sw a6,0x14(sp) - sw a7,0x18(sp) - - PTR_LA t0,O32_FRAMESZ(sp) - PTR_LA t1,0x1c(sp) - li t2,O32_ARGC-7 -1: - lw t3,(t0) - REG_ADDU t0,SZREG - sw t3,(t1) - REG_SUBU t2,1 - REG_ADDU t1,4 - bnez t2,1b - - jalr jp - - REG_L s0,O32_FRAMESZ-11*SZREG(sp) - REG_L s1,O32_FRAMESZ-10*SZREG(sp) - REG_L s2,O32_FRAMESZ-9*SZREG(sp) - REG_L s3,O32_FRAMESZ-8*SZREG(sp) - REG_L s4,O32_FRAMESZ-7*SZREG(sp) - REG_L s5,O32_FRAMESZ-6*SZREG(sp) - REG_L s6,O32_FRAMESZ-5*SZREG(sp) - REG_L s7,O32_FRAMESZ-4*SZREG(sp) - REG_L gp,O32_FRAMESZ-3*SZREG(sp) - REG_L fp,O32_FRAMESZ-2*SZREG(sp) - REG_L ra,O32_FRAMESZ-1*SZREG(sp) - - REG_ADDU sp,O32_FRAMESZ - jr ra -END(call_o32) diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S index b308b2a..4703fe4 100644 --- a/arch/mips/fw/lib/call_o32.S +++ b/arch/mips/fw/lib/call_o32.S @@ -1,7 +1,7 @@ /* * O32 interface for the 64 (or N32) ABI. * - * Copyright (C) 2002 Maciej W. Rozycki + * Copyright (C) 2002, 2014 Maciej W. Rozycki * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -12,28 +12,37 @@ #include <asm/asm.h> #include <asm/regdef.h> +/* O32 register size. */ +#define O32_SZREG 4 /* Maximum number of arguments supported. Must be even! */ #define O32_ARGC 32 -/* Number of static registers we save. */ +/* Number of static registers we save. */ #define O32_STATC 11 -/* Frame size for static register */ -#define O32_FRAMESZ (SZREG * O32_STATC) -/* Frame size on new stack */ -#define O32_FRAMESZ_NEW (SZREG + 4 * O32_ARGC) +/* Argument area frame size. */ +#define O32_ARGSZ (O32_SZREG * O32_ARGC) +/* Static register save area frame size. */ +#define O32_STATSZ (SZREG * O32_STATC) +/* Stack pointer register save area frame size. */ +#define O32_SPSZ SZREG +/* Combined area frame size. */ +#define O32_FRAMESZ (O32_ARGSZ + O32_SPSZ + O32_STATSZ) +/* Switched stack frame size. */ +#define O32_NFRAMESZ (O32_ARGSZ + O32_SPSZ) .text /* * O32 function call dispatcher, for interfacing 32-bit ROM routines. * - * The standard 64 (N32) calling sequence is supported, with a0 - * holding a function pointer, a1 a new stack pointer, a2-a7 -- its - * first six arguments and the stack -- remaining ones (up to O32_ARGC, - * including a2-a7). Static registers, gp and fp are preserved, v0 holds - * a result. This code relies on the called o32 function for sp and ra - * restoration and this dispatcher has to be placed in a KSEGx (or KUSEG) - * address space. Any pointers passed have to point to addresses within - * one of these spaces as well. + * The standard 64 (N32) calling sequence is supported, with a0 holding + * a function pointer, a1 a pointer to the new stack to call the + * function with or 0 if no stack switching is requested, a2-a7 -- the + * function call's first six arguments, and the stack -- the remaining + * arguments (up to O32_ARGC, including a2-a7). Static registers, gp + * and fp are preserved, v0 holds the result. This code relies on the + * called o32 function for sp and ra restoration and this dispatcher has + * to be placed in a KSEGx (or KUSEG) address space. Any pointers + * passed have to point to addresses within one of these spaces as well. */ NESTED(call_o32, O32_FRAMESZ, ra) REG_SUBU sp,O32_FRAMESZ @@ -51,32 +60,36 @@ NESTED(call_o32, O32_FRAMESZ, ra) REG_S s0,O32_FRAMESZ-11*SZREG(sp) move jp,a0 - REG_SUBU s0,a1,O32_FRAMESZ_NEW - REG_S sp,O32_FRAMESZ_NEW-1*SZREG(s0) + + move fp,sp + beqz a1,0f + REG_SUBU fp,a1,O32_NFRAMESZ +0: + REG_S sp,O32_NFRAMESZ-1*SZREG(fp) sll a0,a2,zero sll a1,a3,zero sll a2,a4,zero sll a3,a5,zero - sw a6,0x10(s0) - sw a7,0x14(s0) + sw a6,4*O32_SZREG(fp) + sw a7,5*O32_SZREG(fp) PTR_LA t0,O32_FRAMESZ(sp) - PTR_LA t1,0x18(s0) + PTR_LA t1,6*O32_SZREG(fp) li t2,O32_ARGC-6 1: lw t3,(t0) REG_ADDU t0,SZREG sw t3,(t1) REG_SUBU t2,1 - REG_ADDU t1,4 + REG_ADDU t1,O32_SZREG bnez t2,1b - move sp,s0 + move sp,fp jalr jp - REG_L sp,O32_FRAMESZ_NEW-1*SZREG(sp) + REG_L sp,O32_NFRAMESZ-1*SZREG(sp) REG_L s0,O32_FRAMESZ-11*SZREG(sp) REG_L s1,O32_FRAMESZ-10*SZREG(sp) diff --git a/arch/mips/fw/sni/sniprom.c b/arch/mips/fw/sni/sniprom.c index 2c2cb18..6aa264b 100644 --- a/arch/mips/fw/sni/sniprom.c +++ b/arch/mips/fw/sni/sniprom.c @@ -40,7 +40,8 @@ #ifdef CONFIG_64BIT -static u8 o32_stk[16384]; +/* O32 stack has to be 8-byte aligned. */ +static u64 o32_stk[4096]; #define O32_STK &o32_stk[sizeof(o32_stk)] #define __PROM_O32(fun, arg) fun arg __asm__(#fun); \ diff --git a/arch/mips/include/asm/dec/prom.h b/arch/mips/include/asm/dec/prom.h index c0ead63..b59a210 100644 --- a/arch/mips/include/asm/dec/prom.h +++ b/arch/mips/include/asm/dec/prom.h @@ -113,31 +113,31 @@ extern int (*__pmax_close)(int); #define __DEC_PROM_O32(fun, arg) fun arg __asm__(#fun); \ __asm__(#fun " = call_o32") -int __DEC_PROM_O32(_rex_bootinit, (int (*)(void))); -int __DEC_PROM_O32(_rex_bootread, (int (*)(void))); -int __DEC_PROM_O32(_rex_getbitmap, (int (*)(memmap *), memmap *)); +int __DEC_PROM_O32(_rex_bootinit, (int (*)(void), void *)); +int __DEC_PROM_O32(_rex_bootread, (int (*)(void), void *)); +int __DEC_PROM_O32(_rex_getbitmap, (int (*)(memmap *), void *, memmap *)); unsigned long *__DEC_PROM_O32(_rex_slot_address, - (unsigned long *(*)(int), int)); -void *__DEC_PROM_O32(_rex_gettcinfo, (void *(*)(void))); -int __DEC_PROM_O32(_rex_getsysid, (int (*)(void))); -void __DEC_PROM_O32(_rex_clear_cache, (void (*)(void))); - -int __DEC_PROM_O32(_prom_getchar, (int (*)(void))); -char *__DEC_PROM_O32(_prom_getenv, (char *(*)(char *), char *)); -int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), char *, ...)); - - -#define rex_bootinit() _rex_bootinit(__rex_bootinit) -#define rex_bootread() _rex_bootread(__rex_bootread) -#define rex_getbitmap(x) _rex_getbitmap(__rex_getbitmap, x) -#define rex_slot_address(x) _rex_slot_address(__rex_slot_address, x) -#define rex_gettcinfo() _rex_gettcinfo(__rex_gettcinfo) -#define rex_getsysid() _rex_getsysid(__rex_getsysid) -#define rex_clear_cache() _rex_clear_cache(__rex_clear_cache) - -#define prom_getchar() _prom_getchar(__prom_getchar) -#define prom_getenv(x) _prom_getenv(__prom_getenv, x) -#define prom_printf(x...) _prom_printf(__prom_printf, x) + (unsigned long *(*)(int), void *, int)); +void *__DEC_PROM_O32(_rex_gettcinfo, (void *(*)(void), void *)); +int __DEC_PROM_O32(_rex_getsysid, (int (*)(void), void *)); +void __DEC_PROM_O32(_rex_clear_cache, (void (*)(void), void *)); + +int __DEC_PROM_O32(_prom_getchar, (int (*)(void), void *)); +char *__DEC_PROM_O32(_prom_getenv, (char *(*)(char *), void *, char *)); +int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...)); + + +#define rex_bootinit() _rex_bootinit(__rex_bootinit, NULL) +#define rex_bootread() _rex_bootread(__rex_bootread, NULL) +#define rex_getbitmap(x) _rex_getbitmap(__rex_getbitmap, NULL, x) +#define rex_slot_address(x) _rex_slot_address(__rex_slot_address, NULL, x) +#define rex_gettcinfo() _rex_gettcinfo(__rex_gettcinfo, NULL) +#define rex_getsysid() _rex_getsysid(__rex_getsysid, NULL) +#define rex_clear_cache() _rex_clear_cache(__rex_clear_cache, NULL) + +#define prom_getchar() _prom_getchar(__prom_getchar, NULL) +#define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x) +#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x) #else /* !CONFIG_64BIT */ diff --git a/arch/mips/include/asm/rm9k-ocd.h b/arch/mips/include/asm/rm9k-ocd.h deleted file mode 100644 index b0b80d9..0000000 --- a/arch/mips/include/asm/rm9k-ocd.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2004 by Basler Vision Technologies AG - * Author: Thomas Koeller <thomas.koeller@baslerweb.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined(_ASM_RM9K_OCD_H) -#define _ASM_RM9K_OCD_H - -#include <linux/types.h> -#include <linux/spinlock.h> -#include <asm/io.h> - -extern volatile void __iomem * const ocd_base; -extern volatile void __iomem * const titan_base; - -#define ocd_addr(__x__) (ocd_base + (__x__)) -#define titan_addr(__x__) (titan_base + (__x__)) -#define scram_addr(__x__) (scram_base + (__x__)) - -/* OCD register access */ -#define ocd_readl(__offs__) __raw_readl(ocd_addr(__offs__)) -#define ocd_readw(__offs__) __raw_readw(ocd_addr(__offs__)) -#define ocd_readb(__offs__) __raw_readb(ocd_addr(__offs__)) -#define ocd_writel(__val__, __offs__) \ - __raw_writel((__val__), ocd_addr(__offs__)) -#define ocd_writew(__val__, __offs__) \ - __raw_writew((__val__), ocd_addr(__offs__)) -#define ocd_writeb(__val__, __offs__) \ - __raw_writeb((__val__), ocd_addr(__offs__)) - -/* TITAN register access - 32 bit-wide only */ -#define titan_readl(__offs__) __raw_readl(titan_addr(__offs__)) -#define titan_writel(__val__, __offs__) \ - __raw_writel((__val__), titan_addr(__offs__)) - -/* Protect access to shared TITAN registers */ -extern spinlock_t titan_lock; -extern int titan_irqflags; -#define lock_titan_regs() spin_lock_irqsave(&titan_lock, titan_irqflags) -#define unlock_titan_regs() spin_unlock_irqrestore(&titan_lock, titan_irqflags) - -#endif /* !defined(_ASM_RM9K_OCD_H) */ diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index c6e9cd2..17960fe 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -133,6 +133,8 @@ static inline int syscall_get_arch(void) #ifdef CONFIG_64BIT if (!test_thread_flag(TIF_32BIT_REGS)) arch |= __AUDIT_ARCH_64BIT; + if (test_thread_flag(TIF_32BIT_ADDR)) + arch |= __AUDIT_ARCH_CONVENTION_MIPS64_N32; #endif #if defined(__LITTLE_ENDIAN) arch |= __AUDIT_ARCH_LE; diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index df6e775..3125797 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h @@ -484,13 +484,13 @@ enum MIPS6e_i8_func { * Damn ... bitfields depend from byteorder :-( */ #ifdef __MIPSEB__ -#define BITFIELD_FIELD(field, more) \ +#define __BITFIELD_FIELD(field, more) \ field; \ more #elif defined(__MIPSEL__) -#define BITFIELD_FIELD(field, more) \ +#define __BITFIELD_FIELD(field, more) \ more \ field; @@ -499,112 +499,112 @@ enum MIPS6e_i8_func { #endif struct j_format { - BITFIELD_FIELD(unsigned int opcode : 6, /* Jump format */ - BITFIELD_FIELD(unsigned int target : 26, + __BITFIELD_FIELD(unsigned int opcode : 6, /* Jump format */ + __BITFIELD_FIELD(unsigned int target : 26, ;)) }; struct i_format { /* signed immediate format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(signed int simmediate : 16, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(signed int simmediate : 16, ;)))) }; struct u_format { /* unsigned immediate format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int uimmediate : 16, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int uimmediate : 16, ;)))) }; struct c_format { /* Cache (>= R6000) format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(unsigned int c_op : 3, - BITFIELD_FIELD(unsigned int cache : 2, - BITFIELD_FIELD(unsigned int simmediate : 16, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int c_op : 3, + __BITFIELD_FIELD(unsigned int cache : 2, + __BITFIELD_FIELD(unsigned int simmediate : 16, ;))))) }; struct r_format { /* Register format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int rd : 5, - BITFIELD_FIELD(unsigned int re : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int rd : 5, + __BITFIELD_FIELD(unsigned int re : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct p_format { /* Performance counter format (R10000) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int rd : 5, - BITFIELD_FIELD(unsigned int re : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int rd : 5, + __BITFIELD_FIELD(unsigned int re : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct f_format { /* FPU register format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int : 1, - BITFIELD_FIELD(unsigned int fmt : 4, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int rd : 5, - BITFIELD_FIELD(unsigned int re : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int : 1, + __BITFIELD_FIELD(unsigned int fmt : 4, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int rd : 5, + __BITFIELD_FIELD(unsigned int re : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;))))))) }; struct ma_format { /* FPU multiply and add format (MIPS IV) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int fr : 5, - BITFIELD_FIELD(unsigned int ft : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int func : 4, - BITFIELD_FIELD(unsigned int fmt : 2, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int fr : 5, + __BITFIELD_FIELD(unsigned int ft : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int func : 4, + __BITFIELD_FIELD(unsigned int fmt : 2, ;))))))) }; struct b_format { /* BREAK and SYSCALL */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int code : 20, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int code : 20, + __BITFIELD_FIELD(unsigned int func : 6, ;))) }; struct ps_format { /* MIPS-3D / paired single format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(unsigned int ft : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int ft : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct v_format { /* MDMX vector format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int sel : 4, - BITFIELD_FIELD(unsigned int fmt : 1, - BITFIELD_FIELD(unsigned int vt : 5, - BITFIELD_FIELD(unsigned int vs : 5, - BITFIELD_FIELD(unsigned int vd : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int sel : 4, + __BITFIELD_FIELD(unsigned int fmt : 1, + __BITFIELD_FIELD(unsigned int vt : 5, + __BITFIELD_FIELD(unsigned int vs : 5, + __BITFIELD_FIELD(unsigned int vd : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;))))))) }; struct spec3_format { /* SPEC3 */ - BITFIELD_FIELD(unsigned int opcode:6, - BITFIELD_FIELD(unsigned int rs:5, - BITFIELD_FIELD(unsigned int rt:5, - BITFIELD_FIELD(signed int simmediate:9, - BITFIELD_FIELD(unsigned int func:7, + __BITFIELD_FIELD(unsigned int opcode:6, + __BITFIELD_FIELD(unsigned int rs:5, + __BITFIELD_FIELD(unsigned int rt:5, + __BITFIELD_FIELD(signed int simmediate:9, + __BITFIELD_FIELD(unsigned int func:7, ;))))) }; @@ -616,141 +616,141 @@ struct spec3_format { /* SPEC3 */ * if it is MIPS32 instruction re-encoded for use in the microMIPS ASE. */ struct fb_format { /* FPU branch format (MIPS32) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int bc : 5, - BITFIELD_FIELD(unsigned int cc : 3, - BITFIELD_FIELD(unsigned int flag : 2, - BITFIELD_FIELD(signed int simmediate : 16, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int bc : 5, + __BITFIELD_FIELD(unsigned int cc : 3, + __BITFIELD_FIELD(unsigned int flag : 2, + __BITFIELD_FIELD(signed int simmediate : 16, ;))))) }; struct fp0_format { /* FPU multiply and add format (MIPS32) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int fmt : 5, - BITFIELD_FIELD(unsigned int ft : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int fmt : 5, + __BITFIELD_FIELD(unsigned int ft : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct mm_fp0_format { /* FPU multipy and add format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int ft : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int fmt : 3, - BITFIELD_FIELD(unsigned int op : 2, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int ft : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int fmt : 3, + __BITFIELD_FIELD(unsigned int op : 2, + __BITFIELD_FIELD(unsigned int func : 6, ;))))))) }; struct fp1_format { /* FPU mfc1 and cfc1 format (MIPS32) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int op : 5, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int op : 5, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct mm_fp1_format { /* FPU mfc1 and cfc1 format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fmt : 2, - BITFIELD_FIELD(unsigned int op : 8, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fmt : 2, + __BITFIELD_FIELD(unsigned int op : 8, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct mm_fp2_format { /* FPU movt and movf format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int cc : 3, - BITFIELD_FIELD(unsigned int zero : 2, - BITFIELD_FIELD(unsigned int fmt : 2, - BITFIELD_FIELD(unsigned int op : 3, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int cc : 3, + __BITFIELD_FIELD(unsigned int zero : 2, + __BITFIELD_FIELD(unsigned int fmt : 2, + __BITFIELD_FIELD(unsigned int op : 3, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))))) }; struct mm_fp3_format { /* FPU abs and neg format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fmt : 3, - BITFIELD_FIELD(unsigned int op : 7, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fmt : 3, + __BITFIELD_FIELD(unsigned int op : 7, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct mm_fp4_format { /* FPU c.cond format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int cc : 3, - BITFIELD_FIELD(unsigned int fmt : 3, - BITFIELD_FIELD(unsigned int cond : 4, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int cc : 3, + __BITFIELD_FIELD(unsigned int fmt : 3, + __BITFIELD_FIELD(unsigned int cond : 4, + __BITFIELD_FIELD(unsigned int func : 6, ;))))))) }; struct mm_fp5_format { /* FPU lwxc1 and swxc1 format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int index : 5, - BITFIELD_FIELD(unsigned int base : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int op : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int index : 5, + __BITFIELD_FIELD(unsigned int base : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int op : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct fp6_format { /* FPU madd and msub format (MIPS IV) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int fr : 5, - BITFIELD_FIELD(unsigned int ft : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int fr : 5, + __BITFIELD_FIELD(unsigned int ft : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct mm_fp6_format { /* FPU madd and msub format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int ft : 5, - BITFIELD_FIELD(unsigned int fs : 5, - BITFIELD_FIELD(unsigned int fd : 5, - BITFIELD_FIELD(unsigned int fr : 5, - BITFIELD_FIELD(unsigned int func : 6, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int ft : 5, + __BITFIELD_FIELD(unsigned int fs : 5, + __BITFIELD_FIELD(unsigned int fd : 5, + __BITFIELD_FIELD(unsigned int fr : 5, + __BITFIELD_FIELD(unsigned int func : 6, ;)))))) }; struct mm_i_format { /* Immediate format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(unsigned int rs : 5, - BITFIELD_FIELD(signed int simmediate : 16, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(signed int simmediate : 16, ;)))) }; struct mm_m_format { /* Multi-word load/store format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rd : 5, - BITFIELD_FIELD(unsigned int base : 5, - BITFIELD_FIELD(unsigned int func : 4, - BITFIELD_FIELD(signed int simmediate : 12, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rd : 5, + __BITFIELD_FIELD(unsigned int base : 5, + __BITFIELD_FIELD(unsigned int func : 4, + __BITFIELD_FIELD(signed int simmediate : 12, ;))))) }; struct mm_x_format { /* Scaled indexed load format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int index : 5, - BITFIELD_FIELD(unsigned int base : 5, - BITFIELD_FIELD(unsigned int rd : 5, - BITFIELD_FIELD(unsigned int func : 11, + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int index : 5, + __BITFIELD_FIELD(unsigned int base : 5, + __BITFIELD_FIELD(unsigned int rd : 5, + __BITFIELD_FIELD(unsigned int func : 11, ;))))) }; @@ -758,51 +758,51 @@ struct mm_x_format { /* Scaled indexed load format (microMIPS) */ * microMIPS instruction formats (16-bit length) */ struct mm_b0_format { /* Unconditional branch format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(signed int simmediate : 10, - BITFIELD_FIELD(unsigned int : 16, /* Ignored */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(signed int simmediate : 10, + __BITFIELD_FIELD(unsigned int : 16, /* Ignored */ ;))) }; struct mm_b1_format { /* Conditional branch format (microMIPS) */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rs : 3, - BITFIELD_FIELD(signed int simmediate : 7, - BITFIELD_FIELD(unsigned int : 16, /* Ignored */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 3, + __BITFIELD_FIELD(signed int simmediate : 7, + __BITFIELD_FIELD(unsigned int : 16, /* Ignored */ ;)))) }; struct mm16_m_format { /* Multi-word load/store format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int func : 4, - BITFIELD_FIELD(unsigned int rlist : 2, - BITFIELD_FIELD(unsigned int imm : 4, - BITFIELD_FIELD(unsigned int : 16, /* Ignored */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int func : 4, + __BITFIELD_FIELD(unsigned int rlist : 2, + __BITFIELD_FIELD(unsigned int imm : 4, + __BITFIELD_FIELD(unsigned int : 16, /* Ignored */ ;))))) }; struct mm16_rb_format { /* Signed immediate format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 3, - BITFIELD_FIELD(unsigned int base : 3, - BITFIELD_FIELD(signed int simmediate : 4, - BITFIELD_FIELD(unsigned int : 16, /* Ignored */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 3, + __BITFIELD_FIELD(unsigned int base : 3, + __BITFIELD_FIELD(signed int simmediate : 4, + __BITFIELD_FIELD(unsigned int : 16, /* Ignored */ ;))))) }; struct mm16_r3_format { /* Load from global pointer format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 3, - BITFIELD_FIELD(signed int simmediate : 7, - BITFIELD_FIELD(unsigned int : 16, /* Ignored */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 3, + __BITFIELD_FIELD(signed int simmediate : 7, + __BITFIELD_FIELD(unsigned int : 16, /* Ignored */ ;)))) }; struct mm16_r5_format { /* Load/store from stack pointer format */ - BITFIELD_FIELD(unsigned int opcode : 6, - BITFIELD_FIELD(unsigned int rt : 5, - BITFIELD_FIELD(signed int simmediate : 5, - BITFIELD_FIELD(unsigned int : 16, /* Ignored */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(signed int simmediate : 5, + __BITFIELD_FIELD(unsigned int : 16, /* Ignored */ ;)))) }; @@ -810,57 +810,57 @@ struct mm16_r5_format { /* Load/store from stack pointer format */ * MIPS16e instruction formats (16-bit length) */ struct m16e_rr { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int rx : 3, - BITFIELD_FIELD(unsigned int nd : 1, - BITFIELD_FIELD(unsigned int l : 1, - BITFIELD_FIELD(unsigned int ra : 1, - BITFIELD_FIELD(unsigned int func : 5, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int rx : 3, + __BITFIELD_FIELD(unsigned int nd : 1, + __BITFIELD_FIELD(unsigned int l : 1, + __BITFIELD_FIELD(unsigned int ra : 1, + __BITFIELD_FIELD(unsigned int func : 5, ;)))))) }; struct m16e_jal { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int x : 1, - BITFIELD_FIELD(unsigned int imm20_16 : 5, - BITFIELD_FIELD(signed int imm25_21 : 5, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int x : 1, + __BITFIELD_FIELD(unsigned int imm20_16 : 5, + __BITFIELD_FIELD(signed int imm25_21 : 5, ;)))) }; struct m16e_i64 { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int func : 3, - BITFIELD_FIELD(unsigned int imm : 8, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int func : 3, + __BITFIELD_FIELD(unsigned int imm : 8, ;))) }; struct m16e_ri64 { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int func : 3, - BITFIELD_FIELD(unsigned int ry : 3, - BITFIELD_FIELD(unsigned int imm : 5, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int func : 3, + __BITFIELD_FIELD(unsigned int ry : 3, + __BITFIELD_FIELD(unsigned int imm : 5, ;)))) }; struct m16e_ri { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int rx : 3, - BITFIELD_FIELD(unsigned int imm : 8, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int rx : 3, + __BITFIELD_FIELD(unsigned int imm : 8, ;))) }; struct m16e_rri { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int rx : 3, - BITFIELD_FIELD(unsigned int ry : 3, - BITFIELD_FIELD(unsigned int imm : 5, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int rx : 3, + __BITFIELD_FIELD(unsigned int ry : 3, + __BITFIELD_FIELD(unsigned int imm : 5, ;)))) }; struct m16e_i8 { - BITFIELD_FIELD(unsigned int opcode : 5, - BITFIELD_FIELD(unsigned int func : 3, - BITFIELD_FIELD(unsigned int imm : 8, + __BITFIELD_FIELD(unsigned int opcode : 5, + __BITFIELD_FIELD(unsigned int func : 3, + __BITFIELD_FIELD(unsigned int imm : 8, ;))) }; diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h index d6e154a..2692abb 100644 --- a/arch/mips/include/uapi/asm/unistd.h +++ b/arch/mips/include/uapi/asm/unistd.h @@ -371,11 +371,12 @@ #define __NR_finit_module (__NR_Linux + 348) #define __NR_sched_setattr (__NR_Linux + 349) #define __NR_sched_getattr (__NR_Linux + 350) +#define __NR_renameat2 (__NR_Linux + 351) /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 350 +#define __NR_Linux_syscalls 351 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ @@ -699,11 +700,12 @@ #define __NR_getdents64 (__NR_Linux + 308) #define __NR_sched_setattr (__NR_Linux + 309) #define __NR_sched_getattr (__NR_Linux + 310) +#define __NR_renameat2 (__NR_Linux + 311) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 310 +#define __NR_Linux_syscalls 311 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ @@ -1031,11 +1033,12 @@ #define __NR_finit_module (__NR_Linux + 312) #define __NR_sched_setattr (__NR_Linux + 313) #define __NR_sched_getattr (__NR_Linux + 314) +#define __NR_renameat2 (__NR_Linux + 315) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 314 +#define __NR_Linux_syscalls 315 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index e40971b..037a44d 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -124,14 +124,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, "kscratch registers\t: %d\n", hweight8(cpu_data[n].kscratch_mask)); seq_printf(m, "core\t\t\t: %d\n", cpu_data[n].core); -#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_MIPS_MT_SMTC) - if (cpu_has_mipsmt) { - seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id); -#if defined(CONFIG_MIPS_MT_SMTC) - seq_printf(m, "TC\t\t\t: %d\n", cpu_data[n].tc_id); -#endif - } -#endif + sprintf(fmt, "VCE%%c exceptions\t\t: %s\n", cpu_has_vce ? "%u" : "not available"); seq_printf(m, fmt, 'D', vced_count); diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index fdc70b4..3245474 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -577,3 +577,4 @@ EXPORT(sys_call_table) PTR sys_finit_module PTR sys_sched_setattr PTR sys_sched_getattr /* 4350 */ + PTR sys_renameat2 diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index dd99c328..be2fedd 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -430,4 +430,5 @@ EXPORT(sys_call_table) PTR sys_getdents64 PTR sys_sched_setattr PTR sys_sched_getattr /* 5310 */ + PTR sys_renameat2 .size sys_call_table,.-sys_call_table diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index f68d2f4..c1dbcda 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -423,4 +423,5 @@ EXPORT(sysn32_call_table) PTR sys_finit_module PTR sys_sched_setattr PTR sys_sched_getattr + PTR sys_renameat2 /* 6315 */ .size sysn32_call_table,.-sysn32_call_table diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 70f6ace..f1343cc 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -556,4 +556,5 @@ EXPORT(sys32_call_table) PTR sys_finit_module PTR sys_sched_setattr PTR sys_sched_getattr /* 4350 */ + PTR sys_renameat2 .size sys32_call_table,.-sys32_call_table diff --git a/arch/mips/lantiq/dts/easy50712.dts b/arch/mips/lantiq/dts/easy50712.dts index fac1f5b..143b8a3 100644 --- a/arch/mips/lantiq/dts/easy50712.dts +++ b/arch/mips/lantiq/dts/easy50712.dts @@ -8,6 +8,7 @@ }; memory@0 { + device_type = "memory"; reg = <0x0 0x2000000>; }; diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S index 2e4825e..9901237 100644 --- a/arch/mips/lib/csum_partial.S +++ b/arch/mips/lib/csum_partial.S @@ -56,14 +56,20 @@ #define UNIT(unit) ((unit)*NBYTES) #define ADDC(sum,reg) \ + .set push; \ + .set noat; \ ADD sum, reg; \ sltu v1, sum, reg; \ ADD sum, v1; \ + .set pop #define ADDC32(sum,reg) \ + .set push; \ + .set noat; \ addu sum, reg; \ sltu v1, sum, reg; \ addu sum, v1; \ + .set pop #define CSUM_BIGCHUNK1(src, offset, sum, _t0, _t1, _t2, _t3) \ LOAD _t0, (offset + UNIT(0))(src); \ @@ -710,6 +716,8 @@ LEAF(csum_partial) ADDC(sum, t2) .Ldone\@: /* fold checksum */ + .set push + .set noat #ifdef USE_DOUBLE dsll32 v1, sum, 0 daddu sum, v1 @@ -732,6 +740,7 @@ LEAF(csum_partial) or sum, sum, t0 1: #endif + .set pop .set reorder ADDC32(sum, psum) jr ra diff --git a/arch/mips/lib/delay.c b/arch/mips/lib/delay.c index 44713af..705cfb7 100644 --- a/arch/mips/lib/delay.c +++ b/arch/mips/lib/delay.c @@ -6,7 +6,7 @@ * Copyright (C) 1994 by Waldorf Electronics * Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle * Copyright (C) 1999, 2000 Silicon Graphics, Inc. - * Copyright (C) 2007 Maciej W. Rozycki + * Copyright (C) 2007, 2014 Maciej W. Rozycki */ #include <linux/module.h> #include <linux/param.h> @@ -15,6 +15,12 @@ #include <asm/compiler.h> #include <asm/war.h> +#ifndef CONFIG_CPU_DADDI_WORKAROUNDS +#define GCC_DADDI_IMM_ASM() "I" +#else +#define GCC_DADDI_IMM_ASM() "r" +#endif + void __delay(unsigned long loops) { __asm__ __volatile__ ( @@ -22,13 +28,13 @@ void __delay(unsigned long loops) " .align 3 \n" "1: bnez %0, 1b \n" #if BITS_PER_LONG == 32 - " subu %0, 1 \n" + " subu %0, %1 \n" #else - " dsubu %0, 1 \n" + " dsubu %0, %1 \n" #endif " .set reorder \n" : "=r" (loops) - : "0" (loops)); + : GCC_DADDI_IMM_ASM() (1), "0" (loops)); } EXPORT_SYMBOL(__delay); diff --git a/arch/mips/lib/strncpy_user.S b/arch/mips/lib/strncpy_user.S index d3301cd..3c32baf 100644 --- a/arch/mips/lib/strncpy_user.S +++ b/arch/mips/lib/strncpy_user.S @@ -35,7 +35,6 @@ LEAF(__strncpy_from_\func\()_asm) bnez v0, .Lfault\@ FEXPORT(__strncpy_from_\func\()_nocheck_asm) - .set noreorder move t0, zero move v1, a1 .ifeqs "\func","kernel" @@ -45,21 +44,21 @@ FEXPORT(__strncpy_from_\func\()_nocheck_asm) .endif PTR_ADDIU v1, 1 R10KCBARRIER(0(ra)) + sb v0, (a0) beqz v0, 2f - sb v0, (a0) PTR_ADDIU t0, 1 + PTR_ADDIU a0, 1 bne t0, a2, 1b - PTR_ADDIU a0, 1 2: PTR_ADDU v0, a1, t0 xor v0, a1 bltz v0, .Lfault\@ - nop + move v0, t0 jr ra # return n - move v0, t0 END(__strncpy_from_\func\()_asm) -.Lfault\@: jr ra - li v0, -EFAULT +.Lfault\@: + li v0, -EFAULT + jr ra .section __ex_table,"a" PTR 1b, .Lfault\@ diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig index 7397be2..603d79a 100644 --- a/arch/mips/loongson/Kconfig +++ b/arch/mips/loongson/Kconfig @@ -64,7 +64,6 @@ config LEMOTE_MACH3A bool "Lemote Loongson 3A family machines" select ARCH_SPARSEMEM_ENABLE select GENERIC_ISA_DMA_SUPPORT_BROKEN - select GENERIC_HARDIRQS_NO__DO_IRQ select BOOT_ELF32 select BOARD_SCACHE select CSRC_R4K diff --git a/arch/mips/loongson/lemote-2f/clock.c b/arch/mips/loongson/lemote-2f/clock.c index e1f427f..67dd94e 100644 --- a/arch/mips/loongson/lemote-2f/clock.c +++ b/arch/mips/loongson/lemote-2f/clock.c @@ -91,6 +91,7 @@ EXPORT_SYMBOL(clk_put); int clk_set_rate(struct clk *clk, unsigned long rate) { + unsigned int rate_khz = rate / 1000; int ret = 0; int regval; int i; @@ -111,10 +112,10 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (loongson2_clockmod_table[i].frequency == CPUFREQ_ENTRY_INVALID) continue; - if (rate == loongson2_clockmod_table[i].frequency) + if (rate_khz == loongson2_clockmod_table[i].frequency) break; } - if (rate != loongson2_clockmod_table[i].frequency) + if (rate_khz != loongson2_clockmod_table[i].frequency) return -ENOTSUPP; clk->rate = rate; diff --git a/arch/mips/mm/tlb-funcs.S b/arch/mips/mm/tlb-funcs.S index 30a494d..a5427c6 100644 --- a/arch/mips/mm/tlb-funcs.S +++ b/arch/mips/mm/tlb-funcs.S @@ -16,8 +16,10 @@ #define FASTPATH_SIZE 128 +EXPORT(tlbmiss_handler_setup_pgd_start) LEAF(tlbmiss_handler_setup_pgd) - .space 16 * 4 +1: j 1b /* Dummy, will be replaced. */ + .space 64 END(tlbmiss_handler_setup_pgd) EXPORT(tlbmiss_handler_setup_pgd_end) diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index ee88367..f99ec587 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -1422,16 +1422,17 @@ static void build_r4000_tlb_refill_handler(void) extern u32 handle_tlbl[], handle_tlbl_end[]; extern u32 handle_tlbs[], handle_tlbs_end[]; extern u32 handle_tlbm[], handle_tlbm_end[]; -extern u32 tlbmiss_handler_setup_pgd[], tlbmiss_handler_setup_pgd_end[]; +extern u32 tlbmiss_handler_setup_pgd_start[], tlbmiss_handler_setup_pgd[]; +extern u32 tlbmiss_handler_setup_pgd_end[]; static void build_setup_pgd(void) { const int a0 = 4; const int __maybe_unused a1 = 5; const int __maybe_unused a2 = 6; - u32 *p = tlbmiss_handler_setup_pgd; + u32 *p = tlbmiss_handler_setup_pgd_start; const int tlbmiss_handler_setup_pgd_size = - tlbmiss_handler_setup_pgd_end - tlbmiss_handler_setup_pgd; + tlbmiss_handler_setup_pgd_end - tlbmiss_handler_setup_pgd_start; #ifndef CONFIG_MIPS_PGD_C0_CONTEXT long pgdc = (long)pgd_current; #endif diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/ralink/dts/mt7620a_eval.dts index 35eb874..709f581 100644 --- a/arch/mips/ralink/dts/mt7620a_eval.dts +++ b/arch/mips/ralink/dts/mt7620a_eval.dts @@ -7,6 +7,7 @@ model = "Ralink MT7620A evaluation board"; memory@0 { + device_type = "memory"; reg = <0x0 0x2000000>; }; diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/ralink/dts/rt2880_eval.dts index 322d700..0a685db 100644 --- a/arch/mips/ralink/dts/rt2880_eval.dts +++ b/arch/mips/ralink/dts/rt2880_eval.dts @@ -7,6 +7,7 @@ model = "Ralink RT2880 evaluation board"; memory@0 { + device_type = "memory"; reg = <0x8000000 0x2000000>; }; diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts index 0ac73ea..ec9e9a0 100644 --- a/arch/mips/ralink/dts/rt3052_eval.dts +++ b/arch/mips/ralink/dts/rt3052_eval.dts @@ -7,6 +7,7 @@ model = "Ralink RT3052 evaluation board"; memory@0 { + device_type = "memory"; reg = <0x0 0x2000000>; }; diff --git a/arch/mips/ralink/dts/rt3883_eval.dts b/arch/mips/ralink/dts/rt3883_eval.dts index 2fa6b33..e8df21a 100644 --- a/arch/mips/ralink/dts/rt3883_eval.dts +++ b/arch/mips/ralink/dts/rt3883_eval.dts @@ -7,6 +7,7 @@ model = "Ralink RT3883 evaluation board"; memory@0 { + device_type = "memory"; reg = <0x0 0x2000000>; }; diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 1faefed..108d48e 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -22,6 +22,7 @@ config PARISC select GENERIC_SMP_IDLE_THREAD select GENERIC_STRNCPY_FROM_USER select SYSCTL_ARCH_UNALIGN_ALLOW + select SYSCTL_EXCEPTION_TRACE select HAVE_MOD_ARCH_SPECIFIC select VIRT_TO_BUS select MODULES_USE_ELF_RELA diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index 198a86f..d951c96 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h @@ -55,6 +55,11 @@ #define STACK_TOP TASK_SIZE #define STACK_TOP_MAX DEFAULT_TASK_SIZE +/* Allow bigger stacks for 64-bit processes */ +#define STACK_SIZE_MAX (USER_WIDE_MODE \ + ? (1 << 30) /* 1 GB */ \ + : (CONFIG_MAX_STACK_SIZE_MB*1024*1024)) + #endif #ifndef __ASSEMBLY__ diff --git a/arch/parisc/include/uapi/asm/unistd.h b/arch/parisc/include/uapi/asm/unistd.h index 265ae51..47e0e21 100644 --- a/arch/parisc/include/uapi/asm/unistd.h +++ b/arch/parisc/include/uapi/asm/unistd.h @@ -829,8 +829,9 @@ #define __NR_sched_setattr (__NR_Linux + 334) #define __NR_sched_getattr (__NR_Linux + 335) #define __NR_utimes (__NR_Linux + 336) +#define __NR_renameat2 (__NR_Linux + 337) -#define __NR_Linux_syscalls (__NR_utimes + 1) +#define __NR_Linux_syscalls (__NR_renameat2 + 1) #define __IGNORE_select /* newselect */ diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index 31ffa9b..e1ffea2 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -72,10 +72,10 @@ static unsigned long mmap_upper_limit(void) { unsigned long stack_base; - /* Limit stack size to 1GB - see setup_arg_pages() in fs/exec.c */ + /* Limit stack size - see setup_arg_pages() in fs/exec.c */ stack_base = rlimit_max(RLIMIT_STACK); - if (stack_base > (1 << 30)) - stack_base = 1 << 30; + if (stack_base > STACK_SIZE_MAX) + stack_base = STACK_SIZE_MAX; return PAGE_ALIGN(STACK_TOP - stack_base); } diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index a63bb179..8387860 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -589,10 +589,13 @@ cas_nocontend: # endif /* ENABLE_LWS_DEBUG */ + rsm PSW_SM_I, %r0 /* Disable interrupts */ + /* COW breaks can cause contention on UP systems */ LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */ cmpb,<>,n %r0, %r28, cas_action /* Did we get it? */ cas_wouldblock: ldo 2(%r0), %r28 /* 2nd case */ + ssm PSW_SM_I, %r0 b lws_exit /* Contended... */ ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ @@ -619,15 +622,17 @@ cas_action: stw %r1, 4(%sr2,%r20) #endif /* The load and store could fail */ -1: ldw 0(%sr3,%r26), %r28 +1: ldw,ma 0(%sr3,%r26), %r28 sub,<> %r28, %r25, %r0 -2: stw %r24, 0(%sr3,%r26) +2: stw,ma %r24, 0(%sr3,%r26) /* Free lock */ - stw %r20, 0(%sr2,%r20) + stw,ma %r20, 0(%sr2,%r20) #if ENABLE_LWS_DEBUG /* Clear thread register indicator */ stw %r0, 4(%sr2,%r20) #endif + /* Enable interrupts */ + ssm PSW_SM_I, %r0 /* Return to userspace, set no error */ b lws_exit copy %r0, %r21 @@ -639,6 +644,7 @@ cas_action: #if ENABLE_LWS_DEBUG stw %r0, 4(%sr2,%r20) #endif + ssm PSW_SM_I, %r0 b lws_exit ldo -EFAULT(%r0),%r21 /* set errno */ nop diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 83ead0e..c5fa7a6 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -432,6 +432,7 @@ ENTRY_SAME(sched_setattr) ENTRY_SAME(sched_getattr) /* 335 */ ENTRY_COMP(utimes) + ENTRY_SAME(renameat2) /* Nothing yet */ diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index 1cd1d0c..47ee620 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -25,6 +25,7 @@ #include <linux/interrupt.h> #include <linux/console.h> #include <linux/bug.h> +#include <linux/ratelimit.h> #include <asm/assembly.h> #include <asm/uaccess.h> @@ -42,9 +43,6 @@ #include "../math-emu/math-emu.h" /* for handle_fpe() */ -#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */ - /* dumped to the console via printk) */ - #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) DEFINE_SPINLOCK(pa_dbit_lock); #endif @@ -160,6 +158,17 @@ void show_regs(struct pt_regs *regs) } } +static DEFINE_RATELIMIT_STATE(_hppa_rs, + DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); + +#define parisc_printk_ratelimited(critical, regs, fmt, ...) { \ + if ((critical || show_unhandled_signals) && __ratelimit(&_hppa_rs)) { \ + printk(fmt, ##__VA_ARGS__); \ + show_regs(regs); \ + } \ +} + + static void do_show_stack(struct unwind_frame_info *info) { int i = 1; @@ -229,12 +238,10 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) if (err == 0) return; /* STFU */ - printk(KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n", + parisc_printk_ratelimited(1, regs, + KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n", current->comm, task_pid_nr(current), str, err, regs->iaoq[0]); -#ifdef PRINT_USER_FAULTS - /* XXX for debugging only */ - show_regs(regs); -#endif + return; } @@ -321,14 +328,11 @@ static void handle_break(struct pt_regs *regs) (tt == BUG_TRAP_TYPE_NONE) ? 9 : 0); } -#ifdef PRINT_USER_FAULTS - if (unlikely(iir != GDB_BREAK_INSN)) { - printk(KERN_DEBUG "break %d,%d: pid=%d command='%s'\n", + if (unlikely(iir != GDB_BREAK_INSN)) + parisc_printk_ratelimited(0, regs, + KERN_DEBUG "break %d,%d: pid=%d command='%s'\n", iir & 31, (iir>>13) & ((1<<13)-1), task_pid_nr(current), current->comm); - show_regs(regs); - } -#endif /* send standard GDB signal */ handle_gdb_break(regs, TRAP_BRKPT); @@ -758,11 +762,9 @@ void notrace handle_interruption(int code, struct pt_regs *regs) default: if (user_mode(regs)) { -#ifdef PRINT_USER_FAULTS - printk(KERN_DEBUG "\nhandle_interruption() pid=%d command='%s'\n", - task_pid_nr(current), current->comm); - show_regs(regs); -#endif + parisc_printk_ratelimited(0, regs, KERN_DEBUG + "handle_interruption() pid=%d command='%s'\n", + task_pid_nr(current), current->comm); /* SIGBUS, for lack of a better one. */ si.si_signo = SIGBUS; si.si_code = BUS_OBJERR; @@ -779,16 +781,10 @@ void notrace handle_interruption(int code, struct pt_regs *regs) if (user_mode(regs)) { if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) { -#ifdef PRINT_USER_FAULTS - if (fault_space == 0) - printk(KERN_DEBUG "User Fault on Kernel Space "); - else - printk(KERN_DEBUG "User Fault (long pointer) (fault %d) ", - code); - printk(KERN_CONT "pid=%d command='%s'\n", - task_pid_nr(current), current->comm); - show_regs(regs); -#endif + parisc_printk_ratelimited(0, regs, KERN_DEBUG + "User fault %d on space 0x%08lx, pid=%d command='%s'\n", + code, fault_space, + task_pid_nr(current), current->comm); si.si_signo = SIGSEGV; si.si_errno = 0; si.si_code = SEGV_MAPERR; diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index 7475507..3ca9c11 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -19,10 +19,6 @@ #include <asm/uaccess.h> #include <asm/traps.h> -#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */ - /* dumped to the console via printk) */ - - /* Various important other fields */ #define bit22set(x) (x & 0x00000200) #define bits23_25set(x) (x & 0x000001c0) @@ -34,6 +30,8 @@ DEFINE_PER_CPU(struct exception_data, exception_data); +int show_unhandled_signals = 1; + /* * parisc_acctyp(unsigned int inst) -- * Given a PA-RISC memory access instruction, determine if the @@ -173,6 +171,32 @@ int fixup_exception(struct pt_regs *regs) return 0; } +/* + * Print out info about fatal segfaults, if the show_unhandled_signals + * sysctl is set: + */ +static inline void +show_signal_msg(struct pt_regs *regs, unsigned long code, + unsigned long address, struct task_struct *tsk, + struct vm_area_struct *vma) +{ + if (!unhandled_signal(tsk, SIGSEGV)) + return; + + if (!printk_ratelimit()) + return; + + pr_warn("\n"); + pr_warn("do_page_fault() command='%s' type=%lu address=0x%08lx", + tsk->comm, code, address); + print_vma_addr(KERN_CONT " in ", regs->iaoq[0]); + if (vma) + pr_warn(" vm_start = 0x%08lx, vm_end = 0x%08lx\n", + vma->vm_start, vma->vm_end); + + show_regs(regs); +} + void do_page_fault(struct pt_regs *regs, unsigned long code, unsigned long address) { @@ -270,16 +294,8 @@ bad_area: if (user_mode(regs)) { struct siginfo si; -#ifdef PRINT_USER_FAULTS - printk(KERN_DEBUG "\n"); - printk(KERN_DEBUG "do_page_fault() pid=%d command='%s' type=%lu address=0x%08lx\n", - task_pid_nr(tsk), tsk->comm, code, address); - if (vma) { - printk(KERN_DEBUG "vm_start = 0x%08lx, vm_end = 0x%08lx\n", - vma->vm_start, vma->vm_end); - } - show_regs(regs); -#endif + show_signal_msg(regs, code, address, tsk, vma); + switch (code) { case 15: /* Data TLB miss fault/Data page fault */ /* send SIGSEGV when outside of vma */ diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 122a580..7e711bd 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -813,9 +813,6 @@ static void __init clocksource_init(void) static int decrementer_set_next_event(unsigned long evt, struct clock_event_device *dev) { - /* Don't adjust the decrementer if some irq work is pending */ - if (test_irq_work_pending()) - return 0; __get_cpu_var(decrementers_next_tb) = get_tb_or_rtc() + evt; set_dec(evt); diff --git a/arch/powerpc/net/bpf_jit_64.S b/arch/powerpc/net/bpf_jit_64.S index e76eba7..8f87d92 100644 --- a/arch/powerpc/net/bpf_jit_64.S +++ b/arch/powerpc/net/bpf_jit_64.S @@ -78,7 +78,7 @@ sk_load_byte_positive_offset: blr /* - * BPF_S_LDX_B_MSH: ldxb 4*([offset]&0xf) + * BPF_LDX | BPF_B | BPF_MSH: ldxb 4*([offset]&0xf) * r_addr is the offset value */ .globl sk_load_byte_msh diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 808ce1c..6dcdade 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -79,19 +79,11 @@ static void bpf_jit_build_prologue(struct sk_filter *fp, u32 *image, } switch (filter[0].code) { - case BPF_S_RET_K: - case BPF_S_LD_W_LEN: - case BPF_S_ANC_PROTOCOL: - case BPF_S_ANC_IFINDEX: - case BPF_S_ANC_MARK: - case BPF_S_ANC_RXHASH: - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - case BPF_S_ANC_CPU: - case BPF_S_ANC_QUEUE: - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: + case BPF_RET | BPF_K: + case BPF_LD | BPF_W | BPF_LEN: + case BPF_LD | BPF_W | BPF_ABS: + case BPF_LD | BPF_H | BPF_ABS: + case BPF_LD | BPF_B | BPF_ABS: /* first instruction sets A register (or is RET 'constant') */ break; default: @@ -144,6 +136,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, for (i = 0; i < flen; i++) { unsigned int K = filter[i].k; + u16 code = bpf_anc_helper(&filter[i]); /* * addrs[] maps a BPF bytecode address into a real offset from @@ -151,35 +144,35 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, */ addrs[i] = ctx->idx * 4; - switch (filter[i].code) { + switch (code) { /*** ALU ops ***/ - case BPF_S_ALU_ADD_X: /* A += X; */ + case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */ ctx->seen |= SEEN_XREG; PPC_ADD(r_A, r_A, r_X); break; - case BPF_S_ALU_ADD_K: /* A += K; */ + case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ if (!K) break; PPC_ADDI(r_A, r_A, IMM_L(K)); if (K >= 32768) PPC_ADDIS(r_A, r_A, IMM_HA(K)); break; - case BPF_S_ALU_SUB_X: /* A -= X; */ + case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */ ctx->seen |= SEEN_XREG; PPC_SUB(r_A, r_A, r_X); break; - case BPF_S_ALU_SUB_K: /* A -= K */ + case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ if (!K) break; PPC_ADDI(r_A, r_A, IMM_L(-K)); if (K >= 32768) PPC_ADDIS(r_A, r_A, IMM_HA(-K)); break; - case BPF_S_ALU_MUL_X: /* A *= X; */ + case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */ ctx->seen |= SEEN_XREG; PPC_MUL(r_A, r_A, r_X); break; - case BPF_S_ALU_MUL_K: /* A *= K */ + case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ if (K < 32768) PPC_MULI(r_A, r_A, K); else { @@ -187,7 +180,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_MUL(r_A, r_A, r_scratch1); } break; - case BPF_S_ALU_MOD_X: /* A %= X; */ + case BPF_ALU | BPF_MOD | BPF_X: /* A %= X; */ ctx->seen |= SEEN_XREG; PPC_CMPWI(r_X, 0); if (ctx->pc_ret0 != -1) { @@ -201,13 +194,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_MUL(r_scratch1, r_X, r_scratch1); PPC_SUB(r_A, r_A, r_scratch1); break; - case BPF_S_ALU_MOD_K: /* A %= K; */ + case BPF_ALU | BPF_MOD | BPF_K: /* A %= K; */ PPC_LI32(r_scratch2, K); PPC_DIVWU(r_scratch1, r_A, r_scratch2); PPC_MUL(r_scratch1, r_scratch2, r_scratch1); PPC_SUB(r_A, r_A, r_scratch1); break; - case BPF_S_ALU_DIV_X: /* A /= X; */ + case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ ctx->seen |= SEEN_XREG; PPC_CMPWI(r_X, 0); if (ctx->pc_ret0 != -1) { @@ -223,17 +216,17 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, } PPC_DIVWU(r_A, r_A, r_X); break; - case BPF_S_ALU_DIV_K: /* A /= K */ + case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */ if (K == 1) break; PPC_LI32(r_scratch1, K); PPC_DIVWU(r_A, r_A, r_scratch1); break; - case BPF_S_ALU_AND_X: + case BPF_ALU | BPF_AND | BPF_X: ctx->seen |= SEEN_XREG; PPC_AND(r_A, r_A, r_X); break; - case BPF_S_ALU_AND_K: + case BPF_ALU | BPF_AND | BPF_K: if (!IMM_H(K)) PPC_ANDI(r_A, r_A, K); else { @@ -241,51 +234,51 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_AND(r_A, r_A, r_scratch1); } break; - case BPF_S_ALU_OR_X: + case BPF_ALU | BPF_OR | BPF_X: ctx->seen |= SEEN_XREG; PPC_OR(r_A, r_A, r_X); break; - case BPF_S_ALU_OR_K: + case BPF_ALU | BPF_OR | BPF_K: if (IMM_L(K)) PPC_ORI(r_A, r_A, IMM_L(K)); if (K >= 65536) PPC_ORIS(r_A, r_A, IMM_H(K)); break; - case BPF_S_ANC_ALU_XOR_X: - case BPF_S_ALU_XOR_X: /* A ^= X */ + case BPF_ANC | SKF_AD_ALU_XOR_X: + case BPF_ALU | BPF_XOR | BPF_X: /* A ^= X */ ctx->seen |= SEEN_XREG; PPC_XOR(r_A, r_A, r_X); break; - case BPF_S_ALU_XOR_K: /* A ^= K */ + case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ if (IMM_L(K)) PPC_XORI(r_A, r_A, IMM_L(K)); if (K >= 65536) PPC_XORIS(r_A, r_A, IMM_H(K)); break; - case BPF_S_ALU_LSH_X: /* A <<= X; */ + case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */ ctx->seen |= SEEN_XREG; PPC_SLW(r_A, r_A, r_X); break; - case BPF_S_ALU_LSH_K: + case BPF_ALU | BPF_LSH | BPF_K: if (K == 0) break; else PPC_SLWI(r_A, r_A, K); break; - case BPF_S_ALU_RSH_X: /* A >>= X; */ + case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */ ctx->seen |= SEEN_XREG; PPC_SRW(r_A, r_A, r_X); break; - case BPF_S_ALU_RSH_K: /* A >>= K; */ + case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */ if (K == 0) break; else PPC_SRWI(r_A, r_A, K); break; - case BPF_S_ALU_NEG: + case BPF_ALU | BPF_NEG: PPC_NEG(r_A, r_A); break; - case BPF_S_RET_K: + case BPF_RET | BPF_K: PPC_LI32(r_ret, K); if (!K) { if (ctx->pc_ret0 == -1) @@ -312,7 +305,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_BLR(); } break; - case BPF_S_RET_A: + case BPF_RET | BPF_A: PPC_MR(r_ret, r_A); if (i != flen - 1) { if (ctx->seen) @@ -321,53 +314,53 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_BLR(); } break; - case BPF_S_MISC_TAX: /* X = A */ + case BPF_MISC | BPF_TAX: /* X = A */ PPC_MR(r_X, r_A); break; - case BPF_S_MISC_TXA: /* A = X */ + case BPF_MISC | BPF_TXA: /* A = X */ ctx->seen |= SEEN_XREG; PPC_MR(r_A, r_X); break; /*** Constant loads/M[] access ***/ - case BPF_S_LD_IMM: /* A = K */ + case BPF_LD | BPF_IMM: /* A = K */ PPC_LI32(r_A, K); break; - case BPF_S_LDX_IMM: /* X = K */ + case BPF_LDX | BPF_IMM: /* X = K */ PPC_LI32(r_X, K); break; - case BPF_S_LD_MEM: /* A = mem[K] */ + case BPF_LD | BPF_MEM: /* A = mem[K] */ PPC_MR(r_A, r_M + (K & 0xf)); ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); break; - case BPF_S_LDX_MEM: /* X = mem[K] */ + case BPF_LDX | BPF_MEM: /* X = mem[K] */ PPC_MR(r_X, r_M + (K & 0xf)); ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); break; - case BPF_S_ST: /* mem[K] = A */ + case BPF_ST: /* mem[K] = A */ PPC_MR(r_M + (K & 0xf), r_A); ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); break; - case BPF_S_STX: /* mem[K] = X */ + case BPF_STX: /* mem[K] = X */ PPC_MR(r_M + (K & 0xf), r_X); ctx->seen |= SEEN_XREG | SEEN_MEM | (1<<(K & 0xf)); break; - case BPF_S_LD_W_LEN: /* A = skb->len; */ + case BPF_LD | BPF_W | BPF_LEN: /* A = skb->len; */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len)); break; - case BPF_S_LDX_W_LEN: /* X = skb->len; */ + case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */ PPC_LWZ_OFFS(r_X, r_skb, offsetof(struct sk_buff, len)); break; /*** Ancillary info loads ***/ - case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */ + case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); PPC_NTOHS_OFFS(r_A, r_skb, offsetof(struct sk_buff, protocol)); break; - case BPF_S_ANC_IFINDEX: + case BPF_ANC | SKF_AD_IFINDEX: PPC_LD_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, dev)); PPC_CMPDI(r_scratch1, 0); @@ -384,33 +377,33 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_LWZ_OFFS(r_A, r_scratch1, offsetof(struct net_device, ifindex)); break; - case BPF_S_ANC_MARK: + case BPF_ANC | SKF_AD_MARK: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, mark)); break; - case BPF_S_ANC_RXHASH: + case BPF_ANC | SKF_AD_RXHASH: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, hash)); break; - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: + case BPF_ANC | SKF_AD_VLAN_TAG: + case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, vlan_tci)); - if (filter[i].code == BPF_S_ANC_VLAN_TAG) + if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) PPC_ANDI(r_A, r_A, VLAN_VID_MASK); else PPC_ANDI(r_A, r_A, VLAN_TAG_PRESENT); break; - case BPF_S_ANC_QUEUE: + case BPF_ANC | SKF_AD_QUEUE: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, queue_mapping)); break; - case BPF_S_ANC_CPU: + case BPF_ANC | SKF_AD_CPU: #ifdef CONFIG_SMP /* * PACA ptr is r13: @@ -426,13 +419,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, break; /*** Absolute loads from packet header/data ***/ - case BPF_S_LD_W_ABS: + case BPF_LD | BPF_W | BPF_ABS: func = CHOOSE_LOAD_FUNC(K, sk_load_word); goto common_load; - case BPF_S_LD_H_ABS: + case BPF_LD | BPF_H | BPF_ABS: func = CHOOSE_LOAD_FUNC(K, sk_load_half); goto common_load; - case BPF_S_LD_B_ABS: + case BPF_LD | BPF_B | BPF_ABS: func = CHOOSE_LOAD_FUNC(K, sk_load_byte); common_load: /* Load from [K]. */ @@ -449,13 +442,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, break; /*** Indirect loads from packet header/data ***/ - case BPF_S_LD_W_IND: + case BPF_LD | BPF_W | BPF_IND: func = sk_load_word; goto common_load_ind; - case BPF_S_LD_H_IND: + case BPF_LD | BPF_H | BPF_IND: func = sk_load_half; goto common_load_ind; - case BPF_S_LD_B_IND: + case BPF_LD | BPF_B | BPF_IND: func = sk_load_byte; common_load_ind: /* @@ -473,31 +466,31 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_BCC(COND_LT, exit_addr); break; - case BPF_S_LDX_B_MSH: + case BPF_LDX | BPF_B | BPF_MSH: func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh); goto common_load; break; /*** Jump and branches ***/ - case BPF_S_JMP_JA: + case BPF_JMP | BPF_JA: if (K != 0) PPC_JMP(addrs[i + 1 + K]); break; - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGT_X: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGT | BPF_X: true_cond = COND_GT; goto cond_branch; - case BPF_S_JMP_JGE_K: - case BPF_S_JMP_JGE_X: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JGE | BPF_X: true_cond = COND_GE; goto cond_branch; - case BPF_S_JMP_JEQ_K: - case BPF_S_JMP_JEQ_X: + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JEQ | BPF_X: true_cond = COND_EQ; goto cond_branch; - case BPF_S_JMP_JSET_K: - case BPF_S_JMP_JSET_X: + case BPF_JMP | BPF_JSET | BPF_K: + case BPF_JMP | BPF_JSET | BPF_X: true_cond = COND_NE; /* Fall through */ cond_branch: @@ -508,20 +501,20 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, break; } - switch (filter[i].code) { - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JEQ_X: + switch (code) { + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JEQ | BPF_X: ctx->seen |= SEEN_XREG; PPC_CMPLW(r_A, r_X); break; - case BPF_S_JMP_JSET_X: + case BPF_JMP | BPF_JSET | BPF_X: ctx->seen |= SEEN_XREG; PPC_AND_DOT(r_scratch1, r_A, r_X); break; - case BPF_S_JMP_JEQ_K: - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGE_K: + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: if (K < 32768) PPC_CMPLWI(r_A, K); else { @@ -529,7 +522,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, PPC_CMPLW(r_A, r_scratch1); } break; - case BPF_S_JMP_JSET_K: + case BPF_JMP | BPF_JSET | BPF_K: if (K < 32768) /* PPC_ANDI is /only/ dot-form */ PPC_ANDI(r_scratch1, r_A, K); diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index 253fefe..5b51079 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -549,7 +549,8 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option) ret = ioda_eeh_phb_reset(hose, option); } else { bus = eeh_pe_bus_get(pe); - if (pci_is_root_bus(bus)) + if (pci_is_root_bus(bus) || + pci_is_root_bus(bus->parent)) ret = ioda_eeh_root_reset(hose, option); else ret = ioda_eeh_bridge_reset(hose, bus->self, option); diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index 228cf91..ffd1169 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -25,7 +25,6 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/phy.h> -#include <linux/phy_fixed.h> #include <linux/spi/spi.h> #include <linux/fsl_devices.h> #include <linux/fs_enet_pd.h> @@ -178,37 +177,6 @@ u32 get_baudrate(void) EXPORT_SYMBOL(get_baudrate); #endif /* CONFIG_CPM2 */ -#ifdef CONFIG_FIXED_PHY -static int __init of_add_fixed_phys(void) -{ - int ret; - struct device_node *np; - u32 *fixed_link; - struct fixed_phy_status status = {}; - - for_each_node_by_name(np, "ethernet") { - fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL); - if (!fixed_link) - continue; - - status.link = 1; - status.duplex = fixed_link[1]; - status.speed = fixed_link[2]; - status.pause = fixed_link[3]; - status.asym_pause = fixed_link[4]; - - ret = fixed_phy_add(PHY_POLL, fixed_link[0], &status); - if (ret) { - of_node_put(np); - return ret; - } - } - - return 0; -} -arch_initcall(of_add_fixed_phys); -#endif /* CONFIG_FIXED_PHY */ - #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) static __be32 __iomem *rstcr; diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c index cf3c008..23223cd 100644 --- a/arch/s390/crypto/aes_s390.c +++ b/arch/s390/crypto/aes_s390.c @@ -820,6 +820,9 @@ static int ctr_aes_crypt(struct blkcipher_desc *desc, long func, else memcpy(walk->iv, ctrptr, AES_BLOCK_SIZE); spin_unlock(&ctrblk_lock); + } else { + if (!nbytes) + memcpy(walk->iv, ctrptr, AES_BLOCK_SIZE); } /* * final block may be < AES_BLOCK_SIZE, copy only nbytes diff --git a/arch/s390/crypto/des_s390.c b/arch/s390/crypto/des_s390.c index 0a5aac8..7acb77f 100644 --- a/arch/s390/crypto/des_s390.c +++ b/arch/s390/crypto/des_s390.c @@ -429,6 +429,9 @@ static int ctr_desall_crypt(struct blkcipher_desc *desc, long func, else memcpy(walk->iv, ctrptr, DES_BLOCK_SIZE); spin_unlock(&ctrblk_lock); + } else { + if (!nbytes) + memcpy(walk->iv, ctrptr, DES_BLOCK_SIZE); } /* final block may be < DES_BLOCK_SIZE, copy only nbytes */ if (nbytes) { diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 452d3eb..a2cbd87 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -269,27 +269,17 @@ static void bpf_jit_noleaks(struct bpf_jit *jit, struct sock_filter *filter) EMIT4(0xa7c80000); /* Clear A if the first register does not set it. */ switch (filter[0].code) { - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: - case BPF_S_LD_W_LEN: - case BPF_S_LD_W_IND: - case BPF_S_LD_H_IND: - case BPF_S_LD_B_IND: - case BPF_S_LD_IMM: - case BPF_S_LD_MEM: - case BPF_S_MISC_TXA: - case BPF_S_ANC_PROTOCOL: - case BPF_S_ANC_PKTTYPE: - case BPF_S_ANC_IFINDEX: - case BPF_S_ANC_MARK: - case BPF_S_ANC_QUEUE: - case BPF_S_ANC_HATYPE: - case BPF_S_ANC_RXHASH: - case BPF_S_ANC_CPU: - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - case BPF_S_RET_K: + case BPF_LD | BPF_W | BPF_ABS: + case BPF_LD | BPF_H | BPF_ABS: + case BPF_LD | BPF_B | BPF_ABS: + case BPF_LD | BPF_W | BPF_LEN: + case BPF_LD | BPF_W | BPF_IND: + case BPF_LD | BPF_H | BPF_IND: + case BPF_LD | BPF_B | BPF_IND: + case BPF_LD | BPF_IMM: + case BPF_LD | BPF_MEM: + case BPF_MISC | BPF_TXA: + case BPF_RET | BPF_K: /* first instruction sets A register */ break; default: /* A = 0 */ @@ -304,15 +294,18 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, unsigned int K; int offset; unsigned int mask; + u16 code; K = filter->k; - switch (filter->code) { - case BPF_S_ALU_ADD_X: /* A += X */ + code = bpf_anc_helper(filter); + + switch (code) { + case BPF_ALU | BPF_ADD | BPF_X: /* A += X */ jit->seen |= SEEN_XREG; /* ar %r5,%r12 */ EMIT2(0x1a5c); break; - case BPF_S_ALU_ADD_K: /* A += K */ + case BPF_ALU | BPF_ADD | BPF_K: /* A += K */ if (!K) break; if (K <= 16383) @@ -325,12 +318,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* a %r5,<d(K)>(%r13) */ EMIT4_DISP(0x5a50d000, EMIT_CONST(K)); break; - case BPF_S_ALU_SUB_X: /* A -= X */ + case BPF_ALU | BPF_SUB | BPF_X: /* A -= X */ jit->seen |= SEEN_XREG; /* sr %r5,%r12 */ EMIT2(0x1b5c); break; - case BPF_S_ALU_SUB_K: /* A -= K */ + case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ if (!K) break; if (K <= 16384) @@ -343,12 +336,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* s %r5,<d(K)>(%r13) */ EMIT4_DISP(0x5b50d000, EMIT_CONST(K)); break; - case BPF_S_ALU_MUL_X: /* A *= X */ + case BPF_ALU | BPF_MUL | BPF_X: /* A *= X */ jit->seen |= SEEN_XREG; /* msr %r5,%r12 */ EMIT4(0xb252005c); break; - case BPF_S_ALU_MUL_K: /* A *= K */ + case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ if (K <= 16383) /* mhi %r5,K */ EMIT4_IMM(0xa75c0000, K); @@ -359,7 +352,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* ms %r5,<d(K)>(%r13) */ EMIT4_DISP(0x7150d000, EMIT_CONST(K)); break; - case BPF_S_ALU_DIV_X: /* A /= X */ + case BPF_ALU | BPF_DIV | BPF_X: /* A /= X */ jit->seen |= SEEN_XREG | SEEN_RET0; /* ltr %r12,%r12 */ EMIT2(0x12cc); @@ -370,7 +363,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* dlr %r4,%r12 */ EMIT4(0xb997004c); break; - case BPF_S_ALU_DIV_K: /* A /= K */ + case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */ if (K == 1) break; /* lhi %r4,0 */ @@ -378,7 +371,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* dl %r4,<d(K)>(%r13) */ EMIT6_DISP(0xe340d000, 0x0097, EMIT_CONST(K)); break; - case BPF_S_ALU_MOD_X: /* A %= X */ + case BPF_ALU | BPF_MOD | BPF_X: /* A %= X */ jit->seen |= SEEN_XREG | SEEN_RET0; /* ltr %r12,%r12 */ EMIT2(0x12cc); @@ -391,7 +384,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* lr %r5,%r4 */ EMIT2(0x1854); break; - case BPF_S_ALU_MOD_K: /* A %= K */ + case BPF_ALU | BPF_MOD | BPF_K: /* A %= K */ if (K == 1) { /* lhi %r5,0 */ EMIT4(0xa7580000); @@ -404,12 +397,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* lr %r5,%r4 */ EMIT2(0x1854); break; - case BPF_S_ALU_AND_X: /* A &= X */ + case BPF_ALU | BPF_AND | BPF_X: /* A &= X */ jit->seen |= SEEN_XREG; /* nr %r5,%r12 */ EMIT2(0x145c); break; - case BPF_S_ALU_AND_K: /* A &= K */ + case BPF_ALU | BPF_AND | BPF_K: /* A &= K */ if (test_facility(21)) /* nilf %r5,<K> */ EMIT6_IMM(0xc05b0000, K); @@ -417,12 +410,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* n %r5,<d(K)>(%r13) */ EMIT4_DISP(0x5450d000, EMIT_CONST(K)); break; - case BPF_S_ALU_OR_X: /* A |= X */ + case BPF_ALU | BPF_OR | BPF_X: /* A |= X */ jit->seen |= SEEN_XREG; /* or %r5,%r12 */ EMIT2(0x165c); break; - case BPF_S_ALU_OR_K: /* A |= K */ + case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ if (test_facility(21)) /* oilf %r5,<K> */ EMIT6_IMM(0xc05d0000, K); @@ -430,55 +423,55 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter, /* o %r5,<d(K)>(%r13) */ EMIT4_DISP(0x5650d000, EMIT_CONST(K)); break; - case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */ - case BPF_S_ALU_XOR_X: + case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */ + case BPF_ALU | BPF_XOR | BPF_X: jit->seen |= SEEN_XREG; /* xr %r5,%r12 */ EMIT2(0x175c); break; - case BPF_S_ALU_XOR_K: /* A ^= K */ + case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ if (!K) break; /* x %r5,<d(K)>(%r13) */ EMIT4_DISP(0x5750d000, EMIT_CONST(K)); break; - case BPF_S_ALU_LSH_X: /* A <<= X; */ + case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */ jit->seen |= SEEN_XREG; /* sll %r5,0(%r12) */ EMIT4(0x8950c000); break; - case BPF_S_ALU_LSH_K: /* A <<= K */ + case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */ if (K == 0) break; /* sll %r5,K */ EMIT4_DISP(0x89500000, K); break; - case BPF_S_ALU_RSH_X: /* A >>= X; */ + case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */ jit->seen |= SEEN_XREG; /* srl %r5,0(%r12) */ EMIT4(0x8850c000); break; - case BPF_S_ALU_RSH_K: /* A >>= K; */ + case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */ if (K == 0) break; /* srl %r5,K */ EMIT4_DISP(0x88500000, K); break; - case BPF_S_ALU_NEG: /* A = -A */ + case BPF_ALU | BPF_NEG: /* A = -A */ /* lnr %r5,%r5 */ EMIT2(0x1155); break; - case BPF_S_JMP_JA: /* ip += K */ + case BPF_JMP | BPF_JA: /* ip += K */ offset = addrs[i + K] + jit->start - jit->prg; EMIT4_PCREL(0xa7f40000, offset); break; - case BPF_S_JMP_JGT_K: /* ip += (A > K) ? jt : jf */ + case BPF_JMP | BPF_JGT | BPF_K: /* ip += (A > K) ? jt : jf */ mask = 0x200000; /* jh */ goto kbranch; - case BPF_S_JMP_JGE_K: /* ip += (A >= K) ? jt : jf */ + case BPF_JMP | BPF_JGE | BPF_K: /* ip += (A >= K) ? jt : jf */ mask = 0xa00000; /* jhe */ goto kbranch; - case BPF_S_JMP_JEQ_K: /* ip += (A == K) ? jt : jf */ + case BPF_JMP | BPF_JEQ | BPF_K: /* ip += (A == K) ? jt : jf */ mask = 0x800000; /* je */ kbranch: /* Emit compare if the branch targets are different */ if (filter->jt != filter->jf) { @@ -511,7 +504,7 @@ branch: if (filter->jt == filter->jf) { EMIT4_PCREL(0xa7040000 | (mask ^ 0xf00000), offset); } break; - case BPF_S_JMP_JSET_K: /* ip += (A & K) ? jt : jf */ + case BPF_JMP | BPF_JSET | BPF_K: /* ip += (A & K) ? jt : jf */ mask = 0x700000; /* jnz */ /* Emit test if the branch targets are different */ if (filter->jt != filter->jf) { @@ -525,13 +518,13 @@ branch: if (filter->jt == filter->jf) { EMIT4_IMM(0xa7510000, K); } goto branch; - case BPF_S_JMP_JGT_X: /* ip += (A > X) ? jt : jf */ + case BPF_JMP | BPF_JGT | BPF_X: /* ip += (A > X) ? jt : jf */ mask = 0x200000; /* jh */ goto xbranch; - case BPF_S_JMP_JGE_X: /* ip += (A >= X) ? jt : jf */ + case BPF_JMP | BPF_JGE | BPF_X: /* ip += (A >= X) ? jt : jf */ mask = 0xa00000; /* jhe */ goto xbranch; - case BPF_S_JMP_JEQ_X: /* ip += (A == X) ? jt : jf */ + case BPF_JMP | BPF_JEQ | BPF_X: /* ip += (A == X) ? jt : jf */ mask = 0x800000; /* je */ xbranch: /* Emit compare if the branch targets are different */ if (filter->jt != filter->jf) { @@ -540,7 +533,7 @@ xbranch: /* Emit compare if the branch targets are different */ EMIT2(0x195c); } goto branch; - case BPF_S_JMP_JSET_X: /* ip += (A & X) ? jt : jf */ + case BPF_JMP | BPF_JSET | BPF_X: /* ip += (A & X) ? jt : jf */ mask = 0x700000; /* jnz */ /* Emit test if the branch targets are different */ if (filter->jt != filter->jf) { @@ -551,15 +544,15 @@ xbranch: /* Emit compare if the branch targets are different */ EMIT2(0x144c); } goto branch; - case BPF_S_LD_W_ABS: /* A = *(u32 *) (skb->data+K) */ + case BPF_LD | BPF_W | BPF_ABS: /* A = *(u32 *) (skb->data+K) */ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_WORD; offset = jit->off_load_word; goto load_abs; - case BPF_S_LD_H_ABS: /* A = *(u16 *) (skb->data+K) */ + case BPF_LD | BPF_H | BPF_ABS: /* A = *(u16 *) (skb->data+K) */ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_HALF; offset = jit->off_load_half; goto load_abs; - case BPF_S_LD_B_ABS: /* A = *(u8 *) (skb->data+K) */ + case BPF_LD | BPF_B | BPF_ABS: /* A = *(u8 *) (skb->data+K) */ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_BYTE; offset = jit->off_load_byte; load_abs: if ((int) K < 0) @@ -573,19 +566,19 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ /* jnz <ret0> */ EMIT4_PCREL(0xa7740000, (jit->ret0_ip - jit->prg)); break; - case BPF_S_LD_W_IND: /* A = *(u32 *) (skb->data+K+X) */ + case BPF_LD | BPF_W | BPF_IND: /* A = *(u32 *) (skb->data+K+X) */ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IWORD; offset = jit->off_load_iword; goto call_fn; - case BPF_S_LD_H_IND: /* A = *(u16 *) (skb->data+K+X) */ + case BPF_LD | BPF_H | BPF_IND: /* A = *(u16 *) (skb->data+K+X) */ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IHALF; offset = jit->off_load_ihalf; goto call_fn; - case BPF_S_LD_B_IND: /* A = *(u8 *) (skb->data+K+X) */ + case BPF_LD | BPF_B | BPF_IND: /* A = *(u8 *) (skb->data+K+X) */ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IBYTE; offset = jit->off_load_ibyte; goto call_fn; - case BPF_S_LDX_B_MSH: + case BPF_LDX | BPF_B | BPF_MSH: /* X = (*(u8 *)(skb->data+K) & 0xf) << 2 */ jit->seen |= SEEN_RET0; if ((int) K < 0) { @@ -596,17 +589,17 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ jit->seen |= SEEN_DATAREF | SEEN_LOAD_BMSH; offset = jit->off_load_bmsh; goto call_fn; - case BPF_S_LD_W_LEN: /* A = skb->len; */ + case BPF_LD | BPF_W | BPF_LEN: /* A = skb->len; */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); /* l %r5,<d(len)>(%r2) */ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, len)); break; - case BPF_S_LDX_W_LEN: /* X = skb->len; */ + case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */ jit->seen |= SEEN_XREG; /* l %r12,<d(len)>(%r2) */ EMIT4_DISP(0x58c02000, offsetof(struct sk_buff, len)); break; - case BPF_S_LD_IMM: /* A = K */ + case BPF_LD | BPF_IMM: /* A = K */ if (K <= 16383) /* lhi %r5,K */ EMIT4_IMM(0xa7580000, K); @@ -617,7 +610,7 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ /* l %r5,<d(K)>(%r13) */ EMIT4_DISP(0x5850d000, EMIT_CONST(K)); break; - case BPF_S_LDX_IMM: /* X = K */ + case BPF_LDX | BPF_IMM: /* X = K */ jit->seen |= SEEN_XREG; if (K <= 16383) /* lhi %r12,<K> */ @@ -629,29 +622,29 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ /* l %r12,<d(K)>(%r13) */ EMIT4_DISP(0x58c0d000, EMIT_CONST(K)); break; - case BPF_S_LD_MEM: /* A = mem[K] */ + case BPF_LD | BPF_MEM: /* A = mem[K] */ jit->seen |= SEEN_MEM; /* l %r5,<K>(%r15) */ EMIT4_DISP(0x5850f000, (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4); break; - case BPF_S_LDX_MEM: /* X = mem[K] */ + case BPF_LDX | BPF_MEM: /* X = mem[K] */ jit->seen |= SEEN_XREG | SEEN_MEM; /* l %r12,<K>(%r15) */ EMIT4_DISP(0x58c0f000, (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4); break; - case BPF_S_MISC_TAX: /* X = A */ + case BPF_MISC | BPF_TAX: /* X = A */ jit->seen |= SEEN_XREG; /* lr %r12,%r5 */ EMIT2(0x18c5); break; - case BPF_S_MISC_TXA: /* A = X */ + case BPF_MISC | BPF_TXA: /* A = X */ jit->seen |= SEEN_XREG; /* lr %r5,%r12 */ EMIT2(0x185c); break; - case BPF_S_RET_K: + case BPF_RET | BPF_K: if (K == 0) { jit->seen |= SEEN_RET0; if (last) @@ -671,33 +664,33 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg); } break; - case BPF_S_RET_A: + case BPF_RET | BPF_A: /* llgfr %r2,%r5 */ EMIT4(0xb9160025); /* j <exit> */ EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg); break; - case BPF_S_ST: /* mem[K] = A */ + case BPF_ST: /* mem[K] = A */ jit->seen |= SEEN_MEM; /* st %r5,<K>(%r15) */ EMIT4_DISP(0x5050f000, (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4); break; - case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */ + case BPF_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */ jit->seen |= SEEN_XREG | SEEN_MEM; /* st %r12,<K>(%r15) */ EMIT4_DISP(0x50c0f000, (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4); break; - case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */ + case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); /* lhi %r5,0 */ EMIT4(0xa7580000); /* icm %r5,3,<d(protocol)>(%r2) */ EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, protocol)); break; - case BPF_S_ANC_IFINDEX: /* if (!skb->dev) return 0; - * A = skb->dev->ifindex */ + case BPF_ANC | SKF_AD_IFINDEX: /* if (!skb->dev) return 0; + * A = skb->dev->ifindex */ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); jit->seen |= SEEN_RET0; /* lg %r1,<d(dev)>(%r2) */ @@ -709,20 +702,20 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ /* l %r5,<d(ifindex)>(%r1) */ EMIT4_DISP(0x58501000, offsetof(struct net_device, ifindex)); break; - case BPF_S_ANC_MARK: /* A = skb->mark */ + case BPF_ANC | SKF_AD_MARK: /* A = skb->mark */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); /* l %r5,<d(mark)>(%r2) */ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, mark)); break; - case BPF_S_ANC_QUEUE: /* A = skb->queue_mapping */ + case BPF_ANC | SKF_AD_QUEUE: /* A = skb->queue_mapping */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); /* lhi %r5,0 */ EMIT4(0xa7580000); /* icm %r5,3,<d(queue_mapping)>(%r2) */ EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, queue_mapping)); break; - case BPF_S_ANC_HATYPE: /* if (!skb->dev) return 0; - * A = skb->dev->type */ + case BPF_ANC | SKF_AD_HATYPE: /* if (!skb->dev) return 0; + * A = skb->dev->type */ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2); jit->seen |= SEEN_RET0; /* lg %r1,<d(dev)>(%r2) */ @@ -736,20 +729,20 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ /* icm %r5,3,<d(type)>(%r1) */ EMIT4_DISP(0xbf531000, offsetof(struct net_device, type)); break; - case BPF_S_ANC_RXHASH: /* A = skb->hash */ + case BPF_ANC | SKF_AD_RXHASH: /* A = skb->hash */ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); /* l %r5,<d(hash)>(%r2) */ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, hash)); break; - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: + case BPF_ANC | SKF_AD_VLAN_TAG: + case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); /* lhi %r5,0 */ EMIT4(0xa7580000); /* icm %r5,3,<d(vlan_tci)>(%r2) */ EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, vlan_tci)); - if (filter->code == BPF_S_ANC_VLAN_TAG) { + if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) { /* nill %r5,0xefff */ EMIT4_IMM(0xa5570000, ~VLAN_TAG_PRESENT); } else { @@ -759,7 +752,7 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ EMIT4_DISP(0x88500000, 12); } break; - case BPF_S_ANC_PKTTYPE: + case BPF_ANC | SKF_AD_PKTTYPE: if (pkt_type_offset < 0) goto out; /* lhi %r5,0 */ @@ -769,7 +762,7 @@ call_fn: /* lg %r1,<d(function)>(%r13) */ /* srl %r5,5 */ EMIT4_DISP(0x88500000, 5); break; - case BPF_S_ANC_CPU: /* A = smp_processor_id() */ + case BPF_ANC | SKF_AD_CPU: /* A = smp_processor_id() */ #ifdef CONFIG_SMP /* l %r5,<d(cpu_nr)> */ EMIT4_DISP(0x58500000, offsetof(struct _lowcore, cpu_nr)); @@ -811,7 +804,7 @@ static struct bpf_binary_header *bpf_alloc_binary(unsigned int bpfsize, return NULL; memset(header, 0, sz); header->pages = sz / PAGE_SIZE; - hole = sz - (bpfsize + sizeof(*header)); + hole = min(sz - (bpfsize + sizeof(*header)), PAGE_SIZE - sizeof(*header)); /* Insert random number of illegal instructions before BPF code * and make sure the first instruction starts at an even address. */ diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index 0f9e945..1a49ffd 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -24,7 +24,8 @@ /* The kernel image occupies 0x4000000 to 0x6000000 (4MB --> 96MB). * The page copy blockops can use 0x6000000 to 0x8000000. - * The TSB is mapped in the 0x8000000 to 0xa000000 range. + * The 8K TSB is mapped in the 0x8000000 to 0x8400000 range. + * The 4M TSB is mapped in the 0x8400000 to 0x8800000 range. * The PROM resides in an area spanning 0xf0000000 to 0x100000000. * The vmalloc area spans 0x100000000 to 0x200000000. * Since modules need to be in the lowest 32-bits of the address space, @@ -33,7 +34,8 @@ * 0x400000000. */ #define TLBTEMP_BASE _AC(0x0000000006000000,UL) -#define TSBMAP_BASE _AC(0x0000000008000000,UL) +#define TSBMAP_8K_BASE _AC(0x0000000008000000,UL) +#define TSBMAP_4M_BASE _AC(0x0000000008400000,UL) #define MODULES_VADDR _AC(0x0000000010000000,UL) #define MODULES_LEN _AC(0x00000000e0000000,UL) #define MODULES_END _AC(0x00000000f0000000,UL) @@ -71,6 +73,23 @@ #include <linux/sched.h> +extern unsigned long sparc64_valid_addr_bitmap[]; + +/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ +static inline bool __kern_addr_valid(unsigned long paddr) +{ + if ((paddr >> MAX_PHYS_ADDRESS_BITS) != 0UL) + return false; + return test_bit(paddr >> ILOG2_4MB, sparc64_valid_addr_bitmap); +} + +static inline bool kern_addr_valid(unsigned long addr) +{ + unsigned long paddr = __pa(addr); + + return __kern_addr_valid(paddr); +} + /* Entries per page directory level. */ #define PTRS_PER_PTE (1UL << (PAGE_SHIFT-3)) #define PTRS_PER_PMD (1UL << PMD_BITS) @@ -79,9 +98,12 @@ /* Kernel has a separate 44bit address space. */ #define FIRST_USER_ADDRESS 0 -#define pte_ERROR(e) __builtin_trap() -#define pmd_ERROR(e) __builtin_trap() -#define pgd_ERROR(e) __builtin_trap() +#define pmd_ERROR(e) \ + pr_err("%s:%d: bad pmd %p(%016lx) seen at (%pS)\n", \ + __FILE__, __LINE__, &(e), pmd_val(e), __builtin_return_address(0)) +#define pgd_ERROR(e) \ + pr_err("%s:%d: bad pgd %p(%016lx) seen at (%pS)\n", \ + __FILE__, __LINE__, &(e), pgd_val(e), __builtin_return_address(0)) #endif /* !(__ASSEMBLY__) */ @@ -258,8 +280,8 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t prot) { unsigned long mask, tmp; - /* SUN4U: 0x600307ffffffecb8 (negated == 0x9ffcf80000001347) - * SUN4V: 0x30ffffffffffee17 (negated == 0xcf000000000011e8) + /* SUN4U: 0x630107ffffffec38 (negated == 0x9cfef800000013c7) + * SUN4V: 0x33ffffffffffee07 (negated == 0xcc000000000011f8) * * Even if we use negation tricks the result is still a 6 * instruction sequence, so don't try to play fancy and just @@ -289,10 +311,10 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t prot) " .previous\n" : "=r" (mask), "=r" (tmp) : "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_4U | - _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_PRESENT_4U | + _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_SPECIAL | _PAGE_PMD_HUGE | _PAGE_SZALL_4U), "i" (_PAGE_PADDR_4V | _PAGE_MODIFIED_4V | _PAGE_ACCESSED_4V | - _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_PRESENT_4V | + _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_SPECIAL | _PAGE_PMD_HUGE | _PAGE_SZALL_4V)); return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask)); @@ -633,7 +655,7 @@ static inline unsigned long pmd_large(pmd_t pmd) { pte_t pte = __pte(pmd_val(pmd)); - return (pte_val(pte) & _PAGE_PMD_HUGE) && pte_present(pte); + return pte_val(pte) & _PAGE_PMD_HUGE; } #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -719,20 +741,6 @@ static inline pmd_t pmd_mkwrite(pmd_t pmd) return __pmd(pte_val(pte)); } -static inline pmd_t pmd_mknotpresent(pmd_t pmd) -{ - unsigned long mask; - - if (tlb_type == hypervisor) - mask = _PAGE_PRESENT_4V; - else - mask = _PAGE_PRESENT_4U; - - pmd_val(pmd) &= ~mask; - - return pmd; -} - static inline pmd_t pmd_mksplitting(pmd_t pmd) { pte_t pte = __pte(pmd_val(pmd)); @@ -757,6 +765,20 @@ static inline int pmd_present(pmd_t pmd) #define pmd_none(pmd) (!pmd_val(pmd)) +/* pmd_bad() is only called on non-trans-huge PMDs. Our encoding is + * very simple, it's just the physical address. PTE tables are of + * size PAGE_SIZE so make sure the sub-PAGE_SIZE bits are clear and + * the top bits outside of the range of any physical address size we + * support are clear as well. We also validate the physical itself. + */ +#define pmd_bad(pmd) ((pmd_val(pmd) & ~PAGE_MASK) || \ + !__kern_addr_valid(pmd_val(pmd))) + +#define pud_none(pud) (!pud_val(pud)) + +#define pud_bad(pud) ((pud_val(pud) & ~PAGE_MASK) || \ + !__kern_addr_valid(pud_val(pud))) + #ifdef CONFIG_TRANSPARENT_HUGEPAGE extern void set_pmd_at(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp, pmd_t pmd); @@ -790,10 +812,7 @@ static inline unsigned long __pmd_page(pmd_t pmd) #define pud_page_vaddr(pud) \ ((unsigned long) __va(pud_val(pud))) #define pud_page(pud) virt_to_page((void *)pud_page_vaddr(pud)) -#define pmd_bad(pmd) (0) #define pmd_clear(pmdp) (pmd_val(*(pmdp)) = 0UL) -#define pud_none(pud) (!pud_val(pud)) -#define pud_bad(pud) (0) #define pud_present(pud) (pud_val(pud) != 0U) #define pud_clear(pudp) (pud_val(*(pudp)) = 0UL) @@ -893,6 +912,10 @@ extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t *); extern void update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmd); +#define __HAVE_ARCH_PMDP_INVALIDATE +extern void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, + pmd_t *pmdp); + #define __HAVE_ARCH_PGTABLE_DEPOSIT extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, pgtable_t pgtable); @@ -919,18 +942,6 @@ extern unsigned long pte_file(pte_t); extern pte_t pgoff_to_pte(unsigned long); #define PTE_FILE_MAX_BITS (64UL - PAGE_SHIFT - 1UL) -extern unsigned long sparc64_valid_addr_bitmap[]; - -/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ -static inline bool kern_addr_valid(unsigned long addr) -{ - unsigned long paddr = __pa(addr); - - if ((paddr >> 41UL) != 0UL) - return false; - return test_bit(paddr >> 22, sparc64_valid_addr_bitmap); -} - extern int page_in_phys_avail(unsigned long paddr); /* diff --git a/arch/sparc/include/asm/tsb.h b/arch/sparc/include/asm/tsb.h index 2230f80..90916f9 100644 --- a/arch/sparc/include/asm/tsb.h +++ b/arch/sparc/include/asm/tsb.h @@ -171,7 +171,8 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end; andcc REG1, REG2, %g0; \ be,pt %xcc, 700f; \ sethi %hi(4 * 1024 * 1024), REG2; \ - andn REG1, REG2, REG1; \ + brgez,pn REG1, FAIL_LABEL; \ + andn REG1, REG2, REG1; \ and VADDR, REG2, REG2; \ brlz,pt REG1, PTE_LABEL; \ or REG1, REG2, REG1; \ diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index 26b706a..452f04f 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S @@ -282,8 +282,8 @@ sun4v_chip_type: stx %l2, [%l4 + 0x0] ldx [%sp + 2047 + 128 + 0x50], %l3 ! physaddr low /* 4MB align */ - srlx %l3, 22, %l3 - sllx %l3, 22, %l3 + srlx %l3, ILOG2_4MB, %l3 + sllx %l3, ILOG2_4MB, %l3 stx %l3, [%l4 + 0x8] /* Leave service as-is, "call-method" */ diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S index 542e96a..605d492 100644 --- a/arch/sparc/kernel/ktlb.S +++ b/arch/sparc/kernel/ktlb.S @@ -277,7 +277,7 @@ kvmap_dtlb_load: #ifdef CONFIG_SPARSEMEM_VMEMMAP kvmap_vmemmap: sub %g4, %g5, %g5 - srlx %g5, 22, %g5 + srlx %g5, ILOG2_4MB, %g5 sethi %hi(vmemmap_table), %g1 sllx %g5, 3, %g5 or %g1, %lo(vmemmap_table), %g1 diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 6479256..3370945 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -68,27 +68,16 @@ EXPORT_SYMBOL(touch_nmi_watchdog); static void die_nmi(const char *str, struct pt_regs *regs, int do_panic) { + int this_cpu = smp_processor_id(); + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) return; - console_verbose(); - bust_spinlocks(1); - - printk(KERN_EMERG "%s", str); - printk(" on CPU%d, ip %08lx, registers:\n", - smp_processor_id(), regs->tpc); - show_regs(regs); - dump_stack(); - - bust_spinlocks(0); - if (do_panic || panic_on_oops) - panic("Non maskable interrupt"); - - nmi_exit(); - local_irq_enable(); - do_exit(SIGBUS); + panic("Watchdog detected hard LOCKUP on cpu %d", this_cpu); + else + WARN(1, "Watchdog detected hard LOCKUP on cpu %d", this_cpu); } notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index 9781048..745a363 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -149,7 +149,7 @@ void cpu_panic(void) #define NUM_ROUNDS 64 /* magic value */ #define NUM_ITERS 5 /* likewise */ -static DEFINE_SPINLOCK(itc_sync_lock); +static DEFINE_RAW_SPINLOCK(itc_sync_lock); static unsigned long go[SLAVE + 1]; #define DEBUG_TICK_SYNC 0 @@ -257,7 +257,7 @@ static void smp_synchronize_one_tick(int cpu) go[MASTER] = 0; membar_safe("#StoreLoad"); - spin_lock_irqsave(&itc_sync_lock, flags); + raw_spin_lock_irqsave(&itc_sync_lock, flags); { for (i = 0; i < NUM_ROUNDS*NUM_ITERS; i++) { while (!go[MASTER]) @@ -268,7 +268,7 @@ static void smp_synchronize_one_tick(int cpu) membar_safe("#StoreLoad"); } } - spin_unlock_irqrestore(&itc_sync_lock, flags); + raw_spin_unlock_irqrestore(&itc_sync_lock, flags); } #if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU) diff --git a/arch/sparc/kernel/sys32.S b/arch/sparc/kernel/sys32.S index f7c72b6..d066eb1 100644 --- a/arch/sparc/kernel/sys32.S +++ b/arch/sparc/kernel/sys32.S @@ -44,7 +44,7 @@ SIGN1(sys32_timer_settime, compat_sys_timer_settime, %o1) SIGN1(sys32_io_submit, compat_sys_io_submit, %o1) SIGN1(sys32_mq_open, compat_sys_mq_open, %o1) SIGN1(sys32_select, compat_sys_select, %o0) -SIGN3(sys32_futex, compat_sys_futex, %o1, %o2, %o5) +SIGN1(sys32_futex, compat_sys_futex, %o1) SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0) SIGN1(sys32_recvmsg, compat_sys_recvmsg, %o0) SIGN1(sys32_sendmsg, compat_sys_sendmsg, %o0) diff --git a/arch/sparc/kernel/sysfs.c b/arch/sparc/kernel/sysfs.c index a364000c..7f41d40 100644 --- a/arch/sparc/kernel/sysfs.c +++ b/arch/sparc/kernel/sysfs.c @@ -151,7 +151,7 @@ static ssize_t store_mmustat_enable(struct device *s, size_t count) { unsigned long val, err; - int ret = sscanf(buf, "%ld", &val); + int ret = sscanf(buf, "%lu", &val); if (ret != 1) return -EINVAL; diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c index 3c1a7cb..35ab8b6 100644 --- a/arch/sparc/kernel/unaligned_64.c +++ b/arch/sparc/kernel/unaligned_64.c @@ -166,17 +166,23 @@ static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs) unsigned long compute_effective_address(struct pt_regs *regs, unsigned int insn, unsigned int rd) { + int from_kernel = (regs->tstate & TSTATE_PRIV) != 0; unsigned int rs1 = (insn >> 14) & 0x1f; unsigned int rs2 = insn & 0x1f; - int from_kernel = (regs->tstate & TSTATE_PRIV) != 0; + unsigned long addr; if (insn & 0x2000) { maybe_flush_windows(rs1, 0, rd, from_kernel); - return (fetch_reg(rs1, regs) + sign_extend_imm13(insn)); + addr = (fetch_reg(rs1, regs) + sign_extend_imm13(insn)); } else { maybe_flush_windows(rs1, rs2, rd, from_kernel); - return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs)); + addr = (fetch_reg(rs1, regs) + fetch_reg(rs2, regs)); } + + if (!from_kernel && test_thread_flag(TIF_32BIT)) + addr &= 0xffffffff; + + return addr; } /* This is just to make gcc think die_if_kernel does return... */ diff --git a/arch/sparc/lib/NG2memcpy.S b/arch/sparc/lib/NG2memcpy.S index 2c20ad6..30eee6e 100644 --- a/arch/sparc/lib/NG2memcpy.S +++ b/arch/sparc/lib/NG2memcpy.S @@ -236,6 +236,7 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ */ VISEntryHalf + membar #Sync alignaddr %o1, %g0, %g0 add %o1, (64 - 1), %o4 diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index 69bb818..4ced3fc 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c @@ -96,38 +96,51 @@ static unsigned int get_user_insn(unsigned long tpc) pte_t *ptep, pte; unsigned long pa; u32 insn = 0; - unsigned long pstate; - if (pgd_none(*pgdp)) - goto outret; + if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp))) + goto out; pudp = pud_offset(pgdp, tpc); - if (pud_none(*pudp)) - goto outret; - pmdp = pmd_offset(pudp, tpc); - if (pmd_none(*pmdp)) - goto outret; + if (pud_none(*pudp) || unlikely(pud_bad(*pudp))) + goto out; /* This disables preemption for us as well. */ - __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - __asm__ __volatile__("wrpr %0, %1, %%pstate" - : : "r" (pstate), "i" (PSTATE_IE)); - ptep = pte_offset_map(pmdp, tpc); - pte = *ptep; - if (!pte_present(pte)) - goto out; + local_irq_disable(); + + pmdp = pmd_offset(pudp, tpc); + if (pmd_none(*pmdp) || unlikely(pmd_bad(*pmdp))) + goto out_irq_enable; - pa = (pte_pfn(pte) << PAGE_SHIFT); - pa += (tpc & ~PAGE_MASK); +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + if (pmd_trans_huge(*pmdp)) { + if (pmd_trans_splitting(*pmdp)) + goto out_irq_enable; - /* Use phys bypass so we don't pollute dtlb/dcache. */ - __asm__ __volatile__("lduwa [%1] %2, %0" - : "=r" (insn) - : "r" (pa), "i" (ASI_PHYS_USE_EC)); + pa = pmd_pfn(*pmdp) << PAGE_SHIFT; + pa += tpc & ~HPAGE_MASK; + /* Use phys bypass so we don't pollute dtlb/dcache. */ + __asm__ __volatile__("lduwa [%1] %2, %0" + : "=r" (insn) + : "r" (pa), "i" (ASI_PHYS_USE_EC)); + } else +#endif + { + ptep = pte_offset_map(pmdp, tpc); + pte = *ptep; + if (pte_present(pte)) { + pa = (pte_pfn(pte) << PAGE_SHIFT); + pa += (tpc & ~PAGE_MASK); + + /* Use phys bypass so we don't pollute dtlb/dcache. */ + __asm__ __volatile__("lduwa [%1] %2, %0" + : "=r" (insn) + : "r" (pa), "i" (ASI_PHYS_USE_EC)); + } + pte_unmap(ptep); + } +out_irq_enable: + local_irq_enable(); out: - pte_unmap(ptep); - __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); -outret: return insn; } @@ -153,7 +166,8 @@ show_signal_msg(struct pt_regs *regs, int sig, int code, } static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, - unsigned int insn, int fault_code) + unsigned long fault_addr, unsigned int insn, + int fault_code) { unsigned long addr; siginfo_t info; @@ -161,10 +175,18 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, info.si_code = code; info.si_signo = sig; info.si_errno = 0; - if (fault_code & FAULT_CODE_ITLB) + if (fault_code & FAULT_CODE_ITLB) { addr = regs->tpc; - else - addr = compute_effective_address(regs, insn, 0); + } else { + /* If we were able to probe the faulting instruction, use it + * to compute a precise fault address. Otherwise use the fault + * time provided address which may only have page granularity. + */ + if (insn) + addr = compute_effective_address(regs, insn, 0); + else + addr = fault_addr; + } info.si_addr = (void __user *) addr; info.si_trapno = 0; @@ -239,7 +261,7 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code, /* The si_code was set to make clear whether * this was a SEGV_MAPERR or SEGV_ACCERR fault. */ - do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code); + do_fault_siginfo(si_code, SIGSEGV, regs, address, insn, fault_code); return; } @@ -259,18 +281,6 @@ static void noinline __kprobes bogus_32bit_fault_tpc(struct pt_regs *regs) show_regs(regs); } -static void noinline __kprobes bogus_32bit_fault_address(struct pt_regs *regs, - unsigned long addr) -{ - static int times; - - if (times++ < 10) - printk(KERN_ERR "FAULT[%s:%d]: 32-bit process " - "reports 64-bit fault address [%lx]\n", - current->comm, current->pid, addr); - show_regs(regs); -} - asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) { enum ctx_state prev_state = exception_enter(); @@ -300,10 +310,8 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) goto intr_or_no_mm; } } - if (unlikely((address >> 32) != 0)) { - bogus_32bit_fault_address(regs, address); + if (unlikely((address >> 32) != 0)) goto intr_or_no_mm; - } } if (regs->tstate & TSTATE_PRIV) { @@ -525,7 +533,7 @@ do_sigbus: * Send a sigbus, regardless of whether we were in kernel * or user mode. */ - do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code); + do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, address, insn, fault_code); /* Kernel mode? Handle exceptions or die */ if (regs->tstate & TSTATE_PRIV) diff --git a/arch/sparc/mm/gup.c b/arch/sparc/mm/gup.c index c4d3da6..1aed043 100644 --- a/arch/sparc/mm/gup.c +++ b/arch/sparc/mm/gup.c @@ -73,7 +73,7 @@ static int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr, struct page *head, *page, *tail; int refs; - if (!pmd_large(pmd)) + if (!(pmd_val(pmd) & _PAGE_VALID)) return 0; if (write && !pmd_write(pmd)) diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index eafbc65..ed3c969 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -588,7 +588,7 @@ static void __init remap_kernel(void) int i, tlb_ent = sparc64_highest_locked_tlbent(); tte_vaddr = (unsigned long) KERNBASE; - phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL; + phys_page = (prom_boot_mapping_phys_low >> ILOG2_4MB) << ILOG2_4MB; tte_data = kern_large_tte(phys_page); kern_locked_tte_data = tte_data; @@ -1881,7 +1881,7 @@ void __init paging_init(void) BUILD_BUG_ON(NR_CPUS > 4096); - kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL; + kern_base = (prom_boot_mapping_phys_low >> ILOG2_4MB) << ILOG2_4MB; kern_size = (unsigned long)&_end - (unsigned long)KERNBASE; /* Invalidate both kernel TSBs. */ @@ -1937,7 +1937,7 @@ void __init paging_init(void) shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE); real_end = (unsigned long)_end; - num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << 22); + num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << ILOG2_4MB); printk("Kernel: Using %d locked TLB entries for main kernel image.\n", num_kernel_image_mappings); @@ -2094,7 +2094,7 @@ static void __init setup_valid_addr_bitmap_from_pavail(unsigned long *bitmap) if (new_start <= old_start && new_end >= (old_start + PAGE_SIZE)) { - set_bit(old_start >> 22, bitmap); + set_bit(old_start >> ILOG2_4MB, bitmap); goto do_next_page; } } @@ -2143,7 +2143,7 @@ void __init mem_init(void) addr = PAGE_OFFSET + kern_base; last = PAGE_ALIGN(kern_size) + addr; while (addr < last) { - set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap); + set_bit(__pa(addr) >> ILOG2_4MB, sparc64_valid_addr_bitmap); addr += PAGE_SIZE; } @@ -2267,7 +2267,7 @@ int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend, void *block; if (!(*vmem_pp & _PAGE_VALID)) { - block = vmemmap_alloc_block(1UL << 22, node); + block = vmemmap_alloc_block(1UL << ILOG2_4MB, node); if (!block) return -ENOMEM; diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c index b12cb5e..b89aba2 100644 --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c @@ -134,7 +134,7 @@ no_cache_flush: #ifdef CONFIG_TRANSPARENT_HUGEPAGE static void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr, - pmd_t pmd, bool exec) + pmd_t pmd) { unsigned long end; pte_t *pte; @@ -142,8 +142,11 @@ static void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr, pte = pte_offset_map(&pmd, vaddr); end = vaddr + HPAGE_SIZE; while (vaddr < end) { - if (pte_val(*pte) & _PAGE_VALID) + if (pte_val(*pte) & _PAGE_VALID) { + bool exec = pte_exec(*pte); + tlb_batch_add_one(mm, vaddr, exec); + } pte++; vaddr += PAGE_SIZE; } @@ -177,19 +180,30 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr, } if (!pmd_none(orig)) { - pte_t orig_pte = __pte(pmd_val(orig)); - bool exec = pte_exec(orig_pte); - addr &= HPAGE_MASK; if (pmd_trans_huge(orig)) { + pte_t orig_pte = __pte(pmd_val(orig)); + bool exec = pte_exec(orig_pte); + tlb_batch_add_one(mm, addr, exec); tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec); } else { - tlb_batch_pmd_scan(mm, addr, orig, exec); + tlb_batch_pmd_scan(mm, addr, orig); } } } +void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, + pmd_t *pmdp) +{ + pmd_t entry = *pmdp; + + pmd_val(entry) &= ~_PAGE_VALID; + + set_pmd_at(vma->vm_mm, address, pmdp, entry); + flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE); +} + void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, pgtable_t pgtable) { diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c index f5d506f..fe19b81 100644 --- a/arch/sparc/mm/tsb.c +++ b/arch/sparc/mm/tsb.c @@ -133,7 +133,19 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsign mm->context.tsb_block[tsb_idx].tsb_nentries = tsb_bytes / sizeof(struct tsb); - base = TSBMAP_BASE; + switch (tsb_idx) { + case MM_TSB_BASE: + base = TSBMAP_8K_BASE; + break; +#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) + case MM_TSB_HUGE: + base = TSBMAP_4M_BASE; + break; +#endif + default: + BUG(); + } + tte = pgprot_val(PAGE_KERNEL_LOCKED); tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb); BUG_ON(tsb_paddr & (tsb_bytes - 1UL)); diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c index a82c6b2..c88cf14 100644 --- a/arch/sparc/net/bpf_jit_comp.c +++ b/arch/sparc/net/bpf_jit_comp.c @@ -415,20 +415,11 @@ void bpf_jit_compile(struct sk_filter *fp) emit_reg_move(O7, r_saved_O7); switch (filter[0].code) { - case BPF_S_RET_K: - case BPF_S_LD_W_LEN: - case BPF_S_ANC_PROTOCOL: - case BPF_S_ANC_PKTTYPE: - case BPF_S_ANC_IFINDEX: - case BPF_S_ANC_MARK: - case BPF_S_ANC_RXHASH: - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - case BPF_S_ANC_CPU: - case BPF_S_ANC_QUEUE: - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: + case BPF_RET | BPF_K: + case BPF_LD | BPF_W | BPF_LEN: + case BPF_LD | BPF_W | BPF_ABS: + case BPF_LD | BPF_H | BPF_ABS: + case BPF_LD | BPF_B | BPF_ABS: /* The first instruction sets the A register (or is * a "RET 'constant'") */ @@ -445,59 +436,60 @@ void bpf_jit_compile(struct sk_filter *fp) unsigned int t_offset; unsigned int f_offset; u32 t_op, f_op; + u16 code = bpf_anc_helper(&filter[i]); int ilen; - switch (filter[i].code) { - case BPF_S_ALU_ADD_X: /* A += X; */ + switch (code) { + case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */ emit_alu_X(ADD); break; - case BPF_S_ALU_ADD_K: /* A += K; */ + case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ emit_alu_K(ADD, K); break; - case BPF_S_ALU_SUB_X: /* A -= X; */ + case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */ emit_alu_X(SUB); break; - case BPF_S_ALU_SUB_K: /* A -= K */ + case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ emit_alu_K(SUB, K); break; - case BPF_S_ALU_AND_X: /* A &= X */ + case BPF_ALU | BPF_AND | BPF_X: /* A &= X */ emit_alu_X(AND); break; - case BPF_S_ALU_AND_K: /* A &= K */ + case BPF_ALU | BPF_AND | BPF_K: /* A &= K */ emit_alu_K(AND, K); break; - case BPF_S_ALU_OR_X: /* A |= X */ + case BPF_ALU | BPF_OR | BPF_X: /* A |= X */ emit_alu_X(OR); break; - case BPF_S_ALU_OR_K: /* A |= K */ + case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ emit_alu_K(OR, K); break; - case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */ - case BPF_S_ALU_XOR_X: + case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */ + case BPF_ALU | BPF_XOR | BPF_X: emit_alu_X(XOR); break; - case BPF_S_ALU_XOR_K: /* A ^= K */ + case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ emit_alu_K(XOR, K); break; - case BPF_S_ALU_LSH_X: /* A <<= X */ + case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X */ emit_alu_X(SLL); break; - case BPF_S_ALU_LSH_K: /* A <<= K */ + case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */ emit_alu_K(SLL, K); break; - case BPF_S_ALU_RSH_X: /* A >>= X */ + case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X */ emit_alu_X(SRL); break; - case BPF_S_ALU_RSH_K: /* A >>= K */ + case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K */ emit_alu_K(SRL, K); break; - case BPF_S_ALU_MUL_X: /* A *= X; */ + case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */ emit_alu_X(MUL); break; - case BPF_S_ALU_MUL_K: /* A *= K */ + case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ emit_alu_K(MUL, K); break; - case BPF_S_ALU_DIV_K: /* A /= K with K != 0*/ + case BPF_ALU | BPF_DIV | BPF_K: /* A /= K with K != 0*/ if (K == 1) break; emit_write_y(G0); @@ -512,7 +504,7 @@ void bpf_jit_compile(struct sk_filter *fp) #endif emit_alu_K(DIV, K); break; - case BPF_S_ALU_DIV_X: /* A /= X; */ + case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ emit_cmpi(r_X, 0); if (pc_ret0 > 0) { t_offset = addrs[pc_ret0 - 1]; @@ -544,10 +536,10 @@ void bpf_jit_compile(struct sk_filter *fp) #endif emit_alu_X(DIV); break; - case BPF_S_ALU_NEG: + case BPF_ALU | BPF_NEG: emit_neg(); break; - case BPF_S_RET_K: + case BPF_RET | BPF_K: if (!K) { if (pc_ret0 == -1) pc_ret0 = i; @@ -556,7 +548,7 @@ void bpf_jit_compile(struct sk_filter *fp) emit_loadimm(K, r_A); } /* Fallthrough */ - case BPF_S_RET_A: + case BPF_RET | BPF_A: if (seen_or_pass0) { if (i != flen - 1) { emit_jump(cleanup_addr); @@ -573,18 +565,18 @@ void bpf_jit_compile(struct sk_filter *fp) emit_jmpl(r_saved_O7, 8, G0); emit_reg_move(r_A, O0); /* delay slot */ break; - case BPF_S_MISC_TAX: + case BPF_MISC | BPF_TAX: seen |= SEEN_XREG; emit_reg_move(r_A, r_X); break; - case BPF_S_MISC_TXA: + case BPF_MISC | BPF_TXA: seen |= SEEN_XREG; emit_reg_move(r_X, r_A); break; - case BPF_S_ANC_CPU: + case BPF_ANC | SKF_AD_CPU: emit_load_cpu(r_A); break; - case BPF_S_ANC_PROTOCOL: + case BPF_ANC | SKF_AD_PROTOCOL: emit_skb_load16(protocol, r_A); break; #if 0 @@ -592,38 +584,38 @@ void bpf_jit_compile(struct sk_filter *fp) * a bit field even though we very much * know what we are doing here. */ - case BPF_S_ANC_PKTTYPE: + case BPF_ANC | SKF_AD_PKTTYPE: __emit_skb_load8(pkt_type, r_A); emit_alu_K(SRL, 5); break; #endif - case BPF_S_ANC_IFINDEX: + case BPF_ANC | SKF_AD_IFINDEX: emit_skb_loadptr(dev, r_A); emit_cmpi(r_A, 0); emit_branch(BNE_PTR, cleanup_addr + 4); emit_nop(); emit_load32(r_A, struct net_device, ifindex, r_A); break; - case BPF_S_ANC_MARK: + case BPF_ANC | SKF_AD_MARK: emit_skb_load32(mark, r_A); break; - case BPF_S_ANC_QUEUE: + case BPF_ANC | SKF_AD_QUEUE: emit_skb_load16(queue_mapping, r_A); break; - case BPF_S_ANC_HATYPE: + case BPF_ANC | SKF_AD_HATYPE: emit_skb_loadptr(dev, r_A); emit_cmpi(r_A, 0); emit_branch(BNE_PTR, cleanup_addr + 4); emit_nop(); emit_load16(r_A, struct net_device, type, r_A); break; - case BPF_S_ANC_RXHASH: + case BPF_ANC | SKF_AD_RXHASH: emit_skb_load32(hash, r_A); break; - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: + case BPF_ANC | SKF_AD_VLAN_TAG: + case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: emit_skb_load16(vlan_tci, r_A); - if (filter[i].code == BPF_S_ANC_VLAN_TAG) { + if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) { emit_andi(r_A, VLAN_VID_MASK, r_A); } else { emit_loadimm(VLAN_TAG_PRESENT, r_TMP); @@ -631,44 +623,44 @@ void bpf_jit_compile(struct sk_filter *fp) } break; - case BPF_S_LD_IMM: + case BPF_LD | BPF_IMM: emit_loadimm(K, r_A); break; - case BPF_S_LDX_IMM: + case BPF_LDX | BPF_IMM: emit_loadimm(K, r_X); break; - case BPF_S_LD_MEM: + case BPF_LD | BPF_MEM: emit_ldmem(K * 4, r_A); break; - case BPF_S_LDX_MEM: + case BPF_LDX | BPF_MEM: emit_ldmem(K * 4, r_X); break; - case BPF_S_ST: + case BPF_ST: emit_stmem(K * 4, r_A); break; - case BPF_S_STX: + case BPF_STX: emit_stmem(K * 4, r_X); break; #define CHOOSE_LOAD_FUNC(K, func) \ ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) - case BPF_S_LD_W_ABS: + case BPF_LD | BPF_W | BPF_ABS: func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_word); common_load: seen |= SEEN_DATAREF; emit_loadimm(K, r_OFF); emit_call(func); break; - case BPF_S_LD_H_ABS: + case BPF_LD | BPF_H | BPF_ABS: func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_half); goto common_load; - case BPF_S_LD_B_ABS: + case BPF_LD | BPF_B | BPF_ABS: func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte); goto common_load; - case BPF_S_LDX_B_MSH: + case BPF_LDX | BPF_B | BPF_MSH: func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte_msh); goto common_load; - case BPF_S_LD_W_IND: + case BPF_LD | BPF_W | BPF_IND: func = bpf_jit_load_word; common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; if (K) { @@ -683,13 +675,13 @@ common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; } emit_call(func); break; - case BPF_S_LD_H_IND: + case BPF_LD | BPF_H | BPF_IND: func = bpf_jit_load_half; goto common_load_ind; - case BPF_S_LD_B_IND: + case BPF_LD | BPF_B | BPF_IND: func = bpf_jit_load_byte; goto common_load_ind; - case BPF_S_JMP_JA: + case BPF_JMP | BPF_JA: emit_jump(addrs[i + K]); emit_nop(); break; @@ -700,14 +692,14 @@ common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; f_op = FOP; \ goto cond_branch - COND_SEL(BPF_S_JMP_JGT_K, BGU, BLEU); - COND_SEL(BPF_S_JMP_JGE_K, BGEU, BLU); - COND_SEL(BPF_S_JMP_JEQ_K, BE, BNE); - COND_SEL(BPF_S_JMP_JSET_K, BNE, BE); - COND_SEL(BPF_S_JMP_JGT_X, BGU, BLEU); - COND_SEL(BPF_S_JMP_JGE_X, BGEU, BLU); - COND_SEL(BPF_S_JMP_JEQ_X, BE, BNE); - COND_SEL(BPF_S_JMP_JSET_X, BNE, BE); + COND_SEL(BPF_JMP | BPF_JGT | BPF_K, BGU, BLEU); + COND_SEL(BPF_JMP | BPF_JGE | BPF_K, BGEU, BLU); + COND_SEL(BPF_JMP | BPF_JEQ | BPF_K, BE, BNE); + COND_SEL(BPF_JMP | BPF_JSET | BPF_K, BNE, BE); + COND_SEL(BPF_JMP | BPF_JGT | BPF_X, BGU, BLEU); + COND_SEL(BPF_JMP | BPF_JGE | BPF_X, BGEU, BLU); + COND_SEL(BPF_JMP | BPF_JEQ | BPF_X, BE, BNE); + COND_SEL(BPF_JMP | BPF_JSET | BPF_X, BNE, BE); cond_branch: f_offset = addrs[i + filter[i].jf]; t_offset = addrs[i + filter[i].jt]; @@ -719,20 +711,20 @@ cond_branch: f_offset = addrs[i + filter[i].jf]; break; } - switch (filter[i].code) { - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JEQ_X: + switch (code) { + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JEQ | BPF_X: seen |= SEEN_XREG; emit_cmp(r_A, r_X); break; - case BPF_S_JMP_JSET_X: + case BPF_JMP | BPF_JSET | BPF_X: seen |= SEEN_XREG; emit_btst(r_A, r_X); break; - case BPF_S_JMP_JEQ_K: - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGE_K: + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: if (is_simm13(K)) { emit_cmpi(r_A, K); } else { @@ -740,7 +732,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf]; emit_cmp(r_A, r_TMP); } break; - case BPF_S_JMP_JSET_K: + case BPF_JMP | BPF_JSET | BPF_K: if (is_simm13(K)) { emit_btsti(r_A, K); } else { diff --git a/arch/x86/Makefile b/arch/x86/Makefile index ce6ad7e..33f71b0 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -79,6 +79,7 @@ else UTS_MACHINE := x86_64 CHECKFLAGS += -D__x86_64__ -m64 + biarch := -m64 KBUILD_AFLAGS += -m64 KBUILD_CFLAGS += -m64 diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index abb9eba..dbe8dd2 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -71,7 +71,7 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) -sed-voffset := -e 's/^\([0-9a-fA-F]*\) . \(_text\|_end\)$$/\#define VO_\2 0x\1/p' +sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(_text\|_end\)$$/\#define VO_\2 0x\1/p' quiet_cmd_voffset = VOFFSET $@ cmd_voffset = $(NM) $< | sed -n $(sed-voffset) > $@ @@ -80,7 +80,7 @@ targets += voffset.h $(obj)/voffset.h: vmlinux FORCE $(call if_changed,voffset) -sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p' +sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p' quiet_cmd_zoffset = ZOFFSET $@ cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@ diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 1768461..57ab74d 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -354,7 +354,7 @@ static void parse_elf(void *output) free(phdrs); } -asmlinkage void *decompress_kernel(void *rmode, memptr heap, +asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap, unsigned char *input_data, unsigned long input_len, unsigned char *output, diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h index b18df57..36f7125 100644 --- a/arch/x86/include/asm/hpet.h +++ b/arch/x86/include/asm/hpet.h @@ -63,6 +63,7 @@ /* hpet memory map physical address */ extern unsigned long hpet_address; extern unsigned long force_hpet_address; +extern int boot_hpet_disable; extern u8 hpet_blockid; extern int hpet_force_user; extern u8 hpet_msi_disable; diff --git a/arch/x86/include/asm/hugetlb.h b/arch/x86/include/asm/hugetlb.h index a809121..68c0539 100644 --- a/arch/x86/include/asm/hugetlb.h +++ b/arch/x86/include/asm/hugetlb.h @@ -52,6 +52,7 @@ static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { + ptep_clear_flush(vma, addr, ptep); } static inline int huge_pte_none(pte_t pte) diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index c827ace..fcf2b3a 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h @@ -384,7 +384,7 @@ #define MSR_IA32_MISC_ENABLE_MWAIT_BIT 18 #define MSR_IA32_MISC_ENABLE_MWAIT (1ULL << MSR_IA32_MISC_ENABLE_MWAIT_BIT) #define MSR_IA32_MISC_ENABLE_LIMIT_CPUID_BIT 22 -#define MSR_IA32_MISC_ENABLE_LIMIT_CPUID (1ULL << MSR_IA32_MISC_ENABLE_LIMIT_CPUID_BIT); +#define MSR_IA32_MISC_ENABLE_LIMIT_CPUID (1ULL << MSR_IA32_MISC_ENABLE_LIMIT_CPUID_BIT) #define MSR_IA32_MISC_ENABLE_XTPR_DISABLE_BIT 23 #define MSR_IA32_MISC_ENABLE_XTPR_DISABLE (1ULL << MSR_IA32_MISC_ENABLE_XTPR_DISABLE_BIT) #define MSR_IA32_MISC_ENABLE_XD_DISABLE_BIT 34 diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index 3a2ae4c..3136820 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -31,7 +31,7 @@ static char temp_stack[4096]; * * Wrapper around acpi_enter_sleep_state() to be called by assmebly. */ -acpi_status asmlinkage x86_acpi_enter_sleep_state(u8 state) +acpi_status asmlinkage __visible x86_acpi_enter_sleep_state(u8 state) { return acpi_enter_sleep_state(state); } diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index d23aa82..992060e 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2189,7 +2189,7 @@ void send_cleanup_vector(struct irq_cfg *cfg) cfg->move_in_progress = 0; } -asmlinkage void smp_irq_move_cleanup_interrupt(void) +asmlinkage __visible void smp_irq_move_cleanup_interrupt(void) { unsigned vector, me; diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index d921b7e..36a1bb6 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -429,14 +429,14 @@ static inline void __smp_thermal_interrupt(void) smp_thermal_vector(); } -asmlinkage void smp_thermal_interrupt(struct pt_regs *regs) +asmlinkage __visible void smp_thermal_interrupt(struct pt_regs *regs) { entering_irq(); __smp_thermal_interrupt(); exiting_ack_irq(); } -asmlinkage void smp_trace_thermal_interrupt(struct pt_regs *regs) +asmlinkage __visible void smp_trace_thermal_interrupt(struct pt_regs *regs) { entering_irq(); trace_thermal_apic_entry(THERMAL_APIC_VECTOR); diff --git a/arch/x86/kernel/cpu/mcheck/threshold.c b/arch/x86/kernel/cpu/mcheck/threshold.c index fe6b1c8..7245980 100644 --- a/arch/x86/kernel/cpu/mcheck/threshold.c +++ b/arch/x86/kernel/cpu/mcheck/threshold.c @@ -24,14 +24,14 @@ static inline void __smp_threshold_interrupt(void) mce_threshold_vector(); } -asmlinkage void smp_threshold_interrupt(void) +asmlinkage __visible void smp_threshold_interrupt(void) { entering_irq(); __smp_threshold_interrupt(); exiting_ack_irq(); } -asmlinkage void smp_trace_threshold_interrupt(void) +asmlinkage __visible void smp_trace_threshold_interrupt(void) { entering_irq(); trace_threshold_apic_entry(THRESHOLD_APIC_VECTOR); diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index aa333d9..adb02aa 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -169,7 +169,6 @@ static struct event_constraint intel_slm_event_constraints[] __read_mostly = { FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ - FIXED_EVENT_CONSTRAINT(0x013c, 2), /* CPU_CLK_UNHALTED.REF */ FIXED_EVENT_CONSTRAINT(0x0300, 2), /* pseudo CPU_CLK_UNHALTED.REF */ EVENT_CONSTRAINT_END }; diff --git a/arch/x86/kernel/cpu/rdrand.c b/arch/x86/kernel/cpu/rdrand.c index 384df51..136ac74 100644 --- a/arch/x86/kernel/cpu/rdrand.c +++ b/arch/x86/kernel/cpu/rdrand.c @@ -27,6 +27,7 @@ static int __init x86_rdrand_setup(char *s) { setup_clear_cpu_cap(X86_FEATURE_RDRAND); + setup_clear_cpu_cap(X86_FEATURE_RDSEED); return 1; } __setup("nordrand", x86_rdrand_setup); diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 6e2537c..6cda0ba 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -17,6 +17,7 @@ #include <asm/dma.h> #include <asm/io_apic.h> #include <asm/apic.h> +#include <asm/hpet.h> #include <asm/iommu.h> #include <asm/gart.h> #include <asm/irq_remapping.h> @@ -530,6 +531,15 @@ static void __init intel_graphics_stolen(int num, int slot, int func) } } +static void __init force_disable_hpet(int num, int slot, int func) +{ +#ifdef CONFIG_HPET_TIMER + boot_hpet_disable = 1; + pr_info("x86/hpet: Will disable the HPET for this platform because it's not reliable\n"); +#endif +} + + #define QFLAG_APPLY_ONCE 0x1 #define QFLAG_APPLIED 0x2 #define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED) @@ -567,6 +577,12 @@ static struct chipset early_qrk[] __initdata = { PCI_BASE_CLASS_BRIDGE, 0, intel_remapping_check }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, PCI_ANY_ID, QFLAG_APPLY_ONCE, intel_graphics_stolen }, + /* + * HPET on current version of Baytrail platform has accuracy + * problems, disable it for now: + */ + { PCI_VENDOR_ID_INTEL, 0x0f00, + PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, {} }; diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c index c61a14a..d6c1b983 100644 --- a/arch/x86/kernel/head32.c +++ b/arch/x86/kernel/head32.c @@ -29,7 +29,7 @@ static void __init i386_default_early_setup(void) reserve_ebda_region(); } -asmlinkage void __init i386_start_kernel(void) +asmlinkage __visible void __init i386_start_kernel(void) { sanitize_boot_params(&boot_params); diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 85126cc..068054f 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -137,7 +137,7 @@ static void __init copy_bootdata(char *real_mode_data) } } -asmlinkage void __init x86_64_start_kernel(char * real_mode_data) +asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data) { int i; diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 8d80ae0..4177bfb 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -88,7 +88,7 @@ static inline void hpet_clear_mapping(void) /* * HPET command line enable / disable */ -static int boot_hpet_disable; +int boot_hpet_disable; int hpet_force_user; static int hpet_verbose; diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c index af1d14a..dcbbaa1 100644 --- a/arch/x86/kernel/ldt.c +++ b/arch/x86/kernel/ldt.c @@ -20,6 +20,8 @@ #include <asm/mmu_context.h> #include <asm/syscalls.h> +int sysctl_ldt16 = 0; + #ifdef CONFIG_SMP static void flush_ldt(void *current_mm) { @@ -234,7 +236,7 @@ static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode) * IRET leaking the high bits of the kernel stack address. */ #ifdef CONFIG_X86_64 - if (!ldt_info.seg_32bit) { + if (!ldt_info.seg_32bit && !sysctl_ldt16) { error = -EINVAL; goto out_unlock; } diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 9c0280f..898d077 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -52,7 +52,7 @@ asmlinkage extern void ret_from_fork(void); -asmlinkage DEFINE_PER_CPU(unsigned long, old_rsp); +__visible DEFINE_PER_CPU(unsigned long, old_rsp); /* Prints also some state that isn't saved in the pt_regs */ void __show_regs(struct pt_regs *regs, int all) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 3399d3a..52b1157 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -191,6 +191,16 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { }, }, + /* Certec */ + { /* Handle problems with rebooting on Certec BPC600 */ + .callback = set_pci_reboot, + .ident = "Certec BPC600", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Certec"), + DMI_MATCH(DMI_PRODUCT_NAME, "BPC600"), + }, + }, + /* Dell */ { /* Handle problems with rebooting on Dell DXP061 */ .callback = set_bios_reboot, diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 7c3a5a6..be8e1bd 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -168,7 +168,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) * this function calls the 'stop' function on all other CPUs in the system. */ -asmlinkage void smp_reboot_interrupt(void) +asmlinkage __visible void smp_reboot_interrupt(void) { ack_APIC_irq(); irq_enter(); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 57409f6..f73b5d4 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -357,7 +357,7 @@ exit: * for scheduling or signal handling. The actual stack switch is done in * entry.S */ -asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) +asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) { struct pt_regs *regs = eregs; /* Did already sync */ @@ -601,11 +601,11 @@ do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) #endif } -asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void) +asmlinkage __visible void __attribute__((weak)) smp_thermal_interrupt(void) { } -asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void) +asmlinkage __visible void __attribute__((weak)) smp_threshold_interrupt(void) { } diff --git a/arch/x86/kernel/vsmp_64.c b/arch/x86/kernel/vsmp_64.c index 5edc34b..b99b9ad8 100644 --- a/arch/x86/kernel/vsmp_64.c +++ b/arch/x86/kernel/vsmp_64.c @@ -36,7 +36,7 @@ static int irq_routing_comply = 1; * and vice versa. */ -asmlinkage unsigned long vsmp_save_fl(void) +asmlinkage __visible unsigned long vsmp_save_fl(void) { unsigned long flags = native_save_fl(); @@ -56,7 +56,7 @@ __visible void vsmp_restore_fl(unsigned long flags) } PV_CALLEE_SAVE_REGS_THUNK(vsmp_restore_fl); -asmlinkage void vsmp_irq_disable(void) +asmlinkage __visible void vsmp_irq_disable(void) { unsigned long flags = native_save_fl(); @@ -64,7 +64,7 @@ asmlinkage void vsmp_irq_disable(void) } PV_CALLEE_SAVE_REGS_THUNK(vsmp_irq_disable); -asmlinkage void vsmp_irq_enable(void) +asmlinkage __visible void vsmp_irq_enable(void) { unsigned long flags = native_save_fl(); diff --git a/arch/x86/kernel/vsyscall_gtod.c b/arch/x86/kernel/vsyscall_gtod.c index f9c6e56..9531fbb 100644 --- a/arch/x86/kernel/vsyscall_gtod.c +++ b/arch/x86/kernel/vsyscall_gtod.c @@ -43,7 +43,7 @@ void update_vsyscall(struct timekeeper *tk) vdata->monotonic_time_sec = tk->xtime_sec + tk->wall_to_monotonic.tv_sec; vdata->monotonic_time_snsec = tk->xtime_nsec - + (tk->wall_to_monotonic.tv_nsec + + ((u64)tk->wall_to_monotonic.tv_nsec << tk->shift); while (vdata->monotonic_time_snsec >= (((u64)NSEC_PER_SEC) << tk->shift)) { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8b8fc0b..b6c0bac 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -280,7 +280,7 @@ int kvm_set_apic_base(struct kvm_vcpu *vcpu, struct msr_data *msr_info) } EXPORT_SYMBOL_GPL(kvm_set_apic_base); -asmlinkage void kvm_spurious_fault(void) +asmlinkage __visible void kvm_spurious_fault(void) { /* Fault while not rebooting. We want the trace. */ BUG(); diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index ad1fb5f..aae9413 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -233,13 +233,13 @@ static void lguest_end_context_switch(struct task_struct *next) * flags word contains all kind of stuff, but in practice Linux only cares * about the interrupt flag. Our "save_flags()" just returns that. */ -asmlinkage unsigned long lguest_save_fl(void) +asmlinkage __visible unsigned long lguest_save_fl(void) { return lguest_data.irq_enabled; } /* Interrupts go off... */ -asmlinkage void lguest_irq_disable(void) +asmlinkage __visible void lguest_irq_disable(void) { lguest_data.irq_enabled = 0; } diff --git a/arch/x86/lib/msr.c b/arch/x86/lib/msr.c index db9db44..4362373 100644 --- a/arch/x86/lib/msr.c +++ b/arch/x86/lib/msr.c @@ -76,7 +76,7 @@ static inline int __flip_bit(u32 msr, u8 bit, bool set) if (m1.q == m.q) return 0; - err = msr_write(msr, &m); + err = msr_write(msr, &m1); if (err) return err; diff --git a/arch/x86/math-emu/errors.c b/arch/x86/math-emu/errors.c index a544908..9e6545f 100644 --- a/arch/x86/math-emu/errors.c +++ b/arch/x86/math-emu/errors.c @@ -302,7 +302,7 @@ static struct { 0x242 in div_Xsig.S */ -asmlinkage void FPU_exception(int n) +asmlinkage __visible void FPU_exception(int n) { int i, int_type; @@ -492,7 +492,7 @@ int real_2op_NaN(FPU_REG const *b, u_char tagb, /* Invalid arith operation on Valid registers */ /* Returns < 0 if the exception is unmasked */ -asmlinkage int arith_invalid(int deststnr) +asmlinkage __visible int arith_invalid(int deststnr) { EXCEPTION(EX_Invalid); @@ -507,7 +507,7 @@ asmlinkage int arith_invalid(int deststnr) } /* Divide a finite number by zero */ -asmlinkage int FPU_divide_by_zero(int deststnr, u_char sign) +asmlinkage __visible int FPU_divide_by_zero(int deststnr, u_char sign) { FPU_REG *dest = &st(deststnr); int tag = TAG_Valid; @@ -539,7 +539,7 @@ int set_precision_flag(int flags) } /* This may be called often, so keep it lean */ -asmlinkage void set_precision_flag_up(void) +asmlinkage __visible void set_precision_flag_up(void) { if (control_word & CW_Precision) partial_status |= (SW_Precision | SW_C1); /* The masked response */ @@ -548,7 +548,7 @@ asmlinkage void set_precision_flag_up(void) } /* This may be called often, so keep it lean */ -asmlinkage void set_precision_flag_down(void) +asmlinkage __visible void set_precision_flag_down(void) { if (control_word & CW_Precision) { /* The masked response */ partial_status &= ~SW_C1; @@ -557,7 +557,7 @@ asmlinkage void set_precision_flag_down(void) EXCEPTION(EX_Precision); } -asmlinkage int denormal_operand(void) +asmlinkage __visible int denormal_operand(void) { if (control_word & CW_Denormal) { /* The masked response */ partial_status |= SW_Denorm_Op; @@ -568,7 +568,7 @@ asmlinkage int denormal_operand(void) } } -asmlinkage int arith_overflow(FPU_REG *dest) +asmlinkage __visible int arith_overflow(FPU_REG *dest) { int tag = TAG_Valid; @@ -596,7 +596,7 @@ asmlinkage int arith_overflow(FPU_REG *dest) } -asmlinkage int arith_underflow(FPU_REG *dest) +asmlinkage __visible int arith_underflow(FPU_REG *dest) { int tag = TAG_Valid; diff --git a/arch/x86/net/bpf_jit.S b/arch/x86/net/bpf_jit.S index 0149575..6440221 100644 --- a/arch/x86/net/bpf_jit.S +++ b/arch/x86/net/bpf_jit.S @@ -12,13 +12,16 @@ /* * Calling convention : - * rdi : skb pointer + * rbx : skb pointer (callee saved) * esi : offset of byte(s) to fetch in skb (can be scratched) - * r8 : copy of skb->data + * r10 : copy of skb->data * r9d : hlen = skb->len - skb->data_len */ -#define SKBDATA %r8 +#define SKBDATA %r10 #define SKF_MAX_NEG_OFF $(-0x200000) /* SKF_LL_OFF from filter.h */ +#define MAX_BPF_STACK (512 /* from filter.h */ + \ + 32 /* space for rbx,r13,r14,r15 */ + \ + 8 /* space for skb_copy_bits */) sk_load_word: .globl sk_load_word @@ -68,53 +71,31 @@ sk_load_byte_positive_offset: movzbl (SKBDATA,%rsi),%eax ret -/** - * sk_load_byte_msh - BPF_S_LDX_B_MSH helper - * - * Implements BPF_S_LDX_B_MSH : ldxb 4*([offset]&0xf) - * Must preserve A accumulator (%eax) - * Inputs : %esi is the offset value - */ -sk_load_byte_msh: - .globl sk_load_byte_msh - test %esi,%esi - js bpf_slow_path_byte_msh_neg - -sk_load_byte_msh_positive_offset: - .globl sk_load_byte_msh_positive_offset - cmp %esi,%r9d /* if (offset >= hlen) goto bpf_slow_path_byte_msh */ - jle bpf_slow_path_byte_msh - movzbl (SKBDATA,%rsi),%ebx - and $15,%bl - shl $2,%bl - ret - /* rsi contains offset and can be scratched */ #define bpf_slow_path_common(LEN) \ - push %rdi; /* save skb */ \ + mov %rbx, %rdi; /* arg1 == skb */ \ push %r9; \ push SKBDATA; \ /* rsi already has offset */ \ mov $LEN,%ecx; /* len */ \ - lea -12(%rbp),%rdx; \ + lea - MAX_BPF_STACK + 32(%rbp),%rdx; \ call skb_copy_bits; \ test %eax,%eax; \ pop SKBDATA; \ - pop %r9; \ - pop %rdi + pop %r9; bpf_slow_path_word: bpf_slow_path_common(4) js bpf_error - mov -12(%rbp),%eax + mov - MAX_BPF_STACK + 32(%rbp),%eax bswap %eax ret bpf_slow_path_half: bpf_slow_path_common(2) js bpf_error - mov -12(%rbp),%ax + mov - MAX_BPF_STACK + 32(%rbp),%ax rol $8,%ax movzwl %ax,%eax ret @@ -122,21 +103,11 @@ bpf_slow_path_half: bpf_slow_path_byte: bpf_slow_path_common(1) js bpf_error - movzbl -12(%rbp),%eax - ret - -bpf_slow_path_byte_msh: - xchg %eax,%ebx /* dont lose A , X is about to be scratched */ - bpf_slow_path_common(1) - js bpf_error - movzbl -12(%rbp),%eax - and $15,%al - shl $2,%al - xchg %eax,%ebx + movzbl - MAX_BPF_STACK + 32(%rbp),%eax ret #define sk_negative_common(SIZE) \ - push %rdi; /* save skb */ \ + mov %rbx, %rdi; /* arg1 == skb */ \ push %r9; \ push SKBDATA; \ /* rsi already has offset */ \ @@ -145,10 +116,8 @@ bpf_slow_path_byte_msh: test %rax,%rax; \ pop SKBDATA; \ pop %r9; \ - pop %rdi; \ jz bpf_error - bpf_slow_path_word_neg: cmp SKF_MAX_NEG_OFF, %esi /* test range */ jl bpf_error /* offset lower -> error */ @@ -179,22 +148,12 @@ sk_load_byte_negative_offset: movzbl (%rax), %eax ret -bpf_slow_path_byte_msh_neg: - cmp SKF_MAX_NEG_OFF, %esi - jl bpf_error -sk_load_byte_msh_negative_offset: - .globl sk_load_byte_msh_negative_offset - xchg %eax,%ebx /* dont lose A , X is about to be scratched */ - sk_negative_common(1) - movzbl (%rax),%eax - and $15,%al - shl $2,%al - xchg %eax,%ebx - ret - bpf_error: # force a return 0 from jit handler - xor %eax,%eax - mov -8(%rbp),%rbx + xor %eax,%eax + mov - MAX_BPF_STACK(%rbp),%rbx + mov - MAX_BPF_STACK + 8(%rbp),%r13 + mov - MAX_BPF_STACK + 16(%rbp),%r14 + mov - MAX_BPF_STACK + 24(%rbp),%r15 leaveq ret diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index dc01773..080f3f0 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1,6 +1,7 @@ /* bpf_jit_comp.c : BPF JIT compiler * * Copyright (C) 2011-2013 Eric Dumazet (eric.dumazet@gmail.com) + * Internal BPF Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,28 +15,16 @@ #include <linux/if_vlan.h> #include <linux/random.h> -/* - * Conventions : - * EAX : BPF A accumulator - * EBX : BPF X accumulator - * RDI : pointer to skb (first argument given to JIT function) - * RBP : frame pointer (even if CONFIG_FRAME_POINTER=n) - * ECX,EDX,ESI : scratch registers - * r9d : skb->len - skb->data_len (headlen) - * r8 : skb->data - * -8(RBP) : saved RBX value - * -16(RBP)..-80(RBP) : BPF_MEMWORDS values - */ int bpf_jit_enable __read_mostly; /* * assembly code in arch/x86/net/bpf_jit.S */ -extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[]; +extern u8 sk_load_word[], sk_load_half[], sk_load_byte[]; extern u8 sk_load_word_positive_offset[], sk_load_half_positive_offset[]; -extern u8 sk_load_byte_positive_offset[], sk_load_byte_msh_positive_offset[]; +extern u8 sk_load_byte_positive_offset[]; extern u8 sk_load_word_negative_offset[], sk_load_half_negative_offset[]; -extern u8 sk_load_byte_negative_offset[], sk_load_byte_msh_negative_offset[]; +extern u8 sk_load_byte_negative_offset[]; static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) { @@ -56,30 +45,44 @@ static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) #define EMIT2(b1, b2) EMIT((b1) + ((b2) << 8), 2) #define EMIT3(b1, b2, b3) EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3) #define EMIT4(b1, b2, b3, b4) EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4) -#define EMIT1_off32(b1, off) do { EMIT1(b1); EMIT(off, 4);} while (0) - -#define CLEAR_A() EMIT2(0x31, 0xc0) /* xor %eax,%eax */ -#define CLEAR_X() EMIT2(0x31, 0xdb) /* xor %ebx,%ebx */ +#define EMIT1_off32(b1, off) \ + do {EMIT1(b1); EMIT(off, 4); } while (0) +#define EMIT2_off32(b1, b2, off) \ + do {EMIT2(b1, b2); EMIT(off, 4); } while (0) +#define EMIT3_off32(b1, b2, b3, off) \ + do {EMIT3(b1, b2, b3); EMIT(off, 4); } while (0) +#define EMIT4_off32(b1, b2, b3, b4, off) \ + do {EMIT4(b1, b2, b3, b4); EMIT(off, 4); } while (0) static inline bool is_imm8(int value) { return value <= 127 && value >= -128; } -static inline bool is_near(int offset) +static inline bool is_simm32(s64 value) { - return offset <= 127 && offset >= -128; + return value == (s64) (s32) value; } -#define EMIT_JMP(offset) \ -do { \ - if (offset) { \ - if (is_near(offset)) \ - EMIT2(0xeb, offset); /* jmp .+off8 */ \ - else \ - EMIT1_off32(0xe9, offset); /* jmp .+off32 */ \ - } \ -} while (0) +/* mov A, X */ +#define EMIT_mov(A, X) \ + do {if (A != X) \ + EMIT3(add_2mod(0x48, A, X), 0x89, add_2reg(0xC0, A, X)); \ + } while (0) + +static int bpf_size_to_x86_bytes(int bpf_size) +{ + if (bpf_size == BPF_W) + return 4; + else if (bpf_size == BPF_H) + return 2; + else if (bpf_size == BPF_B) + return 1; + else if (bpf_size == BPF_DW) + return 4; /* imm32 */ + else + return 0; +} /* list of x86 cond jumps opcodes (. + s8) * Add 0x10 (and an extra 0x0f) to generate far jumps (. + s32) @@ -90,27 +93,8 @@ do { \ #define X86_JNE 0x75 #define X86_JBE 0x76 #define X86_JA 0x77 - -#define EMIT_COND_JMP(op, offset) \ -do { \ - if (is_near(offset)) \ - EMIT2(op, offset); /* jxx .+off8 */ \ - else { \ - EMIT2(0x0f, op + 0x10); \ - EMIT(offset, 4); /* jxx .+off32 */ \ - } \ -} while (0) - -#define COND_SEL(CODE, TOP, FOP) \ - case CODE: \ - t_op = TOP; \ - f_op = FOP; \ - goto cond_branch - - -#define SEEN_DATAREF 1 /* might call external helpers */ -#define SEEN_XREG 2 /* ebx is used */ -#define SEEN_MEM 4 /* use mem[] for temporary storage */ +#define X86_JGE 0x7D +#define X86_JG 0x7F static inline void bpf_flush_icache(void *start, void *end) { @@ -125,26 +109,6 @@ static inline void bpf_flush_icache(void *start, void *end) #define CHOOSE_LOAD_FUNC(K, func) \ ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) -/* Helper to find the offset of pkt_type in sk_buff - * We want to make sure its still a 3bit field starting at a byte boundary. - */ -#define PKT_TYPE_MAX 7 -static int pkt_type_offset(void) -{ - struct sk_buff skb_probe = { - .pkt_type = ~0, - }; - char *ct = (char *)&skb_probe; - unsigned int off; - - for (off = 0; off < sizeof(struct sk_buff); off++) { - if (ct[off] == PKT_TYPE_MAX) - return off; - } - pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n"); - return -1; -} - struct bpf_binary_header { unsigned int pages; /* Note : for security reasons, bpf code will follow a randomly @@ -171,590 +135,778 @@ static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen, memset(header, 0xcc, sz); /* fill whole space with int3 instructions */ header->pages = sz / PAGE_SIZE; - hole = sz - (proglen + sizeof(*header)); + hole = min(sz - (proglen + sizeof(*header)), PAGE_SIZE - sizeof(*header)); /* insert a random number of int3 instructions before BPF code */ *image_ptr = &header->image[prandom_u32() % hole]; return header; } -void bpf_jit_compile(struct sk_filter *fp) +/* pick a register outside of BPF range for JIT internal work */ +#define AUX_REG (MAX_BPF_REG + 1) + +/* the following table maps BPF registers to x64 registers. + * x64 register r12 is unused, since if used as base address register + * in load/store instructions, it always needs an extra byte of encoding + */ +static const int reg2hex[] = { + [BPF_REG_0] = 0, /* rax */ + [BPF_REG_1] = 7, /* rdi */ + [BPF_REG_2] = 6, /* rsi */ + [BPF_REG_3] = 2, /* rdx */ + [BPF_REG_4] = 1, /* rcx */ + [BPF_REG_5] = 0, /* r8 */ + [BPF_REG_6] = 3, /* rbx callee saved */ + [BPF_REG_7] = 5, /* r13 callee saved */ + [BPF_REG_8] = 6, /* r14 callee saved */ + [BPF_REG_9] = 7, /* r15 callee saved */ + [BPF_REG_FP] = 5, /* rbp readonly */ + [AUX_REG] = 3, /* r11 temp register */ +}; + +/* is_ereg() == true if BPF register 'reg' maps to x64 r8..r15 + * which need extra byte of encoding. + * rax,rcx,...,rbp have simpler encoding + */ +static inline bool is_ereg(u32 reg) { - u8 temp[64]; - u8 *prog; - unsigned int proglen, oldproglen = 0; - int ilen, i; - int t_offset, f_offset; - u8 t_op, f_op, seen = 0, pass; - u8 *image = NULL; - struct bpf_binary_header *header = NULL; - u8 *func; - int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */ - unsigned int cleanup_addr; /* epilogue code offset */ - unsigned int *addrs; - const struct sock_filter *filter = fp->insns; - int flen = fp->len; + if (reg == BPF_REG_5 || reg == AUX_REG || + (reg >= BPF_REG_7 && reg <= BPF_REG_9)) + return true; + else + return false; +} - if (!bpf_jit_enable) - return; +/* add modifiers if 'reg' maps to x64 registers r8..r15 */ +static inline u8 add_1mod(u8 byte, u32 reg) +{ + if (is_ereg(reg)) + byte |= 1; + return byte; +} - addrs = kmalloc(flen * sizeof(*addrs), GFP_KERNEL); - if (addrs == NULL) - return; +static inline u8 add_2mod(u8 byte, u32 r1, u32 r2) +{ + if (is_ereg(r1)) + byte |= 1; + if (is_ereg(r2)) + byte |= 4; + return byte; +} - /* Before first pass, make a rough estimation of addrs[] - * each bpf instruction is translated to less than 64 bytes +/* encode dest register 'a_reg' into x64 opcode 'byte' */ +static inline u8 add_1reg(u8 byte, u32 a_reg) +{ + return byte + reg2hex[a_reg]; +} + +/* encode dest 'a_reg' and src 'x_reg' registers into x64 opcode 'byte' */ +static inline u8 add_2reg(u8 byte, u32 a_reg, u32 x_reg) +{ + return byte + reg2hex[a_reg] + (reg2hex[x_reg] << 3); +} + +struct jit_context { + unsigned int cleanup_addr; /* epilogue code offset */ + bool seen_ld_abs; +}; + +static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image, + int oldproglen, struct jit_context *ctx) +{ + struct sock_filter_int *insn = bpf_prog->insnsi; + int insn_cnt = bpf_prog->len; + u8 temp[64]; + int i; + int proglen = 0; + u8 *prog = temp; + int stacksize = MAX_BPF_STACK + + 32 /* space for rbx, r13, r14, r15 */ + + 8 /* space for skb_copy_bits() buffer */; + + EMIT1(0x55); /* push rbp */ + EMIT3(0x48, 0x89, 0xE5); /* mov rbp,rsp */ + + /* sub rsp, stacksize */ + EMIT3_off32(0x48, 0x81, 0xEC, stacksize); + + /* all classic BPF filters use R6(rbx) save it */ + + /* mov qword ptr [rbp-X],rbx */ + EMIT3_off32(0x48, 0x89, 0x9D, -stacksize); + + /* sk_convert_filter() maps classic BPF register X to R7 and uses R8 + * as temporary, so all tcpdump filters need to spill/fill R7(r13) and + * R8(r14). R9(r15) spill could be made conditional, but there is only + * one 'bpf_error' return path out of helper functions inside bpf_jit.S + * The overhead of extra spill is negligible for any filter other + * than synthetic ones. Therefore not worth adding complexity. */ - for (proglen = 0, i = 0; i < flen; i++) { - proglen += 64; - addrs[i] = proglen; + + /* mov qword ptr [rbp-X],r13 */ + EMIT3_off32(0x4C, 0x89, 0xAD, -stacksize + 8); + /* mov qword ptr [rbp-X],r14 */ + EMIT3_off32(0x4C, 0x89, 0xB5, -stacksize + 16); + /* mov qword ptr [rbp-X],r15 */ + EMIT3_off32(0x4C, 0x89, 0xBD, -stacksize + 24); + + /* clear A and X registers */ + EMIT2(0x31, 0xc0); /* xor eax, eax */ + EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */ + + if (ctx->seen_ld_abs) { + /* r9d : skb->len - skb->data_len (headlen) + * r10 : skb->data + */ + if (is_imm8(offsetof(struct sk_buff, len))) + /* mov %r9d, off8(%rdi) */ + EMIT4(0x44, 0x8b, 0x4f, + offsetof(struct sk_buff, len)); + else + /* mov %r9d, off32(%rdi) */ + EMIT3_off32(0x44, 0x8b, 0x8f, + offsetof(struct sk_buff, len)); + + if (is_imm8(offsetof(struct sk_buff, data_len))) + /* sub %r9d, off8(%rdi) */ + EMIT4(0x44, 0x2b, 0x4f, + offsetof(struct sk_buff, data_len)); + else + EMIT3_off32(0x44, 0x2b, 0x8f, + offsetof(struct sk_buff, data_len)); + + if (is_imm8(offsetof(struct sk_buff, data))) + /* mov %r10, off8(%rdi) */ + EMIT4(0x4c, 0x8b, 0x57, + offsetof(struct sk_buff, data)); + else + /* mov %r10, off32(%rdi) */ + EMIT3_off32(0x4c, 0x8b, 0x97, + offsetof(struct sk_buff, data)); } - cleanup_addr = proglen; /* epilogue address */ - for (pass = 0; pass < 10; pass++) { - u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen; - /* no prologue/epilogue for trivial filters (RET something) */ - proglen = 0; - prog = temp; + for (i = 0; i < insn_cnt; i++, insn++) { + const s32 K = insn->imm; + u32 a_reg = insn->a_reg; + u32 x_reg = insn->x_reg; + u8 b1 = 0, b2 = 0, b3 = 0; + s64 jmp_offset; + u8 jmp_cond; + int ilen; + u8 *func; + + switch (insn->code) { + /* ALU */ + case BPF_ALU | BPF_ADD | BPF_X: + case BPF_ALU | BPF_SUB | BPF_X: + case BPF_ALU | BPF_AND | BPF_X: + case BPF_ALU | BPF_OR | BPF_X: + case BPF_ALU | BPF_XOR | BPF_X: + case BPF_ALU64 | BPF_ADD | BPF_X: + case BPF_ALU64 | BPF_SUB | BPF_X: + case BPF_ALU64 | BPF_AND | BPF_X: + case BPF_ALU64 | BPF_OR | BPF_X: + case BPF_ALU64 | BPF_XOR | BPF_X: + switch (BPF_OP(insn->code)) { + case BPF_ADD: b2 = 0x01; break; + case BPF_SUB: b2 = 0x29; break; + case BPF_AND: b2 = 0x21; break; + case BPF_OR: b2 = 0x09; break; + case BPF_XOR: b2 = 0x31; break; + } + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_2mod(0x48, a_reg, x_reg)); + else if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT1(add_2mod(0x40, a_reg, x_reg)); + EMIT2(b2, add_2reg(0xC0, a_reg, x_reg)); + break; - if (seen_or_pass0) { - EMIT4(0x55, 0x48, 0x89, 0xe5); /* push %rbp; mov %rsp,%rbp */ - EMIT4(0x48, 0x83, 0xec, 96); /* subq $96,%rsp */ - /* note : must save %rbx in case bpf_error is hit */ - if (seen_or_pass0 & (SEEN_XREG | SEEN_DATAREF)) - EMIT4(0x48, 0x89, 0x5d, 0xf8); /* mov %rbx, -8(%rbp) */ - if (seen_or_pass0 & SEEN_XREG) - CLEAR_X(); /* make sure we dont leek kernel memory */ - - /* - * If this filter needs to access skb data, - * loads r9 and r8 with : - * r9 = skb->len - skb->data_len - * r8 = skb->data + /* mov A, X */ + case BPF_ALU64 | BPF_MOV | BPF_X: + EMIT_mov(a_reg, x_reg); + break; + + /* mov32 A, X */ + case BPF_ALU | BPF_MOV | BPF_X: + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT1(add_2mod(0x40, a_reg, x_reg)); + EMIT2(0x89, add_2reg(0xC0, a_reg, x_reg)); + break; + + /* neg A */ + case BPF_ALU | BPF_NEG: + case BPF_ALU64 | BPF_NEG: + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, a_reg)); + else if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + EMIT2(0xF7, add_1reg(0xD8, a_reg)); + break; + + case BPF_ALU | BPF_ADD | BPF_K: + case BPF_ALU | BPF_SUB | BPF_K: + case BPF_ALU | BPF_AND | BPF_K: + case BPF_ALU | BPF_OR | BPF_K: + case BPF_ALU | BPF_XOR | BPF_K: + case BPF_ALU64 | BPF_ADD | BPF_K: + case BPF_ALU64 | BPF_SUB | BPF_K: + case BPF_ALU64 | BPF_AND | BPF_K: + case BPF_ALU64 | BPF_OR | BPF_K: + case BPF_ALU64 | BPF_XOR | BPF_K: + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, a_reg)); + else if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + + switch (BPF_OP(insn->code)) { + case BPF_ADD: b3 = 0xC0; break; + case BPF_SUB: b3 = 0xE8; break; + case BPF_AND: b3 = 0xE0; break; + case BPF_OR: b3 = 0xC8; break; + case BPF_XOR: b3 = 0xF0; break; + } + + if (is_imm8(K)) + EMIT3(0x83, add_1reg(b3, a_reg), K); + else + EMIT2_off32(0x81, add_1reg(b3, a_reg), K); + break; + + case BPF_ALU64 | BPF_MOV | BPF_K: + /* optimization: if imm32 is positive, + * use 'mov eax, imm32' (which zero-extends imm32) + * to save 2 bytes */ - if (seen_or_pass0 & SEEN_DATAREF) { - if (offsetof(struct sk_buff, len) <= 127) - /* mov off8(%rdi),%r9d */ - EMIT4(0x44, 0x8b, 0x4f, offsetof(struct sk_buff, len)); - else { - /* mov off32(%rdi),%r9d */ - EMIT3(0x44, 0x8b, 0x8f); - EMIT(offsetof(struct sk_buff, len), 4); - } - if (is_imm8(offsetof(struct sk_buff, data_len))) - /* sub off8(%rdi),%r9d */ - EMIT4(0x44, 0x2b, 0x4f, offsetof(struct sk_buff, data_len)); - else { - EMIT3(0x44, 0x2b, 0x8f); - EMIT(offsetof(struct sk_buff, data_len), 4); - } + if (K < 0) { + /* 'mov rax, imm32' sign extends imm32 */ + b1 = add_1mod(0x48, a_reg); + b2 = 0xC7; + b3 = 0xC0; + EMIT3_off32(b1, b2, add_1reg(b3, a_reg), K); + break; + } - if (is_imm8(offsetof(struct sk_buff, data))) - /* mov off8(%rdi),%r8 */ - EMIT4(0x4c, 0x8b, 0x47, offsetof(struct sk_buff, data)); - else { - /* mov off32(%rdi),%r8 */ - EMIT3(0x4c, 0x8b, 0x87); - EMIT(offsetof(struct sk_buff, data), 4); - } + case BPF_ALU | BPF_MOV | BPF_K: + /* mov %eax, imm32 */ + if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + EMIT1_off32(add_1reg(0xB8, a_reg), K); + break; + + /* A %= X, A /= X, A %= K, A /= K */ + case BPF_ALU | BPF_MOD | BPF_X: + case BPF_ALU | BPF_DIV | BPF_X: + case BPF_ALU | BPF_MOD | BPF_K: + case BPF_ALU | BPF_DIV | BPF_K: + case BPF_ALU64 | BPF_MOD | BPF_X: + case BPF_ALU64 | BPF_DIV | BPF_X: + case BPF_ALU64 | BPF_MOD | BPF_K: + case BPF_ALU64 | BPF_DIV | BPF_K: + EMIT1(0x50); /* push rax */ + EMIT1(0x52); /* push rdx */ + + if (BPF_SRC(insn->code) == BPF_X) + /* mov r11, X */ + EMIT_mov(AUX_REG, x_reg); + else + /* mov r11, K */ + EMIT3_off32(0x49, 0xC7, 0xC3, K); + + /* mov rax, A */ + EMIT_mov(BPF_REG_0, a_reg); + + /* xor edx, edx + * equivalent to 'xor rdx, rdx', but one byte less + */ + EMIT2(0x31, 0xd2); + + if (BPF_SRC(insn->code) == BPF_X) { + /* if (X == 0) return 0 */ + + /* cmp r11, 0 */ + EMIT4(0x49, 0x83, 0xFB, 0x00); + + /* jne .+9 (skip over pop, pop, xor and jmp) */ + EMIT2(X86_JNE, 1 + 1 + 2 + 5); + EMIT1(0x5A); /* pop rdx */ + EMIT1(0x58); /* pop rax */ + EMIT2(0x31, 0xc0); /* xor eax, eax */ + + /* jmp cleanup_addr + * addrs[i] - 11, because there are 11 bytes + * after this insn: div, mov, pop, pop, mov + */ + jmp_offset = ctx->cleanup_addr - (addrs[i] - 11); + EMIT1_off32(0xE9, jmp_offset); } - } - switch (filter[0].code) { - case BPF_S_RET_K: - case BPF_S_LD_W_LEN: - case BPF_S_ANC_PROTOCOL: - case BPF_S_ANC_IFINDEX: - case BPF_S_ANC_MARK: - case BPF_S_ANC_RXHASH: - case BPF_S_ANC_CPU: - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - case BPF_S_ANC_QUEUE: - case BPF_S_ANC_PKTTYPE: - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: - /* first instruction sets A register (or is RET 'constant') */ + if (BPF_CLASS(insn->code) == BPF_ALU64) + /* div r11 */ + EMIT3(0x49, 0xF7, 0xF3); + else + /* div r11d */ + EMIT3(0x41, 0xF7, 0xF3); + + if (BPF_OP(insn->code) == BPF_MOD) + /* mov r11, rdx */ + EMIT3(0x49, 0x89, 0xD3); + else + /* mov r11, rax */ + EMIT3(0x49, 0x89, 0xC3); + + EMIT1(0x5A); /* pop rdx */ + EMIT1(0x58); /* pop rax */ + + /* mov A, r11 */ + EMIT_mov(a_reg, AUX_REG); break; - default: - /* make sure we dont leak kernel information to user */ - CLEAR_A(); /* A = 0 */ - } - for (i = 0; i < flen; i++) { - unsigned int K = filter[i].k; + case BPF_ALU | BPF_MUL | BPF_K: + case BPF_ALU | BPF_MUL | BPF_X: + case BPF_ALU64 | BPF_MUL | BPF_K: + case BPF_ALU64 | BPF_MUL | BPF_X: + EMIT1(0x50); /* push rax */ + EMIT1(0x52); /* push rdx */ + + /* mov r11, A */ + EMIT_mov(AUX_REG, a_reg); + + if (BPF_SRC(insn->code) == BPF_X) + /* mov rax, X */ + EMIT_mov(BPF_REG_0, x_reg); + else + /* mov rax, K */ + EMIT3_off32(0x48, 0xC7, 0xC0, K); + + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, AUX_REG)); + else if (is_ereg(AUX_REG)) + EMIT1(add_1mod(0x40, AUX_REG)); + /* mul(q) r11 */ + EMIT2(0xF7, add_1reg(0xE0, AUX_REG)); + + /* mov r11, rax */ + EMIT_mov(AUX_REG, BPF_REG_0); + + EMIT1(0x5A); /* pop rdx */ + EMIT1(0x58); /* pop rax */ + + /* mov A, r11 */ + EMIT_mov(a_reg, AUX_REG); + break; - switch (filter[i].code) { - case BPF_S_ALU_ADD_X: /* A += X; */ - seen |= SEEN_XREG; - EMIT2(0x01, 0xd8); /* add %ebx,%eax */ - break; - case BPF_S_ALU_ADD_K: /* A += K; */ - if (!K) - break; - if (is_imm8(K)) - EMIT3(0x83, 0xc0, K); /* add imm8,%eax */ - else - EMIT1_off32(0x05, K); /* add imm32,%eax */ - break; - case BPF_S_ALU_SUB_X: /* A -= X; */ - seen |= SEEN_XREG; - EMIT2(0x29, 0xd8); /* sub %ebx,%eax */ - break; - case BPF_S_ALU_SUB_K: /* A -= K */ - if (!K) - break; - if (is_imm8(K)) - EMIT3(0x83, 0xe8, K); /* sub imm8,%eax */ - else - EMIT1_off32(0x2d, K); /* sub imm32,%eax */ - break; - case BPF_S_ALU_MUL_X: /* A *= X; */ - seen |= SEEN_XREG; - EMIT3(0x0f, 0xaf, 0xc3); /* imul %ebx,%eax */ - break; - case BPF_S_ALU_MUL_K: /* A *= K */ - if (is_imm8(K)) - EMIT3(0x6b, 0xc0, K); /* imul imm8,%eax,%eax */ - else { - EMIT2(0x69, 0xc0); /* imul imm32,%eax */ - EMIT(K, 4); - } - break; - case BPF_S_ALU_DIV_X: /* A /= X; */ - seen |= SEEN_XREG; - EMIT2(0x85, 0xdb); /* test %ebx,%ebx */ - if (pc_ret0 > 0) { - /* addrs[pc_ret0 - 1] is start address of target - * (addrs[i] - 4) is the address following this jmp - * ("xor %edx,%edx; div %ebx" being 4 bytes long) - */ - EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] - - (addrs[i] - 4)); - } else { - EMIT_COND_JMP(X86_JNE, 2 + 5); - CLEAR_A(); - EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 4)); /* jmp .+off32 */ - } - EMIT4(0x31, 0xd2, 0xf7, 0xf3); /* xor %edx,%edx; div %ebx */ - break; - case BPF_S_ALU_MOD_X: /* A %= X; */ - seen |= SEEN_XREG; - EMIT2(0x85, 0xdb); /* test %ebx,%ebx */ - if (pc_ret0 > 0) { - /* addrs[pc_ret0 - 1] is start address of target - * (addrs[i] - 6) is the address following this jmp - * ("xor %edx,%edx; div %ebx;mov %edx,%eax" being 6 bytes long) - */ - EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] - - (addrs[i] - 6)); - } else { - EMIT_COND_JMP(X86_JNE, 2 + 5); - CLEAR_A(); - EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 6)); /* jmp .+off32 */ - } - EMIT2(0x31, 0xd2); /* xor %edx,%edx */ - EMIT2(0xf7, 0xf3); /* div %ebx */ - EMIT2(0x89, 0xd0); /* mov %edx,%eax */ - break; - case BPF_S_ALU_MOD_K: /* A %= K; */ - if (K == 1) { - CLEAR_A(); - break; - } - EMIT2(0x31, 0xd2); /* xor %edx,%edx */ - EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */ - EMIT2(0xf7, 0xf1); /* div %ecx */ - EMIT2(0x89, 0xd0); /* mov %edx,%eax */ - break; - case BPF_S_ALU_DIV_K: /* A /= K */ - if (K == 1) - break; - EMIT2(0x31, 0xd2); /* xor %edx,%edx */ - EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */ - EMIT2(0xf7, 0xf1); /* div %ecx */ - break; - case BPF_S_ALU_AND_X: - seen |= SEEN_XREG; - EMIT2(0x21, 0xd8); /* and %ebx,%eax */ - break; - case BPF_S_ALU_AND_K: - if (K >= 0xFFFFFF00) { - EMIT2(0x24, K & 0xFF); /* and imm8,%al */ - } else if (K >= 0xFFFF0000) { - EMIT2(0x66, 0x25); /* and imm16,%ax */ - EMIT(K, 2); - } else { - EMIT1_off32(0x25, K); /* and imm32,%eax */ - } - break; - case BPF_S_ALU_OR_X: - seen |= SEEN_XREG; - EMIT2(0x09, 0xd8); /* or %ebx,%eax */ - break; - case BPF_S_ALU_OR_K: - if (is_imm8(K)) - EMIT3(0x83, 0xc8, K); /* or imm8,%eax */ - else - EMIT1_off32(0x0d, K); /* or imm32,%eax */ - break; - case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */ - case BPF_S_ALU_XOR_X: - seen |= SEEN_XREG; - EMIT2(0x31, 0xd8); /* xor %ebx,%eax */ - break; - case BPF_S_ALU_XOR_K: /* A ^= K; */ - if (K == 0) - break; - if (is_imm8(K)) - EMIT3(0x83, 0xf0, K); /* xor imm8,%eax */ - else - EMIT1_off32(0x35, K); /* xor imm32,%eax */ - break; - case BPF_S_ALU_LSH_X: /* A <<= X; */ - seen |= SEEN_XREG; - EMIT4(0x89, 0xd9, 0xd3, 0xe0); /* mov %ebx,%ecx; shl %cl,%eax */ - break; - case BPF_S_ALU_LSH_K: - if (K == 0) - break; - else if (K == 1) - EMIT2(0xd1, 0xe0); /* shl %eax */ - else - EMIT3(0xc1, 0xe0, K); - break; - case BPF_S_ALU_RSH_X: /* A >>= X; */ - seen |= SEEN_XREG; - EMIT4(0x89, 0xd9, 0xd3, 0xe8); /* mov %ebx,%ecx; shr %cl,%eax */ - break; - case BPF_S_ALU_RSH_K: /* A >>= K; */ - if (K == 0) - break; - else if (K == 1) - EMIT2(0xd1, 0xe8); /* shr %eax */ - else - EMIT3(0xc1, 0xe8, K); - break; - case BPF_S_ALU_NEG: - EMIT2(0xf7, 0xd8); /* neg %eax */ - break; - case BPF_S_RET_K: - if (!K) { - if (pc_ret0 == -1) - pc_ret0 = i; - CLEAR_A(); - } else { - EMIT1_off32(0xb8, K); /* mov $imm32,%eax */ - } - /* fallinto */ - case BPF_S_RET_A: - if (seen_or_pass0) { - if (i != flen - 1) { - EMIT_JMP(cleanup_addr - addrs[i]); - break; - } - if (seen_or_pass0 & SEEN_XREG) - EMIT4(0x48, 0x8b, 0x5d, 0xf8); /* mov -8(%rbp),%rbx */ - EMIT1(0xc9); /* leaveq */ - } - EMIT1(0xc3); /* ret */ - break; - case BPF_S_MISC_TAX: /* X = A */ - seen |= SEEN_XREG; - EMIT2(0x89, 0xc3); /* mov %eax,%ebx */ - break; - case BPF_S_MISC_TXA: /* A = X */ - seen |= SEEN_XREG; - EMIT2(0x89, 0xd8); /* mov %ebx,%eax */ - break; - case BPF_S_LD_IMM: /* A = K */ - if (!K) - CLEAR_A(); - else - EMIT1_off32(0xb8, K); /* mov $imm32,%eax */ - break; - case BPF_S_LDX_IMM: /* X = K */ - seen |= SEEN_XREG; - if (!K) - CLEAR_X(); + /* shifts */ + case BPF_ALU | BPF_LSH | BPF_K: + case BPF_ALU | BPF_RSH | BPF_K: + case BPF_ALU | BPF_ARSH | BPF_K: + case BPF_ALU64 | BPF_LSH | BPF_K: + case BPF_ALU64 | BPF_RSH | BPF_K: + case BPF_ALU64 | BPF_ARSH | BPF_K: + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, a_reg)); + else if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + + switch (BPF_OP(insn->code)) { + case BPF_LSH: b3 = 0xE0; break; + case BPF_RSH: b3 = 0xE8; break; + case BPF_ARSH: b3 = 0xF8; break; + } + EMIT3(0xC1, add_1reg(b3, a_reg), K); + break; + + case BPF_ALU | BPF_END | BPF_FROM_BE: + switch (K) { + case 16: + /* emit 'ror %ax, 8' to swap lower 2 bytes */ + EMIT1(0x66); + if (is_ereg(a_reg)) + EMIT1(0x41); + EMIT3(0xC1, add_1reg(0xC8, a_reg), 8); + break; + case 32: + /* emit 'bswap eax' to swap lower 4 bytes */ + if (is_ereg(a_reg)) + EMIT2(0x41, 0x0F); else - EMIT1_off32(0xbb, K); /* mov $imm32,%ebx */ - break; - case BPF_S_LD_MEM: /* A = mem[K] : mov off8(%rbp),%eax */ - seen |= SEEN_MEM; - EMIT3(0x8b, 0x45, 0xf0 - K*4); - break; - case BPF_S_LDX_MEM: /* X = mem[K] : mov off8(%rbp),%ebx */ - seen |= SEEN_XREG | SEEN_MEM; - EMIT3(0x8b, 0x5d, 0xf0 - K*4); - break; - case BPF_S_ST: /* mem[K] = A : mov %eax,off8(%rbp) */ - seen |= SEEN_MEM; - EMIT3(0x89, 0x45, 0xf0 - K*4); - break; - case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */ - seen |= SEEN_XREG | SEEN_MEM; - EMIT3(0x89, 0x5d, 0xf0 - K*4); - break; - case BPF_S_LD_W_LEN: /* A = skb->len; */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); - if (is_imm8(offsetof(struct sk_buff, len))) - /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, len)); - else { - EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, len), 4); - } - break; - case BPF_S_LDX_W_LEN: /* X = skb->len; */ - seen |= SEEN_XREG; - if (is_imm8(offsetof(struct sk_buff, len))) - /* mov off8(%rdi),%ebx */ - EMIT3(0x8b, 0x5f, offsetof(struct sk_buff, len)); - else { - EMIT2(0x8b, 0x9f); - EMIT(offsetof(struct sk_buff, len), 4); - } - break; - case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); - if (is_imm8(offsetof(struct sk_buff, protocol))) { - /* movzwl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, protocol)); - } else { - EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */ - EMIT(offsetof(struct sk_buff, protocol), 4); - } - EMIT2(0x86, 0xc4); /* ntohs() : xchg %al,%ah */ - break; - case BPF_S_ANC_IFINDEX: - if (is_imm8(offsetof(struct sk_buff, dev))) { - /* movq off8(%rdi),%rax */ - EMIT4(0x48, 0x8b, 0x47, offsetof(struct sk_buff, dev)); - } else { - EMIT3(0x48, 0x8b, 0x87); /* movq off32(%rdi),%rax */ - EMIT(offsetof(struct sk_buff, dev), 4); - } - EMIT3(0x48, 0x85, 0xc0); /* test %rax,%rax */ - EMIT_COND_JMP(X86_JE, cleanup_addr - (addrs[i] - 6)); - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); - EMIT2(0x8b, 0x80); /* mov off32(%rax),%eax */ - EMIT(offsetof(struct net_device, ifindex), 4); + EMIT1(0x0F); + EMIT1(add_1reg(0xC8, a_reg)); break; - case BPF_S_ANC_MARK: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); - if (is_imm8(offsetof(struct sk_buff, mark))) { - /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, mark)); - } else { - EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, mark), 4); - } - break; - case BPF_S_ANC_RXHASH: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); - if (is_imm8(offsetof(struct sk_buff, hash))) { - /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, hash)); - } else { - EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, hash), 4); - } - break; - case BPF_S_ANC_QUEUE: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); - if (is_imm8(offsetof(struct sk_buff, queue_mapping))) { - /* movzwl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, queue_mapping)); - } else { - EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */ - EMIT(offsetof(struct sk_buff, queue_mapping), 4); - } - break; - case BPF_S_ANC_CPU: -#ifdef CONFIG_SMP - EMIT4(0x65, 0x8b, 0x04, 0x25); /* mov %gs:off32,%eax */ - EMIT((u32)(unsigned long)&cpu_number, 4); /* A = smp_processor_id(); */ -#else - CLEAR_A(); -#endif - break; - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); - if (is_imm8(offsetof(struct sk_buff, vlan_tci))) { - /* movzwl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, vlan_tci)); - } else { - EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */ - EMIT(offsetof(struct sk_buff, vlan_tci), 4); - } - BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); - if (filter[i].code == BPF_S_ANC_VLAN_TAG) { - EMIT3(0x80, 0xe4, 0xef); /* and $0xef,%ah */ - } else { - EMIT3(0xc1, 0xe8, 0x0c); /* shr $0xc,%eax */ - EMIT3(0x83, 0xe0, 0x01); /* and $0x1,%eax */ - } - break; - case BPF_S_ANC_PKTTYPE: - { - int off = pkt_type_offset(); - - if (off < 0) - goto out; - if (is_imm8(off)) { - /* movzbl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb6, 0x47, off); - } else { - /* movbl off32(%rdi),%eax */ - EMIT3(0x0f, 0xb6, 0x87); - EMIT(off, 4); - } - EMIT3(0x83, 0xe0, PKT_TYPE_MAX); /* and $0x7,%eax */ + case 64: + /* emit 'bswap rax' to swap 8 bytes */ + EMIT3(add_1mod(0x48, a_reg), 0x0F, + add_1reg(0xC8, a_reg)); break; } - case BPF_S_LD_W_ABS: - func = CHOOSE_LOAD_FUNC(K, sk_load_word); -common_load: seen |= SEEN_DATAREF; - t_offset = func - (image + addrs[i]); - EMIT1_off32(0xbe, K); /* mov imm32,%esi */ - EMIT1_off32(0xe8, t_offset); /* call */ - break; - case BPF_S_LD_H_ABS: - func = CHOOSE_LOAD_FUNC(K, sk_load_half); - goto common_load; - case BPF_S_LD_B_ABS: - func = CHOOSE_LOAD_FUNC(K, sk_load_byte); - goto common_load; - case BPF_S_LDX_B_MSH: - func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh); - seen |= SEEN_DATAREF | SEEN_XREG; - t_offset = func - (image + addrs[i]); - EMIT1_off32(0xbe, K); /* mov imm32,%esi */ - EMIT1_off32(0xe8, t_offset); /* call sk_load_byte_msh */ - break; - case BPF_S_LD_W_IND: - func = sk_load_word; -common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; - t_offset = func - (image + addrs[i]); - if (K) { - if (is_imm8(K)) { - EMIT3(0x8d, 0x73, K); /* lea imm8(%rbx), %esi */ - } else { - EMIT2(0x8d, 0xb3); /* lea imm32(%rbx),%esi */ - EMIT(K, 4); - } - } else { - EMIT2(0x89,0xde); /* mov %ebx,%esi */ - } - EMIT1_off32(0xe8, t_offset); /* call sk_load_xxx_ind */ - break; - case BPF_S_LD_H_IND: - func = sk_load_half; - goto common_load_ind; - case BPF_S_LD_B_IND: - func = sk_load_byte; - goto common_load_ind; - case BPF_S_JMP_JA: - t_offset = addrs[i + K] - addrs[i]; - EMIT_JMP(t_offset); - break; - COND_SEL(BPF_S_JMP_JGT_K, X86_JA, X86_JBE); - COND_SEL(BPF_S_JMP_JGE_K, X86_JAE, X86_JB); - COND_SEL(BPF_S_JMP_JEQ_K, X86_JE, X86_JNE); - COND_SEL(BPF_S_JMP_JSET_K,X86_JNE, X86_JE); - COND_SEL(BPF_S_JMP_JGT_X, X86_JA, X86_JBE); - COND_SEL(BPF_S_JMP_JGE_X, X86_JAE, X86_JB); - COND_SEL(BPF_S_JMP_JEQ_X, X86_JE, X86_JNE); - COND_SEL(BPF_S_JMP_JSET_X,X86_JNE, X86_JE); - -cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; - t_offset = addrs[i + filter[i].jt] - addrs[i]; - - /* same targets, can avoid doing the test :) */ - if (filter[i].jt == filter[i].jf) { - EMIT_JMP(t_offset); - break; - } + break; + + case BPF_ALU | BPF_END | BPF_FROM_LE: + break; - switch (filter[i].code) { - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JEQ_X: - seen |= SEEN_XREG; - EMIT2(0x39, 0xd8); /* cmp %ebx,%eax */ - break; - case BPF_S_JMP_JSET_X: - seen |= SEEN_XREG; - EMIT2(0x85, 0xd8); /* test %ebx,%eax */ - break; - case BPF_S_JMP_JEQ_K: - if (K == 0) { - EMIT2(0x85, 0xc0); /* test %eax,%eax */ - break; - } - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGE_K: - if (K <= 127) - EMIT3(0x83, 0xf8, K); /* cmp imm8,%eax */ + /* ST: *(u8*)(a_reg + off) = imm */ + case BPF_ST | BPF_MEM | BPF_B: + if (is_ereg(a_reg)) + EMIT2(0x41, 0xC6); + else + EMIT1(0xC6); + goto st; + case BPF_ST | BPF_MEM | BPF_H: + if (is_ereg(a_reg)) + EMIT3(0x66, 0x41, 0xC7); + else + EMIT2(0x66, 0xC7); + goto st; + case BPF_ST | BPF_MEM | BPF_W: + if (is_ereg(a_reg)) + EMIT2(0x41, 0xC7); + else + EMIT1(0xC7); + goto st; + case BPF_ST | BPF_MEM | BPF_DW: + EMIT2(add_1mod(0x48, a_reg), 0xC7); + +st: if (is_imm8(insn->off)) + EMIT2(add_1reg(0x40, a_reg), insn->off); + else + EMIT1_off32(add_1reg(0x80, a_reg), insn->off); + + EMIT(K, bpf_size_to_x86_bytes(BPF_SIZE(insn->code))); + break; + + /* STX: *(u8*)(a_reg + off) = x_reg */ + case BPF_STX | BPF_MEM | BPF_B: + /* emit 'mov byte ptr [rax + off], al' */ + if (is_ereg(a_reg) || is_ereg(x_reg) || + /* have to add extra byte for x86 SIL, DIL regs */ + x_reg == BPF_REG_1 || x_reg == BPF_REG_2) + EMIT2(add_2mod(0x40, a_reg, x_reg), 0x88); + else + EMIT1(0x88); + goto stx; + case BPF_STX | BPF_MEM | BPF_H: + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT3(0x66, add_2mod(0x40, a_reg, x_reg), 0x89); + else + EMIT2(0x66, 0x89); + goto stx; + case BPF_STX | BPF_MEM | BPF_W: + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT2(add_2mod(0x40, a_reg, x_reg), 0x89); + else + EMIT1(0x89); + goto stx; + case BPF_STX | BPF_MEM | BPF_DW: + EMIT2(add_2mod(0x48, a_reg, x_reg), 0x89); +stx: if (is_imm8(insn->off)) + EMIT2(add_2reg(0x40, a_reg, x_reg), insn->off); + else + EMIT1_off32(add_2reg(0x80, a_reg, x_reg), + insn->off); + break; + + /* LDX: a_reg = *(u8*)(x_reg + off) */ + case BPF_LDX | BPF_MEM | BPF_B: + /* emit 'movzx rax, byte ptr [rax + off]' */ + EMIT3(add_2mod(0x48, x_reg, a_reg), 0x0F, 0xB6); + goto ldx; + case BPF_LDX | BPF_MEM | BPF_H: + /* emit 'movzx rax, word ptr [rax + off]' */ + EMIT3(add_2mod(0x48, x_reg, a_reg), 0x0F, 0xB7); + goto ldx; + case BPF_LDX | BPF_MEM | BPF_W: + /* emit 'mov eax, dword ptr [rax+0x14]' */ + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT2(add_2mod(0x40, x_reg, a_reg), 0x8B); + else + EMIT1(0x8B); + goto ldx; + case BPF_LDX | BPF_MEM | BPF_DW: + /* emit 'mov rax, qword ptr [rax+0x14]' */ + EMIT2(add_2mod(0x48, x_reg, a_reg), 0x8B); +ldx: /* if insn->off == 0 we can save one extra byte, but + * special case of x86 r13 which always needs an offset + * is not worth the hassle + */ + if (is_imm8(insn->off)) + EMIT2(add_2reg(0x40, x_reg, a_reg), insn->off); + else + EMIT1_off32(add_2reg(0x80, x_reg, a_reg), + insn->off); + break; + + /* STX XADD: lock *(u32*)(a_reg + off) += x_reg */ + case BPF_STX | BPF_XADD | BPF_W: + /* emit 'lock add dword ptr [rax + off], eax' */ + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT3(0xF0, add_2mod(0x40, a_reg, x_reg), 0x01); + else + EMIT2(0xF0, 0x01); + goto xadd; + case BPF_STX | BPF_XADD | BPF_DW: + EMIT3(0xF0, add_2mod(0x48, a_reg, x_reg), 0x01); +xadd: if (is_imm8(insn->off)) + EMIT2(add_2reg(0x40, a_reg, x_reg), insn->off); + else + EMIT1_off32(add_2reg(0x80, a_reg, x_reg), + insn->off); + break; + + /* call */ + case BPF_JMP | BPF_CALL: + func = (u8 *) __bpf_call_base + K; + jmp_offset = func - (image + addrs[i]); + if (ctx->seen_ld_abs) { + EMIT2(0x41, 0x52); /* push %r10 */ + EMIT2(0x41, 0x51); /* push %r9 */ + /* need to adjust jmp offset, since + * pop %r9, pop %r10 take 4 bytes after call insn + */ + jmp_offset += 4; + } + if (!K || !is_simm32(jmp_offset)) { + pr_err("unsupported bpf func %d addr %p image %p\n", + K, func, image); + return -EINVAL; + } + EMIT1_off32(0xE8, jmp_offset); + if (ctx->seen_ld_abs) { + EMIT2(0x41, 0x59); /* pop %r9 */ + EMIT2(0x41, 0x5A); /* pop %r10 */ + } + break; + + /* cond jump */ + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JNE | BPF_X: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JSGT | BPF_X: + case BPF_JMP | BPF_JSGE | BPF_X: + /* cmp a_reg, x_reg */ + EMIT3(add_2mod(0x48, a_reg, x_reg), 0x39, + add_2reg(0xC0, a_reg, x_reg)); + goto emit_cond_jmp; + + case BPF_JMP | BPF_JSET | BPF_X: + /* test a_reg, x_reg */ + EMIT3(add_2mod(0x48, a_reg, x_reg), 0x85, + add_2reg(0xC0, a_reg, x_reg)); + goto emit_cond_jmp; + + case BPF_JMP | BPF_JSET | BPF_K: + /* test a_reg, imm32 */ + EMIT1(add_1mod(0x48, a_reg)); + EMIT2_off32(0xF7, add_1reg(0xC0, a_reg), K); + goto emit_cond_jmp; + + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JNE | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JSGT | BPF_K: + case BPF_JMP | BPF_JSGE | BPF_K: + /* cmp a_reg, imm8/32 */ + EMIT1(add_1mod(0x48, a_reg)); + + if (is_imm8(K)) + EMIT3(0x83, add_1reg(0xF8, a_reg), K); + else + EMIT2_off32(0x81, add_1reg(0xF8, a_reg), K); + +emit_cond_jmp: /* convert BPF opcode to x86 */ + switch (BPF_OP(insn->code)) { + case BPF_JEQ: + jmp_cond = X86_JE; + break; + case BPF_JSET: + case BPF_JNE: + jmp_cond = X86_JNE; + break; + case BPF_JGT: + /* GT is unsigned '>', JA in x86 */ + jmp_cond = X86_JA; + break; + case BPF_JGE: + /* GE is unsigned '>=', JAE in x86 */ + jmp_cond = X86_JAE; + break; + case BPF_JSGT: + /* signed '>', GT in x86 */ + jmp_cond = X86_JG; + break; + case BPF_JSGE: + /* signed '>=', GE in x86 */ + jmp_cond = X86_JGE; + break; + default: /* to silence gcc warning */ + return -EFAULT; + } + jmp_offset = addrs[i + insn->off] - addrs[i]; + if (is_imm8(jmp_offset)) { + EMIT2(jmp_cond, jmp_offset); + } else if (is_simm32(jmp_offset)) { + EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); + } else { + pr_err("cond_jmp gen bug %llx\n", jmp_offset); + return -EFAULT; + } + + break; + + case BPF_JMP | BPF_JA: + jmp_offset = addrs[i + insn->off] - addrs[i]; + if (!jmp_offset) + /* optimize out nop jumps */ + break; +emit_jmp: + if (is_imm8(jmp_offset)) { + EMIT2(0xEB, jmp_offset); + } else if (is_simm32(jmp_offset)) { + EMIT1_off32(0xE9, jmp_offset); + } else { + pr_err("jmp gen bug %llx\n", jmp_offset); + return -EFAULT; + } + break; + + case BPF_LD | BPF_IND | BPF_W: + func = sk_load_word; + goto common_load; + case BPF_LD | BPF_ABS | BPF_W: + func = CHOOSE_LOAD_FUNC(K, sk_load_word); +common_load: ctx->seen_ld_abs = true; + jmp_offset = func - (image + addrs[i]); + if (!func || !is_simm32(jmp_offset)) { + pr_err("unsupported bpf func %d addr %p image %p\n", + K, func, image); + return -EINVAL; + } + if (BPF_MODE(insn->code) == BPF_ABS) { + /* mov %esi, imm32 */ + EMIT1_off32(0xBE, K); + } else { + /* mov %rsi, x_reg */ + EMIT_mov(BPF_REG_2, x_reg); + if (K) { + if (is_imm8(K)) + /* add %esi, imm8 */ + EMIT3(0x83, 0xC6, K); else - EMIT1_off32(0x3d, K); /* cmp imm32,%eax */ - break; - case BPF_S_JMP_JSET_K: - if (K <= 0xFF) - EMIT2(0xa8, K); /* test imm8,%al */ - else if (!(K & 0xFFFF00FF)) - EMIT3(0xf6, 0xc4, K >> 8); /* test imm8,%ah */ - else if (K <= 0xFFFF) { - EMIT2(0x66, 0xa9); /* test imm16,%ax */ - EMIT(K, 2); - } else { - EMIT1_off32(0xa9, K); /* test imm32,%eax */ - } - break; + /* add %esi, imm32 */ + EMIT2_off32(0x81, 0xC6, K); } - if (filter[i].jt != 0) { - if (filter[i].jf && f_offset) - t_offset += is_near(f_offset) ? 2 : 5; - EMIT_COND_JMP(t_op, t_offset); - if (filter[i].jf) - EMIT_JMP(f_offset); - break; - } - EMIT_COND_JMP(f_op, f_offset); - break; - default: - /* hmm, too complex filter, give up with jit compiler */ - goto out; } - ilen = prog - temp; - if (image) { - if (unlikely(proglen + ilen > oldproglen)) { - pr_err("bpb_jit_compile fatal error\n"); - kfree(addrs); - module_free(NULL, header); - return; - } - memcpy(image + proglen, temp, ilen); + /* skb pointer is in R6 (%rbx), it will be copied into + * %rdi if skb_copy_bits() call is necessary. + * sk_load_* helpers also use %r10 and %r9d. + * See bpf_jit.S + */ + EMIT1_off32(0xE8, jmp_offset); /* call */ + break; + + case BPF_LD | BPF_IND | BPF_H: + func = sk_load_half; + goto common_load; + case BPF_LD | BPF_ABS | BPF_H: + func = CHOOSE_LOAD_FUNC(K, sk_load_half); + goto common_load; + case BPF_LD | BPF_IND | BPF_B: + func = sk_load_byte; + goto common_load; + case BPF_LD | BPF_ABS | BPF_B: + func = CHOOSE_LOAD_FUNC(K, sk_load_byte); + goto common_load; + + case BPF_JMP | BPF_EXIT: + if (i != insn_cnt - 1) { + jmp_offset = ctx->cleanup_addr - addrs[i]; + goto emit_jmp; } - proglen += ilen; - addrs[i] = proglen; - prog = temp; + /* update cleanup_addr */ + ctx->cleanup_addr = proglen; + /* mov rbx, qword ptr [rbp-X] */ + EMIT3_off32(0x48, 0x8B, 0x9D, -stacksize); + /* mov r13, qword ptr [rbp-X] */ + EMIT3_off32(0x4C, 0x8B, 0xAD, -stacksize + 8); + /* mov r14, qword ptr [rbp-X] */ + EMIT3_off32(0x4C, 0x8B, 0xB5, -stacksize + 16); + /* mov r15, qword ptr [rbp-X] */ + EMIT3_off32(0x4C, 0x8B, 0xBD, -stacksize + 24); + + EMIT1(0xC9); /* leave */ + EMIT1(0xC3); /* ret */ + break; + + default: + /* By design x64 JIT should support all BPF instructions + * This error will be seen if new instruction was added + * to interpreter, but not to JIT + * or if there is junk in sk_filter + */ + pr_err("bpf_jit: unknown opcode %02x\n", insn->code); + return -EINVAL; } - /* last bpf instruction is always a RET : - * use it to give the cleanup instruction(s) addr - */ - cleanup_addr = proglen - 1; /* ret */ - if (seen_or_pass0) - cleanup_addr -= 1; /* leaveq */ - if (seen_or_pass0 & SEEN_XREG) - cleanup_addr -= 4; /* mov -8(%rbp),%rbx */ + ilen = prog - temp; + if (image) { + if (unlikely(proglen + ilen > oldproglen)) { + pr_err("bpf_jit_compile fatal error\n"); + return -EFAULT; + } + memcpy(image + proglen, temp, ilen); + } + proglen += ilen; + addrs[i] = proglen; + prog = temp; + } + return proglen; +} + +void bpf_jit_compile(struct sk_filter *prog) +{ +} + +void bpf_int_jit_compile(struct sk_filter *prog) +{ + struct bpf_binary_header *header = NULL; + int proglen, oldproglen = 0; + struct jit_context ctx = {}; + u8 *image = NULL; + int *addrs; + int pass; + int i; + + if (!bpf_jit_enable) + return; + + if (!prog || !prog->len) + return; + + addrs = kmalloc(prog->len * sizeof(*addrs), GFP_KERNEL); + if (!addrs) + return; + + /* Before first pass, make a rough estimation of addrs[] + * each bpf instruction is translated to less than 64 bytes + */ + for (proglen = 0, i = 0; i < prog->len; i++) { + proglen += 64; + addrs[i] = proglen; + } + ctx.cleanup_addr = proglen; + + for (pass = 0; pass < 10; pass++) { + proglen = do_jit(prog, addrs, image, oldproglen, &ctx); + if (proglen <= 0) { + image = NULL; + if (header) + module_free(NULL, header); + goto out; + } if (image) { if (proglen != oldproglen) - pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", proglen, oldproglen); + pr_err("bpf_jit: proglen=%d != oldproglen=%d\n", + proglen, oldproglen); break; } if (proglen == oldproglen) { @@ -766,17 +918,16 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; } if (bpf_jit_enable > 1) - bpf_jit_dump(flen, proglen, pass, image); + bpf_jit_dump(prog->len, proglen, 0, image); if (image) { bpf_flush_icache(header, image + proglen); set_memory_ro((unsigned long)header, header->pages); - fp->bpf_func = (void *)image; - fp->jited = 1; + prog->bpf_func = (void *)image; + prog->jited = 1; } out: kfree(addrs); - return; } static void bpf_jit_free_deferred(struct work_struct *work) diff --git a/arch/x86/platform/efi/early_printk.c b/arch/x86/platform/efi/early_printk.c index 81b506d..5241421 100644 --- a/arch/x86/platform/efi/early_printk.c +++ b/arch/x86/platform/efi/early_printk.c @@ -14,48 +14,92 @@ static const struct font_desc *font; static u32 efi_x, efi_y; +static void *efi_fb; +static bool early_efi_keep; -static __init void early_efi_clear_scanline(unsigned int y) +/* + * efi earlyprintk need use early_ioremap to map the framebuffer. + * But early_ioremap is not usable for earlyprintk=efi,keep, ioremap should + * be used instead. ioremap will be available after paging_init() which is + * earlier than initcall callbacks. Thus adding this early initcall function + * early_efi_map_fb to map the whole efi framebuffer. + */ +static __init int early_efi_map_fb(void) { - unsigned long base, *dst; - u16 len; + unsigned long base, size; + + if (!early_efi_keep) + return 0; base = boot_params.screen_info.lfb_base; - len = boot_params.screen_info.lfb_linelength; + size = boot_params.screen_info.lfb_size; + efi_fb = ioremap(base, size); + + return efi_fb ? 0 : -ENOMEM; +} +early_initcall(early_efi_map_fb); + +/* + * early_efi_map maps efi framebuffer region [start, start + len -1] + * In case earlyprintk=efi,keep we have the whole framebuffer mapped already + * so just return the offset efi_fb + start. + */ +static __init_refok void *early_efi_map(unsigned long start, unsigned long len) +{ + unsigned long base; + + base = boot_params.screen_info.lfb_base; + + if (efi_fb) + return (efi_fb + start); + else + return early_ioremap(base + start, len); +} - dst = early_ioremap(base + y*len, len); +static __init_refok void early_efi_unmap(void *addr, unsigned long len) +{ + if (!efi_fb) + early_iounmap(addr, len); +} + +static void early_efi_clear_scanline(unsigned int y) +{ + unsigned long *dst; + u16 len; + + len = boot_params.screen_info.lfb_linelength; + dst = early_efi_map(y*len, len); if (!dst) return; memset(dst, 0, len); - early_iounmap(dst, len); + early_efi_unmap(dst, len); } -static __init void early_efi_scroll_up(void) +static void early_efi_scroll_up(void) { - unsigned long base, *dst, *src; + unsigned long *dst, *src; u16 len; u32 i, height; - base = boot_params.screen_info.lfb_base; len = boot_params.screen_info.lfb_linelength; height = boot_params.screen_info.lfb_height; for (i = 0; i < height - font->height; i++) { - dst = early_ioremap(base + i*len, len); + dst = early_efi_map(i*len, len); if (!dst) return; - src = early_ioremap(base + (i + font->height) * len, len); + src = early_efi_map((i + font->height) * len, len); if (!src) { - early_iounmap(dst, len); + early_efi_unmap(dst, len); return; } memmove(dst, src, len); - early_iounmap(src, len); - early_iounmap(dst, len); + early_efi_unmap(src, len); + early_efi_unmap(dst, len); } } @@ -79,16 +123,14 @@ static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) } } -static __init void +static void early_efi_write(struct console *con, const char *str, unsigned int num) { struct screen_info *si; - unsigned long base; unsigned int len; const char *s; void *dst; - base = boot_params.screen_info.lfb_base; si = &boot_params.screen_info; len = si->lfb_linelength; @@ -109,7 +151,7 @@ early_efi_write(struct console *con, const char *str, unsigned int num) for (h = 0; h < font->height; h++) { unsigned int n, x; - dst = early_ioremap(base + (efi_y + h) * len, len); + dst = early_efi_map((efi_y + h) * len, len); if (!dst) return; @@ -123,7 +165,7 @@ early_efi_write(struct console *con, const char *str, unsigned int num) s++; } - early_iounmap(dst, len); + early_efi_unmap(dst, len); } num -= count; @@ -179,6 +221,9 @@ static __init int early_efi_setup(struct console *con, char *options) for (i = 0; i < (yres - efi_y) / font->height; i++) early_efi_scroll_up(); + /* early_console_register will unset CON_BOOT in case ,keep */ + if (!(con->flags & CON_BOOT)) + early_efi_keep = true; return 0; } diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c index ff0174d..a9acde7 100644 --- a/arch/x86/platform/olpc/olpc-xo1-pm.c +++ b/arch/x86/platform/olpc/olpc-xo1-pm.c @@ -75,7 +75,7 @@ static int xo1_power_state_enter(suspend_state_t pm_state) return 0; } -asmlinkage int xo1_do_sleep(u8 sleep_state) +asmlinkage __visible int xo1_do_sleep(u8 sleep_state) { void *pgd_addr = __va(read_cr3()); diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index 304fca2..35e2bb6 100644 --- a/arch/x86/power/hibernate_64.c +++ b/arch/x86/power/hibernate_64.c @@ -23,7 +23,7 @@ extern __visible const void __nosave_begin, __nosave_end; /* Defined in hibernate_asm_64.S */ -extern asmlinkage int restore_image(void); +extern asmlinkage __visible int restore_image(void); /* * Address to jump to in the last phase of restore in order to get to the image diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index 0034898..e1f220e 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c @@ -39,6 +39,7 @@ #ifdef CONFIG_X86_64 #define vdso_enabled sysctl_vsyscall32 #define arch_setup_additional_pages syscall32_setup_pages +extern int sysctl_ldt16; #endif /* @@ -249,6 +250,13 @@ static struct ctl_table abi_table2[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "ldt16", + .data = &sysctl_ldt16, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, {} }; diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 201d09a..c34bfc4 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -1515,7 +1515,7 @@ static void __init xen_pvh_early_guest_init(void) } /* First C function to be called on Xen boot */ -asmlinkage void __init xen_start_kernel(void) +asmlinkage __visible void __init xen_start_kernel(void) { struct physdev_set_iopl set_iopl; int rc; diff --git a/arch/x86/xen/irq.c b/arch/x86/xen/irq.c index 08f763d..a1207cb 100644 --- a/arch/x86/xen/irq.c +++ b/arch/x86/xen/irq.c @@ -23,7 +23,7 @@ void xen_force_evtchn_callback(void) (void)HYPERVISOR_xen_version(0, NULL); } -asmlinkage unsigned long xen_save_fl(void) +asmlinkage __visible unsigned long xen_save_fl(void) { struct vcpu_info *vcpu; unsigned long flags; @@ -63,7 +63,7 @@ __visible void xen_restore_fl(unsigned long flags) } PV_CALLEE_SAVE_REGS_THUNK(xen_restore_fl); -asmlinkage void xen_irq_disable(void) +asmlinkage __visible void xen_irq_disable(void) { /* There's a one instruction preempt window here. We need to make sure we're don't switch CPUs between getting the vcpu @@ -74,7 +74,7 @@ asmlinkage void xen_irq_disable(void) } PV_CALLEE_SAVE_REGS_THUNK(xen_irq_disable); -asmlinkage void xen_irq_enable(void) +asmlinkage __visible void xen_irq_enable(void) { struct vcpu_info *vcpu; diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index e4a4145..1039fb9 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -451,7 +451,20 @@ static int blkcg_reset_stats(struct cgroup_subsys_state *css, struct blkcg_gq *blkg; int i; - mutex_lock(&blkcg_pol_mutex); + /* + * XXX: We invoke cgroup_add/rm_cftypes() under blkcg_pol_mutex + * which ends up putting cgroup's internal cgroup_tree_mutex under + * it; however, cgroup_tree_mutex is nested above cgroup file + * active protection and grabbing blkcg_pol_mutex from a cgroup + * file operation creates a possible circular dependency. cgroup + * internal locking is planned to go through further simplification + * and this issue should go away soon. For now, let's trylock + * blkcg_pol_mutex and restart the write on failure. + * + * http://lkml.kernel.org/g/5363C04B.4010400@oracle.com + */ + if (!mutex_trylock(&blkcg_pol_mutex)) + return restart_syscall(); spin_lock_irq(&blkcg->lock); /* diff --git a/drivers/Makefile b/drivers/Makefile index d05d81b..7183b6a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -119,7 +119,7 @@ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ -obj-$(CONFIG_ARCH_SHMOBILE_LEGACY) += sh/ +obj-$(CONFIG_ARCH_SHMOBILE) += sh/ ifndef CONFIG_ARCH_USES_GETTIMEOFFSET obj-y += clocksource/ endif diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ab686b3..a34a228 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -47,6 +47,23 @@ config ACPI_SLEEP depends on SUSPEND || HIBERNATION default y +config ACPI_PROCFS_POWER + bool "Deprecated power /proc/acpi directories" + depends on PROC_FS + help + For backwards compatibility, this option allows + deprecated power /proc/acpi/ directories to exist, even when + they have been replaced by functions in /sys. + The deprecated directories (and their replacements) include: + /proc/acpi/battery/* (/sys/class/power_supply/*) + /proc/acpi/ac_adapter/* (sys/class/power_supply/*) + This option has no effect on /proc/acpi/ directories + and functions, which do not yet exist in /sys + This option, together with the proc directories, will be + deleted in the future. + + Say N to delete power /proc/acpi/ directories that have moved to /sys/ + config ACPI_EC_DEBUGFS tristate "EC read/write access through /sys/kernel/debug/ec" default n diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 0331f91..bce34af 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -47,6 +47,7 @@ acpi-y += sysfs.o acpi-$(CONFIG_X86) += acpi_cmos_rtc.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o +acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o ifdef CONFIG_ACPI_VIDEO acpi-y += video_detect.o endif diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 2c01c1d..c67f6f5 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -52,11 +52,39 @@ MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI AC Adapter Driver"); MODULE_LICENSE("GPL"); +static int acpi_ac_add(struct acpi_device *device); +static int acpi_ac_remove(struct acpi_device *device); +static void acpi_ac_notify(struct acpi_device *device, u32 event); + +static const struct acpi_device_id ac_device_ids[] = { + {"ACPI0003", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, ac_device_ids); + +#ifdef CONFIG_PM_SLEEP +static int acpi_ac_resume(struct device *dev); +#endif +static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); + static int ac_sleep_before_get_state_ms; +static struct acpi_driver acpi_ac_driver = { + .name = "ac", + .class = ACPI_AC_CLASS, + .ids = ac_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = acpi_ac_add, + .remove = acpi_ac_remove, + .notify = acpi_ac_notify, + }, + .drv.pm = &acpi_ac_pm, +}; + struct acpi_ac { struct power_supply charger; - struct platform_device *pdev; + struct acpi_device * device; unsigned long long state; struct notifier_block battery_nb; }; @@ -69,10 +97,12 @@ struct acpi_ac { static int acpi_ac_get_state(struct acpi_ac *ac) { - acpi_status status; - acpi_handle handle = ACPI_HANDLE(&ac->pdev->dev); + acpi_status status = AE_OK; + + if (!ac) + return -EINVAL; - status = acpi_evaluate_integer(handle, "_PSR", NULL, + status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, &ac->state); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, @@ -117,10 +147,9 @@ static enum power_supply_property ac_props[] = { Driver Model -------------------------------------------------------------------------- */ -static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data) +static void acpi_ac_notify(struct acpi_device *device, u32 event) { - struct acpi_ac *ac = data; - struct acpi_device *adev; + struct acpi_ac *ac = acpi_driver_data(device); if (!ac) return; @@ -143,11 +172,10 @@ static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data) msleep(ac_sleep_before_get_state_ms); acpi_ac_get_state(ac); - adev = ACPI_COMPANION(&ac->pdev->dev); - acpi_bus_generate_netlink_event(adev->pnp.device_class, - dev_name(&ac->pdev->dev), - event, (u32) ac->state); - acpi_notifier_call_chain(adev, event, (u32) ac->state); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, + (u32) ac->state); + acpi_notifier_call_chain(device, event, (u32) ac->state); kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); } @@ -192,49 +220,39 @@ static struct dmi_system_id ac_dmi_table[] = { {}, }; -static int acpi_ac_probe(struct platform_device *pdev) +static int acpi_ac_add(struct acpi_device *device) { int result = 0; struct acpi_ac *ac = NULL; - struct acpi_device *adev; - if (!pdev) - return -EINVAL; - adev = ACPI_COMPANION(&pdev->dev); - if (!adev) - return -ENODEV; + if (!device) + return -EINVAL; ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); if (!ac) return -ENOMEM; - strcpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME); - strcpy(acpi_device_class(adev), ACPI_AC_CLASS); - ac->pdev = pdev; - platform_set_drvdata(pdev, ac); + ac->device = device; + strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_AC_CLASS); + device->driver_data = ac; result = acpi_ac_get_state(ac); if (result) goto end; - ac->charger.name = acpi_device_bid(adev); + ac->charger.name = acpi_device_bid(device); ac->charger.type = POWER_SUPPLY_TYPE_MAINS; ac->charger.properties = ac_props; ac->charger.num_properties = ARRAY_SIZE(ac_props); ac->charger.get_property = get_ac_property; - result = power_supply_register(&pdev->dev, &ac->charger); + result = power_supply_register(&ac->device->dev, &ac->charger); if (result) goto end; - result = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev), - ACPI_ALL_NOTIFY, acpi_ac_notify_handler, ac); - if (result) { - power_supply_unregister(&ac->charger); - goto end; - } printk(KERN_INFO PREFIX "%s [%s] (%s)\n", - acpi_device_name(adev), acpi_device_bid(adev), + acpi_device_name(device), acpi_device_bid(device), ac->state ? "on-line" : "off-line"); ac->battery_nb.notifier_call = acpi_ac_battery_notify; @@ -256,7 +274,7 @@ static int acpi_ac_resume(struct device *dev) if (!dev) return -EINVAL; - ac = platform_get_drvdata(to_platform_device(dev)); + ac = acpi_driver_data(to_acpi_device(dev)); if (!ac) return -EINVAL; @@ -270,19 +288,17 @@ static int acpi_ac_resume(struct device *dev) #else #define acpi_ac_resume NULL #endif -static SIMPLE_DEV_PM_OPS(acpi_ac_pm_ops, NULL, acpi_ac_resume); -static int acpi_ac_remove(struct platform_device *pdev) +static int acpi_ac_remove(struct acpi_device *device) { - struct acpi_ac *ac; + struct acpi_ac *ac = NULL; + - if (!pdev) + if (!device || !acpi_driver_data(device)) return -EINVAL; - acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), - ACPI_ALL_NOTIFY, acpi_ac_notify_handler); + ac = acpi_driver_data(device); - ac = platform_get_drvdata(pdev); if (ac->charger.dev) power_supply_unregister(&ac->charger); unregister_acpi_notifier(&ac->battery_nb); @@ -292,23 +308,6 @@ static int acpi_ac_remove(struct platform_device *pdev) return 0; } -static const struct acpi_device_id acpi_ac_match[] = { - { "ACPI0003", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, acpi_ac_match); - -static struct platform_driver acpi_ac_driver = { - .probe = acpi_ac_probe, - .remove = acpi_ac_remove, - .driver = { - .name = "acpi-ac", - .owner = THIS_MODULE, - .pm = &acpi_ac_pm_ops, - .acpi_match_table = ACPI_PTR(acpi_ac_match), - }, -}; - static int __init acpi_ac_init(void) { int result; @@ -316,7 +315,7 @@ static int __init acpi_ac_init(void) if (acpi_disabled) return -ENODEV; - result = platform_driver_register(&acpi_ac_driver); + result = acpi_bus_register_driver(&acpi_ac_driver); if (result < 0) return -ENODEV; @@ -325,7 +324,7 @@ static int __init acpi_ac_init(void) static void __exit acpi_ac_exit(void) { - platform_driver_unregister(&acpi_ac_driver); + acpi_bus_unregister_driver(&acpi_ac_driver); } module_init(acpi_ac_init); module_exit(acpi_ac_exit); diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index dbfe49e..1d49503 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -29,7 +29,6 @@ ACPI_MODULE_NAME("platform"); static const struct acpi_device_id acpi_platform_device_ids[] = { { "PNP0D40" }, - { "ACPI0003" }, { "VPC2004" }, { "BCM4752" }, diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index b06f5f5..52c81c4 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -405,7 +405,6 @@ static int acpi_processor_add(struct acpi_device *device, goto err; pr->dev = dev; - dev->offline = pr->flags.need_hotplug_init; /* Trigger the processor driver's .probe() if present. */ if (device_attach(dev) >= 0) diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 49bbc71..a08a448 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -141,9 +141,9 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE); * address. Although ACPICA adheres to the ACPI specification which * requires the use of the corresponding 64-bit address if it is non-zero, * some machines have been found to have a corrupted non-zero 64-bit - * address. Default is FALSE, do not favor the 32-bit addresses. + * address. Default is TRUE, favor the 32-bit addresses. */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, TRUE); /* * Optionally truncate I/O addresses to 16 bits. Provides compatibility diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index a4702ee..9fb85f3 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -461,6 +461,7 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) u32 table_count; struct acpi_table_header *table; acpi_physical_address address; + acpi_physical_address rsdt_address; u32 length; u8 *table_entry; acpi_status status; @@ -488,11 +489,14 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) * as per the ACPI specification. */ address = (acpi_physical_address) rsdp->xsdt_physical_address; + rsdt_address = + (acpi_physical_address) rsdp->rsdt_physical_address; table_entry_size = ACPI_XSDT_ENTRY_SIZE; } else { /* Root table is an RSDT (32-bit physical addresses) */ address = (acpi_physical_address) rsdp->rsdt_physical_address; + rsdt_address = address; table_entry_size = ACPI_RSDT_ENTRY_SIZE; } @@ -515,8 +519,7 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) /* Fall back to the RSDT */ - address = - (acpi_physical_address) rsdp->rsdt_physical_address; + address = rsdt_address; table_entry_size = ACPI_RSDT_ENTRY_SIZE; } } diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 9a2c63b..6e7b2a1 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -36,6 +36,12 @@ #include <linux/suspend.h> #include <asm/unaligned.h> +#ifdef CONFIG_ACPI_PROCFS_POWER +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> +#endif + #include <linux/acpi.h> #include <linux/power_supply.h> @@ -64,6 +70,19 @@ static unsigned int cache_time = 1000; module_param(cache_time, uint, 0644); MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); +#ifdef CONFIG_ACPI_PROCFS_POWER +extern struct proc_dir_entry *acpi_lock_battery_dir(void); +extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); + +enum acpi_battery_files { + info_tag = 0, + state_tag, + alarm_tag, + ACPI_BATTERY_NUMFILES, +}; + +#endif + static const struct acpi_device_id battery_device_ids[] = { {"PNP0C0A", 0}, {"", 0}, @@ -299,6 +318,14 @@ static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_SERIAL_NUMBER, }; +#ifdef CONFIG_ACPI_PROCFS_POWER +inline char *acpi_battery_units(struct acpi_battery *battery) +{ + return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ? + "mA" : "mW"; +} +#endif + /* -------------------------------------------------------------------------- Battery Management -------------------------------------------------------------------------- */ @@ -717,6 +744,279 @@ static void acpi_battery_refresh(struct acpi_battery *battery) } /* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_PROCFS_POWER +static struct proc_dir_entry *acpi_battery_dir; + +static int acpi_battery_print_info(struct seq_file *seq, int result) +{ + struct acpi_battery *battery = seq->private; + + if (result) + goto end; + + seq_printf(seq, "present: %s\n", + acpi_battery_present(battery) ? "yes" : "no"); + if (!acpi_battery_present(battery)) + goto end; + if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "design capacity: unknown\n"); + else + seq_printf(seq, "design capacity: %d %sh\n", + battery->design_capacity, + acpi_battery_units(battery)); + + if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "last full capacity: unknown\n"); + else + seq_printf(seq, "last full capacity: %d %sh\n", + battery->full_charge_capacity, + acpi_battery_units(battery)); + + seq_printf(seq, "battery technology: %srechargeable\n", + (!battery->technology)?"non-":""); + + if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "design voltage: unknown\n"); + else + seq_printf(seq, "design voltage: %d mV\n", + battery->design_voltage); + seq_printf(seq, "design capacity warning: %d %sh\n", + battery->design_capacity_warning, + acpi_battery_units(battery)); + seq_printf(seq, "design capacity low: %d %sh\n", + battery->design_capacity_low, + acpi_battery_units(battery)); + seq_printf(seq, "cycle count: %i\n", battery->cycle_count); + seq_printf(seq, "capacity granularity 1: %d %sh\n", + battery->capacity_granularity_1, + acpi_battery_units(battery)); + seq_printf(seq, "capacity granularity 2: %d %sh\n", + battery->capacity_granularity_2, + acpi_battery_units(battery)); + seq_printf(seq, "model number: %s\n", battery->model_number); + seq_printf(seq, "serial number: %s\n", battery->serial_number); + seq_printf(seq, "battery type: %s\n", battery->type); + seq_printf(seq, "OEM info: %s\n", battery->oem_info); + end: + if (result) + seq_printf(seq, "ERROR: Unable to read battery info\n"); + return result; +} + +static int acpi_battery_print_state(struct seq_file *seq, int result) +{ + struct acpi_battery *battery = seq->private; + + if (result) + goto end; + + seq_printf(seq, "present: %s\n", + acpi_battery_present(battery) ? "yes" : "no"); + if (!acpi_battery_present(battery)) + goto end; + + seq_printf(seq, "capacity state: %s\n", + (battery->state & 0x04) ? "critical" : "ok"); + if ((battery->state & 0x01) && (battery->state & 0x02)) + seq_printf(seq, + "charging state: charging/discharging\n"); + else if (battery->state & 0x01) + seq_printf(seq, "charging state: discharging\n"); + else if (battery->state & 0x02) + seq_printf(seq, "charging state: charging\n"); + else + seq_printf(seq, "charging state: charged\n"); + + if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "present rate: unknown\n"); + else + seq_printf(seq, "present rate: %d %s\n", + battery->rate_now, acpi_battery_units(battery)); + + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "remaining capacity: unknown\n"); + else + seq_printf(seq, "remaining capacity: %d %sh\n", + battery->capacity_now, acpi_battery_units(battery)); + if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "present voltage: unknown\n"); + else + seq_printf(seq, "present voltage: %d mV\n", + battery->voltage_now); + end: + if (result) + seq_printf(seq, "ERROR: Unable to read battery state\n"); + + return result; +} + +static int acpi_battery_print_alarm(struct seq_file *seq, int result) +{ + struct acpi_battery *battery = seq->private; + + if (result) + goto end; + + if (!acpi_battery_present(battery)) { + seq_printf(seq, "present: no\n"); + goto end; + } + seq_printf(seq, "alarm: "); + if (!battery->alarm) + seq_printf(seq, "unsupported\n"); + else + seq_printf(seq, "%u %sh\n", battery->alarm, + acpi_battery_units(battery)); + end: + if (result) + seq_printf(seq, "ERROR: Unable to read battery alarm\n"); + return result; +} + +static ssize_t acpi_battery_write_alarm(struct file *file, + const char __user * buffer, + size_t count, loff_t * ppos) +{ + int result = 0; + char alarm_string[12] = { '\0' }; + struct seq_file *m = file->private_data; + struct acpi_battery *battery = m->private; + + if (!battery || (count > sizeof(alarm_string) - 1)) + return -EINVAL; + if (!acpi_battery_present(battery)) { + result = -ENODEV; + goto end; + } + if (copy_from_user(alarm_string, buffer, count)) { + result = -EFAULT; + goto end; + } + alarm_string[count] = '\0'; + battery->alarm = simple_strtol(alarm_string, NULL, 0); + result = acpi_battery_set_alarm(battery); + end: + if (!result) + return count; + return result; +} + +typedef int(*print_func)(struct seq_file *seq, int result); + +static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = { + acpi_battery_print_info, + acpi_battery_print_state, + acpi_battery_print_alarm, +}; + +static int acpi_battery_read(int fid, struct seq_file *seq) +{ + struct acpi_battery *battery = seq->private; + int result = acpi_battery_update(battery); + return acpi_print_funcs[fid](seq, result); +} + +#define DECLARE_FILE_FUNCTIONS(_name) \ +static int acpi_battery_read_##_name(struct seq_file *seq, void *offset) \ +{ \ + return acpi_battery_read(_name##_tag, seq); \ +} \ +static int acpi_battery_##_name##_open_fs(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, acpi_battery_read_##_name, PDE_DATA(inode)); \ +} + +DECLARE_FILE_FUNCTIONS(info); +DECLARE_FILE_FUNCTIONS(state); +DECLARE_FILE_FUNCTIONS(alarm); + +#undef DECLARE_FILE_FUNCTIONS + +#define FILE_DESCRIPTION_RO(_name) \ + { \ + .name = __stringify(_name), \ + .mode = S_IRUGO, \ + .ops = { \ + .open = acpi_battery_##_name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define FILE_DESCRIPTION_RW(_name) \ + { \ + .name = __stringify(_name), \ + .mode = S_IFREG | S_IRUGO | S_IWUSR, \ + .ops = { \ + .open = acpi_battery_##_name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .write = acpi_battery_write_##_name, \ + .release = single_release, \ + .owner = THIS_MODULE, \ + }, \ + } + +static const struct battery_file { + struct file_operations ops; + umode_t mode; + const char *name; +} acpi_battery_file[] = { + FILE_DESCRIPTION_RO(info), + FILE_DESCRIPTION_RO(state), + FILE_DESCRIPTION_RW(alarm), +}; + +#undef FILE_DESCRIPTION_RO +#undef FILE_DESCRIPTION_RW + +static int acpi_battery_add_fs(struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + int i; + + printk(KERN_WARNING PREFIX "Deprecated procfs I/F for battery is loaded," + " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_battery_dir); + if (!acpi_device_dir(device)) + return -ENODEV; + } + + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { + entry = proc_create_data(acpi_battery_file[i].name, + acpi_battery_file[i].mode, + acpi_device_dir(device), + &acpi_battery_file[i].ops, + acpi_driver_data(device)); + if (!entry) + return -ENODEV; + } + return 0; +} + +static void acpi_battery_remove_fs(struct acpi_device *device) +{ + int i; + if (!acpi_device_dir(device)) + return; + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) + remove_proc_entry(acpi_battery_file[i].name, + acpi_device_dir(device)); + + remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); + acpi_device_dir(device) = NULL; +} + +#endif + +/* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -790,6 +1090,15 @@ static int acpi_battery_add(struct acpi_device *device) result = acpi_battery_update(battery); if (result) goto fail; +#ifdef CONFIG_ACPI_PROCFS_POWER + result = acpi_battery_add_fs(device); +#endif + if (result) { +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_battery_remove_fs(device); +#endif + goto fail; + } printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), @@ -816,6 +1125,9 @@ static int acpi_battery_remove(struct acpi_device *device) return -EINVAL; battery = acpi_driver_data(device); unregister_pm_notifier(&battery->pm_nb); +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_battery_remove_fs(device); +#endif sysfs_remove_battery(battery); mutex_destroy(&battery->lock); mutex_destroy(&battery->sysfs_lock); @@ -866,7 +1178,19 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) if (dmi_check_system(bat_dmi_table)) battery_bix_broken_package = 1; - acpi_bus_register_driver(&acpi_battery_driver); + +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_battery_dir = acpi_lock_battery_dir(); + if (!acpi_battery_dir) + return; +#endif + if (acpi_bus_register_driver(&acpi_battery_driver) < 0) { +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_unlock_battery_dir(acpi_battery_dir); +#endif + return; + } + return; } static int __init acpi_battery_init(void) @@ -878,6 +1202,9 @@ static int __init acpi_battery_init(void) static void __exit acpi_battery_exit(void) { acpi_bus_unregister_driver(&acpi_battery_driver); +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_unlock_battery_dir(acpi_battery_dir); +#endif } module_init(acpi_battery_init); diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index afec452..3d8413d 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -314,6 +314,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_VERSION, "2349D15"), }, }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 7737", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"), + }, + }, /* * BIOS invocation of _OSI(Linux) is almost always a BIOS bug. @@ -374,6 +382,19 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"), }, }, + /* + * Without this this EEEpc exports a non working WMI interface, with + * this it exports a working "good old" eeepc_laptop interface, fixing + * both brightness control, and rfkill not working. + */ + { + .callback = dmi_enable_osi_linux, + .ident = "Asus EEE PC 1015PX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"), + }, + }, {} }; diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c new file mode 100644 index 0000000..6c9ee68 --- /dev/null +++ b/drivers/acpi/cm_sbs.c @@ -0,0 +1,105 @@ +/* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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/module.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define PREFIX "ACPI: " + +ACPI_MODULE_NAME("cm_sbs"); +#define ACPI_AC_CLASS "ac_adapter" +#define ACPI_BATTERY_CLASS "battery" +#define _COMPONENT ACPI_SBS_COMPONENT +static struct proc_dir_entry *acpi_ac_dir; +static struct proc_dir_entry *acpi_battery_dir; + +static DEFINE_MUTEX(cm_sbs_mutex); + +static int lock_ac_dir_cnt; +static int lock_battery_dir_cnt; + +struct proc_dir_entry *acpi_lock_ac_dir(void) +{ + mutex_lock(&cm_sbs_mutex); + if (!acpi_ac_dir) + acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); + if (acpi_ac_dir) { + lock_ac_dir_cnt++; + } else { + printk(KERN_ERR PREFIX + "Cannot create %s\n", ACPI_AC_CLASS); + } + mutex_unlock(&cm_sbs_mutex); + return acpi_ac_dir; +} +EXPORT_SYMBOL(acpi_lock_ac_dir); + +void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir_param) +{ + mutex_lock(&cm_sbs_mutex); + if (acpi_ac_dir_param) + lock_ac_dir_cnt--; + if (lock_ac_dir_cnt == 0 && acpi_ac_dir_param && acpi_ac_dir) { + remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); + acpi_ac_dir = NULL; + } + mutex_unlock(&cm_sbs_mutex); +} +EXPORT_SYMBOL(acpi_unlock_ac_dir); + +struct proc_dir_entry *acpi_lock_battery_dir(void) +{ + mutex_lock(&cm_sbs_mutex); + if (!acpi_battery_dir) { + acpi_battery_dir = + proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); + } + if (acpi_battery_dir) { + lock_battery_dir_cnt++; + } else { + printk(KERN_ERR PREFIX + "Cannot create %s\n", ACPI_BATTERY_CLASS); + } + mutex_unlock(&cm_sbs_mutex); + return acpi_battery_dir; +} +EXPORT_SYMBOL(acpi_lock_battery_dir); + +void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param) +{ + mutex_lock(&cm_sbs_mutex); + if (acpi_battery_dir_param) + lock_battery_dir_cnt--; + if (lock_battery_dir_cnt == 0 && acpi_battery_dir_param + && acpi_battery_dir) { + remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); + acpi_battery_dir = NULL; + } + mutex_unlock(&cm_sbs_mutex); + return; +} +EXPORT_SYMBOL(acpi_unlock_battery_dir); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 8b6990e..f8bc5a7 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -457,10 +457,10 @@ static struct dmi_system_id video_dmi_table[] __initdata = { }, { .callback = video_set_use_native_backlight, - .ident = "ThinkPad T430s", + .ident = "ThinkPad T430 and T430s", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430s"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430"), }, }, { @@ -472,7 +472,7 @@ static struct dmi_system_id video_dmi_table[] __initdata = { }, }, { - .callback = video_set_use_native_backlight, + .callback = video_set_use_native_backlight, .ident = "ThinkPad X1 Carbon", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), @@ -500,7 +500,7 @@ static struct dmi_system_id video_dmi_table[] __initdata = { .ident = "Dell Inspiron 7520", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_VERSION, "Inspiron 7520"), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"), }, }, { @@ -513,6 +513,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = { }, { .callback = video_set_use_native_backlight, + .ident = "Acer Aspire 5742G", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"), + }, + }, + { + .callback = video_set_use_native_backlight, .ident = "Acer Aspire V5-431", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index c270604..0033faf 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -815,7 +815,7 @@ config PATA_AT32 config PATA_AT91 tristate "PATA support for AT91SAM9260" - depends on ARM && ARCH_AT91 + depends on ARM && SOC_AT91SAM9 help This option enables support for IDE devices on the Atmel AT91SAM9260 SoC. diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 71e15b7..6070781 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1115,6 +1115,17 @@ static bool ahci_broken_online(struct pci_dev *pdev) return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff); } +static bool ahci_broken_devslp(struct pci_dev *pdev) +{ + /* device with broken DEVSLP but still showing SDS capability */ + static const struct pci_device_id ids[] = { + { PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */ + {} + }; + + return pci_match_id(ids, pdev); +} + #ifdef CONFIG_ATA_ACPI static void ahci_gtf_filter_workaround(struct ata_host *host) { @@ -1364,6 +1375,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; + /* must set flag prior to save config in order to take effect */ + if (ahci_broken_devslp(pdev)) + hpriv->flags |= AHCI_HFLAG_NO_DEVSLP; + /* save initial config */ ahci_pci_save_initial_config(pdev, hpriv); diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index b5eb886..af63c75 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -236,6 +236,7 @@ enum { port start (wait until error-handling stage) */ AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */ + AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */ /* ap->flags bits */ diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 497c7ab..8befeb6 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -29,9 +29,25 @@ #include "ahci.h" enum { - PORT_PHY_CTL = 0x178, /* Port0 PHY Control */ - PORT_PHY_CTL_PDDQ_LOC = 0x100000, /* PORT_PHY_CTL bits */ - HOST_TIMER1MS = 0xe0, /* Timer 1-ms */ + /* Timer 1-ms Register */ + IMX_TIMER1MS = 0x00e0, + /* Port0 PHY Control Register */ + IMX_P0PHYCR = 0x0178, + IMX_P0PHYCR_TEST_PDDQ = 1 << 20, + IMX_P0PHYCR_CR_READ = 1 << 19, + IMX_P0PHYCR_CR_WRITE = 1 << 18, + IMX_P0PHYCR_CR_CAP_DATA = 1 << 17, + IMX_P0PHYCR_CR_CAP_ADDR = 1 << 16, + /* Port0 PHY Status Register */ + IMX_P0PHYSR = 0x017c, + IMX_P0PHYSR_CR_ACK = 1 << 18, + IMX_P0PHYSR_CR_DATA_OUT = 0xffff << 0, + /* Lane0 Output Status Register */ + IMX_LANE0_OUT_STAT = 0x2003, + IMX_LANE0_OUT_STAT_RX_PLL_STATE = 1 << 1, + /* Clock Reset Register */ + IMX_CLOCK_RESET = 0x7f3f, + IMX_CLOCK_RESET_RESET = 1 << 0, }; enum ahci_imx_type { @@ -54,9 +70,149 @@ MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support static void ahci_imx_host_stop(struct ata_host *host); +static int imx_phy_crbit_assert(void __iomem *mmio, u32 bit, bool assert) +{ + int timeout = 10; + u32 crval; + u32 srval; + + /* Assert or deassert the bit */ + crval = readl(mmio + IMX_P0PHYCR); + if (assert) + crval |= bit; + else + crval &= ~bit; + writel(crval, mmio + IMX_P0PHYCR); + + /* Wait for the cr_ack signal */ + do { + srval = readl(mmio + IMX_P0PHYSR); + if ((assert ? srval : ~srval) & IMX_P0PHYSR_CR_ACK) + break; + usleep_range(100, 200); + } while (--timeout); + + return timeout ? 0 : -ETIMEDOUT; +} + +static int imx_phy_reg_addressing(u16 addr, void __iomem *mmio) +{ + u32 crval = addr; + int ret; + + /* Supply the address on cr_data_in */ + writel(crval, mmio + IMX_P0PHYCR); + + /* Assert the cr_cap_addr signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, true); + if (ret) + return ret; + + /* Deassert cr_cap_addr */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, false); + if (ret) + return ret; + + return 0; +} + +static int imx_phy_reg_write(u16 val, void __iomem *mmio) +{ + u32 crval = val; + int ret; + + /* Supply the data on cr_data_in */ + writel(crval, mmio + IMX_P0PHYCR); + + /* Assert the cr_cap_data signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, true); + if (ret) + return ret; + + /* Deassert cr_cap_data */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, false); + if (ret) + return ret; + + if (val & IMX_CLOCK_RESET_RESET) { + /* + * In case we're resetting the phy, it's unable to acknowledge, + * so we return immediately here. + */ + crval |= IMX_P0PHYCR_CR_WRITE; + writel(crval, mmio + IMX_P0PHYCR); + goto out; + } + + /* Assert the cr_write signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, true); + if (ret) + return ret; + + /* Deassert cr_write */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, false); + if (ret) + return ret; + +out: + return 0; +} + +static int imx_phy_reg_read(u16 *val, void __iomem *mmio) +{ + int ret; + + /* Assert the cr_read signal */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, true); + if (ret) + return ret; + + /* Capture the data from cr_data_out[] */ + *val = readl(mmio + IMX_P0PHYSR) & IMX_P0PHYSR_CR_DATA_OUT; + + /* Deassert cr_read */ + ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, false); + if (ret) + return ret; + + return 0; +} + +static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) +{ + void __iomem *mmio = hpriv->mmio; + int timeout = 10; + u16 val; + int ret; + + /* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */ + ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio); + if (ret) + return ret; + ret = imx_phy_reg_write(IMX_CLOCK_RESET_RESET, mmio); + if (ret) + return ret; + + /* Wait for PHY RX_PLL to be stable */ + do { + usleep_range(100, 200); + ret = imx_phy_reg_addressing(IMX_LANE0_OUT_STAT, mmio); + if (ret) + return ret; + ret = imx_phy_reg_read(&val, mmio); + if (ret) + return ret; + if (val & IMX_LANE0_OUT_STAT_RX_PLL_STATE) + break; + } while (--timeout); + + return timeout ? 0 : -ETIMEDOUT; +} + static int imx_sata_enable(struct ahci_host_priv *hpriv) { struct imx_ahci_priv *imxpriv = hpriv->plat_data; + struct device *dev = &imxpriv->ahci_pdev->dev; int ret; if (imxpriv->no_device) @@ -101,6 +257,14 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, IMX6Q_GPR13_SATA_MPLL_CLK_EN, IMX6Q_GPR13_SATA_MPLL_CLK_EN); + + usleep_range(100, 200); + + ret = imx_sata_phy_reset(hpriv); + if (ret) { + dev_err(dev, "failed to reset phy: %d\n", ret); + goto disable_regulator; + } } usleep_range(1000, 2000); @@ -156,8 +320,8 @@ static void ahci_imx_error_handler(struct ata_port *ap) * without full reset once the pddq mode is enabled making it * impossible to use as part of libata LPM. */ - reg_val = readl(mmio + PORT_PHY_CTL); - writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL); + reg_val = readl(mmio + IMX_P0PHYCR); + writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR); imx_sata_disable(hpriv); imxpriv->no_device = true; } @@ -217,6 +381,7 @@ static int imx_ahci_probe(struct platform_device *pdev) if (!imxpriv) return -ENOMEM; + imxpriv->ahci_pdev = pdev; imxpriv->no_device = false; imxpriv->first_time = true; imxpriv->type = (enum ahci_imx_type)of_id->data; @@ -248,7 +413,7 @@ static int imx_ahci_probe(struct platform_device *pdev) /* * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, - * and IP vendor specific register HOST_TIMER1MS. + * and IP vendor specific register IMX_TIMER1MS. * Configure CAP_SSS (support stagered spin up). * Implement the port0. * Get the ahb clock rate, and configure the TIMER1MS register. @@ -265,7 +430,7 @@ static int imx_ahci_probe(struct platform_device *pdev) } reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; - writel(reg_val, hpriv->mmio + HOST_TIMER1MS); + writel(reg_val, hpriv->mmio + IMX_TIMER1MS); ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, 0, 0); if (ret) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 6bd4f66..b986145 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -452,6 +452,13 @@ void ahci_save_initial_config(struct device *dev, cap &= ~HOST_CAP_SNTF; } + if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) { + dev_info(dev, + "controller can't do DEVSLP, turning off\n"); + cap2 &= ~HOST_CAP2_SDS; + cap2 &= ~HOST_CAP2_SADM; + } + if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) { dev_info(dev, "controller can do FBS, turning on CAP_FBS\n"); cap |= HOST_CAP_FBS; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 943cc8b..ea83828 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6314,6 +6314,8 @@ int ata_host_activate(struct ata_host *host, int irq, static void ata_port_detach(struct ata_port *ap) { unsigned long flags; + struct ata_link *link; + struct ata_device *dev; if (!ap->ops->error_handler) goto skip_eh; @@ -6333,6 +6335,13 @@ static void ata_port_detach(struct ata_port *ap) cancel_delayed_work_sync(&ap->hotplug_task); skip_eh: + /* clean up zpodd on port removal */ + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(dev, link, ALL) { + if (zpodd_dev_enabled(dev)) + zpodd_exit(dev); + } + } if (ap->pmp_link) { int i; for (i = 0; i < SATA_PMP_MAX_PORTS; i++) diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index 204814e..d4725fc 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -2780,7 +2780,7 @@ static struct pci_driver fore200e_pca_driver = { static int __init fore200e_module_init(void) { - int err; + int err = 0; printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n"); diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 1bdf104..b621f56 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -2551,12 +2551,12 @@ done: timeout = 5 * 1000; while (atomic_read(&vc->scq->used) > 0) { timeout = msleep_interruptible(timeout); - if (!timeout) + if (!timeout) { + pr_warn("%s: SCQ drain timeout: %u used\n", + card->name, atomic_read(&vc->scq->used)); break; + } } - if (!timeout) - printk("%s: SCQ drain timeout: %u used\n", - card->name, atomic_read(&vc->scq->used)); writel(TCMDQ_HALT | vc->index, SAR_REG_TCMDQ); clear_scd(card, vc->scq, vc->class); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a7dfbf9..a1c80b0 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -49,6 +49,7 @@ static struct usb_driver btusb_driver; #define BTUSB_WRONG_SCO_MTU 0x40 #define BTUSB_ATH3012 0x80 #define BTUSB_INTEL 0x100 +#define BTUSB_BCM_PATCHRAM 0x200 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, /* Broadcom devices with vendor specific id */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* Belkin F8065bf - Broadcom based */ { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, @@ -1381,6 +1383,154 @@ exit_mfg_deactivate: return 0; } +static int btusb_setup_bcm_patchram(struct hci_dev *hdev) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + struct usb_device *udev = data->udev; + char fw_name[64]; + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + const struct hci_command_hdr *cmd; + const u8 *cmd_param; + u16 opcode; + struct sk_buff *skb; + struct hci_rp_read_local_version *ver; + long ret; + + snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd", + udev->product ? udev->product : "BCM", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + ret = request_firmware(&fw, fw_name, &hdev->dev); + if (ret < 0) { + BT_INFO("%s: BCM: patch %s not found", hdev->name, + fw_name); + return 0; + } + + /* Reset */ + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); + goto done; + } + kfree_skb(skb); + + /* Read Local Version Info */ + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", + hdev->name, ret); + goto done; + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", + hdev->name); + kfree_skb(skb); + ret = -EIO; + goto done; + } + + ver = (struct hci_rp_read_local_version *) skb->data; + BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x " + "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, + ver->lmp_ver, ver->lmp_subver); + kfree_skb(skb); + + /* Start Download */ + skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: BCM: Download Minidrv command failed (%ld)", + hdev->name, ret); + goto reset_fw; + } + kfree_skb(skb); + + /* 50 msec delay after Download Minidrv completes */ + msleep(50); + + fw_ptr = fw->data; + fw_size = fw->size; + + while (fw_size >= sizeof(*cmd)) { + cmd = (struct hci_command_hdr *) fw_ptr; + fw_ptr += sizeof(*cmd); + fw_size -= sizeof(*cmd); + + if (fw_size < cmd->plen) { + BT_ERR("%s: BCM: patch %s is corrupted", + hdev->name, fw_name); + ret = -EINVAL; + goto reset_fw; + } + + cmd_param = fw_ptr; + fw_ptr += cmd->plen; + fw_size -= cmd->plen; + + opcode = le16_to_cpu(cmd->opcode); + + skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: BCM: patch command %04x failed (%ld)", + hdev->name, opcode, ret); + goto reset_fw; + } + kfree_skb(skb); + } + + /* 250 msec delay after Launch Ram completes */ + msleep(250); + +reset_fw: + /* Reset */ + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); + goto done; + } + kfree_skb(skb); + + /* Read Local Version Info */ + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", + hdev->name, ret); + goto done; + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", + hdev->name); + kfree_skb(skb); + ret = -EIO; + goto done; + } + + ver = (struct hci_rp_read_local_version *) skb->data; + BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x " + "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, + ver->lmp_ver, ver->lmp_subver); + kfree_skb(skb); + +done: + release_firmware(fw); + + return ret; +} + static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1486,6 +1636,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_BCM92035) hdev->setup = btusb_setup_bcm92035; + if (id->driver_info & BTUSB_BCM_PATCHRAM) + hdev->setup = btusb_setup_bcm_patchram; + if (id->driver_info & BTUSB_INTEL) hdev->setup = btusb_setup_intel; diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 293e2e0..00b7344 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -56,6 +56,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/debugfs.h> +#include <linux/log2.h> /* * DDR target is the same on all platforms. @@ -222,12 +223,6 @@ static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus, */ if ((u64)base < wend && end > wbase) return 0; - - /* - * Check if target/attribute conflicts - */ - if (target == wtarget && attr == wattr) - return 0; } return 1; @@ -266,6 +261,17 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus, mbus->soc->win_cfg_offset(win); u32 ctrl, remap_addr; + if (!is_power_of_2(size)) { + WARN(true, "Invalid MBus window size: 0x%zx\n", size); + return -EINVAL; + } + + if ((base & (phys_addr_t)(size - 1)) != 0) { + WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base, + size); + return -EINVAL; + } + ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | (attr << WIN_CTRL_ATTR_SHIFT) | (target << WIN_CTRL_TGT_SHIFT) | @@ -413,6 +419,10 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v) win, (unsigned long long)wbase, (unsigned long long)(wbase + wsize), wtarget, wattr); + if (!is_power_of_2(wsize) || + ((wbase & (u64)(wsize - 1)) != 0)) + seq_puts(seq, " (Invalid base/size!!)"); + if (win < mbus->soc->num_remappable_wins) { seq_printf(seq, " (remap %016llx)\n", (unsigned long long)wremap); diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index 8121b4c..b297033 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -730,6 +730,7 @@ static int agpioc_info_wrap(struct agp_file_private *priv, void __user *arg) agp_copy_info(agp_bridge, &kerninfo); + memset(&userinfo, 0, sizeof(userinfo)); userinfo.version.major = kerninfo.version.major; userinfo.version.minor = kerninfo.version.minor; userinfo.bridge_id = kerninfo.device->vendor | diff --git a/drivers/char/random.c b/drivers/char/random.c index 6b75713..102c50d 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -995,8 +995,11 @@ retry: ibytes = min_t(size_t, ibytes, have_bytes - reserved); if (ibytes < min) ibytes = 0; - entropy_count = max_t(int, 0, - entropy_count - (ibytes << (ENTROPY_SHIFT + 3))); + if (have_bytes >= ibytes + reserved) + entropy_count -= ibytes << (ENTROPY_SHIFT + 3); + else + entropy_count = reserved << (ENTROPY_SHIFT + 3); + if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) goto retry; diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index b3ea223..61dcc80 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -328,13 +328,11 @@ int tpm_add_ppi(struct kobject *parent) /* Cache TPM ACPI handle and version string */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ppi_callback, NULL, NULL, &tpm_ppi_handle); - if (tpm_ppi_handle == NULL) - return -ENODEV; - - return sysfs_create_group(parent, &ppi_attr_grp); + return tpm_ppi_handle ? sysfs_create_group(parent, &ppi_attr_grp) : 0; } void tpm_remove_ppi(struct kobject *parent) { - sysfs_remove_group(parent, &ppi_attr_grp); + if (tpm_ppi_handle) + sysfs_remove_group(parent, &ppi_attr_grp); } diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index c7607fe..54a0652 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -27,7 +27,7 @@ LIST_HEAD(ccu_list); /* The list of set up CCUs */ static bool clk_requires_trigger(struct kona_clk *bcm_clk) { - struct peri_clk_data *peri = bcm_clk->peri; + struct peri_clk_data *peri = bcm_clk->u.peri; struct bcm_clk_sel *sel; struct bcm_clk_div *div; @@ -63,7 +63,7 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) u32 limit; BUG_ON(bcm_clk->type != bcm_clk_peri); - peri = bcm_clk->peri; + peri = bcm_clk->u.peri; name = bcm_clk->name; range = bcm_clk->ccu->range; @@ -81,19 +81,19 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) div = &peri->div; if (divider_exists(div)) { - if (div->offset > limit) { + if (div->u.s.offset > limit) { pr_err("%s: bad divider offset for %s (%u > %u)\n", - __func__, name, div->offset, limit); + __func__, name, div->u.s.offset, limit); return false; } } div = &peri->pre_div; if (divider_exists(div)) { - if (div->offset > limit) { + if (div->u.s.offset > limit) { pr_err("%s: bad pre-divider offset for %s " "(%u > %u)\n", - __func__, name, div->offset, limit); + __func__, name, div->u.s.offset, limit); return false; } } @@ -249,21 +249,22 @@ static bool div_valid(struct bcm_clk_div *div, const char *field_name, { if (divider_is_fixed(div)) { /* Any fixed divider value but 0 is OK */ - if (div->fixed == 0) { + if (div->u.fixed == 0) { pr_err("%s: bad %s fixed value 0 for %s\n", __func__, field_name, clock_name); return false; } return true; } - if (!bitfield_valid(div->shift, div->width, field_name, clock_name)) + if (!bitfield_valid(div->u.s.shift, div->u.s.width, + field_name, clock_name)) return false; if (divider_has_fraction(div)) - if (div->frac_width > div->width) { + if (div->u.s.frac_width > div->u.s.width) { pr_warn("%s: bad %s fraction width for %s (%u > %u)\n", __func__, field_name, clock_name, - div->frac_width, div->width); + div->u.s.frac_width, div->u.s.width); return false; } @@ -278,7 +279,7 @@ static bool div_valid(struct bcm_clk_div *div, const char *field_name, */ static bool kona_dividers_valid(struct kona_clk *bcm_clk) { - struct peri_clk_data *peri = bcm_clk->peri; + struct peri_clk_data *peri = bcm_clk->u.peri; struct bcm_clk_div *div; struct bcm_clk_div *pre_div; u32 limit; @@ -295,7 +296,7 @@ static bool kona_dividers_valid(struct kona_clk *bcm_clk) limit = BITS_PER_BYTE * sizeof(u32); - return div->frac_width + pre_div->frac_width <= limit; + return div->u.s.frac_width + pre_div->u.s.frac_width <= limit; } @@ -328,7 +329,7 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) if (!peri_clk_data_offsets_valid(bcm_clk)) return false; - peri = bcm_clk->peri; + peri = bcm_clk->u.peri; name = bcm_clk->name; gate = &peri->gate; if (gate_exists(gate) && !gate_valid(gate, "gate", name)) @@ -588,12 +589,12 @@ static void bcm_clk_teardown(struct kona_clk *bcm_clk) { switch (bcm_clk->type) { case bcm_clk_peri: - peri_clk_teardown(bcm_clk->data, &bcm_clk->init_data); + peri_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data); break; default: break; } - bcm_clk->data = NULL; + bcm_clk->u.data = NULL; bcm_clk->type = bcm_clk_none; } @@ -644,7 +645,7 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, break; } bcm_clk->type = type; - bcm_clk->data = data; + bcm_clk->u.data = data; /* Make sure everything makes sense before we set it up */ if (!kona_clk_valid(bcm_clk)) { diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index e3d339e..db11a87 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -61,7 +61,7 @@ u64 do_div_round_closest(u64 dividend, unsigned long divisor) /* Convert a divider into the scaled divisor value it represents. */ static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div) { - return (u64)reg_div + ((u64)1 << div->frac_width); + return (u64)reg_div + ((u64)1 << div->u.s.frac_width); } /* @@ -77,7 +77,7 @@ u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths) BUG_ON(billionths >= BILLION); combined = (u64)div_value * BILLION + billionths; - combined <<= div->frac_width; + combined <<= div->u.s.frac_width; return do_div_round_closest(combined, BILLION); } @@ -87,7 +87,7 @@ static inline u64 scaled_div_min(struct bcm_clk_div *div) { if (divider_is_fixed(div)) - return (u64)div->fixed; + return (u64)div->u.fixed; return scaled_div_value(div, 0); } @@ -98,9 +98,9 @@ u64 scaled_div_max(struct bcm_clk_div *div) u32 reg_div; if (divider_is_fixed(div)) - return (u64)div->fixed; + return (u64)div->u.fixed; - reg_div = ((u32)1 << div->width) - 1; + reg_div = ((u32)1 << div->u.s.width) - 1; return scaled_div_value(div, reg_div); } @@ -115,7 +115,7 @@ divider(struct bcm_clk_div *div, u64 scaled_div) BUG_ON(scaled_div < scaled_div_min(div)); BUG_ON(scaled_div > scaled_div_max(div)); - return (u32)(scaled_div - ((u64)1 << div->frac_width)); + return (u32)(scaled_div - ((u64)1 << div->u.s.frac_width)); } /* Return a rate scaled for use when dividing by a scaled divisor. */ @@ -125,7 +125,7 @@ scale_rate(struct bcm_clk_div *div, u32 rate) if (divider_is_fixed(div)) return (u64)rate; - return (u64)rate << div->frac_width; + return (u64)rate << div->u.s.frac_width; } /* CCU access */ @@ -398,14 +398,14 @@ static u64 divider_read_scaled(struct ccu_data *ccu, struct bcm_clk_div *div) u32 reg_div; if (divider_is_fixed(div)) - return (u64)div->fixed; + return (u64)div->u.fixed; flags = ccu_lock(ccu); - reg_val = __ccu_read(ccu, div->offset); + reg_val = __ccu_read(ccu, div->u.s.offset); ccu_unlock(ccu, flags); /* Extract the full divider field from the register value */ - reg_div = bitfield_extract(reg_val, div->shift, div->width); + reg_div = bitfield_extract(reg_val, div->u.s.shift, div->u.s.width); /* Return the scaled divisor value it represents */ return scaled_div_value(div, reg_div); @@ -433,16 +433,17 @@ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate, * state was defined in the device tree, we just find out * what its current value is rather than updating it. */ - if (div->scaled_div == BAD_SCALED_DIV_VALUE) { - reg_val = __ccu_read(ccu, div->offset); - reg_div = bitfield_extract(reg_val, div->shift, div->width); - div->scaled_div = scaled_div_value(div, reg_div); + if (div->u.s.scaled_div == BAD_SCALED_DIV_VALUE) { + reg_val = __ccu_read(ccu, div->u.s.offset); + reg_div = bitfield_extract(reg_val, div->u.s.shift, + div->u.s.width); + div->u.s.scaled_div = scaled_div_value(div, reg_div); return 0; } /* Convert the scaled divisor to the value we need to record */ - reg_div = divider(div, div->scaled_div); + reg_div = divider(div, div->u.s.scaled_div); /* Clock needs to be enabled before changing the rate */ enabled = __is_clk_gate_enabled(ccu, gate); @@ -452,9 +453,10 @@ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate, } /* Replace the divider value and record the result */ - reg_val = __ccu_read(ccu, div->offset); - reg_val = bitfield_replace(reg_val, div->shift, div->width, reg_div); - __ccu_write(ccu, div->offset, reg_val); + reg_val = __ccu_read(ccu, div->u.s.offset); + reg_val = bitfield_replace(reg_val, div->u.s.shift, div->u.s.width, + reg_div); + __ccu_write(ccu, div->u.s.offset, reg_val); /* If the trigger fails we still want to disable the gate */ if (!__clk_trigger(ccu, trig)) @@ -490,11 +492,11 @@ static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, BUG_ON(divider_is_fixed(div)); - previous = div->scaled_div; + previous = div->u.s.scaled_div; if (previous == scaled_div) return 0; /* No change */ - div->scaled_div = scaled_div; + div->u.s.scaled_div = scaled_div; flags = ccu_lock(ccu); __ccu_write_enable(ccu); @@ -505,7 +507,7 @@ static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, ccu_unlock(ccu, flags); if (ret) - div->scaled_div = previous; /* Revert the change */ + div->u.s.scaled_div = previous; /* Revert the change */ return ret; @@ -802,7 +804,7 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, static int kona_peri_clk_enable(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_gate *gate = &bcm_clk->peri->gate; + struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true); } @@ -810,7 +812,7 @@ static int kona_peri_clk_enable(struct clk_hw *hw) static void kona_peri_clk_disable(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_gate *gate = &bcm_clk->peri->gate; + struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; (void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false); } @@ -818,7 +820,7 @@ static void kona_peri_clk_disable(struct clk_hw *hw) static int kona_peri_clk_is_enabled(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_gate *gate = &bcm_clk->peri->gate; + struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0; } @@ -827,7 +829,7 @@ static unsigned long kona_peri_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; return clk_recalc_rate(bcm_clk->ccu, &data->div, &data->pre_div, parent_rate); @@ -837,20 +839,20 @@ static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_div *div = &bcm_clk->peri->div; + struct bcm_clk_div *div = &bcm_clk->u.peri->div; if (!divider_exists(div)) return __clk_get_rate(hw->clk); /* Quietly avoid a zero rate */ - return round_rate(bcm_clk->ccu, div, &bcm_clk->peri->pre_div, + return round_rate(bcm_clk->ccu, div, &bcm_clk->u.peri->pre_div, rate ? rate : 1, *parent_rate, NULL); } static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; struct bcm_clk_sel *sel = &data->sel; struct bcm_clk_trig *trig; int ret; @@ -884,7 +886,7 @@ static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) static u8 kona_peri_clk_get_parent(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; u8 index; index = selector_read_index(bcm_clk->ccu, &data->sel); @@ -897,7 +899,7 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; struct bcm_clk_div *div = &data->div; u64 scaled_div = 0; int ret; @@ -958,7 +960,7 @@ struct clk_ops kona_peri_clk_ops = { static bool __peri_clk_init(struct kona_clk *bcm_clk) { struct ccu_data *ccu = bcm_clk->ccu; - struct peri_clk_data *peri = bcm_clk->peri; + struct peri_clk_data *peri = bcm_clk->u.peri; const char *name = bcm_clk->name; struct bcm_clk_trig *trig; diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 5e139ad..dee6909 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -57,7 +57,7 @@ #define divider_exists(div) FLAG_TEST(div, DIV, EXISTS) #define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED) #define divider_has_fraction(div) (!divider_is_fixed(div) && \ - (div)->frac_width > 0) + (div)->u.s.frac_width > 0) #define selector_exists(sel) ((sel)->width != 0) #define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS) @@ -244,9 +244,9 @@ struct bcm_clk_div { u32 frac_width; /* field fraction width */ u64 scaled_div; /* scaled divider value */ - }; + } s; u32 fixed; /* non-zero fixed divider value */ - }; + } u; u32 flags; /* BCM_CLK_DIV_FLAGS_* below */ }; @@ -263,28 +263,28 @@ struct bcm_clk_div { /* A fixed (non-zero) divider */ #define FIXED_DIVIDER(_value) \ { \ - .fixed = (_value), \ + .u.fixed = (_value), \ .flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \ } /* A divider with an integral divisor */ #define DIVIDER(_offset, _shift, _width) \ { \ - .offset = (_offset), \ - .shift = (_shift), \ - .width = (_width), \ - .scaled_div = BAD_SCALED_DIV_VALUE, \ + .u.s.offset = (_offset), \ + .u.s.shift = (_shift), \ + .u.s.width = (_width), \ + .u.s.scaled_div = BAD_SCALED_DIV_VALUE, \ .flags = FLAG(DIV, EXISTS), \ } /* A divider whose divisor has an integer and fractional part */ #define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \ { \ - .offset = (_offset), \ - .shift = (_shift), \ - .width = (_width), \ - .frac_width = (_frac_width), \ - .scaled_div = BAD_SCALED_DIV_VALUE, \ + .u.s.offset = (_offset), \ + .u.s.shift = (_shift), \ + .u.s.width = (_width), \ + .u.s.frac_width = (_frac_width), \ + .u.s.scaled_div = BAD_SCALED_DIV_VALUE, \ .flags = FLAG(DIV, EXISTS), \ } @@ -380,7 +380,7 @@ struct kona_clk { union { void *data; struct peri_clk_data *peri; - }; + } u; }; #define to_kona_clk(_hw) \ container_of(_hw, struct kona_clk, hw) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index ec22112..4637697 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -144,6 +144,37 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div) return true; } +static int _round_up_table(const struct clk_div_table *table, int div) +{ + const struct clk_div_table *clkt; + int up = _get_table_maxdiv(table); + + for (clkt = table; clkt->div; clkt++) { + if (clkt->div == div) + return clkt->div; + else if (clkt->div < div) + continue; + + if ((clkt->div - div) < (up - div)) + up = clkt->div; + } + + return up; +} + +static int _div_round_up(struct clk_divider *divider, + unsigned long parent_rate, unsigned long rate) +{ + int div = DIV_ROUND_UP(parent_rate, rate); + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + div = __roundup_pow_of_two(div); + if (divider->table) + div = _round_up_table(divider->table, div); + + return div; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -159,7 +190,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; - bestdiv = DIV_ROUND_UP(parent_rate, rate); + bestdiv = _div_round_up(divider, parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; return bestdiv; @@ -219,6 +250,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, u32 val; div = DIV_ROUND_UP(parent_rate, rate); + + if (!_is_valid_div(divider, div)) + return -EINVAL; + value = _get_val(divider, div); if (value > div_mask(divider)) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index dff0373..7cf2c09 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1984,9 +1984,28 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw) } EXPORT_SYMBOL_GPL(__clk_register); -static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) +/** + * clk_register - allocate a new clock, register it and return an opaque cookie + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * clk_register is the primary interface for populating the clock tree with new + * clock nodes. It returns a pointer to the newly allocated struct clk which + * cannot be dereferenced by driver code but may be used in conjuction with the + * rest of the clock API. In the event of an error clk_register will return an + * error code; drivers must test for an error code after calling clk_register. + */ +struct clk *clk_register(struct device *dev, struct clk_hw *hw) { int i, ret; + struct clk *clk; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) { + pr_err("%s: could not allocate clk\n", __func__); + ret = -ENOMEM; + goto fail_out; + } clk->name = kstrdup(hw->init->name, GFP_KERNEL); if (!clk->name) { @@ -2026,7 +2045,7 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) ret = __clk_init(dev, clk); if (!ret) - return 0; + return clk; fail_parent_names_copy: while (--i >= 0) @@ -2035,36 +2054,6 @@ fail_parent_names_copy: fail_parent_names: kfree(clk->name); fail_name: - return ret; -} - -/** - * clk_register - allocate a new clock, register it and return an opaque cookie - * @dev: device that is registering this clock - * @hw: link to hardware-specific clock data - * - * clk_register is the primary interface for populating the clock tree with new - * clock nodes. It returns a pointer to the newly allocated struct clk which - * cannot be dereferenced by driver code but may be used in conjuction with the - * rest of the clock API. In the event of an error clk_register will return an - * error code; drivers must test for an error code after calling clk_register. - */ -struct clk *clk_register(struct device *dev, struct clk_hw *hw) -{ - int ret; - struct clk *clk; - - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) { - pr_err("%s: could not allocate clk\n", __func__); - ret = -ENOMEM; - goto fail_out; - } - - ret = _clk_register(dev, hw, clk); - if (!ret) - return clk; - kfree(clk); fail_out: return ERR_PTR(ret); @@ -2151,9 +2140,10 @@ void clk_unregister(struct clk *clk) if (!hlist_empty(&clk->children)) { struct clk *child; + struct hlist_node *t; /* Reparent all children to the orphan list. */ - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry_safe(child, t, &clk->children, child_node) clk_set_parent(child, NULL); } @@ -2173,7 +2163,7 @@ EXPORT_SYMBOL_GPL(clk_unregister); static void devm_clk_release(struct device *dev, void *res) { - clk_unregister(res); + clk_unregister(*(struct clk **)res); } /** @@ -2188,18 +2178,18 @@ static void devm_clk_release(struct device *dev, void *res) struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw) { struct clk *clk; - int ret; + struct clk **clkp; - clk = devres_alloc(devm_clk_release, sizeof(*clk), GFP_KERNEL); - if (!clk) + clkp = devres_alloc(devm_clk_release, sizeof(*clkp), GFP_KERNEL); + if (!clkp) return ERR_PTR(-ENOMEM); - ret = _clk_register(dev, hw, clk); - if (!ret) { - devres_add(dev, clk); + clk = clk_register(dev, hw); + if (!IS_ERR(clk)) { + *clkp = clk; + devres_add(dev, clkp); } else { - devres_free(clk); - clk = ERR_PTR(ret); + devres_free(clkp); } return clk; diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c index 2e5810c..1f6324e 100644 --- a/drivers/clk/shmobile/clk-mstp.c +++ b/drivers/clk/shmobile/clk-mstp.c @@ -156,6 +156,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name, static void __init cpg_mstp_clocks_init(struct device_node *np) { struct mstp_clock_group *group; + const char *idxname; struct clk **clks; unsigned int i; @@ -184,6 +185,11 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) for (i = 0; i < MSTP_MAX_CLOCKS; ++i) clks[i] = ERR_PTR(-ENOENT); + if (of_find_property(np, "clock-indices", &i)) + idxname = "clock-indices"; + else + idxname = "renesas,clock-indices"; + for (i = 0; i < MSTP_MAX_CLOCKS; ++i) { const char *parent_name; const char *name; @@ -197,8 +203,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) continue; parent_name = of_clk_get_parent_name(np, i); - ret = of_property_read_u32_index(np, "renesas,clock-indices", i, - &clkidx); + ret = of_property_read_u32_index(np, idxname, i, &clkidx); if (parent_name == NULL || ret < 0) break; diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index 88dafb5..de6da95 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -20,6 +20,7 @@ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include "clk.h" @@ -43,6 +44,8 @@ #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) +void __iomem *clk_mgr_base_addr; + static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { @@ -87,6 +90,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; + struct device_node *clkmgr_np; int rc; int i = 0; @@ -96,6 +100,9 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, if (WARN_ON(!pll_clk)) return NULL; + clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); + clk_mgr_base_addr = of_iomap(clkmgr_np, 0); + BUG_ON(!clk_mgr_base_addr); pll_clk->hw.reg = clk_mgr_base_addr + reg; of_property_read_string(node, "clock-output-names", &clk_name); diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 35a960a..43db947 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -17,28 +17,11 @@ * 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/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/io.h> #include <linux/of.h> -#include <linux/of_address.h> #include "clk.h" -void __iomem *clk_mgr_base_addr; - -static const struct of_device_id socfpga_child_clocks[] __initconst = { - { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, - { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, }, - { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, }, - {}, -}; - -static void __init socfpga_clkmgr_init(struct device_node *node) -{ - clk_mgr_base_addr = of_iomap(node, 0); - of_clk_init(socfpga_child_clocks); -} -CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init); +CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init); +CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init); +CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init); diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 0d20241..e1769ad 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1718,7 +1718,7 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name, "pll_re_vco"); } else { val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL); - pll_writel(val, pll_params->aux_reg, pll); + pll_writel(val_aux, pll_params->aux_reg, pll); } clk = _tegra_clk_register_pll(pll, name, parent_name, flags, diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 0999673..eab8ccf 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -37,6 +37,7 @@ #define BYT_RATIOS 0x66a #define BYT_VIDS 0x66b #define BYT_TURBO_RATIOS 0x66c +#define BYT_TURBO_VIDS 0x66d #define FRAC_BITS 6 @@ -70,8 +71,9 @@ struct pstate_data { }; struct vid_data { - int32_t min; - int32_t max; + int min; + int max; + int turbo; int32_t ratio; }; @@ -359,14 +361,14 @@ static int byt_get_min_pstate(void) { u64 value; rdmsrl(BYT_RATIOS, value); - return (value >> 8) & 0xFF; + return (value >> 8) & 0x3F; } static int byt_get_max_pstate(void) { u64 value; rdmsrl(BYT_RATIOS, value); - return (value >> 16) & 0xFF; + return (value >> 16) & 0x3F; } static int byt_get_turbo_pstate(void) @@ -393,6 +395,9 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate) vid_fp = clamp_t(int32_t, vid_fp, cpudata->vid.min, cpudata->vid.max); vid = fp_toint(vid_fp); + if (pstate > cpudata->pstate.max_pstate) + vid = cpudata->vid.turbo; + val |= vid; wrmsrl(MSR_IA32_PERF_CTL, val); @@ -402,13 +407,17 @@ static void byt_get_vid(struct cpudata *cpudata) { u64 value; + rdmsrl(BYT_VIDS, value); - cpudata->vid.min = int_tofp((value >> 8) & 0x7f); - cpudata->vid.max = int_tofp((value >> 16) & 0x7f); + cpudata->vid.min = int_tofp((value >> 8) & 0x3f); + cpudata->vid.max = int_tofp((value >> 16) & 0x3f); cpudata->vid.ratio = div_fp( cpudata->vid.max - cpudata->vid.min, int_tofp(cpudata->pstate.max_pstate - cpudata->pstate.min_pstate)); + + rdmsrl(BYT_TURBO_VIDS, value); + cpudata->vid.turbo = value & 0x7f; } @@ -545,12 +554,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) if (pstate_funcs.get_vid) pstate_funcs.get_vid(cpu); - - /* - * goto max pstate so we don't slow up boot if we are built-in if we are - * a module we will take care of it during normal operation - */ - intel_pstate_set_pstate(cpu, cpu->pstate.max_pstate); + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); } static inline void intel_pstate_calc_busy(struct cpudata *cpu, @@ -695,11 +699,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum) cpu = all_cpu_data[cpunum]; intel_pstate_get_cpu_pstates(cpu); - if (!cpu->pstate.current_pstate) { - all_cpu_data[cpunum] = NULL; - kfree(cpu); - return -ENODATA; - } cpu->cpu = cpunum; @@ -710,7 +709,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum) cpu->timer.expires = jiffies + HZ/100; intel_pstate_busy_pid_reset(cpu); intel_pstate_sample(cpu); - intel_pstate_set_pstate(cpu, cpu->pstate.max_pstate); add_timer_on(&cpu->timer, cpunum); diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index f0bc31f..d4add86 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -62,7 +62,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy, set_cpus_allowed_ptr(current, &cpus_allowed); /* setting the cpu frequency */ - clk_set_rate(policy->clk, freq); + clk_set_rate(policy->clk, freq * 1000); return 0; } @@ -92,7 +92,7 @@ static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) i++) loongson2_clockmod_table[i].frequency = (rate * i) / 8; - ret = clk_set_rate(cpuclk, rate); + ret = clk_set_rate(cpuclk, rate * 1000); if (ret) { clk_put(cpuclk); return ret; diff --git a/drivers/crypto/caam/error.c b/drivers/crypto/caam/error.c index 9f25f52..0eabd81 100644 --- a/drivers/crypto/caam/error.c +++ b/drivers/crypto/caam/error.c @@ -16,9 +16,13 @@ char *tmp; \ \ tmp = kmalloc(sizeof(format) + max_alloc, GFP_ATOMIC); \ - sprintf(tmp, format, param); \ - strcat(str, tmp); \ - kfree(tmp); \ + if (likely(tmp)) { \ + sprintf(tmp, format, param); \ + strcat(str, tmp); \ + kfree(tmp); \ + } else { \ + strcat(str, "kmalloc failure in SPRINTFCAT"); \ + } \ } static void report_jump_idx(u32 status, char *outstr) diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index 3ee852c..071c2c9 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c @@ -756,6 +756,7 @@ static const struct { */ { ACPI_SIG_IBFT }, { "iBFT" }, + { "BIFT" }, /* Broadcom iSCSI Offload */ }; static void __init acpi_find_ibft_region(void) diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index e73c675..7030422 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -305,6 +305,8 @@ static struct ichx_desc ich6_desc = { .ngpio = 50, .have_blink = true, + .regs = ichx_regs, + .reglen = ichx_reglen, }; /* Intel 3100 */ @@ -324,6 +326,8 @@ static struct ichx_desc i3100_desc = { .uses_gpe0 = true, .ngpio = 50, + .regs = ichx_regs, + .reglen = ichx_reglen, }; /* ICH7 and ICH8-based */ diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 99a6831..3d53fd6 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -894,9 +894,11 @@ static int mcp23s08_probe(struct spi_device *spi) dev_err(&spi->dev, "invalid spi-present-mask\n"); return -ENODEV; } - - for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) + for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { + if ((spi_present_mask & (1 << addr))) + chips++; pullups[addr] = 0; + } } else { type = spi_get_device_id(spi)->driver_data; pdata = dev_get_platdata(&spi->dev); @@ -919,12 +921,12 @@ static int mcp23s08_probe(struct spi_device *spi) pullups[addr] = pdata->chip[addr].pullups; } - if (!chips) - return -ENODEV; - base = pdata->base; } + if (!chips) + return -ENODEV; + data = kzalloc(sizeof(*data) + chips * sizeof(struct mcp23s08), GFP_KERNEL); if (!data) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ec82f6b..108e1ec 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1954,6 +1954,9 @@ struct drm_i915_cmd_table { #define IS_ULT(dev) (IS_HSW_ULT(dev) || IS_BDW_ULT(dev)) #define IS_HSW_GT3(dev) (IS_HASWELL(dev) && \ ((dev)->pdev->device & 0x00F0) == 0x0020) +/* ULX machines are also considered ULT. */ +#define IS_HSW_ULX(dev) ((dev)->pdev->device == 0x0A0E || \ + (dev)->pdev->device == 0x0A1E) #define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary) /* diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 62a5c36..154b0f8 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -34,25 +34,35 @@ static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv); bool intel_enable_ppgtt(struct drm_device *dev, bool full) { - if (i915.enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev)) + if (i915.enable_ppgtt == 0) return false; if (i915.enable_ppgtt == 1 && full) return false; + return true; +} + +static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt) +{ + if (enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev)) + return 0; + + if (enable_ppgtt == 1) + return 1; + + if (enable_ppgtt == 2 && HAS_PPGTT(dev)) + return 2; + #ifdef CONFIG_INTEL_IOMMU /* Disable ppgtt on SNB if VT-d is on. */ if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) { DRM_INFO("Disabling PPGTT because VT-d is on\n"); - return false; + return 0; } #endif - /* Full ppgtt disabled by default for now due to issues. */ - if (full) - return HAS_PPGTT(dev) && (i915.enable_ppgtt == 2); - else - return HAS_ALIASING_PPGTT(dev); + return HAS_ALIASING_PPGTT(dev) ? 1 : 0; } #define GEN6_PPGTT_PD_ENTRIES 512 @@ -2031,6 +2041,14 @@ int i915_gem_gtt_init(struct drm_device *dev) gtt->base.total >> 20); DRM_DEBUG_DRIVER("GMADR size = %ldM\n", gtt->mappable_end >> 20); DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", gtt->stolen_size >> 20); + /* + * i915.enable_ppgtt is read-only, so do an early pass to validate the + * user's requested state against the hardware/driver capabilities. We + * do this now so that we can print out any log messages once rather + * than every time we check intel_enable_ppgtt(). + */ + i915.enable_ppgtt = sanitize_enable_ppgtt(dev, i915.enable_ppgtt); + DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt); return 0; } diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index fa486c5..aff4a113 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -560,47 +560,71 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) dev_priv->vbt.edp_pps = *edp_pps; - dev_priv->vbt.edp_rate = edp_link_params->rate ? DP_LINK_BW_2_7 : - DP_LINK_BW_1_62; + switch (edp_link_params->rate) { + case EDP_RATE_1_62: + dev_priv->vbt.edp_rate = DP_LINK_BW_1_62; + break; + case EDP_RATE_2_7: + dev_priv->vbt.edp_rate = DP_LINK_BW_2_7; + break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP link rate value %u\n", + edp_link_params->rate); + break; + } + switch (edp_link_params->lanes) { - case 0: + case EDP_LANE_1: dev_priv->vbt.edp_lanes = 1; break; - case 1: + case EDP_LANE_2: dev_priv->vbt.edp_lanes = 2; break; - case 3: - default: + case EDP_LANE_4: dev_priv->vbt.edp_lanes = 4; break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP lane count value %u\n", + edp_link_params->lanes); + break; } + switch (edp_link_params->preemphasis) { - case 0: + case EDP_PREEMPHASIS_NONE: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0; break; - case 1: + case EDP_PREEMPHASIS_3_5dB: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; break; - case 2: + case EDP_PREEMPHASIS_6dB: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6; break; - case 3: + case EDP_PREEMPHASIS_9_5dB: dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP pre-emphasis value %u\n", + edp_link_params->preemphasis); + break; } + switch (edp_link_params->vswing) { - case 0: + case EDP_VSWING_0_4V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400; break; - case 1: + case EDP_VSWING_0_6V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600; break; - case 2: + case EDP_VSWING_0_8V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800; break; - case 3: + case EDP_VSWING_1_2V: dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200; break; + default: + DRM_DEBUG_KMS("VBT has unknown eDP voltage swing value %u\n", + edp_link_params->vswing); + break; } } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 69bcc42..48aa516 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11395,15 +11395,6 @@ void intel_modeset_init(struct drm_device *dev) } } -static void -intel_connector_break_all_links(struct intel_connector *connector) -{ - connector->base.dpms = DRM_MODE_DPMS_OFF; - connector->base.encoder = NULL; - connector->encoder->connectors_active = false; - connector->encoder->base.crtc = NULL; -} - static void intel_enable_pipe_a(struct drm_device *dev) { struct intel_connector *connector; @@ -11485,8 +11476,17 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) if (connector->encoder->base.crtc != &crtc->base) continue; - intel_connector_break_all_links(connector); + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; } + /* multiple connectors may have the same encoder: + * handle them and break crtc link separately */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) + if (connector->encoder->base.crtc == &crtc->base) { + connector->encoder->base.crtc = NULL; + connector->encoder->connectors_active = false; + } WARN_ON(crtc->active); crtc->base.enabled = false; @@ -11568,6 +11568,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) drm_get_encoder_name(&encoder->base)); encoder->disable(encoder); } + encoder->base.crtc = NULL; + encoder->connectors_active = false; /* Inconsistent output/port/pipe state happens presumably due to * a bug in one of the get_hw_state functions. Or someplace else @@ -11578,8 +11580,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) base.head) { if (connector->encoder != encoder) continue; - - intel_connector_break_all_links(connector); + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; } } /* Enabled encoders without active connectors will be fixed in diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index dfa8528..2a00cb8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -105,7 +105,8 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) case DP_LINK_BW_2_7: break; case DP_LINK_BW_5_4: /* 1.2 capable displays may advertise higher bw */ - if ((IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8) && + if (((IS_HASWELL(dev) && !IS_HSW_ULX(dev)) || + INTEL_INFO(dev)->gen >= 8) && intel_dp->dpcd[DP_DPCD_REV] >= 0x12) max_link_bw = DP_LINK_BW_5_4; else @@ -120,6 +121,22 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) return max_link_bw; } +static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + u8 source_max, sink_max; + + source_max = 4; + if (HAS_DDI(dev) && intel_dig_port->port == PORT_A && + (intel_dig_port->saved_port_bits & DDI_A_4_LANES) == 0) + source_max = 2; + + sink_max = drm_dp_max_lane_count(intel_dp->dpcd); + + return min(source_max, sink_max); +} + /* * The units on the numbers in the next two are... bizarre. Examples will * make it clearer; this one parallels an example in the eDP spec. @@ -170,7 +187,7 @@ intel_dp_mode_valid(struct drm_connector *connector, } max_link_clock = drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); - max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); + max_lanes = intel_dp_max_lane_count(intel_dp); max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); mode_rate = intel_dp_link_required(target_clock, 18); @@ -750,8 +767,10 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc *intel_crtc = encoder->new_crtc; struct intel_connector *intel_connector = intel_dp->attached_connector; int lane_count, clock; - int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); + int min_lane_count = 1; + int max_lane_count = intel_dp_max_lane_count(intel_dp); /* Conveniently, the link BW constants become indices with a shift...*/ + int min_clock = 0; int max_clock = intel_dp_max_link_bw(intel_dp) >> 3; int bpp, mode_rate; static int bws[] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7, DP_LINK_BW_5_4 }; @@ -784,19 +803,38 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ bpp = pipe_config->pipe_bpp; - if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp && - dev_priv->vbt.edp_bpp < bpp) { - DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n", - dev_priv->vbt.edp_bpp); - bpp = dev_priv->vbt.edp_bpp; + if (is_edp(intel_dp)) { + if (dev_priv->vbt.edp_bpp && dev_priv->vbt.edp_bpp < bpp) { + DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n", + dev_priv->vbt.edp_bpp); + bpp = dev_priv->vbt.edp_bpp; + } + + if (IS_BROADWELL(dev)) { + /* Yes, it's an ugly hack. */ + min_lane_count = max_lane_count; + DRM_DEBUG_KMS("forcing lane count to max (%u) on BDW\n", + min_lane_count); + } else if (dev_priv->vbt.edp_lanes) { + min_lane_count = min(dev_priv->vbt.edp_lanes, + max_lane_count); + DRM_DEBUG_KMS("using min %u lanes per VBT\n", + min_lane_count); + } + + if (dev_priv->vbt.edp_rate) { + min_clock = min(dev_priv->vbt.edp_rate >> 3, max_clock); + DRM_DEBUG_KMS("using min %02x link bw per VBT\n", + bws[min_clock]); + } } for (; bpp >= 6*3; bpp -= 2*3) { mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock, bpp); - for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - for (clock = 0; clock <= max_clock; clock++) { + for (lane_count = min_lane_count; lane_count <= max_lane_count; lane_count <<= 1) { + for (clock = min_clock; clock <= max_clock; clock++) { link_clock = drm_dp_bw_code_to_link_rate(bws[clock]); link_avail = intel_dp_max_data_rate(link_clock, lane_count); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index fce4a0d..f73ba5e 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -387,6 +387,15 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, height); } + /* No preferred mode marked by the EDID? Are there any modes? */ + if (!modes[i] && !list_empty(&connector->modes)) { + DRM_DEBUG_KMS("using first mode listed on connector %s\n", + drm_get_connector_name(connector)); + modes[i] = list_first_entry(&connector->modes, + struct drm_display_mode, + head); + } + /* last resort: use current mode */ if (!modes[i]) { /* diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 0eead16..cb8cfb7 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -492,6 +492,7 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level, enum pipe pipe = intel_get_pipe_from_connector(connector); u32 freq; unsigned long flags; + u64 n; if (!panel->backlight.present || pipe == INVALID_PIPE) return; @@ -502,10 +503,9 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level, /* scale to hardware max, but be careful to not overflow */ freq = panel->backlight.max; - if (freq < max) - level = level * freq / max; - else - level = freq / max * level; + n = (u64)level * freq; + do_div(n, max); + level = n; panel->backlight.level = level; if (panel->backlight.device) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 19e94c3..d93dcf6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2095,6 +2095,43 @@ static void intel_print_wm_latency(struct drm_device *dev, } } +static bool ilk_increase_wm_latency(struct drm_i915_private *dev_priv, + uint16_t wm[5], uint16_t min) +{ + int level, max_level = ilk_wm_max_level(dev_priv->dev); + + if (wm[0] >= min) + return false; + + wm[0] = max(wm[0], min); + for (level = 1; level <= max_level; level++) + wm[level] = max_t(uint16_t, wm[level], DIV_ROUND_UP(min, 5)); + + return true; +} + +static void snb_wm_latency_quirk(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + bool changed; + + /* + * The BIOS provided WM memory latency values are often + * inadequate for high resolution displays. Adjust them. + */ + changed = ilk_increase_wm_latency(dev_priv, dev_priv->wm.pri_latency, 12) | + ilk_increase_wm_latency(dev_priv, dev_priv->wm.spr_latency, 12) | + ilk_increase_wm_latency(dev_priv, dev_priv->wm.cur_latency, 12); + + if (!changed) + return; + + DRM_DEBUG_KMS("WM latency values increased to avoid potential underruns\n"); + intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency); + intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency); + intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency); +} + static void ilk_setup_wm_latency(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2112,6 +2149,9 @@ static void ilk_setup_wm_latency(struct drm_device *dev) intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency); intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency); intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency); + + if (IS_GEN6(dev)) + snb_wm_latency_quirk(dev); } static void ilk_compute_wm_parameters(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index d27155a..46be00d 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2424,8 +2424,8 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, if (ret < 0) goto err1; - ret = sysfs_create_link(&encoder->ddc.dev.kobj, - &drm_connector->kdev->kobj, + ret = sysfs_create_link(&drm_connector->kdev->kobj, + &encoder->ddc.dev.kobj, encoder->ddc.dev.kobj.name); if (ret < 0) goto err2; diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index f729dc71..d0c7577 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -185,6 +185,8 @@ static void vlv_force_wake_reset(struct drm_i915_private *dev_priv) { __raw_i915_write32(dev_priv, FORCEWAKE_VLV, _MASKED_BIT_DISABLE(0xffff)); + __raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV, + _MASKED_BIT_DISABLE(0xffff)); /* something from same cacheline, but !FORCEWAKE_VLV */ __raw_posting_read(dev_priv, FORCEWAKE_ACK_VLV); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 7762665..876de9a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -1009,7 +1009,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, } if (outp == 8) - return false; + return conf; data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1); if (data == 0x0000) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c index 1dc37b1..b0d0fb2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c @@ -863,7 +863,7 @@ gm107_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) { mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); - mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_data(0x200000, 0x1000, NV_MEM_ACCESS_RW); mmio_list(0x40800c, 0x00000000, 8, 1); mmio_list(0x408010, 0x80000000, 0, 0); @@ -877,6 +877,8 @@ gm107_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) mmio_list(0x418e24, 0x00000000, 8, 0); mmio_list(0x418e28, 0x80000030, 0, 0); + mmio_list(0x4064c8, 0x018002c0, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); mmio_list(0x419848, 0x10000000, 12, 2); mmio_list(0x419c2c, 0x10000000, 12, 2); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index fb0b6b2..222e8eb 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -168,7 +168,8 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios) */ i = 16; do { - if ((nv_rd32(bios, 0x300000) & 0xffff) == 0xaa55) + u32 data = le32_to_cpu(nv_rd32(bios, 0x300000)) & 0xffff; + if (data == 0xaa55) break; } while (i--); @@ -176,14 +177,15 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios) goto out; /* read entire bios image to system memory */ - bios->size = ((nv_rd32(bios, 0x300000) >> 16) & 0xff) * 512; + bios->size = (le32_to_cpu(nv_rd32(bios, 0x300000)) >> 16) & 0xff; + bios->size = bios->size * 512; if (!bios->size) goto out; bios->data = kmalloc(bios->size, GFP_KERNEL); if (bios->data) { - for (i = 0; i < bios->size; i+=4) - nv_wo32(bios, i, nv_rd32(bios, 0x300000 + i)); + for (i = 0; i < bios->size; i += 4) + ((u32 *)bios->data)[i/4] = nv_rd32(bios, 0x300000 + i); } /* check the PCI record header */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c index 43fec17..bbf117b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c @@ -40,6 +40,7 @@ pwm_info(struct nouveau_therm *therm, int line) case 0x00: return 2; case 0x19: return 1; case 0x1c: return 0; + case 0x1e: return 2; default: break; } diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 83face3..2792069 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -389,9 +389,6 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev) acpi_status status; acpi_handle dhandle, rom_handle; - if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) - return false; - dhandle = ACPI_HANDLE(&pdev->dev); if (!dhandle) return false; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 3ff030d..da764a4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -764,9 +764,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, } ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); - mutex_unlock(&chan->cli->mutex); if (ret) goto fail_unreserve; + mutex_unlock(&chan->cli->mutex); /* Update the crtc struct and cleanup */ crtc->primary->fb = fb; diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index fb187c7..c31c12b 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1177,27 +1177,43 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, /* Set NUM_BANKS. */ if (rdev->family >= CHIP_TAHITI) { - unsigned tileb, index, num_banks, tile_split_bytes; + unsigned index, num_banks; - /* Calculate the macrotile mode index. */ - tile_split_bytes = 64 << tile_split; - tileb = 8 * 8 * target_fb->bits_per_pixel / 8; - tileb = min(tile_split_bytes, tileb); + if (rdev->family >= CHIP_BONAIRE) { + unsigned tileb, tile_split_bytes; - for (index = 0; tileb > 64; index++) { - tileb >>= 1; - } + /* Calculate the macrotile mode index. */ + tile_split_bytes = 64 << tile_split; + tileb = 8 * 8 * target_fb->bits_per_pixel / 8; + tileb = min(tile_split_bytes, tileb); - if (index >= 16) { - DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", - target_fb->bits_per_pixel, tile_split); - return -EINVAL; - } + for (index = 0; tileb > 64; index++) + tileb >>= 1; + + if (index >= 16) { + DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", + target_fb->bits_per_pixel, tile_split); + return -EINVAL; + } - if (rdev->family >= CHIP_BONAIRE) num_banks = (rdev->config.cik.macrotile_mode_array[index] >> 6) & 0x3; - else + } else { + switch (target_fb->bits_per_pixel) { + case 8: + index = 10; + break; + case 16: + index = SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP; + break; + default: + case 32: + index = SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP; + break; + } + num_banks = (rdev->config.si.tile_mode_array[index] >> 20) & 0x3; + } + fb_format |= EVERGREEN_GRPH_NUM_BANKS(num_banks); } else { /* NI and older. */ @@ -1720,8 +1736,9 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) } /* otherwise, pick one of the plls */ if ((rdev->family == CHIP_KAVERI) || - (rdev->family == CHIP_KABINI)) { - /* KB/KV has PPLL1 and PPLL2 */ + (rdev->family == CHIP_KABINI) || + (rdev->family == CHIP_MULLINS)) { + /* KB/KV/ML has PPLL1 and PPLL2 */ pll_in_use = radeon_get_pll_use_mask(crtc); if (!(pll_in_use & (1 << ATOM_PPLL2))) return ATOM_PPLL2; @@ -1885,6 +1902,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) is_tvcv = true; + if (!radeon_crtc->adjusted_clock) + return -EINVAL; + atombios_crtc_set_pll(crtc, adjusted_mode); if (ASIC_IS_DCE4(rdev)) diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index bc0119f..54e4f52 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -366,11 +366,11 @@ static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector) if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) return; - if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3)) + if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3) == 3) DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); - if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3)) + if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3) == 3) DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); } @@ -419,21 +419,23 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder, if (dp_bridge != ENCODER_OBJECT_ID_NONE) { /* DP bridge chips */ - drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, - DP_EDP_CONFIGURATION_CAP, &tmp); - if (tmp & 1) - panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; - else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) || - (dp_bridge == ENCODER_OBJECT_ID_TRAVIS)) - panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; - else - panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + if (drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, + DP_EDP_CONFIGURATION_CAP, &tmp) == 1) { + if (tmp & 1) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) || + (dp_bridge == ENCODER_OBJECT_ID_TRAVIS)) + panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; + else + panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + } } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { /* eDP */ - drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, - DP_EDP_CONFIGURATION_CAP, &tmp); - if (tmp & 1) - panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + if (drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, + DP_EDP_CONFIGURATION_CAP, &tmp) == 1) { + if (tmp & 1) + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + } } return panel_mode; @@ -809,11 +811,15 @@ void radeon_dp_link_train(struct drm_encoder *encoder, else dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A; - drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp); - if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) - dp_info.tp3_supported = true; - else + if (drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp) + == 1) { + if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED)) + dp_info.tp3_supported = true; + else + dp_info.tp3_supported = false; + } else { dp_info.tp3_supported = false; + } memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE); dp_info.rdev = rdev; diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 199eb19..d2fd989 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -63,6 +63,12 @@ MODULE_FIRMWARE("radeon/KABINI_ce.bin"); MODULE_FIRMWARE("radeon/KABINI_mec.bin"); MODULE_FIRMWARE("radeon/KABINI_rlc.bin"); MODULE_FIRMWARE("radeon/KABINI_sdma.bin"); +MODULE_FIRMWARE("radeon/MULLINS_pfp.bin"); +MODULE_FIRMWARE("radeon/MULLINS_me.bin"); +MODULE_FIRMWARE("radeon/MULLINS_ce.bin"); +MODULE_FIRMWARE("radeon/MULLINS_mec.bin"); +MODULE_FIRMWARE("radeon/MULLINS_rlc.bin"); +MODULE_FIRMWARE("radeon/MULLINS_sdma.bin"); extern int r600_ih_ring_alloc(struct radeon_device *rdev); extern void r600_ih_ring_fini(struct radeon_device *rdev); @@ -1473,6 +1479,43 @@ static const u32 hawaii_mgcg_cgcg_init[] = 0xd80c, 0xff000ff0, 0x00000100 }; +static const u32 godavari_golden_registers[] = +{ + 0x55e4, 0xff607fff, 0xfc000100, + 0x6ed8, 0x00010101, 0x00010000, + 0x9830, 0xffffffff, 0x00000000, + 0x98302, 0xf00fffff, 0x00000400, + 0x6130, 0xffffffff, 0x00010000, + 0x5bb0, 0x000000f0, 0x00000070, + 0x5bc0, 0xf0311fff, 0x80300000, + 0x98f8, 0x73773777, 0x12010001, + 0x98fc, 0xffffffff, 0x00000010, + 0x8030, 0x00001f0f, 0x0000100a, + 0x2f48, 0x73773777, 0x12010001, + 0x2408, 0x000fffff, 0x000c007f, + 0x8a14, 0xf000003f, 0x00000007, + 0x8b24, 0xffffffff, 0x00ff0fff, + 0x30a04, 0x0000ff0f, 0x00000000, + 0x28a4c, 0x07ffffff, 0x06000000, + 0x4d8, 0x00000fff, 0x00000100, + 0xd014, 0x00010000, 0x00810001, + 0xd814, 0x00010000, 0x00810001, + 0x3e78, 0x00000001, 0x00000002, + 0xc768, 0x00000008, 0x00000008, + 0xc770, 0x00000f00, 0x00000800, + 0xc774, 0x00000f00, 0x00000800, + 0xc798, 0x00ffffff, 0x00ff7fbf, + 0xc79c, 0x00ffffff, 0x00ff7faf, + 0x8c00, 0x000000ff, 0x00000001, + 0x214f8, 0x01ff01ff, 0x00000002, + 0x21498, 0x007ff800, 0x00200000, + 0x2015c, 0xffffffff, 0x00000f40, + 0x88c4, 0x001f3ae3, 0x00000082, + 0x88d4, 0x0000001f, 0x00000010, + 0x30934, 0xffffffff, 0x00000000 +}; + + static void cik_init_golden_registers(struct radeon_device *rdev) { switch (rdev->family) { @@ -1504,6 +1547,20 @@ static void cik_init_golden_registers(struct radeon_device *rdev) kalindi_golden_spm_registers, (const u32)ARRAY_SIZE(kalindi_golden_spm_registers)); break; + case CHIP_MULLINS: + radeon_program_register_sequence(rdev, + kalindi_mgcg_cgcg_init, + (const u32)ARRAY_SIZE(kalindi_mgcg_cgcg_init)); + radeon_program_register_sequence(rdev, + godavari_golden_registers, + (const u32)ARRAY_SIZE(godavari_golden_registers)); + radeon_program_register_sequence(rdev, + kalindi_golden_common_registers, + (const u32)ARRAY_SIZE(kalindi_golden_common_registers)); + radeon_program_register_sequence(rdev, + kalindi_golden_spm_registers, + (const u32)ARRAY_SIZE(kalindi_golden_spm_registers)); + break; case CHIP_KAVERI: radeon_program_register_sequence(rdev, spectre_mgcg_cgcg_init, @@ -1834,6 +1891,15 @@ static int cik_init_microcode(struct radeon_device *rdev) rlc_req_size = KB_RLC_UCODE_SIZE * 4; sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; break; + case CHIP_MULLINS: + chip_name = "MULLINS"; + pfp_req_size = CIK_PFP_UCODE_SIZE * 4; + me_req_size = CIK_ME_UCODE_SIZE * 4; + ce_req_size = CIK_CE_UCODE_SIZE * 4; + mec_req_size = CIK_MEC_UCODE_SIZE * 4; + rlc_req_size = ML_RLC_UCODE_SIZE * 4; + sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; + break; default: BUG(); } @@ -3272,6 +3338,7 @@ static void cik_gpu_init(struct radeon_device *rdev) gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_KABINI: + case CHIP_MULLINS: default: rdev->config.cik.max_shader_engines = 1; rdev->config.cik.max_tile_pipes = 2; @@ -3702,6 +3769,7 @@ int cik_copy_cpdma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } @@ -5800,6 +5868,9 @@ static int cik_rlc_resume(struct radeon_device *rdev) case CHIP_KABINI: size = KB_RLC_UCODE_SIZE; break; + case CHIP_MULLINS: + size = ML_RLC_UCODE_SIZE; + break; } cik_rlc_stop(rdev); @@ -6548,6 +6619,7 @@ void cik_get_csb_buffer(struct radeon_device *rdev, volatile u32 *buffer) buffer[count++] = cpu_to_le32(0x00000000); break; case CHIP_KABINI: + case CHIP_MULLINS: buffer[count++] = cpu_to_le32(0x00000000); /* XXX */ buffer[count++] = cpu_to_le32(0x00000000); break; @@ -6693,6 +6765,19 @@ static void cik_disable_interrupt_state(struct radeon_device *rdev) WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); } + /* pflip */ + if (rdev->num_crtc >= 2) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } /* dac hotplug */ WREG32(DAC_AUTODETECT_INT_CONTROL, 0); @@ -7049,6 +7134,25 @@ int cik_irq_set(struct radeon_device *rdev) WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); } + if (rdev->num_crtc >= 2) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + } + if (rdev->num_crtc >= 4) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + } + if (rdev->num_crtc >= 6) { + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + } + WREG32(DC_HPD1_INT_CONTROL, hpd1); WREG32(DC_HPD2_INT_CONTROL, hpd2); WREG32(DC_HPD3_INT_CONTROL, hpd3); @@ -7085,6 +7189,29 @@ static inline void cik_irq_ack(struct radeon_device *rdev) rdev->irq.stat_regs.cik.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); rdev->irq.stat_regs.cik.disp_int_cont6 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE6); + rdev->irq.stat_regs.cik.d1grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC0_REGISTER_OFFSET); + rdev->irq.stat_regs.cik.d2grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC1_REGISTER_OFFSET); + if (rdev->num_crtc >= 4) { + rdev->irq.stat_regs.cik.d3grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC2_REGISTER_OFFSET); + rdev->irq.stat_regs.cik.d4grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC3_REGISTER_OFFSET); + } + if (rdev->num_crtc >= 6) { + rdev->irq.stat_regs.cik.d5grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC4_REGISTER_OFFSET); + rdev->irq.stat_regs.cik.d6grph_int = RREG32(GRPH_INT_STATUS + + EVERGREEN_CRTC5_REGISTER_OFFSET); + } + + if (rdev->irq.stat_regs.cik.d1grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.cik.d2grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VBLANK_INTERRUPT) WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK); if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VLINE_INTERRUPT) @@ -7095,6 +7222,12 @@ static inline void cik_irq_ack(struct radeon_device *rdev) WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK); if (rdev->num_crtc >= 4) { + if (rdev->irq.stat_regs.cik.d3grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.cik.d4grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK); if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) @@ -7106,6 +7239,12 @@ static inline void cik_irq_ack(struct radeon_device *rdev) } if (rdev->num_crtc >= 6) { + if (rdev->irq.stat_regs.cik.d5grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.cik.d6grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_CLEAR); if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK); if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) @@ -7457,6 +7596,15 @@ restart_ih: break; } break; + case 8: /* D1 page flip */ + case 10: /* D2 page flip */ + case 12: /* D3 page flip */ + case 14: /* D4 page flip */ + case 16: /* D5 page flip */ + case 18: /* D6 page flip */ + DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index f7e46cf..72e464c 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -562,6 +562,7 @@ int cik_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 2138732..dd79263 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -888,6 +888,15 @@ # define DC_HPD6_RX_INTERRUPT (1 << 18) #define DISP_INTERRUPT_STATUS_CONTINUE6 0x6780 +/* 0x6858, 0x7458, 0x10058, 0x10c58, 0x11858, 0x12458 */ +#define GRPH_INT_STATUS 0x6858 +# define GRPH_PFLIP_INT_OCCURRED (1 << 0) +# define GRPH_PFLIP_INT_CLEAR (1 << 8) +/* 0x685c, 0x745c, 0x1005c, 0x10c5c, 0x1185c, 0x1245c */ +#define GRPH_INT_CONTROL 0x685c +# define GRPH_PFLIP_INT_MASK (1 << 0) +# define GRPH_PFLIP_INT_TYPE (1 << 8) + #define DAC_AUTODETECT_INT_CONTROL 0x67c8 #define DC_HPD1_INT_STATUS 0x601c diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index b406546..0f7a51a 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4371,7 +4371,6 @@ int evergreen_irq_set(struct radeon_device *rdev) u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; u32 grbm_int_cntl = 0; - u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; u32 afmt1 = 0, afmt2 = 0, afmt3 = 0, afmt4 = 0, afmt5 = 0, afmt6 = 0; u32 dma_cntl, dma_cntl1 = 0; u32 thermal_int = 0; @@ -4554,15 +4553,21 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); } - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); if (rdev->num_crtc >= 4) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (rdev->num_crtc >= 6) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } WREG32(DC_HPD1_INT_CONTROL, hpd1); @@ -4951,6 +4956,15 @@ restart_ih: break; } break; + case 8: /* D1 page flip */ + case 10: /* D2 page flip */ + case 12: /* D3 page flip */ + case 14: /* D4 page flip */ + case 16: /* D5 page flip */ + case 18: /* D6 page flip */ + DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: diff --git a/drivers/gpu/drm/radeon/evergreen_dma.c b/drivers/gpu/drm/radeon/evergreen_dma.c index 287fe96..478caef 100644 --- a/drivers/gpu/drm/radeon/evergreen_dma.c +++ b/drivers/gpu/drm/radeon/evergreen_dma.c @@ -151,6 +151,7 @@ int evergreen_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c index 16ec9d5..3f6e817 100644 --- a/drivers/gpu/drm/radeon/kv_dpm.c +++ b/drivers/gpu/drm/radeon/kv_dpm.c @@ -546,6 +546,52 @@ static int kv_set_divider_value(struct radeon_device *rdev, return 0; } +static u32 kv_convert_vid2_to_vid7(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + u32 vid_2bit) +{ + struct radeon_clock_voltage_dependency_table *vddc_sclk_table = + &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk; + u32 i; + + if (vddc_sclk_table && vddc_sclk_table->count) { + if (vid_2bit < vddc_sclk_table->count) + return vddc_sclk_table->entries[vid_2bit].v; + else + return vddc_sclk_table->entries[vddc_sclk_table->count - 1].v; + } else { + for (i = 0; i < vid_mapping_table->num_entries; i++) { + if (vid_mapping_table->entries[i].vid_2bit == vid_2bit) + return vid_mapping_table->entries[i].vid_7bit; + } + return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_7bit; + } +} + +static u32 kv_convert_vid7_to_vid2(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + u32 vid_7bit) +{ + struct radeon_clock_voltage_dependency_table *vddc_sclk_table = + &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk; + u32 i; + + if (vddc_sclk_table && vddc_sclk_table->count) { + for (i = 0; i < vddc_sclk_table->count; i++) { + if (vddc_sclk_table->entries[i].v == vid_7bit) + return i; + } + return vddc_sclk_table->count - 1; + } else { + for (i = 0; i < vid_mapping_table->num_entries; i++) { + if (vid_mapping_table->entries[i].vid_7bit == vid_7bit) + return vid_mapping_table->entries[i].vid_2bit; + } + + return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_2bit; + } +} + static u16 kv_convert_8bit_index_to_voltage(struct radeon_device *rdev, u16 voltage) { @@ -556,9 +602,9 @@ static u16 kv_convert_2bit_index_to_voltage(struct radeon_device *rdev, u32 vid_2bit) { struct kv_power_info *pi = kv_get_pi(rdev); - u32 vid_8bit = sumo_convert_vid2_to_vid7(rdev, - &pi->sys_info.vid_mapping_table, - vid_2bit); + u32 vid_8bit = kv_convert_vid2_to_vid7(rdev, + &pi->sys_info.vid_mapping_table, + vid_2bit); return kv_convert_8bit_index_to_voltage(rdev, (u16)vid_8bit); } @@ -639,7 +685,7 @@ static int kv_force_lowest_valid(struct radeon_device *rdev) static int kv_unforce_levels(struct radeon_device *rdev) { - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return kv_notify_message_to_smu(rdev, PPSMC_MSG_NoForcedLevel); else return kv_set_enabled_levels(rdev); @@ -1362,13 +1408,20 @@ static int kv_update_uvd_dpm(struct radeon_device *rdev, bool gate) struct radeon_uvd_clock_voltage_dependency_table *table = &rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; int ret; + u32 mask; if (!gate) { - if (!pi->caps_uvd_dpm || table->count || pi->caps_stable_p_state) + if (table->count) pi->uvd_boot_level = table->count - 1; else pi->uvd_boot_level = 0; + if (!pi->caps_uvd_dpm || pi->caps_stable_p_state) { + mask = 1 << pi->uvd_boot_level; + } else { + mask = 0x1f; + } + ret = kv_copy_bytes_to_smc(rdev, pi->dpm_table_start + offsetof(SMU7_Fusion_DpmTable, UvdBootLevel), @@ -1377,11 +1430,9 @@ static int kv_update_uvd_dpm(struct radeon_device *rdev, bool gate) if (ret) return ret; - if (!pi->caps_uvd_dpm || - pi->caps_stable_p_state) - kv_send_msg_to_smc_with_parameter(rdev, - PPSMC_MSG_UVDDPM_SetEnabledMask, - (1 << pi->uvd_boot_level)); + kv_send_msg_to_smc_with_parameter(rdev, + PPSMC_MSG_UVDDPM_SetEnabledMask, + mask); } return kv_enable_uvd_dpm(rdev, !gate); @@ -1617,7 +1668,7 @@ static void kv_dpm_powergate_acp(struct radeon_device *rdev, bool gate) if (pi->acp_power_gated == gate) return; - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return; pi->acp_power_gated = gate; @@ -1786,7 +1837,7 @@ int kv_dpm_set_power_state(struct radeon_device *rdev) } } - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { if (pi->enable_dpm) { kv_set_valid_clock_range(rdev, new_ps); kv_update_dfs_bypass_settings(rdev, new_ps); @@ -1812,6 +1863,8 @@ int kv_dpm_set_power_state(struct radeon_device *rdev) return ret; } kv_update_sclk_t(rdev); + if (rdev->family == CHIP_MULLINS) + kv_enable_nb_dpm(rdev); } } else { if (pi->enable_dpm) { @@ -1862,7 +1915,7 @@ void kv_dpm_reset_asic(struct radeon_device *rdev) { struct kv_power_info *pi = kv_get_pi(rdev); - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { kv_force_lowest_valid(rdev); kv_init_graphics_levels(rdev); kv_program_bootup_state(rdev); @@ -1901,14 +1954,41 @@ static void kv_construct_max_power_limits_table(struct radeon_device *rdev, static void kv_patch_voltage_values(struct radeon_device *rdev) { int i; - struct radeon_uvd_clock_voltage_dependency_table *table = + struct radeon_uvd_clock_voltage_dependency_table *uvd_table = &rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; + struct radeon_vce_clock_voltage_dependency_table *vce_table = + &rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; + struct radeon_clock_voltage_dependency_table *samu_table = + &rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table; + struct radeon_clock_voltage_dependency_table *acp_table = + &rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table; - if (table->count) { - for (i = 0; i < table->count; i++) - table->entries[i].v = + if (uvd_table->count) { + for (i = 0; i < uvd_table->count; i++) + uvd_table->entries[i].v = kv_convert_8bit_index_to_voltage(rdev, - table->entries[i].v); + uvd_table->entries[i].v); + } + + if (vce_table->count) { + for (i = 0; i < vce_table->count; i++) + vce_table->entries[i].v = + kv_convert_8bit_index_to_voltage(rdev, + vce_table->entries[i].v); + } + + if (samu_table->count) { + for (i = 0; i < samu_table->count; i++) + samu_table->entries[i].v = + kv_convert_8bit_index_to_voltage(rdev, + samu_table->entries[i].v); + } + + if (acp_table->count) { + for (i = 0; i < acp_table->count; i++) + acp_table->entries[i].v = + kv_convert_8bit_index_to_voltage(rdev, + acp_table->entries[i].v); } } @@ -1941,7 +2021,7 @@ static int kv_force_dpm_highest(struct radeon_device *rdev) break; } - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return kv_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_DPM_ForceState, i); else return kv_set_enabled_level(rdev, i); @@ -1961,7 +2041,7 @@ static int kv_force_dpm_lowest(struct radeon_device *rdev) break; } - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return kv_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_DPM_ForceState, i); else return kv_set_enabled_level(rdev, i); @@ -2118,7 +2198,7 @@ static void kv_apply_state_adjust_rules(struct radeon_device *rdev, else pi->battery_state = false; - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { ps->dpm0_pg_nb_ps_lo = 0x1; ps->dpm0_pg_nb_ps_hi = 0x0; ps->dpmx_nb_ps_lo = 0x1; @@ -2179,7 +2259,7 @@ static int kv_calculate_nbps_level_settings(struct radeon_device *rdev) if (pi->lowest_valid > pi->highest_valid) return -EINVAL; - if (rdev->family == CHIP_KABINI) { + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) { for (i = pi->lowest_valid; i <= pi->highest_valid; i++) { pi->graphics_level[i].GnbSlow = 1; pi->graphics_level[i].ForceNbPs1 = 0; @@ -2253,9 +2333,9 @@ static void kv_init_graphics_levels(struct radeon_device *rdev) break; kv_set_divider_value(rdev, i, table->entries[i].clk); - vid_2bit = sumo_convert_vid7_to_vid2(rdev, - &pi->sys_info.vid_mapping_table, - table->entries[i].v); + vid_2bit = kv_convert_vid7_to_vid2(rdev, + &pi->sys_info.vid_mapping_table, + table->entries[i].v); kv_set_vid(rdev, i, vid_2bit); kv_set_at(rdev, i, pi->at[i]); kv_dpm_power_level_enabled_for_throttle(rdev, i, true); @@ -2324,7 +2404,7 @@ static void kv_program_nbps_index_settings(struct radeon_device *rdev, struct kv_power_info *pi = kv_get_pi(rdev); u32 nbdpmconfig1; - if (rdev->family == CHIP_KABINI) + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) return; if (pi->sys_info.nb_dpm_enable) { @@ -2631,9 +2711,6 @@ int kv_dpm_init(struct radeon_device *rdev) pi->sram_end = SMC_RAM_END; - if (rdev->family == CHIP_KABINI) - pi->high_voltage_t = 4001; - pi->enable_nb_dpm = true; pi->caps_power_containment = true; diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 6e887d0..bbc189f 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2839,6 +2839,7 @@ int r600_copy_cpdma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } @@ -3505,7 +3506,6 @@ int r600_irq_set(struct radeon_device *rdev) u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 grbm_int_cntl = 0; u32 hdmi0, hdmi1; - u32 d1grph = 0, d2grph = 0; u32 dma_cntl; u32 thermal_int = 0; @@ -3614,8 +3614,8 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(CP_INT_CNTL, cp_int_cntl); WREG32(DMA_CNTL, dma_cntl); WREG32(DxMODE_INT_MASK, mode_int); - WREG32(D1GRPH_INTERRUPT_CONTROL, d1grph); - WREG32(D2GRPH_INTERRUPT_CONTROL, d2grph); + WREG32(D1GRPH_INTERRUPT_CONTROL, DxGRPH_PFLIP_INT_MASK); + WREG32(D2GRPH_INTERRUPT_CONTROL, DxGRPH_PFLIP_INT_MASK); WREG32(GRBM_INT_CNTL, grbm_int_cntl); if (ASIC_IS_DCE3(rdev)) { WREG32(DC_HPD1_INT_CONTROL, hpd1); @@ -3918,6 +3918,14 @@ restart_ih: break; } break; + case 9: /* D1 pflip */ + DRM_DEBUG("IH: D1 flip\n"); + radeon_crtc_handle_flip(rdev, 0); + break; + case 11: /* D2 pflip */ + DRM_DEBUG("IH: D2 flip\n"); + radeon_crtc_handle_flip(rdev, 1); + break; case 19: /* HPD/DAC hotplug */ switch (src_data) { case 0: diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c index 53fcb28..4969cef 100644 --- a/drivers/gpu/drm/radeon/r600_dma.c +++ b/drivers/gpu/drm/radeon/r600_dma.c @@ -489,6 +489,7 @@ int r600_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b58e1af..8149e7c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -730,6 +730,12 @@ struct cik_irq_stat_regs { u32 disp_int_cont4; u32 disp_int_cont5; u32 disp_int_cont6; + u32 d1grph_int; + u32 d2grph_int; + u32 d3grph_int; + u32 d4grph_int; + u32 d5grph_int; + u32 d6grph_int; }; union radeon_irq_stat_regs { @@ -1636,6 +1642,7 @@ struct radeon_vce { unsigned fb_version; atomic_t handles[RADEON_MAX_VCE_HANDLES]; struct drm_file *filp[RADEON_MAX_VCE_HANDLES]; + unsigned img_size[RADEON_MAX_VCE_HANDLES]; struct delayed_work idle_work; }; @@ -1649,7 +1656,7 @@ int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring, uint32_t handle, struct radeon_fence **fence); void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp); void radeon_vce_note_usage(struct radeon_device *rdev); -int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi); +int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi, unsigned size); int radeon_vce_cs_parse(struct radeon_cs_parser *p); bool radeon_vce_semaphore_emit(struct radeon_device *rdev, struct radeon_ring *ring, @@ -2634,7 +2641,8 @@ void r100_pll_errata_after_index(struct radeon_device *rdev); #define ASIC_IS_DCE8(rdev) ((rdev->family >= CHIP_BONAIRE)) #define ASIC_IS_DCE81(rdev) ((rdev->family == CHIP_KAVERI)) #define ASIC_IS_DCE82(rdev) ((rdev->family == CHIP_BONAIRE)) -#define ASIC_IS_DCE83(rdev) ((rdev->family == CHIP_KABINI)) +#define ASIC_IS_DCE83(rdev) ((rdev->family == CHIP_KABINI) || \ + (rdev->family == CHIP_MULLINS)) #define ASIC_IS_LOMBOK(rdev) ((rdev->ddev->pdev->device == 0x6849) || \ (rdev->ddev->pdev->device == 0x6850) || \ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index b8a24a7..be20e62 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2516,6 +2516,7 @@ int radeon_asic_init(struct radeon_device *rdev) break; case CHIP_KAVERI: case CHIP_KABINI: + case CHIP_MULLINS: rdev->asic = &kv_asic; /* set num crtcs */ if (rdev->family == CHIP_KAVERI) { diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index b3633d9..9ab3097 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -196,6 +196,20 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev) } } + if (!found) { + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { + dhandle = ACPI_HANDLE(&pdev->dev); + if (!dhandle) + continue; + + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); + if (!ACPI_FAILURE(status)) { + found = true; + break; + } + } + } + if (!found) return false; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 511fe26..0e770bb 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -99,6 +99,7 @@ static const char radeon_family_name[][16] = { "KAVERI", "KABINI", "HAWAII", + "MULLINS", "LAST", }; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 8d99d5e..f00dbbf 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -284,6 +284,10 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) u32 update_pending; int vpos, hpos; + /* can happen during initialization */ + if (radeon_crtc == NULL) + return; + spin_lock_irqsave(&rdev->ddev->event_lock, flags); work = radeon_crtc->unpin_work; if (work == NULL || @@ -826,14 +830,14 @@ static void avivo_reduce_ratio(unsigned *nom, unsigned *den, /* make sure nominator is large enough */ if (*nom < nom_min) { - tmp = (nom_min + *nom - 1) / *nom; + tmp = DIV_ROUND_UP(nom_min, *nom); *nom *= tmp; *den *= tmp; } /* make sure the denominator is large enough */ if (*den < den_min) { - tmp = (den_min + *den - 1) / *den; + tmp = DIV_ROUND_UP(den_min, *den); *nom *= tmp; *den *= tmp; } @@ -858,7 +862,7 @@ static void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, unsigned *fb_div, unsigned *ref_div) { /* limit reference * post divider to a maximum */ - ref_div_max = min(210 / post_div, ref_div_max); + ref_div_max = min(128 / post_div, ref_div_max); /* get matching reference and feedback divider */ *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max); @@ -993,6 +997,16 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, /* this also makes sure that the reference divider is large enough */ avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); + /* avoid high jitter with small fractional dividers */ + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) { + fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 50); + if (fb_div < fb_div_min) { + unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div); + fb_div *= tmp; + ref_div *= tmp; + } + } + /* and finally save the result */ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { *fb_div_p = fb_div / 10; diff --git a/drivers/gpu/drm/radeon/radeon_family.h b/drivers/gpu/drm/radeon/radeon_family.h index 9da5da4..4b7b87f 100644 --- a/drivers/gpu/drm/radeon/radeon_family.h +++ b/drivers/gpu/drm/radeon/radeon_family.h @@ -97,6 +97,7 @@ enum radeon_family { CHIP_KAVERI, CHIP_KABINI, CHIP_HAWAII, + CHIP_MULLINS, CHIP_LAST, }; diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 0cc47f1..eaaedba 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -577,28 +577,29 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) return r; } - r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); - if (r) { - radeon_vm_fini(rdev, &fpriv->vm); - kfree(fpriv); - return r; - } + if (rdev->accel_working) { + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); + if (r) { + radeon_vm_fini(rdev, &fpriv->vm); + kfree(fpriv); + return r; + } - /* map the ib pool buffer read only into - * virtual address space */ - bo_va = radeon_vm_bo_add(rdev, &fpriv->vm, - rdev->ring_tmp_bo.bo); - r = radeon_vm_bo_set_addr(rdev, bo_va, RADEON_VA_IB_OFFSET, - RADEON_VM_PAGE_READABLE | - RADEON_VM_PAGE_SNOOPED); + /* map the ib pool buffer read only into + * virtual address space */ + bo_va = radeon_vm_bo_add(rdev, &fpriv->vm, + rdev->ring_tmp_bo.bo); + r = radeon_vm_bo_set_addr(rdev, bo_va, RADEON_VA_IB_OFFSET, + RADEON_VM_PAGE_READABLE | + RADEON_VM_PAGE_SNOOPED); - radeon_bo_unreserve(rdev->ring_tmp_bo.bo); - if (r) { - radeon_vm_fini(rdev, &fpriv->vm); - kfree(fpriv); - return r; + radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + if (r) { + radeon_vm_fini(rdev, &fpriv->vm); + kfree(fpriv); + return r; + } } - file_priv->driver_priv = fpriv; } @@ -626,13 +627,15 @@ void radeon_driver_postclose_kms(struct drm_device *dev, struct radeon_bo_va *bo_va; int r; - r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); - if (!r) { - bo_va = radeon_vm_bo_find(&fpriv->vm, - rdev->ring_tmp_bo.bo); - if (bo_va) - radeon_vm_bo_rmv(rdev, bo_va); - radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + if (rdev->accel_working) { + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); + if (!r) { + bo_va = radeon_vm_bo_find(&fpriv->vm, + rdev->ring_tmp_bo.bo); + if (bo_va) + radeon_vm_bo_rmv(rdev, bo_va); + radeon_bo_unreserve(rdev->ring_tmp_bo.bo); + } } radeon_vm_fini(rdev, &fpriv->vm); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 19bec0d..4faa4d6 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -458,7 +458,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, * into account. We don't want to disallow buffer moves * completely. */ - if (current_domain != RADEON_GEM_DOMAIN_CPU && + if ((lobj->alt_domain & current_domain) != 0 && (domain & current_domain) == 0 && /* will be moved */ bytes_moved > bytes_moved_threshold) { /* don't move it */ @@ -699,22 +699,30 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) rbo = container_of(bo, struct radeon_bo, tbo); radeon_bo_check_tiling(rbo, 0, 0); rdev = rbo->rdev; - if (bo->mem.mem_type == TTM_PL_VRAM) { - size = bo->mem.num_pages << PAGE_SHIFT; - offset = bo->mem.start << PAGE_SHIFT; - if ((offset + size) > rdev->mc.visible_vram_size) { - /* hurrah the memory is not visible ! */ - radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); - rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; - r = ttm_bo_validate(bo, &rbo->placement, false, false); - if (unlikely(r != 0)) - return r; - offset = bo->mem.start << PAGE_SHIFT; - /* this should not happen */ - if ((offset + size) > rdev->mc.visible_vram_size) - return -EINVAL; - } + if (bo->mem.mem_type != TTM_PL_VRAM) + return 0; + + size = bo->mem.num_pages << PAGE_SHIFT; + offset = bo->mem.start << PAGE_SHIFT; + if ((offset + size) <= rdev->mc.visible_vram_size) + return 0; + + /* hurrah the memory is not visible ! */ + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); + rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; + r = ttm_bo_validate(bo, &rbo->placement, false, false); + if (unlikely(r == -ENOMEM)) { + radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT); + return ttm_bo_validate(bo, &rbo->placement, false, false); + } else if (unlikely(r != 0)) { + return r; } + + offset = bo->mem.start << PAGE_SHIFT; + /* this should never happen */ + if ((offset + size) > rdev->mc.visible_vram_size) + return -EINVAL; + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 6fac8ef..53d6e1b 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -361,6 +361,11 @@ static ssize_t radeon_set_pm_profile(struct device *dev, struct drm_device *ddev = dev_get_drvdata(dev); struct radeon_device *rdev = ddev->dev_private; + /* Can't set profile when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + mutex_lock(&rdev->pm.mutex); if (rdev->pm.pm_method == PM_METHOD_PROFILE) { if (strncmp("default", buf, strlen("default")) == 0) @@ -409,6 +414,13 @@ static ssize_t radeon_set_pm_method(struct device *dev, struct drm_device *ddev = dev_get_drvdata(dev); struct radeon_device *rdev = ddev->dev_private; + /* Can't set method when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { + count = -EINVAL; + goto fail; + } + /* we don't support the legacy modes with dpm */ if (rdev->pm.pm_method == PM_METHOD_DPM) { count = -EINVAL; @@ -446,6 +458,10 @@ static ssize_t radeon_get_dpm_state(struct device *dev, struct radeon_device *rdev = ddev->dev_private; enum radeon_pm_state_type pm = rdev->pm.dpm.user_state; + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return snprintf(buf, PAGE_SIZE, "off\n"); + return snprintf(buf, PAGE_SIZE, "%s\n", (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); @@ -459,6 +475,11 @@ static ssize_t radeon_set_dpm_state(struct device *dev, struct drm_device *ddev = dev_get_drvdata(dev); struct radeon_device *rdev = ddev->dev_private; + /* Can't set dpm state when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + mutex_lock(&rdev->pm.mutex); if (strncmp("battery", buf, strlen("battery")) == 0) rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; @@ -485,6 +506,10 @@ static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev, struct radeon_device *rdev = ddev->dev_private; enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return snprintf(buf, PAGE_SIZE, "off\n"); + return snprintf(buf, PAGE_SIZE, "%s\n", (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" : (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); @@ -500,6 +525,11 @@ static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, enum radeon_dpm_forced_level level; int ret = 0; + /* Can't force performance level when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + mutex_lock(&rdev->pm.mutex); if (strncmp("low", buf, strlen("low")) == 0) { level = RADEON_DPM_FORCED_LEVEL_LOW; @@ -538,8 +568,14 @@ static ssize_t radeon_hwmon_show_temp(struct device *dev, char *buf) { struct radeon_device *rdev = dev_get_drvdata(dev); + struct drm_device *ddev = rdev->ddev; int temp; + /* Can't get temperature when the card is off */ + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) + return -EINVAL; + if (rdev->asic->pm.get_temperature) temp = radeon_get_temperature(rdev); else @@ -1300,6 +1336,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_KABINI: case CHIP_KAVERI: case CHIP_HAWAII: + case CHIP_MULLINS: /* DPM requires the RLC, RV770+ dGPU requires SMC */ if (!rdev->rlc_fw) rdev->pm.pm_method = PM_METHOD_PROFILE; @@ -1613,8 +1650,12 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct radeon_device *rdev = dev->dev_private; + struct drm_device *ddev = rdev->ddev; - if (rdev->pm.dpm_enabled) { + if ((rdev->flags & RADEON_IS_PX) && + (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) { + seq_printf(m, "PX asic powered off\n"); + } else if (rdev->pm.dpm_enabled) { mutex_lock(&rdev->pm.mutex); if (rdev->asic->dpm.debugfs_print_current_performance_level) radeon_dpm_debugfs_print_current_performance_level(rdev, m); diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index 58d1293..4e7c326 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -52,6 +52,7 @@ #define BONAIRE_RLC_UCODE_SIZE 2048 #define KB_RLC_UCODE_SIZE 2560 #define KV_RLC_UCODE_SIZE 2560 +#define ML_RLC_UCODE_SIZE 2560 /* MC */ #define BTC_MC_UCODE_SIZE 6024 diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 5748bda..1b65ae2 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -99,6 +99,7 @@ int radeon_uvd_init(struct radeon_device *rdev) case CHIP_KABINI: case CHIP_KAVERI: case CHIP_HAWAII: + case CHIP_MULLINS: fw_name = FIRMWARE_BONAIRE; break; @@ -465,6 +466,10 @@ static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p, cmd = radeon_get_ib_value(p, p->idx) >> 1; if (cmd < 0x4) { + if (end <= start) { + DRM_ERROR("invalid reloc offset %X!\n", offset); + return -EINVAL; + } if ((end - start) < buf_sizes[cmd]) { DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, (unsigned)(end - start), buf_sizes[cmd]); diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c index ced53dd..3971d96 100644 --- a/drivers/gpu/drm/radeon/radeon_vce.c +++ b/drivers/gpu/drm/radeon/radeon_vce.c @@ -66,6 +66,7 @@ int radeon_vce_init(struct radeon_device *rdev) case CHIP_BONAIRE: case CHIP_KAVERI: case CHIP_KABINI: + case CHIP_MULLINS: fw_name = FIRMWARE_BONAIRE; break; @@ -442,13 +443,16 @@ int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring, * @p: parser context * @lo: address of lower dword * @hi: address of higher dword + * @size: size of checker for relocation buffer * * Patch relocation inside command stream with real buffer address */ -int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi) +int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi, + unsigned size) { struct radeon_cs_chunk *relocs_chunk; - uint64_t offset; + struct radeon_cs_reloc *reloc; + uint64_t start, end, offset; unsigned idx; relocs_chunk = &p->chunks[p->chunk_relocs_idx]; @@ -461,15 +465,60 @@ int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi) return -EINVAL; } - offset += p->relocs_ptr[(idx / 4)]->gpu_offset; + reloc = p->relocs_ptr[(idx / 4)]; + start = reloc->gpu_offset; + end = start + radeon_bo_size(reloc->robj); + start += offset; - p->ib.ptr[lo] = offset & 0xFFFFFFFF; - p->ib.ptr[hi] = offset >> 32; + p->ib.ptr[lo] = start & 0xFFFFFFFF; + p->ib.ptr[hi] = start >> 32; + + if (end <= start) { + DRM_ERROR("invalid reloc offset %llX!\n", offset); + return -EINVAL; + } + if ((end - start) < size) { + DRM_ERROR("buffer to small (%d / %d)!\n", + (unsigned)(end - start), size); + return -EINVAL; + } return 0; } /** + * radeon_vce_validate_handle - validate stream handle + * + * @p: parser context + * @handle: handle to validate + * + * Validates the handle and return the found session index or -EINVAL + * we we don't have another free session index. + */ +int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle) +{ + unsigned i; + + /* validate the handle */ + for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { + if (atomic_read(&p->rdev->vce.handles[i]) == handle) + return i; + } + + /* handle not found try to alloc a new one */ + for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { + if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { + p->rdev->vce.filp[i] = p->filp; + p->rdev->vce.img_size[i] = 0; + return i; + } + } + + DRM_ERROR("No more free VCE handles!\n"); + return -EINVAL; +} + +/** * radeon_vce_cs_parse - parse and validate the command stream * * @p: parser context @@ -477,8 +526,10 @@ int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi) */ int radeon_vce_cs_parse(struct radeon_cs_parser *p) { - uint32_t handle = 0; - bool destroy = false; + int session_idx = -1; + bool destroyed = false; + uint32_t tmp, handle = 0; + uint32_t *size = &tmp; int i, r; while (p->idx < p->chunks[p->chunk_ib_idx].length_dw) { @@ -490,13 +541,29 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } + if (destroyed) { + DRM_ERROR("No other command allowed after destroy!\n"); + return -EINVAL; + } + switch (cmd) { case 0x00000001: // session handle = radeon_get_ib_value(p, p->idx + 2); + session_idx = radeon_vce_validate_handle(p, handle); + if (session_idx < 0) + return session_idx; + size = &p->rdev->vce.img_size[session_idx]; break; case 0x00000002: // task info + break; + case 0x01000001: // create + *size = radeon_get_ib_value(p, p->idx + 8) * + radeon_get_ib_value(p, p->idx + 10) * + 8 * 3 / 2; + break; + case 0x04000001: // config extension case 0x04000002: // pic control case 0x04000005: // rate control @@ -505,23 +572,39 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p) break; case 0x03000001: // encode - r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9); + r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9, + *size); if (r) return r; - r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11); + r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11, + *size / 3); if (r) return r; break; case 0x02000001: // destroy - destroy = true; + destroyed = true; break; case 0x05000001: // context buffer + r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, + *size * 2); + if (r) + return r; + break; + case 0x05000004: // video bitstream buffer + tmp = radeon_get_ib_value(p, p->idx + 4); + r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, + tmp); + if (r) + return r; + break; + case 0x05000005: // feedback buffer - r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2); + r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, + 4096); if (r) return r; break; @@ -531,33 +614,21 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } + if (session_idx == -1) { + DRM_ERROR("no session command at start of IB\n"); + return -EINVAL; + } + p->idx += len / 4; } - if (destroy) { + if (destroyed) { /* IB contains a destroy msg, free the handle */ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0); - - return 0; - } - - /* create or encode, validate the handle */ - for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { - if (atomic_read(&p->rdev->vce.handles[i]) == handle) - return 0; } - /* handle not found try to alloc a new one */ - for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { - if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { - p->rdev->vce.filp[i] = p->filp; - return 0; - } - } - - DRM_ERROR("No more free VCE handles!\n"); - return -EINVAL; + return 0; } /** diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c index 2aae6ce..d9ab99f 100644 --- a/drivers/gpu/drm/radeon/radeon_vm.c +++ b/drivers/gpu/drm/radeon/radeon_vm.c @@ -595,7 +595,7 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev, ndw = 64; /* assume the worst case */ - ndw += vm->max_pde_used * 12; + ndw += vm->max_pde_used * 16; /* update too big for an IB */ if (ndw > 0xfffff) diff --git a/drivers/gpu/drm/radeon/rv770_dma.c b/drivers/gpu/drm/radeon/rv770_dma.c index aca8cbe..bbf2e07 100644 --- a/drivers/gpu/drm/radeon/rv770_dma.c +++ b/drivers/gpu/drm/radeon/rv770_dma.c @@ -86,6 +86,7 @@ int rv770_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index ac708e0..22a63c9 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -5780,7 +5780,6 @@ int si_irq_set(struct radeon_device *rdev) u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; u32 hpd1 = 0, hpd2 = 0, hpd3 = 0, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 grbm_int_cntl = 0; - u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; u32 dma_cntl, dma_cntl1; u32 thermal_int = 0; @@ -5919,16 +5918,22 @@ int si_irq_set(struct radeon_device *rdev) } if (rdev->num_crtc >= 2) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (rdev->num_crtc >= 4) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (rdev->num_crtc >= 6) { - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); - WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, + GRPH_PFLIP_INT_MASK); } if (!ASIC_IS_NODCE(rdev)) { @@ -6292,6 +6297,15 @@ restart_ih: break; } break; + case 8: /* D1 page flip */ + case 10: /* D2 page flip */ + case 12: /* D3 page flip */ + case 14: /* D4 page flip */ + case 16: /* D5 page flip */ + case 18: /* D6 page flip */ + DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c index cf0fdad..de0ca07 100644 --- a/drivers/gpu/drm/radeon/si_dma.c +++ b/drivers/gpu/drm/radeon/si_dma.c @@ -213,6 +213,7 @@ int si_copy_dma(struct radeon_device *rdev, r = radeon_fence_emit(rdev, fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); + radeon_semaphore_free(rdev, &sem, NULL); return r; } diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 683532f..7321283 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -107,8 +107,8 @@ #define SPLL_CHG_STATUS (1 << 1) #define SPLL_CNTL_MODE 0x618 #define SPLL_SW_DIR_CONTROL (1 << 0) -# define SPLL_REFCLK_SEL(x) ((x) << 8) -# define SPLL_REFCLK_SEL_MASK 0xFF00 +# define SPLL_REFCLK_SEL(x) ((x) << 26) +# define SPLL_REFCLK_SEL_MASK (3 << 26) #define CG_SPLL_SPREAD_SPECTRUM 0x620 #define SSEN (1 << 0) diff --git a/drivers/gpu/drm/radeon/uvd_v1_0.c b/drivers/gpu/drm/radeon/uvd_v1_0.c index 0a243f0..be42c81 100644 --- a/drivers/gpu/drm/radeon/uvd_v1_0.c +++ b/drivers/gpu/drm/radeon/uvd_v1_0.c @@ -83,7 +83,10 @@ int uvd_v1_0_init(struct radeon_device *rdev) int r; /* raise clocks while booting up the VCPU */ - radeon_set_uvd_clocks(rdev, 53300, 40000); + if (rdev->family < CHIP_RV740) + radeon_set_uvd_clocks(rdev, 10000, 10000); + else + radeon_set_uvd_clocks(rdev, 53300, 40000); r = uvd_v1_0_start(rdev); if (r) @@ -407,7 +410,10 @@ int uvd_v1_0_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) struct radeon_fence *fence = NULL; int r; - r = radeon_set_uvd_clocks(rdev, 53300, 40000); + if (rdev->family < CHIP_RV740) + r = radeon_set_uvd_clocks(rdev, 10000, 10000); + else + r = radeon_set_uvd_clocks(rdev, 53300, 40000); if (r) { DRM_ERROR("radeon: failed to raise UVD clocks (%d).\n", r); return r; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 10a2c08..da52279 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1253,7 +1253,8 @@ EXPORT_SYMBOL_GPL(hid_output_report); static int hid_report_len(struct hid_report *report) { - return ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; + /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ + return ((report->size - 1) >> 3) + 1 + (report->id > 0); } /* @@ -1266,7 +1267,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) * of implement() working on 8 byte chunks */ - int len = hid_report_len(report); + int len = hid_report_len(report) + 7; return kmalloc(len, flags); } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c8af720..34bb220 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -301,6 +301,9 @@ #define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 +#define USB_VENDOR_ID_ELITEGROUP 0x03fc +#define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8 + #define USB_VENDOR_ID_ELO 0x04E7 #define USB_DEVICE_ID_ELO_TS2515 0x0022 #define USB_DEVICE_ID_ELO_TS2700 0x0020 @@ -834,6 +837,10 @@ #define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 #define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 +#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 + +#define USB_VENDOR_ID_TEXAS_INSTRUMENTS 0x2047 +#define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA 0x0855 #define USB_VENDOR_ID_THINGM 0x27b8 #define USB_DEVICE_ID_BLINK1 0x01ed diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 35278e4..51e25b9 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1155,6 +1155,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, + /* Elitegroup panel */ + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_ELITEGROUP, + USB_DEVICE_ID_ELITEGROUP_05D8) }, + /* Flatfrog Panels */ { .driver_data = MT_CLS_FLATFROG, MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG, diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index af8244b..be14b56 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -708,6 +708,9 @@ static const struct hid_device_id sensor_hub_devices[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, USB_DEVICE_ID_STM_HID_SENSOR), .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS, + USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, { } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index dbd8387..8e4ddb3 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -119,6 +119,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS }, { 0, 0 } }; diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index 90ec117..01723f0 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -163,7 +163,7 @@ static ssize_t store_hyst(struct device *dev, if (retval < 0) goto fail; - hyst = val - retval * 1000; + hyst = retval * 1000 - val; hyst = DIV_ROUND_CLOSEST(hyst, 1000); if (hyst < 0 || hyst > 255) { retval = -ERANGE; @@ -330,7 +330,7 @@ static int emc1403_detect(struct i2c_client *client, } id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG); - if (id != 0x01) + if (id < 0x01 || id > 0x04) return -ENODEV; return 0; @@ -355,9 +355,9 @@ static int emc1403_probe(struct i2c_client *client, if (id->driver_data) data->groups[1] = &emc1404_group; - hwmon_dev = hwmon_device_register_with_groups(&client->dev, - client->name, data, - data->groups); + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 22e92c3..3c20e4b 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -422,6 +422,9 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) */ dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR); + /* enforce disabled interrupts (due to HW issues) */ + i2c_dw_disable_int(dev); + /* Enable the adapter */ __i2c_dw_enable(dev, true); diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 28cbe1b..32c85e9 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -999,7 +999,7 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) dev->virtbase = devm_ioremap(&adev->dev, adev->res.start, resource_size(&adev->res)); - if (IS_ERR(dev->virtbase)) { + if (!dev->virtbase) { ret = -ENOMEM; goto err_no_mem; } diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 1b4cf14..2a5efb5 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -479,7 +479,7 @@ static int qup_i2c_xfer(struct i2c_adapter *adap, int ret, idx; ret = pm_runtime_get_sync(qup->dev); - if (ret) + if (ret < 0) goto out; writel(1, qup->base + QUP_SW_RESET); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index d4fa8eb..06d47aa 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -561,6 +561,12 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, ret = -EINVAL; for (i = 0; i < num; i++) { + /* This HW can't send STOP after address phase */ + if (msgs[i].len == 0) { + ret = -EOPNOTSUPP; + break; + } + /*-------------- spin lock -----------------*/ spin_lock_irqsave(&priv->lock, flags); @@ -625,7 +631,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, static u32 rcar_i2c_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + /* This HW can't do SMBUS_QUICK and NOSTART */ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); } static const struct i2c_algorithm rcar_i2c_algo = { diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index ae44910..bb3a996 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1276,10 +1276,10 @@ static int s3c24xx_i2c_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); - i2c->suspended = 0; clk_prepare_enable(i2c->clk); s3c24xx_i2c_init(i2c); clk_disable_unprepare(i2c->clk); + i2c->suspended = 0; return 0; } diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 1b6dbe15..199c789 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -48,6 +48,7 @@ #include <linux/mlx4/driver.h> #include <linux/mlx4/cmd.h> +#include <linux/mlx4/qp.h> #include "mlx4_ib.h" #include "user.h" @@ -1614,6 +1615,53 @@ static int mlx4_ib_inet6_event(struct notifier_block *this, unsigned long event, } #endif +#define MLX4_IB_INVALID_MAC ((u64)-1) +static void mlx4_ib_update_qps(struct mlx4_ib_dev *ibdev, + struct net_device *dev, + int port) +{ + u64 new_smac = 0; + u64 release_mac = MLX4_IB_INVALID_MAC; + struct mlx4_ib_qp *qp; + + read_lock(&dev_base_lock); + new_smac = mlx4_mac_to_u64(dev->dev_addr); + read_unlock(&dev_base_lock); + + mutex_lock(&ibdev->qp1_proxy_lock[port - 1]); + qp = ibdev->qp1_proxy[port - 1]; + if (qp) { + int new_smac_index; + u64 old_smac = qp->pri.smac; + struct mlx4_update_qp_params update_params; + + if (new_smac == old_smac) + goto unlock; + + new_smac_index = mlx4_register_mac(ibdev->dev, port, new_smac); + + if (new_smac_index < 0) + goto unlock; + + update_params.smac_index = new_smac_index; + if (mlx4_update_qp(ibdev->dev, &qp->mqp, MLX4_UPDATE_QP_SMAC, + &update_params)) { + release_mac = new_smac; + goto unlock; + } + + qp->pri.smac = new_smac; + qp->pri.smac_index = new_smac_index; + + release_mac = old_smac; + } + +unlock: + mutex_unlock(&ibdev->qp1_proxy_lock[port - 1]); + if (release_mac != MLX4_IB_INVALID_MAC) + mlx4_unregister_mac(ibdev->dev, port, release_mac); +} + static void mlx4_ib_get_dev_addr(struct net_device *dev, struct mlx4_ib_dev *ibdev, u8 port) { @@ -1689,9 +1737,13 @@ static int mlx4_ib_init_gid_table(struct mlx4_ib_dev *ibdev) return 0; } -static void mlx4_ib_scan_netdevs(struct mlx4_ib_dev *ibdev) +static void mlx4_ib_scan_netdevs(struct mlx4_ib_dev *ibdev, + struct net_device *dev, + unsigned long event) + { struct mlx4_ib_iboe *iboe; + int update_qps_port = -1; int port; iboe = &ibdev->iboe; @@ -1719,6 +1771,11 @@ static void mlx4_ib_scan_netdevs(struct mlx4_ib_dev *ibdev) } curr_master = iboe->masters[port - 1]; + if (dev == iboe->netdevs[port - 1] && + (event == NETDEV_CHANGEADDR || event == NETDEV_REGISTER || + event == NETDEV_UP || event == NETDEV_CHANGE)) + update_qps_port = port; + if (curr_netdev) { port_state = (netif_running(curr_netdev) && netif_carrier_ok(curr_netdev)) ? IB_PORT_ACTIVE : IB_PORT_DOWN; @@ -1752,6 +1809,9 @@ static void mlx4_ib_scan_netdevs(struct mlx4_ib_dev *ibdev) } spin_unlock(&iboe->lock); + + if (update_qps_port > 0) + mlx4_ib_update_qps(ibdev, dev, update_qps_port); } static int mlx4_ib_netdev_event(struct notifier_block *this, @@ -1764,7 +1824,7 @@ static int mlx4_ib_netdev_event(struct notifier_block *this, return NOTIFY_DONE; ibdev = container_of(this, struct mlx4_ib_dev, iboe.nb); - mlx4_ib_scan_netdevs(ibdev); + mlx4_ib_scan_netdevs(ibdev, dev, event); return NOTIFY_DONE; } @@ -2043,6 +2103,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) goto err_map; for (i = 0; i < ibdev->num_ports; ++i) { + mutex_init(&ibdev->qp1_proxy_lock[i]); if (mlx4_ib_port_link_layer(&ibdev->ib_dev, i + 1) == IB_LINK_LAYER_ETHERNET) { err = mlx4_counter_alloc(ibdev->dev, &ibdev->counters[i]); @@ -2126,7 +2187,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) for (i = 1 ; i <= ibdev->num_ports ; ++i) reset_gid_table(ibdev, i); rtnl_lock(); - mlx4_ib_scan_netdevs(ibdev); + mlx4_ib_scan_netdevs(ibdev, NULL, 0); rtnl_unlock(); mlx4_ib_init_gid_table(ibdev); } diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index f589522..66b0b7d 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -522,6 +522,9 @@ struct mlx4_ib_dev { int steer_qpn_count; int steer_qpn_base; int steering_support; + struct mlx4_ib_qp *qp1_proxy[MLX4_MAX_PORTS]; + /* lock when destroying qp1_proxy and getting netdev events */ + struct mutex qp1_proxy_lock[MLX4_MAX_PORTS]; }; struct ib_event_work { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 41308af..dc57482 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1132,6 +1132,12 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp) if (is_qp0(dev, mqp)) mlx4_CLOSE_PORT(dev->dev, mqp->port); + if (dev->qp1_proxy[mqp->port - 1] == mqp) { + mutex_lock(&dev->qp1_proxy_lock[mqp->port - 1]); + dev->qp1_proxy[mqp->port - 1] = NULL; + mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]); + } + pd = get_pd(mqp); destroy_qp_common(dev, mqp, !!pd->ibpd.uobject); @@ -1646,6 +1652,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, err = handle_eth_ud_smac_index(dev, qp, (u8 *)attr->smac, context); if (err) return -EINVAL; + if (qp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI) + dev->qp1_proxy[qp->port - 1] = qp; } } } diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index c98fdb1..a171046 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -28,6 +28,7 @@ #include <target/target_core_base.h> #include <target/target_core_fabric.h> #include <target/iscsi/iscsi_transport.h> +#include <linux/semaphore.h> #include "isert_proto.h" #include "ib_isert.h" @@ -561,7 +562,15 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) struct isert_device *device; struct ib_device *ib_dev = cma_id->device; int ret = 0; - u8 pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi; + u8 pi_support; + + spin_lock_bh(&np->np_thread_lock); + if (!np->enabled) { + spin_unlock_bh(&np->np_thread_lock); + pr_debug("iscsi_np is not enabled, reject connect request\n"); + return rdma_reject(cma_id, NULL, 0); + } + spin_unlock_bh(&np->np_thread_lock); pr_debug("Entering isert_connect_request cma_id: %p, context: %p\n", cma_id, cma_id->context); @@ -652,6 +661,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) goto out_mr; } + pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi; if (pi_support && !device->pi_capable) { pr_err("Protection information requested but not supported\n"); ret = -EINVAL; @@ -663,11 +673,11 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) goto out_conn_dev; mutex_lock(&isert_np->np_accept_mutex); - list_add_tail(&isert_np->np_accept_list, &isert_conn->conn_accept_node); + list_add_tail(&isert_conn->conn_accept_node, &isert_np->np_accept_list); mutex_unlock(&isert_np->np_accept_mutex); - pr_debug("isert_connect_request() waking up np_accept_wq: %p\n", np); - wake_up(&isert_np->np_accept_wq); + pr_debug("isert_connect_request() up np_sem np: %p\n", np); + up(&isert_np->np_sem); return 0; out_conn_dev: @@ -2999,7 +3009,7 @@ isert_setup_np(struct iscsi_np *np, pr_err("Unable to allocate struct isert_np\n"); return -ENOMEM; } - init_waitqueue_head(&isert_np->np_accept_wq); + sema_init(&isert_np->np_sem, 0); mutex_init(&isert_np->np_accept_mutex); INIT_LIST_HEAD(&isert_np->np_accept_list); init_completion(&isert_np->np_login_comp); @@ -3048,18 +3058,6 @@ out: } static int -isert_check_accept_queue(struct isert_np *isert_np) -{ - int empty; - - mutex_lock(&isert_np->np_accept_mutex); - empty = list_empty(&isert_np->np_accept_list); - mutex_unlock(&isert_np->np_accept_mutex); - - return empty; -} - -static int isert_rdma_accept(struct isert_conn *isert_conn) { struct rdma_cm_id *cm_id = isert_conn->conn_cm_id; @@ -3151,16 +3149,14 @@ isert_accept_np(struct iscsi_np *np, struct iscsi_conn *conn) int max_accept = 0, ret; accept_wait: - ret = wait_event_interruptible(isert_np->np_accept_wq, - !isert_check_accept_queue(isert_np) || - np->np_thread_state == ISCSI_NP_THREAD_RESET); + ret = down_interruptible(&isert_np->np_sem); if (max_accept > 5) return -ENODEV; spin_lock_bh(&np->np_thread_lock); if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { spin_unlock_bh(&np->np_thread_lock); - pr_err("ISCSI_NP_THREAD_RESET for isert_accept_np\n"); + pr_debug("ISCSI_NP_THREAD_RESET for isert_accept_np\n"); return -ENODEV; } spin_unlock_bh(&np->np_thread_lock); diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 4c072ae..da6612e 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -182,7 +182,7 @@ struct isert_device { }; struct isert_np { - wait_queue_head_t np_accept_wq; + struct semaphore np_sem; struct rdma_cm_id *np_cm_id; struct mutex np_accept_mutex; struct list_head np_accept_list; diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 2626773..2dd1d0d 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -243,6 +243,12 @@ static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); static void *atkbd_platform_fixup_data; static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int); +/* + * Certain keyboards to not like ATKBD_CMD_RESET_DIS and stop responding + * to many commands until full reset (ATKBD_CMD_RESET_BAT) is performed. + */ +static bool atkbd_skip_deactivate; + static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, ssize_t (*handler)(struct atkbd *, char *)); static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, @@ -768,7 +774,8 @@ static int atkbd_probe(struct atkbd *atkbd) * Make sure nothing is coming from the keyboard and disturbs our * internal state. */ - atkbd_deactivate(atkbd); + if (!atkbd_skip_deactivate) + atkbd_deactivate(atkbd); return 0; } @@ -1638,6 +1645,12 @@ static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) return 1; } +static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id) +{ + atkbd_skip_deactivate = true; + return 1; +} + static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { .matches = { @@ -1775,6 +1788,20 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { .callback = atkbd_setup_scancode_fixup, .driver_data = atkbd_oqo_01plus_scancode_fixup, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + DMI_MATCH(DMI_PRODUCT_NAME, "LW25-B7HV"), + }, + .callback = atkbd_deactivate_fixup, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + DMI_MATCH(DMI_PRODUCT_NAME, "P1-J273B"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 55c1530..4e491c1 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -392,6 +392,13 @@ static const struct of_device_id tca8418_dt_ids[] = { { } }; MODULE_DEVICE_TABLE(of, tca8418_dt_ids); + +/* + * The device tree based i2c loader looks for + * "i2c:" + second_component_of(property("compatible")) + * and therefore we need an alias to be found. + */ +MODULE_ALIAS("i2c:tca8418"); #endif static struct i2c_driver tca8418_keypad_driver = { diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 52d3a9b..b36831c 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -70,6 +70,7 @@ #define BMA150_CFG_5_REG 0x11 #define BMA150_CHIP_ID 2 +#define BMA180_CHIP_ID 3 #define BMA150_CHIP_ID_REG BMA150_DATA_0_REG #define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG @@ -539,7 +540,7 @@ static int bma150_probe(struct i2c_client *client, } chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG); - if (chip_id != BMA150_CHIP_ID) { + if (chip_id != BMA150_CHIP_ID && chip_id != BMA180_CHIP_ID) { dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id); return -EINVAL; } @@ -643,6 +644,7 @@ static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); static const struct i2c_device_id bma150_id[] = { { "bma150", 0 }, + { "bma180", 0 }, { "smb380", 0 }, { "bma023", 0 }, { } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 088d354..b96e978 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -11,6 +11,7 @@ */ #include <linux/delay.h> +#include <linux/dmi.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/input.h> @@ -831,7 +832,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) break; case 3: - etd->reg_10 = 0x0b; + if (etd->set_hw_resolution) + etd->reg_10 = 0x0b; + else + etd->reg_10 = 0x03; + if (elantech_write_reg(psmouse, 0x10, etd->reg_10)) rc = -1; @@ -1331,6 +1336,22 @@ static int elantech_reconnect(struct psmouse *psmouse) } /* + * Some hw_version 3 models go into error state when we try to set bit 3 of r10 + */ +static const struct dmi_system_id no_hw_res_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + /* Gigabyte U2442 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "U2442"), + }, + }, +#endif + { } +}; + +/* * determine hardware version and set some properties according to it. */ static int elantech_set_properties(struct elantech_data *etd) @@ -1390,6 +1411,9 @@ static int elantech_set_properties(struct elantech_data *etd) */ etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000); + /* Enable real hardware resolution on hw_version 3 ? */ + etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table); + return 0; } diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 036a04a..9e0e2a1 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -130,6 +130,7 @@ struct elantech_data { bool jumpy_cursor; bool reports_pressure; bool crc_enabled; + bool set_hw_resolution; unsigned char hw_version; unsigned int fw_version; unsigned int single_finger_reports; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index ef9f491..d68d33f 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1566,6 +1566,14 @@ static const struct dmi_system_id min_max_dmi_table[] __initconst = { .driver_data = (int []){1232, 5710, 1156, 4696}, }, { + /* Lenovo ThinkPad Edge E431 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Edge E431"), + }, + .driver_data = (int []){1024, 5022, 2508, 4832}, + }, + { /* Lenovo ThinkPad T431s */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c949520..57068e8 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3999,7 +3999,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) iommu_flush_dte(iommu, devid); if (devid != alias) { irq_lookup_table[alias] = table; - set_dte_irq_entry(devid, table); + set_dte_irq_entry(alias, table); iommu_flush_dte(iommu, alias); } diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index b76c58d..0e08545 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -788,7 +788,7 @@ static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m) * per device. But we can enable the exclusion range per * device. This is done here */ - set_dev_entry_bit(m->devid, DEV_ENTRY_EX); + set_dev_entry_bit(devid, DEV_ENTRY_EX); iommu->exclusion_start = m->range_start; iommu->exclusion_length = m->range_length; } diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 5208828..203b2e6 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -504,8 +504,10 @@ static void do_fault(struct work_struct *work) write = !!(fault->flags & PPR_FAULT_WRITE); + down_read(&fault->state->mm->mmap_sem); npages = get_user_pages(fault->state->task, fault->state->mm, fault->address, 1, write, 0, &page, NULL); + up_read(&fault->state->mm->mmap_sem); if (npages == 1) { put_page(page); diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index a5da511..61ac632 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -634,7 +634,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) #ifdef CONFIG_IPPP_FILTER case PPPIOCSPASS: { - struct sock_fprog fprog; + struct sock_fprog_kern fprog; struct sock_filter *code; int err, len = get_filter(argp, &code); @@ -653,7 +653,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) } case PPPIOCSACTIVE: { - struct sock_fprog fprog; + struct sock_fprog_kern fprog; struct sock_filter *code; int err, len = get_filter(argp, &code); diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 2c0d2c2..9f454d7 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -287,11 +287,9 @@ l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask, p = frame; /* restart timer */ - if ((int)(hc->keep_tl.expires-jiffies) < 5 * HZ) { - del_timer(&hc->keep_tl); - hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ; - add_timer(&hc->keep_tl); - } else + if (time_before(hc->keep_tl.expires, jiffies + 5 * HZ)) + mod_timer(&hc->keep_tl, jiffies + L1OIP_KEEPALIVE * HZ); + else hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ; if (debug & DEBUG_L1OIP_MSG) @@ -621,11 +619,9 @@ multiframe: goto multiframe; /* restart timer */ - if ((int)(hc->timeout_tl.expires-jiffies) < 5 * HZ || !hc->timeout_on) { + if (time_before(hc->timeout_tl.expires, jiffies + 5 * HZ) || !hc->timeout_on) { hc->timeout_on = 1; - del_timer(&hc->timeout_tl); - hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ; - add_timer(&hc->timeout_tl); + mod_timer(&hc->timeout_tl, jiffies + L1OIP_TIMEOUT * HZ); } else /* only adjust timer */ hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 784695d..53b2132 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -19,7 +19,6 @@ #include <linux/crypto.h> #include <linux/workqueue.h> #include <linux/backing-dev.h> -#include <linux/percpu.h> #include <linux/atomic.h> #include <linux/scatterlist.h> #include <asm/page.h> @@ -43,6 +42,7 @@ struct convert_context { struct bvec_iter iter_out; sector_t cc_sector; atomic_t cc_pending; + struct ablkcipher_request *req; }; /* @@ -111,15 +111,7 @@ struct iv_tcw_private { enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID }; /* - * Duplicated per-CPU state for cipher. - */ -struct crypt_cpu { - struct ablkcipher_request *req; -}; - -/* - * The fields in here must be read only after initialization, - * changing state should be in crypt_cpu. + * The fields in here must be read only after initialization. */ struct crypt_config { struct dm_dev *dev; @@ -150,12 +142,6 @@ struct crypt_config { sector_t iv_offset; unsigned int iv_size; - /* - * Duplicated per cpu state. Access through - * per_cpu_ptr() only. - */ - struct crypt_cpu __percpu *cpu; - /* ESSIV: struct crypto_cipher *essiv_tfm */ void *iv_private; struct crypto_ablkcipher **tfms; @@ -192,11 +178,6 @@ static void clone_init(struct dm_crypt_io *, struct bio *); static void kcryptd_queue_crypt(struct dm_crypt_io *io); static u8 *iv_of_dmreq(struct crypt_config *cc, struct dm_crypt_request *dmreq); -static struct crypt_cpu *this_crypt_config(struct crypt_config *cc) -{ - return this_cpu_ptr(cc->cpu); -} - /* * Use this to access cipher attributes that are the same for each CPU. */ @@ -903,16 +884,15 @@ static void kcryptd_async_done(struct crypto_async_request *async_req, static void crypt_alloc_req(struct crypt_config *cc, struct convert_context *ctx) { - struct crypt_cpu *this_cc = this_crypt_config(cc); unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1); - if (!this_cc->req) - this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO); + if (!ctx->req) + ctx->req = mempool_alloc(cc->req_pool, GFP_NOIO); - ablkcipher_request_set_tfm(this_cc->req, cc->tfms[key_index]); - ablkcipher_request_set_callback(this_cc->req, + ablkcipher_request_set_tfm(ctx->req, cc->tfms[key_index]); + ablkcipher_request_set_callback(ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - kcryptd_async_done, dmreq_of_req(cc, this_cc->req)); + kcryptd_async_done, dmreq_of_req(cc, ctx->req)); } /* @@ -921,7 +901,6 @@ static void crypt_alloc_req(struct crypt_config *cc, static int crypt_convert(struct crypt_config *cc, struct convert_context *ctx) { - struct crypt_cpu *this_cc = this_crypt_config(cc); int r; atomic_set(&ctx->cc_pending, 1); @@ -932,7 +911,7 @@ static int crypt_convert(struct crypt_config *cc, atomic_inc(&ctx->cc_pending); - r = crypt_convert_block(cc, ctx, this_cc->req); + r = crypt_convert_block(cc, ctx, ctx->req); switch (r) { /* async */ @@ -941,7 +920,7 @@ static int crypt_convert(struct crypt_config *cc, reinit_completion(&ctx->restart); /* fall through*/ case -EINPROGRESS: - this_cc->req = NULL; + ctx->req = NULL; ctx->cc_sector++; continue; @@ -1040,6 +1019,7 @@ static struct dm_crypt_io *crypt_io_alloc(struct crypt_config *cc, io->sector = sector; io->error = 0; io->base_io = NULL; + io->ctx.req = NULL; atomic_set(&io->io_pending, 0); return io; @@ -1065,6 +1045,8 @@ static void crypt_dec_pending(struct dm_crypt_io *io) if (!atomic_dec_and_test(&io->io_pending)) return; + if (io->ctx.req) + mempool_free(io->ctx.req, cc->req_pool); mempool_free(io, cc->io_pool); if (likely(!base_io)) @@ -1492,8 +1474,6 @@ static int crypt_wipe_key(struct crypt_config *cc) static void crypt_dtr(struct dm_target *ti) { struct crypt_config *cc = ti->private; - struct crypt_cpu *cpu_cc; - int cpu; ti->private = NULL; @@ -1505,13 +1485,6 @@ static void crypt_dtr(struct dm_target *ti) if (cc->crypt_queue) destroy_workqueue(cc->crypt_queue); - if (cc->cpu) - for_each_possible_cpu(cpu) { - cpu_cc = per_cpu_ptr(cc->cpu, cpu); - if (cpu_cc->req) - mempool_free(cpu_cc->req, cc->req_pool); - } - crypt_free_tfms(cc); if (cc->bs) @@ -1530,9 +1503,6 @@ static void crypt_dtr(struct dm_target *ti) if (cc->dev) dm_put_device(ti, cc->dev); - if (cc->cpu) - free_percpu(cc->cpu); - kzfree(cc->cipher); kzfree(cc->cipher_string); @@ -1588,13 +1558,6 @@ static int crypt_ctr_cipher(struct dm_target *ti, if (tmp) DMWARN("Ignoring unexpected additional cipher options"); - cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)), - __alignof__(struct crypt_cpu)); - if (!cc->cpu) { - ti->error = "Cannot allocate per cpu state"; - goto bad_mem; - } - /* * For compatibility with the original dm-crypt mapping format, if * only the cipher name is supplied, use cbc-plain. diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index aa009e8..fa0f6cb 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1566,8 +1566,8 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd, } if (m->pg_init_required) __pg_init_all_paths(m); - spin_unlock_irqrestore(&m->lock, flags); dm_table_run_md_queue_async(m->ti->table); + spin_unlock_irqrestore(&m->lock, flags); } return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 13abade..2e71de8 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -27,6 +27,7 @@ #define MAPPING_POOL_SIZE 1024 #define PRISON_CELLS 1024 #define COMMIT_PERIOD HZ +#define NO_SPACE_TIMEOUT (HZ * 60) DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(snapshot_copy_throttle, "A percentage of time allocated for copy on write"); @@ -175,6 +176,7 @@ struct pool { struct workqueue_struct *wq; struct work_struct worker; struct delayed_work waker; + struct delayed_work no_space_timeout; unsigned long last_commit_jiffies; unsigned ref_count; @@ -935,7 +937,7 @@ static int commit(struct pool *pool) { int r; - if (get_pool_mode(pool) != PM_WRITE) + if (get_pool_mode(pool) >= PM_READ_ONLY) return -EINVAL; r = dm_pool_commit_metadata(pool->pmd); @@ -1590,6 +1592,20 @@ static void do_waker(struct work_struct *ws) queue_delayed_work(pool->wq, &pool->waker, COMMIT_PERIOD); } +/* + * We're holding onto IO to allow userland time to react. After the + * timeout either the pool will have been resized (and thus back in + * PM_WRITE mode), or we degrade to PM_READ_ONLY and start erroring IO. + */ +static void do_no_space_timeout(struct work_struct *ws) +{ + struct pool *pool = container_of(to_delayed_work(ws), struct pool, + no_space_timeout); + + if (get_pool_mode(pool) == PM_OUT_OF_DATA_SPACE && !pool->pf.error_if_no_space) + set_pool_mode(pool, PM_READ_ONLY); +} + /*----------------------------------------------------------------*/ struct noflush_work { @@ -1715,6 +1731,9 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) pool->process_discard = process_discard; pool->process_prepared_mapping = process_prepared_mapping; pool->process_prepared_discard = process_prepared_discard_passdown; + + if (!pool->pf.error_if_no_space) + queue_delayed_work(pool->wq, &pool->no_space_timeout, NO_SPACE_TIMEOUT); break; case PM_WRITE: @@ -2100,6 +2119,7 @@ static struct pool *pool_create(struct mapped_device *pool_md, INIT_WORK(&pool->worker, do_worker); INIT_DELAYED_WORK(&pool->waker, do_waker); + INIT_DELAYED_WORK(&pool->no_space_timeout, do_no_space_timeout); spin_lock_init(&pool->lock); bio_list_init(&pool->deferred_flush_bios); INIT_LIST_HEAD(&pool->prepared_mappings); @@ -2662,6 +2682,7 @@ static void pool_postsuspend(struct dm_target *ti) struct pool *pool = pt->pool; cancel_delayed_work(&pool->waker); + cancel_delayed_work(&pool->no_space_timeout); flush_workqueue(pool->wq); (void) commit(pool); } diff --git a/drivers/md/md.c b/drivers/md/md.c index 8fda38d..237b7e0 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8516,7 +8516,8 @@ static int md_notify_reboot(struct notifier_block *this, if (mddev_trylock(mddev)) { if (mddev->pers) __md_stop_writes(mddev); - mddev->safemode = 2; + if (mddev->persistent) + mddev->safemode = 2; mddev_unlock(mddev); } need_delay = 1; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 33fc408..cb882aa 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1172,6 +1172,13 @@ static void __make_request(struct mddev *mddev, struct bio *bio) int max_sectors; int sectors; + /* + * Register the new request and wait if the reconstruction + * thread has put up a bar for new requests. + * Continue immediately if no resync is active currently. + */ + wait_barrier(conf); + sectors = bio_sectors(bio); while (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && bio->bi_iter.bi_sector < conf->reshape_progress && @@ -1552,12 +1559,6 @@ static void make_request(struct mddev *mddev, struct bio *bio) md_write_start(mddev, bio); - /* - * Register the new request and wait if the reconstruction - * thread has put up a bar for new requests. - * Continue immediately if no resync is active currently. - */ - wait_barrier(conf); do { diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e8a1ce2..cdd7c1b 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1109,7 +1109,7 @@ static int ov7670_enum_framesizes(struct v4l2_subdev *sd, * windows that fall outside that. */ for (i = 0; i < n_win_sizes; i++) { - struct ov7670_win_size *win = &info->devtype->win_sizes[index]; + struct ov7670_win_size *win = &info->devtype->win_sizes[i]; if (info->min_width && win->width < info->min_width) continue; if (info->min_height && win->height < info->min_height) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index a445930..ee0f57e 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1616,7 +1616,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) if (ret < 0) return -EINVAL; - node_ep = v4l2_of_get_next_endpoint(node, NULL); + node_ep = of_graph_get_next_endpoint(node, NULL); if (!node_ep) { dev_warn(dev, "no endpoint defined for node: %s\n", node->full_name); diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index d5a7a13..703560f 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -93,6 +93,7 @@ static long media_device_enum_entities(struct media_device *mdev, struct media_entity *ent; struct media_entity_desc u_ent; + memset(&u_ent, 0, sizeof(u_ent)); if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id))) return -EFAULT; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index b4f12d0..6567082 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -372,18 +372,32 @@ static int vpbe_stop_streaming(struct vb2_queue *vq) { struct vpbe_fh *fh = vb2_get_drv_priv(vq); struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + unsigned long flags; if (!vb2_is_streaming(vq)) return 0; /* release all active buffers */ + spin_lock_irqsave(&disp->dma_queue_lock, flags); + if (layer->cur_frm == layer->next_frm) { + vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_ERROR); + } else { + if (layer->cur_frm != NULL) + vb2_buffer_done(&layer->cur_frm->vb, + VB2_BUF_STATE_ERROR); + if (layer->next_frm != NULL) + vb2_buffer_done(&layer->next_frm->vb, + VB2_BUF_STATE_ERROR); + } + while (!list_empty(&layer->dma_queue)) { layer->next_frm = list_entry(layer->dma_queue.next, struct vpbe_disp_buffer, list); list_del(&layer->next_frm->list); vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR); } - + spin_unlock_irqrestore(&disp->dma_queue_lock, flags); return 0; } diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index d762246..0379cb9 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -734,6 +734,8 @@ static int vpfe_release(struct file *file) } vpfe_dev->io_usrs = 0; vpfe_dev->numbuffers = config_params.numbuffers; + videobuf_stop(&vpfe_dev->buffer_queue); + videobuf_mmap_free(&vpfe_dev->buffer_queue); } /* Decrement device usrs counter */ diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 756da78..8dea0b8 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -358,8 +358,31 @@ static int vpif_stop_streaming(struct vb2_queue *vq) common = &ch->common[VPIF_VIDEO_INDEX]; + /* Disable channel as per its device type and channel id */ + if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { + enable_channel0(0); + channel0_intr_enable(0); + } + if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel1(0); + channel1_intr_enable(0); + } + common->started = 0; + /* release all active buffers */ spin_lock_irqsave(&common->irqlock, flags); + if (common->cur_frm == common->next_frm) { + vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR); + } else { + if (common->cur_frm != NULL) + vb2_buffer_done(&common->cur_frm->vb, + VB2_BUF_STATE_ERROR); + if (common->next_frm != NULL) + vb2_buffer_done(&common->next_frm->vb, + VB2_BUF_STATE_ERROR); + } + while (!list_empty(&common->dma_queue)) { common->next_frm = list_entry(common->dma_queue.next, struct vpif_cap_buffer, list); @@ -933,17 +956,6 @@ static int vpif_release(struct file *filep) if (fh->io_allowed[VPIF_VIDEO_INDEX]) { /* Reset io_usrs member of channel object */ common->io_usrs = 0; - /* Disable channel as per its device type and channel id */ - if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { - enable_channel0(0); - channel0_intr_enable(0); - } - if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || - (2 == common->started)) { - enable_channel1(0); - channel1_intr_enable(0); - } - common->started = 0; /* Free buffers allocated */ vb2_queue_release(&common->buffer_queue); vb2_dma_contig_cleanup_ctx(common->alloc_ctx); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 0ac841e..aed41ed 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -320,8 +320,31 @@ static int vpif_stop_streaming(struct vb2_queue *vq) common = &ch->common[VPIF_VIDEO_INDEX]; + /* Disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + common->started = 0; + /* release all active buffers */ spin_lock_irqsave(&common->irqlock, flags); + if (common->cur_frm == common->next_frm) { + vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR); + } else { + if (common->cur_frm != NULL) + vb2_buffer_done(&common->cur_frm->vb, + VB2_BUF_STATE_ERROR); + if (common->next_frm != NULL) + vb2_buffer_done(&common->next_frm->vb, + VB2_BUF_STATE_ERROR); + } + while (!list_empty(&common->dma_queue)) { common->next_frm = list_entry(common->dma_queue.next, struct vpif_disp_buffer, list); @@ -773,18 +796,6 @@ static int vpif_release(struct file *filep) if (fh->io_allowed[VPIF_VIDEO_INDEX]) { /* Reset io_usrs member of channel object */ common->io_usrs = 0; - /* Disable channel */ - if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { - enable_channel2(0); - channel2_intr_enable(0); - } - if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || - (2 == common->started)) { - enable_channel3(0); - channel3_intr_enable(0); - } - common->started = 0; - /* Free buffers allocated */ vb2_queue_release(&common->buffer_queue); vb2_dma_contig_cleanup_ctx(common->alloc_ctx); diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index da2fc86..25dbf5b 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -122,7 +122,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "YUV 4:2:2 planar, Y/Cb/Cr", .fourcc = V4L2_PIX_FMT_YUV422P, - .depth = { 12 }, + .depth = { 16 }, .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 3, diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 3aecaf4..f0c9c42 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -195,7 +195,7 @@ static int fc2580_set_params(struct dvb_frontend *fe) f_ref = 2UL * priv->cfg->clock / r_val; n_val = div_u64_rem(f_vco, f_ref, &k_val); - k_val_reg = 1UL * k_val * (1 << 20) / f_ref; + k_val_reg = div_u64(1ULL * k_val * (1 << 20), f_ref); ret = fc2580_wr_reg(priv, 0x18, r18_val | ((k_val_reg >> 16) & 0xff)); if (ret < 0) @@ -348,8 +348,8 @@ static int fc2580_set_params(struct dvb_frontend *fe) if (ret < 0) goto err; - ret = fc2580_wr_reg(priv, 0x37, 1UL * priv->cfg->clock * \ - fc2580_if_filter_lut[i].mul / 1000000000); + ret = fc2580_wr_reg(priv, 0x37, div_u64(1ULL * priv->cfg->clock * + fc2580_if_filter_lut[i].mul, 1000000000)); if (ret < 0) goto err; diff --git a/drivers/media/tuners/fc2580_priv.h b/drivers/media/tuners/fc2580_priv.h index be38a9e..646c994 100644 --- a/drivers/media/tuners/fc2580_priv.h +++ b/drivers/media/tuners/fc2580_priv.h @@ -22,6 +22,7 @@ #define FC2580_PRIV_H #include "fc2580.h" +#include <linux/math64.h> struct fc2580_reg_val { u8 reg; diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index 7407b83..bc38f03 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -41,4 +41,3 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/common -ccflags-y += -I$(srctree)/drivers/staging/media/rtl2832u_sdr diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 61d196e..dcbd392 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -24,7 +24,6 @@ #include "rtl2830.h" #include "rtl2832.h" -#include "rtl2832_sdr.h" #include "qt1010.h" #include "mt2060.h" @@ -36,6 +35,45 @@ #include "tua9001.h" #include "r820t.h" +/* + * RTL2832_SDR module is in staging. That logic is added in order to avoid any + * hard dependency to drivers/staging/ directory as we want compile mainline + * driver even whole staging directory is missing. + */ +#include <media/v4l2-subdev.h> + +#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR) +struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct rtl2832_config *cfg, + struct v4l2_subdev *sd); +#else +static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, const struct rtl2832_config *cfg, + struct v4l2_subdev *sd) +{ + return NULL; +} +#endif + +#ifdef CONFIG_MEDIA_ATTACH +#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \ + void *__r = NULL; \ + typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ + if (__a) { \ + __r = (void *) __a(ARGS); \ + if (__r == NULL) \ + symbol_put(FUNCTION); \ + } \ + __r; \ +}) + +#else +#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \ + FUNCTION(ARGS); \ +}) + +#endif + static int rtl28xxu_disable_rc; module_param_named(disable_rc, rtl28xxu_disable_rc, int, 0644); MODULE_PARM_DESC(disable_rc, "disable RTL2832U remote controller"); @@ -908,7 +946,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) adap->fe[0]->ops.tuner_ops.get_rf_strength; /* attach SDR */ - dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, + dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, &rtl28xxu_rtl2832_fc0012_config, NULL); break; case TUNER_RTL2832_FC0013: @@ -920,7 +958,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) adap->fe[0]->ops.tuner_ops.get_rf_strength; /* attach SDR */ - dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, + dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, &rtl28xxu_rtl2832_fc0013_config, NULL); break; case TUNER_RTL2832_E4000: { @@ -951,7 +989,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) i2c_set_adapdata(i2c_adap_internal, d); /* attach SDR */ - dvb_attach(rtl2832_sdr_attach, adap->fe[0], + dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], i2c_adap_internal, &rtl28xxu_rtl2832_e4000_config, sd); } @@ -982,7 +1020,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) adap->fe[0]->ops.tuner_ops.get_rf_strength; /* attach SDR */ - dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, + dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, &rtl28xxu_rtl2832_r820t_config, NULL); break; case TUNER_RTL2832_R828D: diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 7277dbd..ecbcb39 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -1430,10 +1430,8 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, -#if !IS_ENABLED(CONFIG_USB_SN9C102) {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, -#endif {USB_DEVICE(0x0c45, 0x6027), SB(OV7630, 101)}, /* Genius Eye 310 */ {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 04b2daf..7e2411c 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -178,6 +178,9 @@ struct v4l2_create_buffers32 { static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { + if (get_user(kp->type, &up->type)) + return -EFAULT; + switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -204,17 +207,16 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || - get_user(kp->type, &up->type)) - return -EFAULT; + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32))) + return -EFAULT; return __get_v4l2_format32(kp, up); } static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) { if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) || - copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt))) - return -EFAULT; + copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format))) + return -EFAULT; return __get_v4l2_format32(&kp->format, &up->format); } diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index 110c036..b59a17f 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -108,8 +108,19 @@ static int devbus_set_timing_params(struct devbus *devbus, node->full_name); return err; } - /* Convert bit width to byte width */ - r.bus_width /= 8; + + /* + * The bus width is encoded into the register as 0 for 8 bits, + * and 1 for 16 bits, so we do the necessary conversion here. + */ + if (r.bus_width == 8) + r.bus_width = 0; + else if (r.bus_width == 16) + r.bus_width = 1; + else { + dev_err(devbus->dev, "invalid bus width %d\n", r.bus_width); + return -EINVAL; + } err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", &r.badr_skew); diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index c9de3d5..1d15735 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -338,28 +338,58 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, int num_sg, bool read, int timeout) { struct completion trans_done; - int err = 0, count; + u8 dir; + int err = 0, i, count; long timeleft; unsigned long flags; + struct scatterlist *sg; + enum dma_data_direction dma_dir; + u32 val; + dma_addr_t addr; + unsigned int len; + + dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg); + + /* don't transfer data during abort processing */ + if (pcr->remove_pci) + return -EINVAL; + + if ((sglist == NULL) || (num_sg <= 0)) + return -EINVAL; - count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read); + if (read) { + dir = DEVICE_TO_HOST; + dma_dir = DMA_FROM_DEVICE; + } else { + dir = HOST_TO_DEVICE; + dma_dir = DMA_TO_DEVICE; + } + + count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir); if (count < 1) { dev_err(&(pcr->pci->dev), "scatterlist map failed\n"); return -EINVAL; } dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count); + val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE; + pcr->sgi = 0; + for_each_sg(sglist, sg, count, i) { + addr = sg_dma_address(sg); + len = sg_dma_len(sg); + rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1); + } spin_lock_irqsave(&pcr->lock, flags); pcr->done = &trans_done; pcr->trans_result = TRANS_NOT_READY; init_completion(&trans_done); + rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, val); spin_unlock_irqrestore(&pcr->lock, flags); - rtsx_pci_dma_transfer(pcr, sglist, count, read); - timeleft = wait_for_completion_interruptible_timeout( &trans_done, msecs_to_jiffies(timeout)); if (timeleft <= 0) { @@ -383,7 +413,7 @@ out: pcr->done = NULL; spin_unlock_irqrestore(&pcr->lock, flags); - rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read); + dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir); if ((err < 0) && (err != -ENODEV)) rtsx_pci_stop_cmd(pcr); @@ -395,73 +425,6 @@ out: } EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data); -int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read) -{ - enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - if (pcr->remove_pci) - return -EINVAL; - - if ((sglist == NULL) || num_sg < 1) - return -EINVAL; - - return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir); -} -EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg); - -int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read) -{ - enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - if (pcr->remove_pci) - return -EINVAL; - - if (sglist == NULL || num_sg < 1) - return -EINVAL; - - dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir); - return num_sg; -} -EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg); - -int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int sg_count, bool read) -{ - struct scatterlist *sg; - dma_addr_t addr; - unsigned int len; - int i; - u32 val; - u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE; - unsigned long flags; - - if (pcr->remove_pci) - return -EINVAL; - - if ((sglist == NULL) || (sg_count < 1)) - return -EINVAL; - - val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE; - pcr->sgi = 0; - for_each_sg(sglist, sg, sg_count, i) { - addr = sg_dma_address(sg); - len = sg_dma_len(sg); - rtsx_pci_add_sg_tbl(pcr, addr, len, i == sg_count - 1); - } - - spin_lock_irqsave(&pcr->lock, flags); - - rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr); - rtsx_pci_writel(pcr, RTSX_HDBCTLR, val); - - spin_unlock_irqrestore(&pcr->lock, flags); - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer); - int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len) { int err; @@ -873,8 +836,6 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) int_reg = rtsx_pci_readl(pcr, RTSX_BIPR); /* Clear interrupt flag */ rtsx_pci_writel(pcr, RTSX_BIPR, int_reg); - dev_dbg(&pcr->pci->dev, "=========== BIPR 0x%8x ==========\n", int_reg); - if ((int_reg & pcr->bier) == 0) { spin_unlock(&pcr->lock); return IRQ_NONE; @@ -905,28 +866,17 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) } if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) { - if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) + if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) { pcr->trans_result = TRANS_RESULT_FAIL; - else if (int_reg & TRANS_OK_INT) + if (pcr->done) + complete(pcr->done); + } else if (int_reg & TRANS_OK_INT) { pcr->trans_result = TRANS_RESULT_OK; - - if (pcr->done) - complete(pcr->done); - - if (int_reg & SD_EXIST) { - struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; - if (slot && slot->done_transfer) - slot->done_transfer(slot->p_dev); - } - - if (int_reg & MS_EXIST) { - struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; - if (slot && slot->done_transfer) - slot->done_transfer(slot->p_dev); + if (pcr->done) + complete(pcr->done); } } - if (pcr->card_inserted || pcr->card_removed) schedule_delayed_work(&pcr->carddet_work, msecs_to_jiffies(200)); diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 0a87e56..cc8d4a6 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -448,7 +448,6 @@ mmc_spi_command_send(struct mmc_spi_host *host, { struct scratch *data = host->data; u8 *cp = data->status; - u32 arg = cmd->arg; int status; struct spi_transfer *t; @@ -465,14 +464,12 @@ mmc_spi_command_send(struct mmc_spi_host *host, * We init the whole buffer to all-ones, which is what we need * to write while we're reading (later) response data. */ - memset(cp++, 0xff, sizeof(data->status)); + memset(cp, 0xff, sizeof(data->status)); - *cp++ = 0x40 | cmd->opcode; - *cp++ = (u8)(arg >> 24); - *cp++ = (u8)(arg >> 16); - *cp++ = (u8)(arg >> 8); - *cp++ = (u8)arg; - *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01; + cp[1] = 0x40 | cmd->opcode; + put_unaligned_be32(cmd->arg, cp+2); + cp[6] = crc7_be(0, cp+1, 5) | 0x01; + cp += 7; /* Then, read up to 13 bytes (while writing all-ones): * - N(CR) (== 1..8) bytes of all-ones @@ -711,10 +708,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, * so we have to cope with this situation and check the response * bit-by-bit. Arggh!!! */ - pattern = scratch->status[0] << 24; - pattern |= scratch->status[1] << 16; - pattern |= scratch->status[2] << 8; - pattern |= scratch->status[3]; + pattern = get_unaligned_be32(scratch->status); /* First 3 bit of pattern are undefined */ pattern |= 0xE0000000; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 5fb994f..0b9ded1 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -31,28 +31,14 @@ #include <linux/mfd/rtsx_pci.h> #include <asm/unaligned.h> -struct realtek_next { - unsigned int sg_count; - s32 cookie; -}; - struct realtek_pci_sdmmc { struct platform_device *pdev; struct rtsx_pcr *pcr; struct mmc_host *mmc; struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - - spinlock_t lock; - struct timer_list timer; - struct tasklet_struct cmd_tasklet; - struct tasklet_struct data_tasklet; - struct tasklet_struct finish_tasklet; - - u8 rsp_type; - u8 rsp_len; - int sg_count; + + struct mutex host_mutex; + u8 ssc_depth; unsigned int clock; bool vpclk; @@ -62,13 +48,8 @@ struct realtek_pci_sdmmc { int power_state; #define SDMMC_POWER_ON 1 #define SDMMC_POWER_OFF 0 - - struct realtek_next next_data; }; -static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, - struct mmc_request *mrq); - static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) { return &(host->pdev->dev); @@ -105,95 +86,6 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) #define sd_print_debug_regs(host) #endif /* DEBUG */ -static void sd_isr_done_transfer(struct platform_device *pdev) -{ - struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - - spin_lock(&host->lock); - if (host->cmd) - tasklet_schedule(&host->cmd_tasklet); - if (host->data) - tasklet_schedule(&host->data_tasklet); - spin_unlock(&host->lock); -} - -static void sd_request_timeout(unsigned long host_addr) -{ - struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - - if (!host->mrq) { - dev_err(sdmmc_dev(host), "error: no request exist\n"); - goto out; - } - - if (host->cmd) - host->cmd->error = -ETIMEDOUT; - if (host->data) - host->data->error = -ETIMEDOUT; - - dev_dbg(sdmmc_dev(host), "timeout for request\n"); - -out: - tasklet_schedule(&host->finish_tasklet); - spin_unlock_irqrestore(&host->lock, flags); -} - -static void sd_finish_request(unsigned long host_addr) -{ - struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; - struct rtsx_pcr *pcr = host->pcr; - struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - unsigned long flags; - bool any_error; - - spin_lock_irqsave(&host->lock, flags); - - del_timer(&host->timer); - mrq = host->mrq; - if (!mrq) { - dev_err(sdmmc_dev(host), "error: no request need finish\n"); - goto out; - } - - cmd = mrq->cmd; - data = mrq->data; - - any_error = (mrq->sbc && mrq->sbc->error) || - (mrq->stop && mrq->stop->error) || - (cmd && cmd->error) || (data && data->error); - - if (any_error) { - rtsx_pci_stop_cmd(pcr); - sd_clear_error(host); - } - - if (data) { - if (any_error) - data->bytes_xfered = 0; - else - data->bytes_xfered = data->blocks * data->blksz; - - if (!data->host_cookie) - rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, - data->flags & MMC_DATA_READ); - - } - - host->mrq = NULL; - host->cmd = NULL; - host->data = NULL; - -out: - spin_unlock_irqrestore(&host->lock, flags); - mutex_unlock(&pcr->pcr_mutex); - mmc_request_done(host->mmc, mrq); -} - static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, u8 *buf, int buf_len, int timeout) { @@ -311,7 +203,8 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, return 0; } -static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) +static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd) { struct rtsx_pcr *pcr = host->pcr; u8 cmd_idx = (u8)cmd->opcode; @@ -319,14 +212,11 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) int err = 0; int timeout = 100; int i; + u8 *ptr; + int stat_idx = 0; u8 rsp_type; int rsp_len = 5; - unsigned long flags; - - if (host->cmd) - dev_err(sdmmc_dev(host), "error: cmd already exist\n"); - - host->cmd = cmd; + bool clock_toggled = false; dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", __func__, cmd_idx, arg); @@ -361,8 +251,6 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) err = -EINVAL; goto out; } - host->rsp_type = rsp_type; - host->rsp_len = rsp_len; if (rsp_type == SD_RSP_TYPE_R1b) timeout = 3000; @@ -372,6 +260,8 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) 0xFF, SD_CLK_TOGGLE_EN); if (err < 0) goto out; + + clock_toggled = true; } rtsx_pci_init_cmd(pcr); @@ -395,60 +285,25 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) /* Read data from ping-pong buffer */ for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); + stat_idx = 16; } else if (rsp_type != SD_RSP_TYPE_R0) { /* Read data from SD_CMDx registers */ for (i = SD_CMD0; i <= SD_CMD4; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); + stat_idx = 5; } rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); - mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout)); - - spin_lock_irqsave(&pcr->lock, flags); - pcr->trans_result = TRANS_NOT_READY; - rtsx_pci_send_cmd_no_wait(pcr); - spin_unlock_irqrestore(&pcr->lock, flags); - - return; - -out: - cmd->error = err; - tasklet_schedule(&host->finish_tasklet); -} - -static void sd_get_rsp(unsigned long host_addr) -{ - struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; - struct rtsx_pcr *pcr = host->pcr; - struct mmc_command *cmd; - int i, err = 0, stat_idx; - u8 *ptr, rsp_type; - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - - cmd = host->cmd; - host->cmd = NULL; - - if (!cmd) { - dev_err(sdmmc_dev(host), "error: cmd not exist\n"); + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + sd_clear_error(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd error (err = %d)\n", err); goto out; } - spin_lock(&pcr->lock); - if (pcr->trans_result == TRANS_NO_DEVICE) - err = -ENODEV; - else if (pcr->trans_result != TRANS_RESULT_OK) - err = -EINVAL; - spin_unlock(&pcr->lock); - - if (err < 0) - goto out; - - rsp_type = host->rsp_type; - stat_idx = host->rsp_len; - if (rsp_type == SD_RSP_TYPE_R0) { err = 0; goto out; @@ -485,106 +340,26 @@ static void sd_get_rsp(unsigned long host_addr) cmd->resp[0]); } - if (cmd == host->mrq->sbc) { - sd_send_cmd(host, host->mrq->cmd); - spin_unlock_irqrestore(&host->lock, flags); - return; - } - - if (cmd == host->mrq->stop) - goto out; - - if (cmd->data) { - sd_start_multi_rw(host, host->mrq); - spin_unlock_irqrestore(&host->lock, flags); - return; - } - out: cmd->error = err; - tasklet_schedule(&host->finish_tasklet); - spin_unlock_irqrestore(&host->lock, flags); -} - -static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host, - struct mmc_data *data, struct realtek_next *next) -{ - struct rtsx_pcr *pcr = host->pcr; - int read = data->flags & MMC_DATA_READ; - int sg_count = 0; - - if (!next && data->host_cookie && - data->host_cookie != host->next_data.cookie) { - dev_err(sdmmc_dev(host), - "error: invalid cookie data[%d] host[%d]\n", - data->host_cookie, host->next_data.cookie); - data->host_cookie = 0; - } - - if (next || (!next && data->host_cookie != host->next_data.cookie)) - sg_count = rtsx_pci_dma_map_sg(pcr, - data->sg, data->sg_len, read); - else - sg_count = host->next_data.sg_count; - - if (next) { - next->sg_count = sg_count; - if (++next->cookie < 0) - next->cookie = 1; - data->host_cookie = next->cookie; - } - - return sg_count; -} - -static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, - bool is_first_req) -{ - struct realtek_pci_sdmmc *host = mmc_priv(mmc); - struct mmc_data *data = mrq->data; - - if (data->host_cookie) { - dev_err(sdmmc_dev(host), - "error: descard already cookie data[%d]\n", - data->host_cookie); - data->host_cookie = 0; - } - - dev_dbg(sdmmc_dev(host), "dma sg prepared: %d\n", - sd_pre_dma_transfer(host, data, &host->next_data)); -} - -static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, - int err) -{ - struct realtek_pci_sdmmc *host = mmc_priv(mmc); - struct rtsx_pcr *pcr = host->pcr; - struct mmc_data *data = mrq->data; - int read = data->flags & MMC_DATA_READ; - - rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, read); - data->host_cookie = 0; + if (err && clock_toggled) + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); } -static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, - struct mmc_request *mrq) +static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) { struct rtsx_pcr *pcr = host->pcr; struct mmc_host *mmc = host->mmc; struct mmc_card *card = mmc->card; struct mmc_data *data = mrq->data; int uhs = mmc_card_uhs(card); - int read = data->flags & MMC_DATA_READ; + int read = (data->flags & MMC_DATA_READ) ? 1 : 0; u8 cfg2, trans_mode; int err; size_t data_len = data->blksz * data->blocks; - if (host->data) - dev_err(sdmmc_dev(host), "error: data already exist\n"); - - host->data = data; - if (read) { cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; @@ -635,54 +410,15 @@ static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END); - mod_timer(&host->timer, jiffies + 10 * HZ); rtsx_pci_send_cmd_no_wait(pcr); - err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read); - if (err < 0) { - data->error = err; - tasklet_schedule(&host->finish_tasklet); - } - return 0; -} - -static void sd_finish_multi_rw(unsigned long host_addr) -{ - struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; - struct rtsx_pcr *pcr = host->pcr; - struct mmc_data *data; - int err = 0; - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - - if (!host->data) { - dev_err(sdmmc_dev(host), "error: no data exist\n"); - goto out; - } - - data = host->data; - host->data = NULL; - - if (pcr->trans_result == TRANS_NO_DEVICE) - err = -ENODEV; - else if (pcr->trans_result != TRANS_RESULT_OK) - err = -EINVAL; - + err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000); if (err < 0) { - data->error = err; - goto out; - } - - if (!host->mrq->sbc && data->stop) { - sd_send_cmd(host, data->stop); - spin_unlock_irqrestore(&host->lock, flags); - return; + sd_clear_error(host); + return err; } -out: - tasklet_schedule(&host->finish_tasklet); - spin_unlock_irqrestore(&host->lock, flags); + return 0; } static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) @@ -901,13 +637,6 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) return 0; } -static inline bool sd_use_muti_rw(struct mmc_command *cmd) -{ - return mmc_op_multi(cmd->opcode) || - (cmd->opcode == MMC_READ_SINGLE_BLOCK) || - (cmd->opcode == MMC_WRITE_BLOCK); -} - static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -916,14 +645,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_data *data = mrq->data; unsigned int data_size = 0; int err; - unsigned long flags; - - mutex_lock(&pcr->pcr_mutex); - spin_lock_irqsave(&host->lock, flags); - - if (host->mrq) - dev_err(sdmmc_dev(host), "error: request already exist\n"); - host->mrq = mrq; if (host->eject) { cmd->error = -ENOMEDIUM; @@ -936,6 +657,8 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) goto finish; } + mutex_lock(&pcr->pcr_mutex); + rtsx_pci_start_run(pcr); rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, @@ -944,28 +667,46 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) rtsx_pci_write_register(pcr, CARD_SHARE_MODE, CARD_SHARE_MASK, CARD_SHARE_48_SD); + mutex_lock(&host->host_mutex); + host->mrq = mrq; + mutex_unlock(&host->host_mutex); + if (mrq->data) data_size = data->blocks * data->blksz; - if (sd_use_muti_rw(cmd)) - host->sg_count = sd_pre_dma_transfer(host, data, NULL); + if (!data_size || mmc_op_multi(cmd->opcode) || + (cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (cmd->opcode == MMC_WRITE_BLOCK)) { + sd_send_cmd_get_rsp(host, cmd); - if (!data_size || sd_use_muti_rw(cmd)) { - if (mrq->sbc) - sd_send_cmd(host, mrq->sbc); - else - sd_send_cmd(host, cmd); - spin_unlock_irqrestore(&host->lock, flags); + if (!cmd->error && data_size) { + sd_rw_multi(host, mrq); + + if (mmc_op_multi(cmd->opcode) && mrq->stop) + sd_send_cmd_get_rsp(host, mrq->stop); + } } else { - spin_unlock_irqrestore(&host->lock, flags); sd_normal_rw(host, mrq); - tasklet_schedule(&host->finish_tasklet); } - return; + + if (mrq->data) { + if (cmd->error || data->error) + data->bytes_xfered = 0; + else + data->bytes_xfered = data->blocks * data->blksz; + } + + mutex_unlock(&pcr->pcr_mutex); finish: - tasklet_schedule(&host->finish_tasklet); - spin_unlock_irqrestore(&host->lock, flags); + if (cmd->error) + dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); + + mutex_lock(&host->host_mutex); + host->mrq = NULL; + mutex_unlock(&host->host_mutex); + + mmc_request_done(mmc, mrq); } static int sd_set_bus_width(struct realtek_pci_sdmmc *host, @@ -1400,8 +1141,6 @@ out: } static const struct mmc_host_ops realtek_pci_sdmmc_ops = { - .pre_req = sdmmc_pre_req, - .post_req = sdmmc_post_req, .request = sdmmc_request, .set_ios = sdmmc_set_ios, .get_ro = sdmmc_get_ro, @@ -1465,7 +1204,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) struct realtek_pci_sdmmc *host; struct rtsx_pcr *pcr; struct pcr_handle *handle = pdev->dev.platform_data; - unsigned long host_addr; if (!handle) return -ENXIO; @@ -1489,15 +1227,8 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) pcr->slots[RTSX_SD_CARD].p_dev = pdev; pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; - host_addr = (unsigned long)host; - host->next_data.cookie = 1; - setup_timer(&host->timer, sd_request_timeout, host_addr); - tasklet_init(&host->cmd_tasklet, sd_get_rsp, host_addr); - tasklet_init(&host->data_tasklet, sd_finish_multi_rw, host_addr); - tasklet_init(&host->finish_tasklet, sd_finish_request, host_addr); - spin_lock_init(&host->lock); + mutex_init(&host->host_mutex); - pcr->slots[RTSX_SD_CARD].done_transfer = sd_isr_done_transfer; realtek_init_host(host); mmc_add_host(mmc); @@ -1510,8 +1241,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); struct rtsx_pcr *pcr; struct mmc_host *mmc; - struct mmc_request *mrq; - unsigned long flags; if (!host) return 0; @@ -1519,33 +1248,22 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pcr = host->pcr; pcr->slots[RTSX_SD_CARD].p_dev = NULL; pcr->slots[RTSX_SD_CARD].card_event = NULL; - pcr->slots[RTSX_SD_CARD].done_transfer = NULL; mmc = host->mmc; - mrq = host->mrq; - spin_lock_irqsave(&host->lock, flags); + mutex_lock(&host->host_mutex); if (host->mrq) { dev_dbg(&(pdev->dev), "%s: Controller removed during transfer\n", mmc_hostname(mmc)); - if (mrq->sbc) - mrq->sbc->error = -ENOMEDIUM; - if (mrq->cmd) - mrq->cmd->error = -ENOMEDIUM; - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - if (mrq->data) - mrq->data->error = -ENOMEDIUM; + rtsx_pci_complete_unfinished_transfer(pcr); - tasklet_schedule(&host->finish_tasklet); + host->mrq->cmd->error = -ENOMEDIUM; + if (host->mrq->stop) + host->mrq->stop->error = -ENOMEDIUM; + mmc_request_done(mmc, host->mrq); } - spin_unlock_irqrestore(&host->lock, flags); - - del_timer_sync(&host->timer); - tasklet_kill(&host->cmd_tasklet); - tasklet_kill(&host->data_tasklet); - tasklet_kill(&host->finish_tasklet); + mutex_unlock(&host->host_mutex); mmc_remove_host(mmc); host->eject = true; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 4615d79..b922c8e 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -523,6 +523,7 @@ static struct nand_ecclayout hwecc4_2048 = { #if defined(CONFIG_OF) static const struct of_device_id davinci_nand_of_match[] = { {.compatible = "ti,davinci-nand", }, + {.compatible = "ti,keystone-nand", }, {}, }; MODULE_DEVICE_TABLE(of, davinci_nand_of_match); @@ -581,6 +582,11 @@ static struct davinci_nand_pdata of_property_read_bool(pdev->dev.of_node, "ti,davinci-nand-use-bbt")) pdata->bbt_options = NAND_BBT_USE_FLASH; + + if (of_device_is_compatible(pdev->dev.of_node, + "ti,keystone-nand")) { + pdata->options |= NAND_NO_SUBPAGE_WRITE; + } } return dev_get_platdata(&pdev->dev); diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 24faddd..0dfeaf5 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -192,7 +192,7 @@ static inline void __enable_port(struct port *port) { struct slave *slave = port->slave; - if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev)) + if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave)) bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER); } @@ -2449,13 +2449,13 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) continue; if (slave_agg_no >= 0) { - if (!first_ok_slave && SLAVE_IS_OK(slave)) + if (!first_ok_slave && bond_slave_can_tx(slave)) first_ok_slave = slave; slave_agg_no--; continue; } - if (SLAVE_IS_OK(slave)) { + if (bond_slave_can_tx(slave)) { bond_dev_queue_xmit(bond, skb, slave->dev); goto out; } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 70de039..7bbbf1c 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -82,7 +82,8 @@ static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb) } /* Forward declaration */ -static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]); +static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], + bool strict_match); static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp); static void rlb_src_unlink(struct bonding *bond, u32 index); static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, @@ -228,7 +229,7 @@ static struct slave *tlb_get_least_loaded_slave(struct bonding *bond) /* Find the slave with the largest gap */ bond_for_each_slave_rcu(bond, slave, iter) { - if (SLAVE_IS_OK(slave)) { + if (bond_slave_can_tx(slave)) { long long gap = compute_gap(slave); if (max_gap < gap) { @@ -383,7 +384,7 @@ static struct slave *rlb_next_rx_slave(struct bonding *bond) bool found = false; bond_for_each_slave(bond, slave, iter) { - if (!SLAVE_IS_OK(slave)) + if (!bond_slave_can_tx(slave)) continue; if (!found) { if (!before || before->speed < slave->speed) @@ -416,7 +417,7 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond) bool found = false; bond_for_each_slave_rcu(bond, slave, iter) { - if (!SLAVE_IS_OK(slave)) + if (!bond_slave_can_tx(slave)) continue; if (!found) { if (!before || before->speed < slave->speed) @@ -459,7 +460,7 @@ static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[]) bond->alb_info.rlb_promisc_timeout_counter = 0; - alb_send_learning_packets(bond->curr_active_slave, addr); + alb_send_learning_packets(bond->curr_active_slave, addr, true); } /* slave being removed should not be active at this point @@ -995,7 +996,7 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id) /*********************** tlb/rlb shared functions *********************/ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[], - u16 vid) + __be16 vlan_proto, u16 vid) { struct learning_pkt pkt; struct sk_buff *skb; @@ -1021,7 +1022,7 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[], skb->dev = slave->dev; if (vid) { - skb = vlan_put_tag(skb, htons(ETH_P_8021Q), vid); + skb = vlan_put_tag(skb, vlan_proto, vid); if (!skb) { pr_err("%s: Error: failed to insert VLAN tag\n", slave->bond->dev->name); @@ -1032,22 +1033,32 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[], dev_queue_xmit(skb); } - -static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]) +static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], + bool strict_match) { struct bonding *bond = bond_get_bond_by_slave(slave); struct net_device *upper; struct list_head *iter; /* send untagged */ - alb_send_lp_vid(slave, mac_addr, 0); + alb_send_lp_vid(slave, mac_addr, 0, 0); /* loop through vlans and send one packet for each */ rcu_read_lock(); netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { - if (upper->priv_flags & IFF_802_1Q_VLAN) - alb_send_lp_vid(slave, mac_addr, - vlan_dev_vlan_id(upper)); + if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) { + if (strict_match && + ether_addr_equal_64bits(mac_addr, + upper->dev_addr)) { + alb_send_lp_vid(slave, mac_addr, + vlan_dev_vlan_proto(upper), + vlan_dev_vlan_id(upper)); + } else if (!strict_match) { + alb_send_lp_vid(slave, upper->dev_addr, + vlan_dev_vlan_proto(upper), + vlan_dev_vlan_id(upper)); + } + } } rcu_read_unlock(); } @@ -1057,7 +1068,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) struct net_device *dev = slave->dev; struct sockaddr s_addr; - if (slave->bond->params.mode == BOND_MODE_TLB) { + if (BOND_MODE(slave->bond) == BOND_MODE_TLB) { memcpy(dev->dev_addr, addr, dev->addr_len); return 0; } @@ -1100,14 +1111,14 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2) static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, struct slave *slave2) { - int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2)); + int slaves_state_differ = (bond_slave_can_tx(slave1) != bond_slave_can_tx(slave2)); struct slave *disabled_slave = NULL; ASSERT_RTNL(); /* fasten the change in the switch */ - if (SLAVE_IS_OK(slave1)) { - alb_send_learning_packets(slave1, slave1->dev->dev_addr); + if (bond_slave_can_tx(slave1)) { + alb_send_learning_packets(slave1, slave1->dev->dev_addr, false); if (bond->alb_info.rlb_enabled) { /* inform the clients that the mac address * has changed @@ -1118,8 +1129,8 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, disabled_slave = slave1; } - if (SLAVE_IS_OK(slave2)) { - alb_send_learning_packets(slave2, slave2->dev->dev_addr); + if (bond_slave_can_tx(slave2)) { + alb_send_learning_packets(slave2, slave2->dev->dev_addr, false); if (bond->alb_info.rlb_enabled) { /* inform the clients that the mac address * has changed @@ -1360,7 +1371,7 @@ static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond, bond_info->unbalanced_load += skb->len; } - if (tx_slave && SLAVE_IS_OK(tx_slave)) { + if (tx_slave && bond_slave_can_tx(tx_slave)) { if (tx_slave != rcu_dereference(bond->curr_active_slave)) { ether_addr_copy(eth_data->h_source, tx_slave->dev->dev_addr); @@ -1536,6 +1547,8 @@ void bond_alb_monitor(struct work_struct *work) /* send learning packets */ if (bond_info->lp_counter >= BOND_ALB_LP_TICKS(bond)) { + bool strict_match; + /* change of curr_active_slave involves swapping of mac addresses. * in order to avoid this swapping from happening while * sending the learning packets, the curr_slave_lock must be held for @@ -1543,8 +1556,15 @@ void bond_alb_monitor(struct work_struct *work) */ read_lock(&bond->curr_slave_lock); - bond_for_each_slave_rcu(bond, slave, iter) - alb_send_learning_packets(slave, slave->dev->dev_addr); + bond_for_each_slave_rcu(bond, slave, iter) { + /* If updating current_active, use all currently + * user mac addreses (!strict_match). Otherwise, only + * use mac of the slave device. + */ + strict_match = (slave != bond->curr_active_slave); + alb_send_learning_packets(slave, slave->dev->dev_addr, + strict_match); + } read_unlock(&bond->curr_slave_lock); @@ -1745,7 +1765,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave /* in TLB mode, the slave might flip down/up with the old dev_addr, * and thus filter bond->dev_addr's packets, so force bond's mac */ - if (bond->params.mode == BOND_MODE_TLB) { + if (BOND_MODE(bond) == BOND_MODE_TLB) { struct sockaddr sa; u8 tmp_addr[ETH_ALEN]; @@ -1767,7 +1787,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave } else { /* set the new_slave to the bond mac address */ alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); - alb_send_learning_packets(new_slave, bond->dev->dev_addr); + alb_send_learning_packets(new_slave, bond->dev->dev_addr, + false); } write_lock_bh(&bond->curr_slave_lock); @@ -1810,7 +1831,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); read_lock(&bond->lock); - alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr); + alb_send_learning_packets(bond->curr_active_slave, + bond_dev->dev_addr, false); if (bond->alb_info.rlb_enabled) { /* inform clients mac address has changed */ rlb_req_update_slave_clients(bond, bond->curr_active_slave); diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c index 2d3f7fa..658e761 100644 --- a/drivers/net/bonding/bond_debugfs.c +++ b/drivers/net/bonding/bond_debugfs.c @@ -23,7 +23,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v) struct rlb_client_info *client_info; u32 hash_index; - if (bond->params.mode != BOND_MODE_ALB) + if (BOND_MODE(bond) != BOND_MODE_ALB) return 0; seq_printf(m, "SourceIP DestinationIP " diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 90ebed6..59a12c61 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -343,7 +343,7 @@ static int bond_set_carrier(struct bonding *bond) if (!bond_has_slaves(bond)) goto down; - if (bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_8023AD) return bond_3ad_set_carrier(bond); bond_for_each_slave(bond, slave, iter) { @@ -497,7 +497,7 @@ static int bond_set_promiscuity(struct bonding *bond, int inc) struct list_head *iter; int err = 0; - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { /* write lock already acquired */ if (bond->curr_active_slave) { err = dev_set_promiscuity(bond->curr_active_slave->dev, @@ -523,7 +523,7 @@ static int bond_set_allmulti(struct bonding *bond, int inc) struct list_head *iter; int err = 0; - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { /* write lock already acquired */ if (bond->curr_active_slave) { err = dev_set_allmulti(bond->curr_active_slave->dev, @@ -574,7 +574,7 @@ static void bond_hw_addr_flush(struct net_device *bond_dev, dev_uc_unsync(slave_dev, bond_dev); dev_mc_unsync(slave_dev, bond_dev); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { /* del lacpdu mc addr from mc list */ u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; @@ -585,8 +585,8 @@ static void bond_hw_addr_flush(struct net_device *bond_dev, /*--------------------------- Active slave change ---------------------------*/ /* Update the hardware address list and promisc/allmulti for the new and - * old active slaves (if any). Modes that are !USES_PRIMARY keep all - * slaves up date at all times; only the USES_PRIMARY modes need to call + * old active slaves (if any). Modes that are not using primary keep all + * slaves up date at all times; only the modes that use primary need to call * this function to swap these settings during a failover. */ static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active, @@ -747,7 +747,7 @@ static struct slave *bond_find_best_slave(struct bonding *bond) bond_for_each_slave(bond, slave, iter) { if (slave->link == BOND_LINK_UP) return slave; - if (slave->link == BOND_LINK_BACK && IS_UP(slave->dev) && + if (slave->link == BOND_LINK_BACK && bond_slave_is_up(slave) && slave->delay < mintime) { mintime = slave->delay; bestslave = slave; @@ -801,7 +801,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) new_active->last_link_up = jiffies; if (new_active->link == BOND_LINK_BACK) { - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { pr_info("%s: making interface %s the new active one %d ms earlier\n", bond->dev->name, new_active->dev->name, (bond->params.updelay - new_active->delay) * bond->params.miimon); @@ -810,20 +810,20 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) new_active->delay = 0; new_active->link = BOND_LINK_UP; - if (bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_handle_link_change(new_active, BOND_LINK_UP); if (bond_is_lb(bond)) bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP); } else { - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { pr_info("%s: making interface %s the new active one\n", bond->dev->name, new_active->dev->name); } } } - if (USES_PRIMARY(bond->params.mode)) + if (bond_uses_primary(bond)) bond_hw_addr_swap(bond, new_active, old_active); if (bond_is_lb(bond)) { @@ -838,7 +838,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) rcu_assign_pointer(bond->curr_active_slave, new_active); } - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) { + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) { if (old_active) bond_set_slave_inactive_flags(old_active, BOND_SLAVE_NOTIFY_NOW); @@ -876,8 +876,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) * resend only if bond is brought up with the affected * bonding modes and the retransmission is enabled */ if (netif_running(bond->dev) && (bond->params.resend_igmp > 0) && - ((USES_PRIMARY(bond->params.mode) && new_active) || - bond->params.mode == BOND_MODE_ROUNDROBIN)) { + ((bond_uses_primary(bond) && new_active) || + BOND_MODE(bond) == BOND_MODE_ROUNDROBIN)) { bond->igmp_retrans = bond->params.resend_igmp; queue_delayed_work(bond->wq, &bond->mcast_work, 1); } @@ -958,7 +958,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev) struct slave *slave; bond_for_each_slave(bond, slave, iter) - if (IS_UP(slave->dev)) + if (bond_slave_is_up(slave)) slave_disable_netpoll(slave); } @@ -1038,6 +1038,7 @@ static void bond_compute_features(struct bonding *bond) if (!bond_has_slaves(bond)) goto done; + vlan_features &= NETIF_F_ALL_FOR_ALL; bond_for_each_slave(bond, slave, iter) { vlan_features = netdev_increment_features(vlan_features, @@ -1084,7 +1085,7 @@ static bool bond_should_deliver_exact_match(struct sk_buff *skb, struct bonding *bond) { if (bond_is_slave_inactive(slave)) { - if (bond->params.mode == BOND_MODE_ALB && + if (BOND_MODE(bond) == BOND_MODE_ALB && skb->pkt_type != PACKET_BROADCAST && skb->pkt_type != PACKET_MULTICAST) return false; @@ -1126,7 +1127,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) skb->dev = bond->dev; - if (bond->params.mode == BOND_MODE_ALB && + if (BOND_MODE(bond) == BOND_MODE_ALB && bond->dev->priv_flags & IFF_BRIDGE_PORT && skb->pkt_type == PACKET_HOST) { @@ -1171,7 +1172,7 @@ static struct slave *bond_alloc_slave(struct bonding *bond) if (!slave) return NULL; - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { SLAVE_AD_INFO(slave) = kzalloc(sizeof(struct ad_slave_info), GFP_KERNEL); if (!SLAVE_AD_INFO(slave)) { @@ -1186,7 +1187,7 @@ static void bond_free_slave(struct slave *slave) { struct bonding *bond = bond_get_bond_by_slave(slave); - if (bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_8023AD) kfree(SLAVE_AD_INFO(slave)); kfree(slave); @@ -1298,7 +1299,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) if (!bond_has_slaves(bond)) { pr_warn("%s: Warning: The first slave device specified does not support setting the MAC address\n", bond_dev->name); - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) { + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) { bond->params.fail_over_mac = BOND_FOM_ACTIVE; pr_warn("%s: Setting fail_over_mac to active for active-backup mode\n", bond_dev->name); @@ -1325,6 +1326,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_undo_flags; } + new_slave->bond = bond; + new_slave->dev = slave_dev; /* * Set the new_slave's queue_id to be zero. Queue ID mapping * is set via sysfs or module option if desired. @@ -1347,7 +1350,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) ether_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr); if (!bond->params.fail_over_mac || - bond->params.mode != BOND_MODE_ACTIVEBACKUP) { + BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* * Set slave to master's mac address. The application already * set the master's mac address to that of the first slave @@ -1368,8 +1371,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_restore_mac; } - new_slave->bond = bond; - new_slave->dev = slave_dev; slave_dev->priv_flags |= IFF_BONDING; if (bond_is_lb(bond)) { @@ -1381,10 +1382,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_close; } - /* If the mode USES_PRIMARY, then the following is handled by + /* If the mode uses primary, then the following is handled by * bond_change_active_slave(). */ - if (!USES_PRIMARY(bond->params.mode)) { + if (!bond_uses_primary(bond)) { /* set promiscuity level to new slave */ if (bond_dev->flags & IFF_PROMISC) { res = dev_set_promiscuity(slave_dev, 1); @@ -1407,7 +1408,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) netif_addr_unlock_bh(bond_dev); } - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { /* add lacpdu mc addr to mc list */ u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR; @@ -1480,7 +1481,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->link == BOND_LINK_DOWN ? "DOWN" : (new_slave->link == BOND_LINK_UP ? "UP" : "BACK")); - if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) { + if (bond_uses_primary(bond) && bond->params.primary[0]) { /* if there is a primary slave, remember it */ if (strcmp(bond->params.primary, new_slave->dev->name) == 0) { bond->primary_slave = new_slave; @@ -1488,7 +1489,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } - switch (bond->params.mode) { + switch (BOND_MODE(bond)) { case BOND_MODE_ACTIVEBACKUP: bond_set_slave_inactive_flags(new_slave, BOND_SLAVE_NOTIFY_NOW); @@ -1569,7 +1570,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_compute_features(bond); bond_set_carrier(bond); - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { block_netpoll_tx(); write_lock_bh(&bond->curr_slave_lock); bond_select_active_slave(bond); @@ -1593,7 +1594,7 @@ err_unregister: netdev_rx_handler_unregister(slave_dev); err_detach: - if (!USES_PRIMARY(bond->params.mode)) + if (!bond_uses_primary(bond)) bond_hw_addr_flush(bond_dev, slave_dev); vlan_vids_del_by_dev(slave_dev, bond_dev); @@ -1615,7 +1616,7 @@ err_close: err_restore_mac: if (!bond->params.fail_over_mac || - bond->params.mode != BOND_MODE_ACTIVEBACKUP) { + BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* XXX TODO - fom follow mode needs to change master's * MAC if this slave's MAC is in use by the bond, or at * least print a warning. @@ -1691,7 +1692,7 @@ static int __bond_release_one(struct net_device *bond_dev, write_lock_bh(&bond->lock); /* Inform AD package of unbinding of slave. */ - if (bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_unbind_slave(slave); write_unlock_bh(&bond->lock); @@ -1706,7 +1707,7 @@ static int __bond_release_one(struct net_device *bond_dev, bond->current_arp_slave = NULL; if (!all && (!bond->params.fail_over_mac || - bond->params.mode != BOND_MODE_ACTIVEBACKUP)) { + BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP)) { if (ether_addr_equal_64bits(bond_dev->dev_addr, slave->perm_hwaddr) && bond_has_slaves(bond)) pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s - set the HWaddr of %s to a different address to avoid conflicts\n", @@ -1778,10 +1779,10 @@ static int __bond_release_one(struct net_device *bond_dev, /* must do this from outside any spinlocks */ vlan_vids_del_by_dev(slave_dev, bond_dev); - /* If the mode USES_PRIMARY, then this cases was handled above by + /* If the mode uses primary, then this cases was handled above by * bond_change_active_slave(..., NULL) */ - if (!USES_PRIMARY(bond->params.mode)) { + if (!bond_uses_primary(bond)) { /* unset promiscuity level from slave * NOTE: The NETDEV_CHANGEADDR call above may change the value * of the IFF_PROMISC flag in the bond_dev, but we need the @@ -1805,7 +1806,7 @@ static int __bond_release_one(struct net_device *bond_dev, dev_close(slave_dev); if (bond->params.fail_over_mac != BOND_FOM_ACTIVE || - bond->params.mode != BOND_MODE_ACTIVEBACKUP) { + BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* restore original ("permanent") mac address */ ether_addr_copy(addr.sa_data, slave->perm_hwaddr); addr.sa_family = slave_dev->type; @@ -1851,7 +1852,7 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info) { struct bonding *bond = netdev_priv(bond_dev); - info->bond_mode = bond->params.mode; + info->bond_mode = BOND_MODE(bond); info->miimon = bond->params.miimon; info->num_slaves = bond->slave_cnt; @@ -1907,7 +1908,7 @@ static int bond_miimon_inspect(struct bonding *bond) if (slave->delay) { pr_info("%s: link status down for %sinterface %s, disabling it in %d ms\n", bond->dev->name, - (bond->params.mode == + (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) ? (bond_is_active_slave(slave) ? "active " : "backup ") : "", @@ -1998,10 +1999,10 @@ static void bond_miimon_commit(struct bonding *bond) slave->link = BOND_LINK_UP; slave->last_link_up = jiffies; - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { /* prevent it from being the active one */ bond_set_backup_slave(slave); - } else if (bond->params.mode != BOND_MODE_ACTIVEBACKUP) { + } else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* make it immediately active */ bond_set_active_slave(slave); } else if (slave != bond->primary_slave) { @@ -2015,7 +2016,7 @@ static void bond_miimon_commit(struct bonding *bond) slave->duplex ? "full" : "half"); /* notify ad that the link status has changed */ - if (bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_handle_link_change(slave, BOND_LINK_UP); if (bond_is_lb(bond)) @@ -2034,15 +2035,15 @@ static void bond_miimon_commit(struct bonding *bond) slave->link = BOND_LINK_DOWN; - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP || - bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP || + BOND_MODE(bond) == BOND_MODE_8023AD) bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); pr_info("%s: link status definitely down for interface %s, disabling it\n", bond->dev->name, slave->dev->name); - if (bond->params.mode == BOND_MODE_8023AD) + if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_handle_link_change(slave, BOND_LINK_DOWN); @@ -2156,10 +2157,10 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip) */ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ip, __be32 src_ip, - struct bond_vlan_tag *inner, - struct bond_vlan_tag *outer) + struct bond_vlan_tag *tags) { struct sk_buff *skb; + int i; pr_debug("arp %d on slave %s: dst %pI4 src %pI4\n", arp_op, slave_dev->name, &dest_ip, &src_ip); @@ -2171,21 +2172,26 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, net_err_ratelimited("ARP packet allocation failed\n"); return; } - if (outer->vlan_id) { - if (inner->vlan_id) { - pr_debug("inner tag: proto %X vid %X\n", - ntohs(inner->vlan_proto), inner->vlan_id); - skb = __vlan_put_tag(skb, inner->vlan_proto, - inner->vlan_id); - if (!skb) { - net_err_ratelimited("failed to insert inner VLAN tag\n"); - return; - } - } - pr_debug("outer reg: proto %X vid %X\n", - ntohs(outer->vlan_proto), outer->vlan_id); - skb = vlan_put_tag(skb, outer->vlan_proto, outer->vlan_id); + /* Go through all the tags backwards and add them to the packet */ + for (i = BOND_MAX_VLAN_ENCAP - 1; i > 0; i--) { + if (!tags[i].vlan_id) + continue; + + pr_debug("inner tag: proto %X vid %X\n", + ntohs(tags[i].vlan_proto), tags[i].vlan_id); + skb = __vlan_put_tag(skb, tags[i].vlan_proto, + tags[i].vlan_id); + if (!skb) { + net_err_ratelimited("failed to insert inner VLAN tag\n"); + return; + } + } + /* Set the outer tag */ + if (tags[0].vlan_id) { + pr_debug("outer tag: proto %X vid %X\n", + ntohs(tags[0].vlan_proto), tags[0].vlan_id); + skb = vlan_put_tag(skb, tags[0].vlan_proto, tags[0].vlan_id); if (!skb) { net_err_ratelimited("failed to insert outer VLAN tag\n"); return; @@ -2194,22 +2200,52 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, arp_xmit(skb); } +/* Validate the device path between the @start_dev and the @end_dev. + * The path is valid if the @end_dev is reachable through device + * stacking. + * When the path is validated, collect any vlan information in the + * path. + */ +static bool bond_verify_device_path(struct net_device *start_dev, + struct net_device *end_dev, + struct bond_vlan_tag *tags) +{ + struct net_device *upper; + struct list_head *iter; + int idx; + + if (start_dev == end_dev) + return true; + + netdev_for_each_upper_dev_rcu(start_dev, upper, iter) { + if (bond_verify_device_path(upper, end_dev, tags)) { + if (is_vlan_dev(upper)) { + idx = vlan_get_encap_level(upper); + if (idx >= BOND_MAX_VLAN_ENCAP) + return false; + + tags[idx].vlan_proto = + vlan_dev_vlan_proto(upper); + tags[idx].vlan_id = vlan_dev_vlan_id(upper); + } + return true; + } + } + + return false; +} static void bond_arp_send_all(struct bonding *bond, struct slave *slave) { - struct net_device *upper, *vlan_upper; - struct list_head *iter, *vlan_iter; struct rtable *rt; - struct bond_vlan_tag inner, outer; + struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP]; __be32 *targets = bond->params.arp_targets, addr; int i; + bool ret; for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) { pr_debug("basa: target %pI4\n", &targets[i]); - inner.vlan_proto = 0; - inner.vlan_id = 0; - outer.vlan_proto = 0; - outer.vlan_id = 0; + memset(tags, 0, sizeof(tags)); /* Find out through which dev should the packet go */ rt = ip_route_output(dev_net(bond->dev), targets[i], 0, @@ -2222,7 +2258,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) net_warn_ratelimited("%s: no route to arp_ip_target %pI4 and arp_validate is set\n", bond->dev->name, &targets[i]); - bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], 0, &inner, &outer); + bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], + 0, tags); continue; } @@ -2231,52 +2268,12 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) goto found; rcu_read_lock(); - /* first we search only for vlan devices. for every vlan - * found we verify its upper dev list, searching for the - * rt->dst.dev. If found we save the tag of the vlan and - * proceed to send the packet. - */ - netdev_for_each_all_upper_dev_rcu(bond->dev, vlan_upper, - vlan_iter) { - if (!is_vlan_dev(vlan_upper)) - continue; - - if (vlan_upper == rt->dst.dev) { - outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper); - outer.vlan_id = vlan_dev_vlan_id(vlan_upper); - rcu_read_unlock(); - goto found; - } - netdev_for_each_all_upper_dev_rcu(vlan_upper, upper, - iter) { - if (upper == rt->dst.dev) { - /* If the upper dev is a vlan dev too, - * set the vlan tag to inner tag. - */ - if (is_vlan_dev(upper)) { - inner.vlan_proto = vlan_dev_vlan_proto(upper); - inner.vlan_id = vlan_dev_vlan_id(upper); - } - outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper); - outer.vlan_id = vlan_dev_vlan_id(vlan_upper); - rcu_read_unlock(); - goto found; - } - } - } - - /* if the device we're looking for is not on top of any of - * our upper vlans, then just search for any dev that - * matches, and in case it's a vlan - save the id - */ - netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { - if (upper == rt->dst.dev) { - rcu_read_unlock(); - goto found; - } - } + ret = bond_verify_device_path(bond->dev, rt->dst.dev, tags); rcu_read_unlock(); + if (ret) + goto found; + /* Not our device - skip */ pr_debug("%s: no path to arp_ip_target %pI4 via rt.dev %s\n", bond->dev->name, &targets[i], @@ -2289,7 +2286,7 @@ found: addr = bond_confirm_addr(rt->dst.dev, targets[i], 0); ip_rt_put(rt); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], - addr, &inner, &outer); + addr, tags); } } @@ -2490,7 +2487,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work) * do - all replies will be rx'ed on same link causing slaves * to be unstable during low/no traffic periods */ - if (IS_UP(slave->dev)) + if (bond_slave_is_up(slave)) bond_arp_send_all(bond, slave); } @@ -2712,10 +2709,10 @@ static bool bond_ab_arp_probe(struct bonding *bond) bond_set_slave_inactive_flags(curr_arp_slave, BOND_SLAVE_NOTIFY_LATER); bond_for_each_slave_rcu(bond, slave, iter) { - if (!found && !before && IS_UP(slave->dev)) + if (!found && !before && bond_slave_is_up(slave)) before = slave; - if (found && !new_slave && IS_UP(slave->dev)) + if (found && !new_slave && bond_slave_is_up(slave)) new_slave = slave; /* if the link state is up at this point, we * mark it down - this can happen if we have @@ -2724,7 +2721,7 @@ static bool bond_ab_arp_probe(struct bonding *bond) * one the current slave so it is still marked * up when it is actually down */ - if (!IS_UP(slave->dev) && slave->link == BOND_LINK_UP) { + if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) { slave->link = BOND_LINK_DOWN; if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; @@ -2887,7 +2884,7 @@ static int bond_slave_netdev_event(unsigned long event, bond_update_speed_duplex(slave); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { if (old_speed != slave->speed) bond_3ad_adapter_speed_changed(slave); if (old_duplex != slave->duplex) @@ -2915,7 +2912,7 @@ static int bond_slave_netdev_event(unsigned long event, break; case NETDEV_CHANGENAME: /* we don't care if we don't have primary set */ - if (!USES_PRIMARY(bond->params.mode) || + if (!bond_uses_primary(bond) || !bond->params.primary[0]) break; @@ -3078,7 +3075,7 @@ static void bond_work_init_all(struct bonding *bond) bond_resend_igmp_join_requests_delayed); INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor); INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor); - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon); else INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon); @@ -3105,7 +3102,7 @@ static int bond_open(struct net_device *bond_dev) if (bond_has_slaves(bond)) { read_lock(&bond->curr_slave_lock); bond_for_each_slave(bond, slave, iter) { - if (USES_PRIMARY(bond->params.mode) + if (bond_uses_primary(bond) && (slave != bond->curr_active_slave)) { bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); @@ -3124,7 +3121,7 @@ static int bond_open(struct net_device *bond_dev) /* bond_alb_initialize must be called before the timer * is started. */ - if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB))) + if (bond_alb_initialize(bond, (BOND_MODE(bond) == BOND_MODE_ALB))) return -ENOMEM; if (bond->params.tlb_dynamic_lb) queue_delayed_work(bond->wq, &bond->alb_work, 0); @@ -3138,7 +3135,7 @@ static int bond_open(struct net_device *bond_dev) bond->recv_probe = bond_arp_rcv; } - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { queue_delayed_work(bond->wq, &bond->ad_work, 0); /* register to receive LACPDUs */ bond->recv_probe = bond_3ad_lacpdu_recv; @@ -3343,7 +3340,7 @@ static void bond_set_rx_mode(struct net_device *bond_dev) rcu_read_lock(); - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { slave = rcu_dereference(bond->curr_active_slave); if (slave) { dev_uc_sync(slave->dev, bond_dev); @@ -3497,7 +3494,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) struct list_head *iter; int res = 0; - if (bond->params.mode == BOND_MODE_ALB) + if (BOND_MODE(bond) == BOND_MODE_ALB) return bond_alb_set_mac_address(bond_dev, addr); @@ -3508,7 +3505,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) * Returning an error causes ifenslave to fail. */ if (bond->params.fail_over_mac && - bond->params.mode == BOND_MODE_ACTIVEBACKUP) + BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) return 0; if (!is_valid_ether_addr(sa->sa_data)) @@ -3588,7 +3585,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl /* Here we start from the slave with slave_id */ bond_for_each_slave_rcu(bond, slave, iter) { if (--i < 0) { - if (slave_can_tx(slave)) { + if (bond_slave_can_tx(slave)) { bond_dev_queue_xmit(bond, skb, slave->dev); return; } @@ -3600,7 +3597,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl bond_for_each_slave_rcu(bond, slave, iter) { if (--i < 0) break; - if (slave_can_tx(slave)) { + if (bond_slave_can_tx(slave)) { bond_dev_queue_xmit(bond, skb, slave->dev); return; } @@ -3657,7 +3654,7 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev */ if (iph->protocol == IPPROTO_IGMP && skb->protocol == htons(ETH_P_IP)) { slave = rcu_dereference(bond->curr_active_slave); - if (slave && slave_can_tx(slave)) + if (slave && bond_slave_can_tx(slave)) bond_dev_queue_xmit(bond, skb, slave->dev); else bond_xmit_slave_id(bond, skb, 0); @@ -3710,7 +3707,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) bond_for_each_slave_rcu(bond, slave, iter) { if (bond_is_last_slave(bond, slave)) break; - if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP) { + if (bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) { @@ -3722,7 +3719,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) bond_dev_queue_xmit(bond, skb2, slave->dev); } } - if (slave && IS_UP(slave->dev) && slave->link == BOND_LINK_UP) + if (slave && bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) bond_dev_queue_xmit(bond, skb, slave->dev); else dev_kfree_skb_any(skb); @@ -3747,7 +3744,7 @@ static inline int bond_slave_override(struct bonding *bond, /* Find out if any slaves have the same mapping as this skb. */ bond_for_each_slave_rcu(bond, slave, iter) { if (slave->queue_id == skb->queue_mapping) { - if (slave_can_tx(slave)) { + if (bond_slave_can_tx(slave)) { bond_dev_queue_xmit(bond, skb, slave->dev); return 0; } @@ -3788,12 +3785,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev { struct bonding *bond = netdev_priv(dev); - if (TX_QUEUE_OVERRIDE(bond->params.mode)) { - if (!bond_slave_override(bond, skb)) - return NETDEV_TX_OK; - } + if (bond_should_override_tx_queue(bond) && + !bond_slave_override(bond, skb)) + return NETDEV_TX_OK; - switch (bond->params.mode) { + switch (BOND_MODE(bond)) { case BOND_MODE_ROUNDROBIN: return bond_xmit_roundrobin(skb, dev); case BOND_MODE_ACTIVEBACKUP: @@ -3811,7 +3807,7 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev default: /* Should never happen, mode already checked */ pr_err("%s: Error: Unknown bonding mode %d\n", - dev->name, bond->params.mode); + dev->name, BOND_MODE(bond)); WARN_ON_ONCE(1); dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -3851,14 +3847,14 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev, ecmd->duplex = DUPLEX_UNKNOWN; ecmd->port = PORT_OTHER; - /* Since SLAVE_IS_OK returns false for all inactive or down slaves, we + /* Since bond_slave_can_tx returns false for all inactive or down slaves, we * do not need to check mode. Though link speed might not represent * the true receive or transmit bandwidth (not all modes are symmetric) * this is an accurate maximum. */ read_lock(&bond->lock); bond_for_each_slave(bond, slave, iter) { - if (SLAVE_IS_OK(slave)) { + if (bond_slave_can_tx(slave)) { if (slave->speed != SPEED_UNKNOWN) speed += slave->speed; if (ecmd->duplex == DUPLEX_UNKNOWN && @@ -4114,7 +4110,7 @@ static int bond_check_params(struct bond_params *params) } /* reset values for 802.3ad/TLB/ALB */ - if (BOND_NO_USES_ARP(bond_mode)) { + if (!bond_mode_uses_arp(bond_mode)) { if (!miimon) { pr_warn("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n"); pr_warn("Forcing miimon to 100msec\n"); @@ -4196,7 +4192,7 @@ static int bond_check_params(struct bond_params *params) catch mistakes */ __be32 ip; if (!in4_pton(arp_ip_target[i], -1, (u8 *)&ip, -1, NULL) || - IS_IP_TARGET_UNUSABLE_ADDRESS(ip)) { + !bond_is_ip_target_ok(ip)) { pr_warn("Warning: bad arp_ip_target module parameter (%s), ARP monitoring will not be performed\n", arp_ip_target[i]); arp_interval = 0; @@ -4269,7 +4265,7 @@ static int bond_check_params(struct bond_params *params) pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details\n"); } - if (primary && !USES_PRIMARY(bond_mode)) { + if (primary && !bond_mode_uses_primary(bond_mode)) { /* currently, using a primary only makes sense * in active backup, TLB or ALB modes */ diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 0d06e75..5ab3c18 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -56,7 +56,7 @@ static int bond_fill_slave_info(struct sk_buff *skb, if (nla_put_u16(skb, IFLA_BOND_SLAVE_QUEUE_ID, slave->queue_id)) goto nla_put_failure; - if (slave->bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { const struct aggregator *agg; agg = SLAVE_AD_INFO(slave)->port.aggregator; @@ -407,7 +407,7 @@ static int bond_fill_info(struct sk_buff *skb, unsigned int packets_per_slave; int i, targets_added; - if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode)) + if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond))) goto nla_put_failure; if (slave_dev && @@ -505,7 +505,7 @@ static int bond_fill_info(struct sk_buff *skb, bond->params.ad_select)) goto nla_put_failure; - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info info; if (!bond_3ad_get_active_agg_info(bond, &info)) { diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 6dc49da..540e016 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -127,6 +127,7 @@ static const struct bond_opt_value bond_fail_over_mac_tbl[] = { static const struct bond_opt_value bond_intmax_tbl[] = { { "off", 0, BOND_VALFLAG_DEFAULT}, { "maxval", INT_MAX, BOND_VALFLAG_MAX}, + { NULL, -1, 0} }; static const struct bond_opt_value bond_lacp_rate_tbl[] = { @@ -672,7 +673,7 @@ const struct bond_option *bond_opt_get(unsigned int option) int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval) { - if (BOND_NO_USES_ARP(newval->value) && bond->params.arp_interval) { + if (!bond_mode_uses_arp(newval->value) && bond->params.arp_interval) { pr_info("%s: %s mode is incompatible with arp monitoring, start mii monitoring\n", bond->dev->name, newval->string); /* disable arp monitoring */ @@ -693,7 +694,7 @@ int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newv static struct net_device *__bond_option_active_slave_get(struct bonding *bond, struct slave *slave) { - return USES_PRIMARY(bond->params.mode) && slave ? slave->dev : NULL; + return bond_uses_primary(bond) && slave ? slave->dev : NULL; } struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond) @@ -758,7 +759,7 @@ static int bond_option_active_slave_set(struct bonding *bond, bond->dev->name, new_active->dev->name); } else { if (old_active && (new_active->link == BOND_LINK_UP) && - IS_UP(new_active->dev)) { + bond_slave_is_up(new_active)) { pr_info("%s: Setting %s as active slave\n", bond->dev->name, new_active->dev->name); bond_change_active_slave(bond, new_active); @@ -942,7 +943,7 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target) __be32 *targets = bond->params.arp_targets; int ind; - if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) { + if (!bond_is_ip_target_ok(target)) { pr_err("%s: invalid ARP target %pI4 specified for addition\n", bond->dev->name, &target); return -EINVAL; @@ -987,7 +988,7 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target) unsigned long *targets_rx; int ind, i; - if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) { + if (!bond_is_ip_target_ok(target)) { pr_err("%s: invalid ARP target %pI4 specified for removal\n", bond->dev->name, &target); return -EINVAL; diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 6a261c8..b215b47 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -72,9 +72,9 @@ static void bond_info_show_master(struct seq_file *seq) curr = rcu_dereference(bond->curr_active_slave); seq_printf(seq, "Bonding Mode: %s", - bond_mode_name(bond->params.mode)); + bond_mode_name(BOND_MODE(bond))); - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP && + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP && bond->params.fail_over_mac) { optval = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC, bond->params.fail_over_mac); @@ -83,15 +83,15 @@ static void bond_info_show_master(struct seq_file *seq) seq_printf(seq, "\n"); - if (bond->params.mode == BOND_MODE_XOR || - bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_XOR || + BOND_MODE(bond) == BOND_MODE_8023AD) { optval = bond_opt_get_val(BOND_OPT_XMIT_HASH, bond->params.xmit_policy); seq_printf(seq, "Transmit Hash Policy: %s (%d)\n", optval->string, bond->params.xmit_policy); } - if (USES_PRIMARY(bond->params.mode)) { + if (bond_uses_primary(bond)) { seq_printf(seq, "Primary Slave: %s", (bond->primary_slave) ? bond->primary_slave->dev->name : "None"); @@ -134,7 +134,7 @@ static void bond_info_show_master(struct seq_file *seq) seq_printf(seq, "\n"); } - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; seq_puts(seq, "\n802.3ad info\n"); @@ -188,7 +188,7 @@ static void bond_info_show_slave(struct seq_file *seq, seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { const struct aggregator *agg = SLAVE_AD_INFO(slave)->port.aggregator; diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 39c4d8d..daed52f 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -214,9 +214,9 @@ static ssize_t bonding_show_mode(struct device *d, struct bonding *bond = to_bond(d); const struct bond_opt_value *val; - val = bond_opt_get_val(BOND_OPT_MODE, bond->params.mode); + val = bond_opt_get_val(BOND_OPT_MODE, BOND_MODE(bond)); - return sprintf(buf, "%s %d\n", val->string, bond->params.mode); + return sprintf(buf, "%s %d\n", val->string, BOND_MODE(bond)); } static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, bonding_show_mode, bonding_sysfs_store_option); @@ -505,7 +505,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d, int count = 0; struct bonding *bond = to_bond(d); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; count = sprintf(buf, "%d\n", bond_3ad_get_active_agg_info(bond, &ad_info) @@ -525,7 +525,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d, int count = 0; struct bonding *bond = to_bond(d); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; count = sprintf(buf, "%d\n", bond_3ad_get_active_agg_info(bond, &ad_info) @@ -545,7 +545,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d, int count = 0; struct bonding *bond = to_bond(d); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; count = sprintf(buf, "%d\n", bond_3ad_get_active_agg_info(bond, &ad_info) @@ -565,7 +565,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d, int count = 0; struct bonding *bond = to_bond(d); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; count = sprintf(buf, "%d\n", bond_3ad_get_active_agg_info(bond, &ad_info) @@ -585,7 +585,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d, int count = 0; struct bonding *bond = to_bond(d); - if (bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; if (!bond_3ad_get_active_agg_info(bond, &ad_info)) count = sprintf(buf, "%pM\n", ad_info.partner_system); diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c index 89bc3b3..198677f 100644 --- a/drivers/net/bonding/bond_sysfs_slave.c +++ b/drivers/net/bonding/bond_sysfs_slave.c @@ -69,7 +69,7 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf) { const struct aggregator *agg; - if (slave->bond->params.mode == BOND_MODE_8023AD) { + if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { agg = SLAVE_AD_INFO(slave)->port.aggregator; if (agg) return sprintf(buf, "%d\n", diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index c51c433..ea64aa2 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -36,46 +36,11 @@ #define bond_version DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n" +#define BOND_MAX_VLAN_ENCAP 2 #define BOND_MAX_ARP_TARGETS 16 #define BOND_DEFAULT_MIIMON 100 -#define IS_UP(dev) \ - ((((dev)->flags & IFF_UP) == IFF_UP) && \ - netif_running(dev) && \ - netif_carrier_ok(dev)) - -/* - * Checks whether slave is ready for transmit. - */ -#define SLAVE_IS_OK(slave) \ - (((slave)->dev->flags & IFF_UP) && \ - netif_running((slave)->dev) && \ - ((slave)->link == BOND_LINK_UP) && \ - bond_is_active_slave(slave)) - - -#define USES_PRIMARY(mode) \ - (((mode) == BOND_MODE_ACTIVEBACKUP) || \ - ((mode) == BOND_MODE_TLB) || \ - ((mode) == BOND_MODE_ALB)) - -#define BOND_NO_USES_ARP(mode) \ - (((mode) == BOND_MODE_8023AD) || \ - ((mode) == BOND_MODE_TLB) || \ - ((mode) == BOND_MODE_ALB)) - -#define TX_QUEUE_OVERRIDE(mode) \ - (((mode) == BOND_MODE_ACTIVEBACKUP) || \ - ((mode) == BOND_MODE_ROUNDROBIN)) - -#define BOND_MODE_IS_LB(mode) \ - (((mode) == BOND_MODE_TLB) || \ - ((mode) == BOND_MODE_ALB)) - -#define IS_IP_TARGET_UNUSABLE_ADDRESS(a) \ - ((htonl(INADDR_BROADCAST) == a) || \ - ipv4_is_zeronet(a)) /* * Less bad way to call ioctl from within the kernel; this needs to be * done some other way to get the call out of interrupt context. @@ -89,6 +54,8 @@ set_fs(fs); \ res; }) +#define BOND_MODE(bond) ((bond)->params.mode) + /* slave list primitives */ #define bond_slave_list(bond) (&(bond)->dev->adj_list.lower) @@ -283,14 +250,41 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond, static inline struct bonding *bond_get_bond_by_slave(struct slave *slave) { - if (!slave || !slave->bond) - return NULL; return slave->bond; } +static inline bool bond_should_override_tx_queue(struct bonding *bond) +{ + return BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP || + BOND_MODE(bond) == BOND_MODE_ROUNDROBIN; +} + static inline bool bond_is_lb(const struct bonding *bond) { - return BOND_MODE_IS_LB(bond->params.mode); + return BOND_MODE(bond) == BOND_MODE_TLB || + BOND_MODE(bond) == BOND_MODE_ALB; +} + +static inline bool bond_mode_uses_arp(int mode) +{ + return mode != BOND_MODE_8023AD && mode != BOND_MODE_TLB && + mode != BOND_MODE_ALB; +} + +static inline bool bond_mode_uses_primary(int mode) +{ + return mode == BOND_MODE_ACTIVEBACKUP || mode == BOND_MODE_TLB || + mode == BOND_MODE_ALB; +} + +static inline bool bond_uses_primary(struct bonding *bond) +{ + return bond_mode_uses_primary(BOND_MODE(bond)); +} + +static inline bool bond_slave_is_up(struct slave *slave) +{ + return netif_running(slave->dev) && netif_carrier_ok(slave->dev); } static inline void bond_set_active_slave(struct slave *slave) @@ -363,6 +357,12 @@ static inline bool bond_is_active_slave(struct slave *slave) return !bond_slave_state(slave); } +static inline bool bond_slave_can_tx(struct slave *slave) +{ + return bond_slave_is_up(slave) && slave->link == BOND_LINK_UP && + bond_is_active_slave(slave); +} + #define BOND_PRI_RESELECT_ALWAYS 0 #define BOND_PRI_RESELECT_BETTER 1 #define BOND_PRI_RESELECT_FAILURE 2 @@ -399,6 +399,11 @@ static inline int slave_do_arp_validate_only(struct bonding *bond) return bond->params.arp_validate & BOND_ARP_FILTER; } +static inline int bond_is_ip_target_ok(__be32 addr) +{ + return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr); +} + /* Get the oldest arp which we've received on this slave for bond's * arp_targets. */ @@ -476,15 +481,6 @@ static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be3 return addr; } -static inline bool slave_can_tx(struct slave *slave) -{ - if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP && - bond_is_active_slave(slave)) - return true; - else - return false; -} - struct bond_net { struct net *net; /* Associated network namespace */ struct list_head dev_list; diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 4aacaa9..4168822 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -65,7 +65,7 @@ config CAN_LEDS config CAN_AT91 tristate "Atmel AT91 onchip CAN controller" - depends on ARM + depends on ARCH_AT91 || COMPILE_TEST ---help--- This is a driver for the SoC CAN controller in Atmel's AT91SAM9263 and AT91SAM9X5 processors. @@ -104,7 +104,7 @@ config CAN_FLEXCAN config PCH_CAN tristate "Intel EG20T PCH CAN controller" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) ---help--- This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which is an IOH for x86 embedded processor (Intel Atom E6xx series). @@ -119,6 +119,24 @@ config CAN_GRCAN endian syntheses of the cores would need some modifications on the hardware level to work. +config CAN_RCAR + tristate "Renesas R-Car CAN controller" + depends on ARM + ---help--- + Say Y here if you want to use CAN controller found on Renesas R-Car + SoCs. + + To compile this driver as a module, choose M here: the module will + be called rcar_can. + +config CAN_XILINXCAN + tristate "Xilinx CAN" + depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST + depends on COMMON_CLK && HAS_IOMEM + ---help--- + Xilinx CAN driver. This driver supports both soft AXI CAN IP and + Zynq CANPS IP. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index c420588..1697f22 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -25,5 +25,7 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_PCH_CAN) += pch_can.o obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_RCAR) += rcar_can.o +obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig index 8ab7103..61ffc12 100644 --- a/drivers/net/can/c_can/Kconfig +++ b/drivers/net/can/c_can/Kconfig @@ -14,13 +14,6 @@ config CAN_C_CAN_PLATFORM SPEAr1310 and SPEAr320 evaluation boards & TI (www.ti.com) boards like am335x, dm814x, dm813x and dm811x. -config CAN_C_CAN_STRICT_FRAME_ORDERING - bool "Force a strict RX CAN frame order (may cause frame loss)" - ---help--- - The RX split buffer prevents packet reordering but can cause packet - loss. Only enable this option when you accept to lose CAN frames - in favour of getting the received CAN frames in the correct order. - config CAN_C_CAN_PCI tristate "Generic PCI Bus based C_CAN/D_CAN driver" depends on PCI diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index a2ca820..8e78bb4 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -252,8 +252,7 @@ static void c_can_obj_update(struct net_device *dev, int iface, u32 cmd, u32 obj struct c_can_priv *priv = netdev_priv(dev); int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface); - priv->write_reg(priv, reg + 1, cmd); - priv->write_reg(priv, reg, obj); + priv->write_reg32(priv, reg, (cmd << 16) | obj); for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) { if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY)) @@ -328,8 +327,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface, change_bit(idx, &priv->tx_dir); } - priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), arb); - priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), arb >> 16); + priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl); @@ -391,8 +389,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl) frame->can_dlc = get_can_dlc(ctrl & 0x0F); - arb = priv->read_reg(priv, C_CAN_IFACE(ARB1_REG, iface)); - arb |= priv->read_reg(priv, C_CAN_IFACE(ARB2_REG, iface)) << 16; + arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface)); if (arb & IF_ARB_MSGXTD) frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG; @@ -424,12 +421,10 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface, struct c_can_priv *priv = netdev_priv(dev); mask |= BIT(29); - priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), mask); - priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), mask >> 16); + priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask); id |= IF_ARB_MSGVAL; - priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), id); - priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), id >> 16); + priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id); priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont); c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP); @@ -732,26 +727,12 @@ static u32 c_can_adjust_pending(u32 pend) static inline void c_can_rx_object_get(struct net_device *dev, struct c_can_priv *priv, u32 obj) { -#ifdef CONFIG_CAN_C_CAN_STRICT_FRAME_ORDERING - if (obj < C_CAN_MSG_RX_LOW_LAST) - c_can_object_get(dev, IF_RX, obj, IF_COMM_RCV_LOW); - else -#endif c_can_object_get(dev, IF_RX, obj, priv->comm_rcv_high); } static inline void c_can_rx_finalize(struct net_device *dev, struct c_can_priv *priv, u32 obj) { -#ifdef CONFIG_CAN_C_CAN_STRICT_FRAME_ORDERING - if (obj < C_CAN_MSG_RX_LOW_LAST) - priv->rxmasked |= BIT(obj - 1); - else if (obj == C_CAN_MSG_RX_LOW_LAST) { - priv->rxmasked = 0; - /* activate all lower message objects */ - c_can_activate_all_lower_rx_msg_obj(dev, IF_RX); - } -#endif if (priv->type != BOSCH_D_CAN) c_can_object_get(dev, IF_RX, obj, IF_COMM_CLR_NEWDAT); } @@ -799,9 +780,6 @@ static inline u32 c_can_get_pending(struct c_can_priv *priv) { u32 pend = priv->read_reg(priv, C_CAN_NEWDAT1_REG); -#ifdef CONFIG_CAN_C_CAN_STRICT_FRAME_ORDERING - pend &= ~priv->rxmasked; -#endif return pend; } @@ -814,25 +792,6 @@ static inline u32 c_can_get_pending(struct c_can_priv *priv) * has arrived. To work-around this issue, we keep two groups of message * objects whose partitioning is defined by C_CAN_MSG_OBJ_RX_SPLIT. * - * If CONFIG_CAN_C_CAN_STRICT_FRAME_ORDERING = y - * - * To ensure in-order frame reception we use the following - * approach while re-activating a message object to receive further - * frames: - * - if the current message object number is lower than - * C_CAN_MSG_RX_LOW_LAST, do not clear the NEWDAT bit while clearing - * the INTPND bit. - * - if the current message object number is equal to - * C_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of all lower - * receive message objects. - * - if the current message object number is greater than - * C_CAN_MSG_RX_LOW_LAST then clear the NEWDAT bit of - * only this message object. - * - * This can cause packet loss! - * - * If CONFIG_CAN_C_CAN_STRICT_FRAME_ORDERING = n - * * We clear the newdat bit right away. * * This can result in packet reordering when the readout is slow. diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index c56f1b1..99ad1aa 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -78,6 +78,7 @@ enum reg { C_CAN_INTPND2_REG, C_CAN_MSGVAL1_REG, C_CAN_MSGVAL2_REG, + C_CAN_FUNCTION_REG, }; static const u16 reg_map_c_can[] = { @@ -129,6 +130,7 @@ static const u16 reg_map_d_can[] = { [C_CAN_BRPEXT_REG] = 0x0E, [C_CAN_INT_REG] = 0x10, [C_CAN_TEST_REG] = 0x14, + [C_CAN_FUNCTION_REG] = 0x18, [C_CAN_TXRQST1_REG] = 0x88, [C_CAN_TXRQST2_REG] = 0x8A, [C_CAN_NEWDAT1_REG] = 0x9C, @@ -176,8 +178,10 @@ struct c_can_priv { atomic_t tx_active; unsigned long tx_dir; int last_status; - u16 (*read_reg) (struct c_can_priv *priv, enum reg index); - void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val); + u16 (*read_reg) (const struct c_can_priv *priv, enum reg index); + void (*write_reg) (const struct c_can_priv *priv, enum reg index, u16 val); + u32 (*read_reg32) (const struct c_can_priv *priv, enum reg index); + void (*write_reg32) (const struct c_can_priv *priv, enum reg index, u32 val); void __iomem *base; const u16 *regs; void *priv; /* for board-specific data */ diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index 58f71e1..5d11e0e 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -47,42 +47,59 @@ struct c_can_pci_data { * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. * Handle the same by providing a common read/write interface. */ -static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv, +static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv, enum reg index) { return readw(priv->base + priv->regs[index]); } -static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv, +static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv, enum reg index, u16 val) { writew(val, priv->base + priv->regs[index]); } -static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv, +static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv, enum reg index) { return readw(priv->base + 2 * priv->regs[index]); } -static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv, +static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv, enum reg index, u16 val) { writew(val, priv->base + 2 * priv->regs[index]); } -static u16 c_can_pci_read_reg_32bit(struct c_can_priv *priv, +static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv, enum reg index) { return (u16)ioread32(priv->base + 2 * priv->regs[index]); } -static void c_can_pci_write_reg_32bit(struct c_can_priv *priv, +static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv, enum reg index, u16 val) { iowrite32((u32)val, priv->base + 2 * priv->regs[index]); } +static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index) +{ + u32 val; + + val = priv->read_reg(priv, index); + val |= ((u32) priv->read_reg(priv, index + 1)) << 16; + + return val; +} + +static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index, + u32 val) +{ + priv->write_reg(priv, index + 1, val >> 16); + priv->write_reg(priv, index, val); +} + static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable) { if (enable) { @@ -187,6 +204,8 @@ static int c_can_pci_probe(struct pci_dev *pdev, ret = -EINVAL; goto out_free_c_can; } + priv->read_reg32 = c_can_pci_read_reg32; + priv->write_reg32 = c_can_pci_write_reg32; priv->raminit = c_can_pci_data->init; diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 1df0b32..824108c 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -40,6 +40,7 @@ #define CAN_RAMINIT_START_MASK(i) (0x001 << (i)) #define CAN_RAMINIT_DONE_MASK(i) (0x100 << (i)) #define CAN_RAMINIT_ALL_MASK(i) (0x101 << (i)) +#define DCAN_RAM_INIT_BIT (1 << 3) static DEFINE_SPINLOCK(raminit_lock); /* * 16-bit c_can registers can be arranged differently in the memory @@ -47,31 +48,31 @@ static DEFINE_SPINLOCK(raminit_lock); * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. * Handle the same by providing a common read/write interface. */ -static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv, +static u16 c_can_plat_read_reg_aligned_to_16bit(const struct c_can_priv *priv, enum reg index) { return readw(priv->base + priv->regs[index]); } -static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv, +static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv *priv, enum reg index, u16 val) { writew(val, priv->base + priv->regs[index]); } -static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv, +static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv *priv, enum reg index) { return readw(priv->base + 2 * priv->regs[index]); } -static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv, +static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv *priv, enum reg index, u16 val) { writew(val, priv->base + 2 * priv->regs[index]); } -static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask, +static void c_can_hw_raminit_wait_ti(const struct c_can_priv *priv, u32 mask, u32 val) { /* We look only at the bits of our instance. */ @@ -80,7 +81,7 @@ static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask, udelay(1); } -static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable) +static void c_can_hw_raminit_ti(const struct c_can_priv *priv, bool enable) { u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance); u32 ctrl; @@ -96,18 +97,68 @@ static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable) ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance); writel(ctrl, priv->raminit_ctrlreg); ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance); - c_can_hw_raminit_wait(priv, ctrl, mask); + c_can_hw_raminit_wait_ti(priv, ctrl, mask); if (enable) { /* Set start bit and wait for the done bit. */ ctrl |= CAN_RAMINIT_START_MASK(priv->instance); writel(ctrl, priv->raminit_ctrlreg); ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance); - c_can_hw_raminit_wait(priv, ctrl, mask); + c_can_hw_raminit_wait_ti(priv, ctrl, mask); } spin_unlock(&raminit_lock); } +static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index) +{ + u32 val; + + val = priv->read_reg(priv, index); + val |= ((u32) priv->read_reg(priv, index + 1)) << 16; + + return val; +} + +static void c_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index, + u32 val) +{ + priv->write_reg(priv, index + 1, val >> 16); + priv->write_reg(priv, index, val); +} + +static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index) +{ + return readl(priv->base + priv->regs[index]); +} + +static void d_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index, + u32 val) +{ + writel(val, priv->base + priv->regs[index]); +} + +static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask) +{ + while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask) + udelay(1); +} + +static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable) +{ + u32 ctrl; + + ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG); + ctrl &= ~DCAN_RAM_INIT_BIT; + priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl); + c_can_hw_raminit_wait(priv, ctrl); + + if (enable) { + ctrl |= DCAN_RAM_INIT_BIT; + priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl); + c_can_hw_raminit_wait(priv, ctrl); + } +} + static struct platform_device_id c_can_id_table[] = { [BOSCH_C_CAN_PLATFORM] = { .name = KBUILD_MODNAME, @@ -201,11 +252,15 @@ static int c_can_plat_probe(struct platform_device *pdev) case IORESOURCE_MEM_32BIT: priv->read_reg = c_can_plat_read_reg_aligned_to_32bit; priv->write_reg = c_can_plat_write_reg_aligned_to_32bit; + priv->read_reg32 = c_can_plat_read_reg32; + priv->write_reg32 = c_can_plat_write_reg32; break; case IORESOURCE_MEM_16BIT: default: priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + priv->read_reg32 = c_can_plat_read_reg32; + priv->write_reg32 = c_can_plat_write_reg32; break; } break; @@ -214,6 +269,8 @@ static int c_can_plat_probe(struct platform_device *pdev) priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + priv->read_reg32 = d_can_plat_read_reg32; + priv->write_reg32 = d_can_plat_write_reg32; if (pdev->dev.of_node) priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can"); @@ -221,11 +278,20 @@ static int c_can_plat_probe(struct platform_device *pdev) priv->instance = pdev->id; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + /* Not all D_CAN modules have a separate register for the D_CAN + * RAM initialization. Use default RAM init bit in D_CAN module + * if not specified in DT. + */ + if (!res) { + priv->raminit = c_can_hw_raminit; + break; + } + priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0) dev_info(&pdev->dev, "control memory is not used for raminit\n"); else - priv->raminit = c_can_hw_raminit; + priv->raminit = c_can_hw_raminit_ti; break; default: ret = -EINVAL; diff --git a/drivers/net/can/mscan/Kconfig b/drivers/net/can/mscan/Kconfig index f19be52..81c7117 100644 --- a/drivers/net/can/mscan/Kconfig +++ b/drivers/net/can/mscan/Kconfig @@ -1,5 +1,5 @@ config CAN_MSCAN - depends on PPC || M68K + depends on PPC tristate "Support for Freescale MSCAN based chips" ---help--- The Motorola Scalable Controller Area Network (MSCAN) definition diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c new file mode 100644 index 0000000..5268d21 --- /dev/null +++ b/drivers/net/can/rcar_can.c @@ -0,0 +1,876 @@ +/* Renesas R-Car CAN device driver + * + * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com> + * Copyright (C) 2013 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/can/led.h> +#include <linux/can/dev.h> +#include <linux/clk.h> +#include <linux/can/platform/rcar_can.h> + +#define RCAR_CAN_DRV_NAME "rcar_can" + +/* Mailbox configuration: + * mailbox 60 - 63 - Rx FIFO mailboxes + * mailbox 56 - 59 - Tx FIFO mailboxes + * non-FIFO mailboxes are not used + */ +#define RCAR_CAN_N_MBX 64 /* Number of mailboxes in non-FIFO mode */ +#define RCAR_CAN_RX_FIFO_MBX 60 /* Mailbox - window to Rx FIFO */ +#define RCAR_CAN_TX_FIFO_MBX 56 /* Mailbox - window to Tx FIFO */ +#define RCAR_CAN_FIFO_DEPTH 4 + +/* Mailbox registers structure */ +struct rcar_can_mbox_regs { + u32 id; /* IDE and RTR bits, SID and EID */ + u8 stub; /* Not used */ + u8 dlc; /* Data Length Code - bits [0..3] */ + u8 data[8]; /* Data Bytes */ + u8 tsh; /* Time Stamp Higher Byte */ + u8 tsl; /* Time Stamp Lower Byte */ +}; + +struct rcar_can_regs { + struct rcar_can_mbox_regs mb[RCAR_CAN_N_MBX]; /* Mailbox registers */ + u32 mkr_2_9[8]; /* Mask Registers 2-9 */ + u32 fidcr[2]; /* FIFO Received ID Compare Register */ + u32 mkivlr1; /* Mask Invalid Register 1 */ + u32 mier1; /* Mailbox Interrupt Enable Register 1 */ + u32 mkr_0_1[2]; /* Mask Registers 0-1 */ + u32 mkivlr0; /* Mask Invalid Register 0*/ + u32 mier0; /* Mailbox Interrupt Enable Register 0 */ + u8 pad_440[0x3c0]; + u8 mctl[64]; /* Message Control Registers */ + u16 ctlr; /* Control Register */ + u16 str; /* Status register */ + u8 bcr[3]; /* Bit Configuration Register */ + u8 clkr; /* Clock Select Register */ + u8 rfcr; /* Receive FIFO Control Register */ + u8 rfpcr; /* Receive FIFO Pointer Control Register */ + u8 tfcr; /* Transmit FIFO Control Register */ + u8 tfpcr; /* Transmit FIFO Pointer Control Register */ + u8 eier; /* Error Interrupt Enable Register */ + u8 eifr; /* Error Interrupt Factor Judge Register */ + u8 recr; /* Receive Error Count Register */ + u8 tecr; /* Transmit Error Count Register */ + u8 ecsr; /* Error Code Store Register */ + u8 cssr; /* Channel Search Support Register */ + u8 mssr; /* Mailbox Search Status Register */ + u8 msmr; /* Mailbox Search Mode Register */ + u16 tsr; /* Time Stamp Register */ + u8 afsr; /* Acceptance Filter Support Register */ + u8 pad_857; + u8 tcr; /* Test Control Register */ + u8 pad_859[7]; + u8 ier; /* Interrupt Enable Register */ + u8 isr; /* Interrupt Status Register */ + u8 pad_862; + u8 mbsmr; /* Mailbox Search Mask Register */ +}; + +struct rcar_can_priv { + struct can_priv can; /* Must be the first member! */ + struct net_device *ndev; + struct napi_struct napi; + struct rcar_can_regs __iomem *regs; + struct clk *clk; + u8 tx_dlc[RCAR_CAN_FIFO_DEPTH]; + u32 tx_head; + u32 tx_tail; + u8 clock_select; + u8 ier; +}; + +static const struct can_bittiming_const rcar_can_bittiming_const = { + .name = RCAR_CAN_DRV_NAME, + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* Control Register bits */ +#define RCAR_CAN_CTLR_BOM (3 << 11) /* Bus-Off Recovery Mode Bits */ +#define RCAR_CAN_CTLR_BOM_ENT (1 << 11) /* Entry to halt mode */ + /* at bus-off entry */ +#define RCAR_CAN_CTLR_SLPM (1 << 10) +#define RCAR_CAN_CTLR_CANM (3 << 8) /* Operating Mode Select Bit */ +#define RCAR_CAN_CTLR_CANM_HALT (1 << 9) +#define RCAR_CAN_CTLR_CANM_RESET (1 << 8) +#define RCAR_CAN_CTLR_CANM_FORCE_RESET (3 << 8) +#define RCAR_CAN_CTLR_MLM (1 << 3) /* Message Lost Mode Select */ +#define RCAR_CAN_CTLR_IDFM (3 << 1) /* ID Format Mode Select Bits */ +#define RCAR_CAN_CTLR_IDFM_MIXED (1 << 2) /* Mixed ID mode */ +#define RCAR_CAN_CTLR_MBM (1 << 0) /* Mailbox Mode select */ + +/* Status Register bits */ +#define RCAR_CAN_STR_RSTST (1 << 8) /* Reset Status Bit */ + +/* FIFO Received ID Compare Registers 0 and 1 bits */ +#define RCAR_CAN_FIDCR_IDE (1 << 31) /* ID Extension Bit */ +#define RCAR_CAN_FIDCR_RTR (1 << 30) /* Remote Transmission Request Bit */ + +/* Receive FIFO Control Register bits */ +#define RCAR_CAN_RFCR_RFEST (1 << 7) /* Receive FIFO Empty Status Flag */ +#define RCAR_CAN_RFCR_RFE (1 << 0) /* Receive FIFO Enable */ + +/* Transmit FIFO Control Register bits */ +#define RCAR_CAN_TFCR_TFUST (7 << 1) /* Transmit FIFO Unsent Message */ + /* Number Status Bits */ +#define RCAR_CAN_TFCR_TFUST_SHIFT 1 /* Offset of Transmit FIFO Unsent */ + /* Message Number Status Bits */ +#define RCAR_CAN_TFCR_TFE (1 << 0) /* Transmit FIFO Enable */ + +#define RCAR_CAN_N_RX_MKREGS1 2 /* Number of mask registers */ + /* for Rx mailboxes 0-31 */ +#define RCAR_CAN_N_RX_MKREGS2 8 + +/* Bit Configuration Register settings */ +#define RCAR_CAN_BCR_TSEG1(x) (((x) & 0x0f) << 20) +#define RCAR_CAN_BCR_BPR(x) (((x) & 0x3ff) << 8) +#define RCAR_CAN_BCR_SJW(x) (((x) & 0x3) << 4) +#define RCAR_CAN_BCR_TSEG2(x) ((x) & 0x07) + +/* Mailbox and Mask Registers bits */ +#define RCAR_CAN_IDE (1 << 31) +#define RCAR_CAN_RTR (1 << 30) +#define RCAR_CAN_SID_SHIFT 18 + +/* Mailbox Interrupt Enable Register 1 bits */ +#define RCAR_CAN_MIER1_RXFIE (1 << 28) /* Receive FIFO Interrupt Enable */ +#define RCAR_CAN_MIER1_TXFIE (1 << 24) /* Transmit FIFO Interrupt Enable */ + +/* Interrupt Enable Register bits */ +#define RCAR_CAN_IER_ERSIE (1 << 5) /* Error (ERS) Interrupt Enable Bit */ +#define RCAR_CAN_IER_RXFIE (1 << 4) /* Reception FIFO Interrupt */ + /* Enable Bit */ +#define RCAR_CAN_IER_TXFIE (1 << 3) /* Transmission FIFO Interrupt */ + /* Enable Bit */ +/* Interrupt Status Register bits */ +#define RCAR_CAN_ISR_ERSF (1 << 5) /* Error (ERS) Interrupt Status Bit */ +#define RCAR_CAN_ISR_RXFF (1 << 4) /* Reception FIFO Interrupt */ + /* Status Bit */ +#define RCAR_CAN_ISR_TXFF (1 << 3) /* Transmission FIFO Interrupt */ + /* Status Bit */ + +/* Error Interrupt Enable Register bits */ +#define RCAR_CAN_EIER_BLIE (1 << 7) /* Bus Lock Interrupt Enable */ +#define RCAR_CAN_EIER_OLIE (1 << 6) /* Overload Frame Transmit */ + /* Interrupt Enable */ +#define RCAR_CAN_EIER_ORIE (1 << 5) /* Receive Overrun Interrupt Enable */ +#define RCAR_CAN_EIER_BORIE (1 << 4) /* Bus-Off Recovery Interrupt Enable */ +#define RCAR_CAN_EIER_BOEIE (1 << 3) /* Bus-Off Entry Interrupt Enable */ +#define RCAR_CAN_EIER_EPIE (1 << 2) /* Error Passive Interrupt Enable */ +#define RCAR_CAN_EIER_EWIE (1 << 1) /* Error Warning Interrupt Enable */ +#define RCAR_CAN_EIER_BEIE (1 << 0) /* Bus Error Interrupt Enable */ + +/* Error Interrupt Factor Judge Register bits */ +#define RCAR_CAN_EIFR_BLIF (1 << 7) /* Bus Lock Detect Flag */ +#define RCAR_CAN_EIFR_OLIF (1 << 6) /* Overload Frame Transmission */ + /* Detect Flag */ +#define RCAR_CAN_EIFR_ORIF (1 << 5) /* Receive Overrun Detect Flag */ +#define RCAR_CAN_EIFR_BORIF (1 << 4) /* Bus-Off Recovery Detect Flag */ +#define RCAR_CAN_EIFR_BOEIF (1 << 3) /* Bus-Off Entry Detect Flag */ +#define RCAR_CAN_EIFR_EPIF (1 << 2) /* Error Passive Detect Flag */ +#define RCAR_CAN_EIFR_EWIF (1 << 1) /* Error Warning Detect Flag */ +#define RCAR_CAN_EIFR_BEIF (1 << 0) /* Bus Error Detect Flag */ + +/* Error Code Store Register bits */ +#define RCAR_CAN_ECSR_EDPM (1 << 7) /* Error Display Mode Select Bit */ +#define RCAR_CAN_ECSR_ADEF (1 << 6) /* ACK Delimiter Error Flag */ +#define RCAR_CAN_ECSR_BE0F (1 << 5) /* Bit Error (dominant) Flag */ +#define RCAR_CAN_ECSR_BE1F (1 << 4) /* Bit Error (recessive) Flag */ +#define RCAR_CAN_ECSR_CEF (1 << 3) /* CRC Error Flag */ +#define RCAR_CAN_ECSR_AEF (1 << 2) /* ACK Error Flag */ +#define RCAR_CAN_ECSR_FEF (1 << 1) /* Form Error Flag */ +#define RCAR_CAN_ECSR_SEF (1 << 0) /* Stuff Error Flag */ + +#define RCAR_CAN_NAPI_WEIGHT 4 +#define MAX_STR_READS 0x100 + +static void tx_failure_cleanup(struct net_device *ndev) +{ + int i; + + for (i = 0; i < RCAR_CAN_FIFO_DEPTH; i++) + can_free_echo_skb(ndev, i); +} + +static void rcar_can_error(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 eifr, txerr = 0, rxerr = 0; + + /* Propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + + eifr = readb(&priv->regs->eifr); + if (eifr & (RCAR_CAN_EIFR_EWIF | RCAR_CAN_EIFR_EPIF)) { + txerr = readb(&priv->regs->tecr); + rxerr = readb(&priv->regs->recr); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } + if (eifr & RCAR_CAN_EIFR_BEIF) { + int rx_errors = 0, tx_errors = 0; + u8 ecsr; + + netdev_dbg(priv->ndev, "Bus error interrupt:\n"); + if (skb) { + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_UNSPEC; + } + ecsr = readb(&priv->regs->ecsr); + if (ecsr & RCAR_CAN_ECSR_ADEF) { + netdev_dbg(priv->ndev, "ACK Delimiter Error\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr); + if (skb) + cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL; + } + if (ecsr & RCAR_CAN_ECSR_BE0F) { + netdev_dbg(priv->ndev, "Bit Error (dominant)\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_BE0F, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_BIT0; + } + if (ecsr & RCAR_CAN_ECSR_BE1F) { + netdev_dbg(priv->ndev, "Bit Error (recessive)\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_BE1F, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_BIT1; + } + if (ecsr & RCAR_CAN_ECSR_CEF) { + netdev_dbg(priv->ndev, "CRC Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr); + if (skb) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + if (ecsr & RCAR_CAN_ECSR_AEF) { + netdev_dbg(priv->ndev, "ACK Error\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr); + if (skb) { + cf->can_id |= CAN_ERR_ACK; + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + } + } + if (ecsr & RCAR_CAN_ECSR_FEF) { + netdev_dbg(priv->ndev, "Form Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_FEF, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (ecsr & RCAR_CAN_ECSR_SEF) { + netdev_dbg(priv->ndev, "Stuff Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_SEF, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + + priv->can.can_stats.bus_error++; + ndev->stats.rx_errors += rx_errors; + ndev->stats.tx_errors += tx_errors; + writeb(~RCAR_CAN_EIFR_BEIF, &priv->regs->eifr); + } + if (eifr & RCAR_CAN_EIFR_EWIF) { + netdev_dbg(priv->ndev, "Error warning interrupt\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_EWIF, &priv->regs->eifr); + if (skb) + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } + if (eifr & RCAR_CAN_EIFR_EPIF) { + netdev_dbg(priv->ndev, "Error passive interrupt\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_EPIF, &priv->regs->eifr); + if (skb) + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + if (eifr & RCAR_CAN_EIFR_BOEIF) { + netdev_dbg(priv->ndev, "Bus-off entry interrupt\n"); + tx_failure_cleanup(ndev); + priv->ier = RCAR_CAN_IER_ERSIE; + writeb(priv->ier, &priv->regs->ier); + priv->can.state = CAN_STATE_BUS_OFF; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr); + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } + if (eifr & RCAR_CAN_EIFR_ORIF) { + netdev_dbg(priv->ndev, "Receive overrun error interrupt\n"); + ndev->stats.rx_over_errors++; + ndev->stats.rx_errors++; + writeb(~RCAR_CAN_EIFR_ORIF, &priv->regs->eifr); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + } + if (eifr & RCAR_CAN_EIFR_OLIF) { + netdev_dbg(priv->ndev, + "Overload Frame Transmission error interrupt\n"); + ndev->stats.rx_over_errors++; + ndev->stats.rx_errors++; + writeb(~RCAR_CAN_EIFR_OLIF, &priv->regs->eifr); + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + } + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +static void rcar_can_tx_done(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u8 isr; + + while (1) { + u8 unsent = readb(&priv->regs->tfcr); + + unsent = (unsent & RCAR_CAN_TFCR_TFUST) >> + RCAR_CAN_TFCR_TFUST_SHIFT; + if (priv->tx_head - priv->tx_tail <= unsent) + break; + stats->tx_packets++; + stats->tx_bytes += priv->tx_dlc[priv->tx_tail % + RCAR_CAN_FIFO_DEPTH]; + priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0; + can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH); + priv->tx_tail++; + netif_wake_queue(ndev); + } + /* Clear interrupt */ + isr = readb(&priv->regs->isr); + writeb(isr & ~RCAR_CAN_ISR_TXFF, &priv->regs->isr); + can_led_event(ndev, CAN_LED_EVENT_TX); +} + +static irqreturn_t rcar_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct rcar_can_priv *priv = netdev_priv(ndev); + u8 isr; + + isr = readb(&priv->regs->isr); + if (!(isr & priv->ier)) + return IRQ_NONE; + + if (isr & RCAR_CAN_ISR_ERSF) + rcar_can_error(ndev); + + if (isr & RCAR_CAN_ISR_TXFF) + rcar_can_tx_done(ndev); + + if (isr & RCAR_CAN_ISR_RXFF) { + if (napi_schedule_prep(&priv->napi)) { + /* Disable Rx FIFO interrupts */ + priv->ier &= ~RCAR_CAN_IER_RXFIE; + writeb(priv->ier, &priv->regs->ier); + __napi_schedule(&priv->napi); + } + } + + return IRQ_HANDLED; +} + +static void rcar_can_set_bittiming(struct net_device *dev) +{ + struct rcar_can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 bcr; + + bcr = RCAR_CAN_BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) | + RCAR_CAN_BCR_BPR(bt->brp - 1) | RCAR_CAN_BCR_SJW(bt->sjw - 1) | + RCAR_CAN_BCR_TSEG2(bt->phase_seg2 - 1); + /* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access. + * All the registers are big-endian but they get byte-swapped on 32-bit + * read/write (but not on 8-bit, contrary to the manuals)... + */ + writel((bcr << 8) | priv->clock_select, &priv->regs->bcr); +} + +static void rcar_can_start(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int i; + + /* Set controller to known mode: + * - FIFO mailbox mode + * - accept all messages + * - overrun mode + * CAN is in sleep mode after MCU hardware or software reset. + */ + ctlr = readw(&priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + /* Go to reset mode */ + ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET; + writew(ctlr, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST) + break; + } + rcar_can_set_bittiming(ndev); + ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */ + ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically */ + /* at bus-off */ + ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */ + ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */ + writew(ctlr, &priv->regs->ctlr); + + /* Accept all SID and EID */ + writel(0, &priv->regs->mkr_2_9[6]); + writel(0, &priv->regs->mkr_2_9[7]); + /* In FIFO mailbox mode, write "0" to bits 24 to 31 */ + writel(0, &priv->regs->mkivlr1); + /* Accept all frames */ + writel(0, &priv->regs->fidcr[0]); + writel(RCAR_CAN_FIDCR_IDE | RCAR_CAN_FIDCR_RTR, &priv->regs->fidcr[1]); + /* Enable and configure FIFO mailbox interrupts */ + writel(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE, &priv->regs->mier1); + + priv->ier = RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE | + RCAR_CAN_IER_TXFIE; + writeb(priv->ier, &priv->regs->ier); + + /* Accumulate error codes */ + writeb(RCAR_CAN_ECSR_EDPM, &priv->regs->ecsr); + /* Enable error interrupts */ + writeb(RCAR_CAN_EIER_EWIE | RCAR_CAN_EIER_EPIE | RCAR_CAN_EIER_BOEIE | + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ? + RCAR_CAN_EIER_BEIE : 0) | RCAR_CAN_EIER_ORIE | + RCAR_CAN_EIER_OLIE, &priv->regs->eier); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Go to operation mode */ + writew(ctlr & ~RCAR_CAN_CTLR_CANM, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)) + break; + } + /* Enable Rx and Tx FIFO */ + writeb(RCAR_CAN_RFCR_RFE, &priv->regs->rfcr); + writeb(RCAR_CAN_TFCR_TFE, &priv->regs->tfcr); +} + +static int rcar_can_open(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(ndev, "clk_prepare_enable() failed, error %d\n", + err); + goto out; + } + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto out_clock; + } + napi_enable(&priv->napi); + err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev); + if (err) { + netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq); + goto out_close; + } + can_led_event(ndev, CAN_LED_EVENT_OPEN); + rcar_can_start(ndev); + netif_start_queue(ndev); + return 0; +out_close: + napi_disable(&priv->napi); + close_candev(ndev); +out_clock: + clk_disable_unprepare(priv->clk); +out: + return err; +} + +static void rcar_can_stop(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int i; + + /* Go to (force) reset mode */ + ctlr = readw(&priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET; + writew(ctlr, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST) + break; + } + writel(0, &priv->regs->mier0); + writel(0, &priv->regs->mier1); + writeb(0, &priv->regs->ier); + writeb(0, &priv->regs->eier); + /* Go to sleep mode */ + ctlr |= RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_STOPPED; +} + +static int rcar_can_close(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + rcar_can_stop(ndev); + free_irq(ndev->irq, ndev); + napi_disable(&priv->napi); + clk_disable_unprepare(priv->clk); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + return 0; +} + +static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + u32 data, i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE; + else /* Standard frame format */ + data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT; + + if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */ + data |= RCAR_CAN_RTR; + } else { + for (i = 0; i < cf->can_dlc; i++) + writeb(cf->data[i], + &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]); + } + + writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id); + + writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc); + + priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc; + can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH); + priv->tx_head++; + /* Start Tx: write 0xff to the TFPCR register to increment + * the CPU-side pointer for the transmit FIFO to the next + * mailbox location + */ + writeb(0xff, &priv->regs->tfpcr); + /* Stop the queue if we've filled all FIFO entries */ + if (priv->tx_head - priv->tx_tail >= RCAR_CAN_FIFO_DEPTH) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops rcar_can_netdev_ops = { + .ndo_open = rcar_can_open, + .ndo_stop = rcar_can_close, + .ndo_start_xmit = rcar_can_start_xmit, +}; + +static void rcar_can_rx_pkt(struct rcar_can_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 data; + u8 dlc; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + data = readl(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].id); + if (data & RCAR_CAN_IDE) + cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK; + + dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc); + cf->can_dlc = get_can_dlc(dlc); + if (data & RCAR_CAN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (dlc = 0; dlc < cf->can_dlc; dlc++) + cf->data[dlc] = + readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]); + } + + can_led_event(priv->ndev, CAN_LED_EVENT_RX); + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_receive_skb(skb); +} + +static int rcar_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct rcar_can_priv *priv = container_of(napi, + struct rcar_can_priv, napi); + int num_pkts; + + for (num_pkts = 0; num_pkts < quota; num_pkts++) { + u8 rfcr, isr; + + isr = readb(&priv->regs->isr); + /* Clear interrupt bit */ + if (isr & RCAR_CAN_ISR_RXFF) + writeb(isr & ~RCAR_CAN_ISR_RXFF, &priv->regs->isr); + rfcr = readb(&priv->regs->rfcr); + if (rfcr & RCAR_CAN_RFCR_RFEST) + break; + rcar_can_rx_pkt(priv); + /* Write 0xff to the RFPCR register to increment + * the CPU-side pointer for the receive FIFO + * to the next mailbox location + */ + writeb(0xff, &priv->regs->rfpcr); + } + /* All packets processed */ + if (num_pkts < quota) { + napi_complete(napi); + priv->ier |= RCAR_CAN_IER_RXFIE; + writeb(priv->ier, &priv->regs->ier); + } + return num_pkts; +} + +static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + rcar_can_start(ndev); + netif_wake_queue(ndev); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int rcar_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct rcar_can_priv *priv = netdev_priv(dev); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + bec->txerr = readb(&priv->regs->tecr); + bec->rxerr = readb(&priv->regs->recr); + clk_disable_unprepare(priv->clk); + return 0; +} + +static int rcar_can_probe(struct platform_device *pdev) +{ + struct rcar_can_platform_data *pdata; + struct rcar_can_priv *priv; + struct net_device *ndev; + struct resource *mem; + void __iomem *addr; + int err = -ENODEV; + int irq; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + goto fail; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "No IRQ resource\n"); + goto fail; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = PTR_ERR(addr); + goto fail; + } + + ndev = alloc_candev(sizeof(struct rcar_can_priv), RCAR_CAN_FIFO_DEPTH); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev() failed\n"); + err = -ENOMEM; + goto fail; + } + + priv = netdev_priv(ndev); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + err = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "cannot get clock: %d\n", err); + goto fail_clk; + } + + ndev->netdev_ops = &rcar_can_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + priv->ndev = ndev; + priv->regs = addr; + priv->clock_select = pdata->clock_select; + priv->can.clock.freq = clk_get_rate(priv->clk); + priv->can.bittiming_const = &rcar_can_bittiming_const; + priv->can.do_set_mode = rcar_can_do_set_mode; + priv->can.do_get_berr_counter = rcar_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll, + RCAR_CAN_NAPI_WEIGHT); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed, error %d\n", + err); + goto fail_candev; + } + + devm_can_led_init(ndev); + + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", + priv->regs, ndev->irq); + + return 0; +fail_candev: + netif_napi_del(&priv->napi); +fail_clk: + free_candev(ndev); +fail: + return err; +} + +static int rcar_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct rcar_can_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); + return 0; +} + +static int __maybe_unused rcar_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + } + ctlr = readw(&priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_CANM_HALT; + writew(ctlr, &priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + return 0; +} + +static int __maybe_unused rcar_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int err; + + err = clk_enable(priv->clk); + if (err) { + netdev_err(ndev, "clk_enable() failed, error %d\n", err); + return err; + } + + ctlr = readw(&priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_CANM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + netif_device_attach(ndev); + netif_start_queue(ndev); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume); + +static struct platform_driver rcar_can_driver = { + .driver = { + .name = RCAR_CAN_DRV_NAME, + .owner = THIS_MODULE, + .pm = &rcar_can_pm_ops, + }, + .probe = rcar_can_probe, + .remove = rcar_can_remove, +}; + +module_platform_driver(rcar_can_driver); + +MODULE_AUTHOR("Cogent Embedded, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC"); +MODULE_ALIAS("platform:" RCAR_CAN_DRV_NAME); diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index c540e3d..564933a 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -551,7 +551,7 @@ static int peak_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct sja1000_priv *priv; struct peak_pci_chan *chan; - struct net_device *dev; + struct net_device *dev, *prev_dev; void __iomem *cfg_base, *reg_base; u16 sub_sys_id, icr; int i, err, channels; @@ -688,11 +688,13 @@ failure_remove_channels: writew(0x0, cfg_base + PITA_ICR + 2); chan = NULL; - for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { - unregister_sja1000dev(dev); - free_sja1000dev(dev); + for (dev = pci_get_drvdata(pdev); dev; dev = prev_dev) { priv = netdev_priv(dev); chan = priv->priv; + prev_dev = chan->prev_dev; + + unregister_sja1000dev(dev); + free_sja1000dev(dev); } /* free any PCIeC resources too */ @@ -726,10 +728,12 @@ static void peak_pci_remove(struct pci_dev *pdev) /* Loop over all registered devices */ while (1) { + struct net_device *prev_dev = chan->prev_dev; + dev_info(&pdev->dev, "removing device %s\n", dev->name); unregister_sja1000dev(dev); free_sja1000dev(dev); - dev = chan->prev_dev; + dev = prev_dev; if (!dev) { /* do that only for first channel */ diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index bc235f9..5df239e 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -951,7 +951,7 @@ static int mcp251x_open(struct net_device *net) priv->tx_len = 0; ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, - flags, DEVICE_NAME, priv); + flags | IRQF_ONESHOT, DEVICE_NAME, priv); if (ret) { dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); mcp251x_power_enable(priv->transceiver, 0); diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 0b918eb..a77db919 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -13,6 +13,14 @@ config CAN_ESD_USB2 This driver supports the CAN-USB/2 interface from esd electronic system design gmbh (http://www.esd.eu). +config CAN_GS_USB + tristate "Geschwister Schneider UG interfaces" + ---help--- + This driver supports the Geschwister Schneider USB/CAN devices. + If unsure choose N, + choose Y for built in support, + M to compile as module (module will be named: gs_usb). + config CAN_KVASER_USB tristate "Kvaser CAN/USB interface" ---help--- diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index becef46..7b9a393 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_GS_USB) += gs_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c new file mode 100644 index 0000000..04b0f84 --- /dev/null +++ b/drivers/net/can/usb/gs_usb.c @@ -0,0 +1,971 @@ +/* CAN driver for Geschwister Schneider USB/CAN devices. + * + * Copyright (C) 2013 Geschwister Schneider Technologie-, + * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). + * + * Many thanks to all socketcan devs! + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +/* Device specific constants */ +#define USB_GSUSB_1_VENDOR_ID 0x1d50 +#define USB_GSUSB_1_PRODUCT_ID 0x606f + +#define GSUSB_ENDPOINT_IN 1 +#define GSUSB_ENDPOINT_OUT 2 + +/* Device specific constants */ +enum gs_usb_breq { + GS_USB_BREQ_HOST_FORMAT = 0, + GS_USB_BREQ_BITTIMING, + GS_USB_BREQ_MODE, + GS_USB_BREQ_BERR, + GS_USB_BREQ_BT_CONST, + GS_USB_BREQ_DEVICE_CONFIG +}; + +enum gs_can_mode { + /* reset a channel. turns it off */ + GS_CAN_MODE_RESET = 0, + /* starts a channel */ + GS_CAN_MODE_START +}; + +enum gs_can_state { + GS_CAN_STATE_ERROR_ACTIVE = 0, + GS_CAN_STATE_ERROR_WARNING, + GS_CAN_STATE_ERROR_PASSIVE, + GS_CAN_STATE_BUS_OFF, + GS_CAN_STATE_STOPPED, + GS_CAN_STATE_SLEEPING +}; + +/* data types passed between host and device */ +struct gs_host_config { + u32 byte_order; +} __packed; +/* All data exchanged between host and device is exchanged in host byte order, + * thanks to the struct gs_host_config byte_order member, which is sent first + * to indicate the desired byte order. + */ + +struct gs_device_config { + u8 reserved1; + u8 reserved2; + u8 reserved3; + u8 icount; + u32 sw_version; + u32 hw_version; +} __packed; + +#define GS_CAN_MODE_NORMAL 0 +#define GS_CAN_MODE_LISTEN_ONLY (1<<0) +#define GS_CAN_MODE_LOOP_BACK (1<<1) +#define GS_CAN_MODE_TRIPLE_SAMPLE (1<<2) +#define GS_CAN_MODE_ONE_SHOT (1<<3) + +struct gs_device_mode { + u32 mode; + u32 flags; +} __packed; + +struct gs_device_state { + u32 state; + u32 rxerr; + u32 txerr; +} __packed; + +struct gs_device_bittiming { + u32 prop_seg; + u32 phase_seg1; + u32 phase_seg2; + u32 sjw; + u32 brp; +} __packed; + +#define GS_CAN_FEATURE_LISTEN_ONLY (1<<0) +#define GS_CAN_FEATURE_LOOP_BACK (1<<1) +#define GS_CAN_FEATURE_TRIPLE_SAMPLE (1<<2) +#define GS_CAN_FEATURE_ONE_SHOT (1<<3) + +struct gs_device_bt_const { + u32 feature; + u32 fclk_can; + u32 tseg1_min; + u32 tseg1_max; + u32 tseg2_min; + u32 tseg2_max; + u32 sjw_max; + u32 brp_min; + u32 brp_max; + u32 brp_inc; +} __packed; + +#define GS_CAN_FLAG_OVERFLOW 1 + +struct gs_host_frame { + u32 echo_id; + u32 can_id; + + u8 can_dlc; + u8 channel; + u8 flags; + u8 reserved; + + u8 data[8]; +} __packed; +/* The GS USB devices make use of the same flags and masks as in + * linux/can.h and linux/can/error.h, and no additional mapping is necessary. + */ + +/* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */ +#define GS_MAX_TX_URBS 10 +/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ +#define GS_MAX_RX_URBS 30 +/* Maximum number of interfaces the driver supports per device. + * Current hardware only supports 2 interfaces. The future may vary. + */ +#define GS_MAX_INTF 2 + +struct gs_tx_context { + struct gs_can *dev; + unsigned int echo_id; +}; + +struct gs_can { + struct can_priv can; /* must be the first member */ + + struct gs_usb *parent; + + struct net_device *netdev; + struct usb_device *udev; + struct usb_interface *iface; + + struct can_bittiming_const bt_const; + unsigned int channel; /* channel number */ + + /* This lock prevents a race condition between xmit and recieve. */ + spinlock_t tx_ctx_lock; + struct gs_tx_context tx_context[GS_MAX_TX_URBS]; + + struct usb_anchor tx_submitted; + atomic_t active_tx_urbs; +}; + +/* usb interface struct */ +struct gs_usb { + struct gs_can *canch[GS_MAX_INTF]; + struct usb_anchor rx_submitted; + atomic_t active_channels; + struct usb_device *udev; +}; + +/* 'allocate' a tx context. + * returns a valid tx context or NULL if there is no space. + */ +static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev) +{ + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&dev->tx_ctx_lock, flags); + + for (; i < GS_MAX_TX_URBS; i++) { + if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) { + dev->tx_context[i].echo_id = i; + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return &dev->tx_context[i]; + } + } + + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return NULL; +} + +/* releases a tx context + */ +static void gs_free_tx_context(struct gs_tx_context *txc) +{ + txc->echo_id = GS_MAX_TX_URBS; +} + +/* Get a tx context by id. + */ +static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, unsigned int id) +{ + unsigned long flags; + + if (id < GS_MAX_TX_URBS) { + spin_lock_irqsave(&dev->tx_ctx_lock, flags); + if (dev->tx_context[id].echo_id == id) { + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + return &dev->tx_context[id]; + } + spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); + } + return NULL; +} + +static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev) +{ + struct gs_device_mode *dm; + struct usb_interface *intf = gsdev->iface; + int rc; + + dm = kzalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return -ENOMEM; + + dm->mode = GS_CAN_MODE_RESET; + + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_MODE, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + gsdev->channel, + 0, + dm, + sizeof(*dm), + 1000); + + return rc; +} + +static void gs_update_state(struct gs_can *dev, struct can_frame *cf) +{ + struct can_device_stats *can_stats = &dev->can.can_stats; + + if (cf->can_id & CAN_ERR_RESTARTED) { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + can_stats->restarts++; + } else if (cf->can_id & CAN_ERR_BUSOFF) { + dev->can.state = CAN_STATE_BUS_OFF; + can_stats->bus_off++; + } else if (cf->can_id & CAN_ERR_CRTL) { + if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) || + (cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) { + dev->can.state = CAN_STATE_ERROR_WARNING; + can_stats->error_warning++; + } else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) || + (cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) { + dev->can.state = CAN_STATE_ERROR_PASSIVE; + can_stats->error_passive++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + } + } +} + +static void gs_usb_recieve_bulk_callback(struct urb *urb) +{ + struct gs_usb *usbcan = urb->context; + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct net_device_stats *stats; + struct gs_host_frame *hf = urb->transfer_buffer; + struct gs_tx_context *txc; + struct can_frame *cf; + struct sk_buff *skb; + + BUG_ON(!usbcan); + + switch (urb->status) { + case 0: /* success */ + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + /* do not resubmit aborted urbs. eg: when device goes down */ + return; + } + + /* device reports out of range channel id */ + if (hf->channel >= GS_MAX_INTF) + goto resubmit_urb; + + dev = usbcan->canch[hf->channel]; + + netdev = dev->netdev; + stats = &netdev->stats; + + if (!netif_device_present(netdev)) + return; + + if (hf->echo_id == -1) { /* normal rx */ + skb = alloc_can_skb(dev->netdev, &cf); + if (!skb) + return; + + cf->can_id = hf->can_id; + + cf->can_dlc = get_can_dlc(hf->can_dlc); + memcpy(cf->data, hf->data, 8); + + /* ERROR frames tell us information about the controller */ + if (hf->can_id & CAN_ERR_FLAG) + gs_update_state(dev, cf); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += hf->can_dlc; + + netif_rx(skb); + } else { /* echo_id == hf->echo_id */ + if (hf->echo_id >= GS_MAX_TX_URBS) { + netdev_err(netdev, + "Unexpected out of range echo id %d\n", + hf->echo_id); + goto resubmit_urb; + } + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += hf->can_dlc; + + txc = gs_get_tx_context(dev, hf->echo_id); + + /* bad devices send bad echo_ids. */ + if (!txc) { + netdev_err(netdev, + "Unexpected unused echo id %d\n", + hf->echo_id); + goto resubmit_urb; + } + + can_get_echo_skb(netdev, hf->echo_id); + + gs_free_tx_context(txc); + + netif_wake_queue(netdev); + } + + if (hf->flags & GS_CAN_FLAG_OVERFLOW) { + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) + goto resubmit_urb; + + cf->can_id |= CAN_ERR_CRTL; + cf->can_dlc = CAN_ERR_DLC; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + netif_rx(skb); + } + + resubmit_urb: + usb_fill_bulk_urb(urb, + usbcan->udev, + usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN), + hf, + sizeof(struct gs_host_frame), + gs_usb_recieve_bulk_callback, + usbcan + ); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + + /* USB failure take down all interfaces */ + if (rc == -ENODEV) { + for (rc = 0; rc < GS_MAX_INTF; rc++) { + if (usbcan->canch[rc]) + netif_device_detach(usbcan->canch[rc]->netdev); + } + } +} + +static int gs_usb_set_bittiming(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + struct usb_interface *intf = dev->iface; + int rc; + struct gs_device_bittiming *dbt; + + dbt = kmalloc(sizeof(*dbt), GFP_KERNEL); + if (!dbt) + return -ENOMEM; + + dbt->prop_seg = bt->prop_seg; + dbt->phase_seg1 = bt->phase_seg1; + dbt->phase_seg2 = bt->phase_seg2; + dbt->sjw = bt->sjw; + dbt->brp = bt->brp; + + /* request bit timings */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BITTIMING, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + dev->channel, + 0, + dbt, + sizeof(*dbt), + 1000); + + kfree(dbt); + + if (rc < 0) + dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)", + rc); + + return rc; +} + +static void gs_usb_xmit_callback(struct urb *urb) +{ + struct gs_tx_context *txc = urb->context; + struct gs_can *dev = txc->dev; + struct net_device *netdev = dev->netdev; + + if (urb->status) + netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id); + + usb_free_coherent(urb->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); +} + +static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct net_device_stats *stats = &dev->netdev->stats; + struct urb *urb; + struct gs_host_frame *hf; + struct can_frame *cf; + int rc; + unsigned int idx; + struct gs_tx_context *txc; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + /* find an empty context to keep track of transmission */ + txc = gs_alloc_tx_context(dev); + if (!txc) + return NETDEV_TX_BUSY; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URB\n"); + goto nomem_urb; + } + + hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC, + &urb->transfer_dma); + if (!hf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + goto nomem_hf; + } + + idx = txc->echo_id; + + if (idx >= GS_MAX_TX_URBS) { + netdev_err(netdev, "Invalid tx context %d\n", idx); + goto badidx; + } + + hf->echo_id = idx; + hf->channel = dev->channel; + + cf = (struct can_frame *)skb->data; + + hf->can_id = cf->can_id; + hf->can_dlc = cf->can_dlc; + memcpy(hf->data, cf->data, cf->can_dlc); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT), + hf, + sizeof(*hf), + gs_usb_xmit_callback, + txc); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, idx); + + atomic_inc(&dev->active_tx_urbs); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(rc)) { /* usb send failed */ + atomic_dec(&dev->active_tx_urbs); + + can_free_echo_skb(netdev, idx); + gs_free_tx_context(txc); + + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, + sizeof(*hf), + hf, + urb->transfer_dma); + + + if (rc == -ENODEV) { + netif_device_detach(netdev); + } else { + netdev_err(netdev, "usb_submit failed (err=%d)\n", rc); + stats->tx_dropped++; + } + } else { + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + /* let usb core take care of this urb */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + + badidx: + usb_free_coherent(dev->udev, + sizeof(*hf), + hf, + urb->transfer_dma); + nomem_hf: + usb_free_urb(urb); + + nomem_urb: + gs_free_tx_context(txc); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; +} + +static int gs_can_open(struct net_device *netdev) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_usb *parent = dev->parent; + int rc, i; + struct gs_device_mode *dm; + u32 ctrlmode; + + rc = open_candev(netdev); + if (rc) + return rc; + + if (atomic_add_return(1, &parent->active_channels) == 1) { + for (i = 0; i < GS_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + /* alloc rx urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, + "No memory left for URB\n"); + return -ENOMEM; + } + + /* alloc rx buffer */ + buf = usb_alloc_coherent(dev->udev, + sizeof(struct gs_host_frame), + GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + /* fill, anchor, and submit rx urb */ + usb_fill_bulk_urb(urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, + GSUSB_ENDPOINT_IN), + buf, + sizeof(struct gs_host_frame), + gs_usb_recieve_bulk_callback, + parent); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &parent->rx_submitted); + + rc = usb_submit_urb(urb, GFP_KERNEL); + if (rc) { + if (rc == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_err(netdev, + "usb_submit failed (err=%d)\n", + rc); + + usb_unanchor_urb(urb); + break; + } + + /* Drop reference, + * USB core will take care of freeing it + */ + usb_free_urb(urb); + } + } + + dm = kmalloc(sizeof(*dm), GFP_KERNEL); + if (!dm) + return -ENOMEM; + + /* flags */ + ctrlmode = dev->can.ctrlmode; + dm->flags = 0; + + if (ctrlmode & CAN_CTRLMODE_LOOPBACK) + dm->flags |= GS_CAN_MODE_LOOP_BACK; + else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + dm->flags |= GS_CAN_MODE_LISTEN_ONLY; + + /* Controller is not allowed to retry TX + * this mode is unavailable on atmels uc3c hardware + */ + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) + dm->flags |= GS_CAN_MODE_ONE_SHOT; + + if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) + dm->flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + + /* finally start device */ + dm->mode = GS_CAN_MODE_START; + rc = usb_control_msg(interface_to_usbdev(dev->iface), + usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0), + GS_USB_BREQ_MODE, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + dev->channel, + 0, + dm, + sizeof(*dm), + 1000); + + if (rc < 0) { + netdev_err(netdev, "Couldn't start device (err=%d)\n", rc); + kfree(dm); + return rc; + } + + kfree(dm); + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_start_queue(netdev); + + return 0; +} + +static int gs_can_close(struct net_device *netdev) +{ + int rc; + struct gs_can *dev = netdev_priv(netdev); + struct gs_usb *parent = dev->parent; + + netif_stop_queue(netdev); + + /* Stop polling */ + if (atomic_dec_and_test(&parent->active_channels)) + usb_kill_anchored_urbs(&parent->rx_submitted); + + /* Stop sending URBs */ + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + /* reset the device */ + rc = gs_cmd_reset(parent, dev); + if (rc < 0) + netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc); + + /* reset tx contexts */ + for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { + dev->tx_context[rc].dev = dev; + dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; + } + + /* close the netdev */ + close_candev(netdev); + + return 0; +} + +static const struct net_device_ops gs_usb_netdev_ops = { + .ndo_open = gs_can_open, + .ndo_stop = gs_can_close, + .ndo_start_xmit = gs_can_start_xmit, +}; + +static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf) +{ + struct gs_can *dev; + struct net_device *netdev; + int rc; + struct gs_device_bt_const *bt_const; + + bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL); + if (!bt_const) + return ERR_PTR(-ENOMEM); + + /* fetch bit timing constants */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_BT_CONST, + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + channel, + 0, + bt_const, + sizeof(*bt_const), + 1000); + + if (rc < 0) { + dev_err(&intf->dev, + "Couldn't get bit timing const for channel (err=%d)\n", + rc); + kfree(bt_const); + return ERR_PTR(rc); + } + + /* create netdev */ + netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't allocate candev\n"); + kfree(bt_const); + return ERR_PTR(-ENOMEM); + } + + dev = netdev_priv(netdev); + + netdev->netdev_ops = &gs_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ + + /* dev settup */ + strcpy(dev->bt_const.name, "gs_usb"); + dev->bt_const.tseg1_min = bt_const->tseg1_min; + dev->bt_const.tseg1_max = bt_const->tseg1_max; + dev->bt_const.tseg2_min = bt_const->tseg2_min; + dev->bt_const.tseg2_max = bt_const->tseg2_max; + dev->bt_const.sjw_max = bt_const->sjw_max; + dev->bt_const.brp_min = bt_const->brp_min; + dev->bt_const.brp_max = bt_const->brp_max; + dev->bt_const.brp_inc = bt_const->brp_inc; + + dev->udev = interface_to_usbdev(intf); + dev->iface = intf; + dev->netdev = netdev; + dev->channel = channel; + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + spin_lock_init(&dev->tx_ctx_lock); + for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { + dev->tx_context[rc].dev = dev; + dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; + } + + /* can settup */ + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = bt_const->fclk_can; + dev->can.bittiming_const = &dev->bt_const; + dev->can.do_set_bittiming = gs_usb_set_bittiming; + + dev->can.ctrlmode_supported = 0; + + if (bt_const->feature & GS_CAN_FEATURE_LISTEN_ONLY) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + if (bt_const->feature & GS_CAN_FEATURE_LOOP_BACK) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; + + if (bt_const->feature & GS_CAN_FEATURE_TRIPLE_SAMPLE) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + + if (bt_const->feature & GS_CAN_FEATURE_ONE_SHOT) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; + + kfree(bt_const); + + SET_NETDEV_DEV(netdev, &intf->dev); + + rc = register_candev(dev->netdev); + if (rc) { + free_candev(dev->netdev); + dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc); + return ERR_PTR(rc); + } + + return dev; +} + +static void gs_destroy_candev(struct gs_can *dev) +{ + unregister_candev(dev->netdev); + free_candev(dev->netdev); + usb_kill_anchored_urbs(&dev->tx_submitted); + kfree(dev); +} + +static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct gs_usb *dev; + int rc = -ENOMEM; + unsigned int icount, i; + struct gs_host_config *hconf; + struct gs_device_config *dconf; + + hconf = kmalloc(sizeof(*hconf), GFP_KERNEL); + if (!hconf) + return -ENOMEM; + + hconf->byte_order = 0x0000beef; + + /* send host config */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_HOST_FORMAT, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + 1, + intf->altsetting[0].desc.bInterfaceNumber, + hconf, + sizeof(*hconf), + 1000); + + kfree(hconf); + + if (rc < 0) { + dev_err(&intf->dev, "Couldn't send data format (err=%d)\n", + rc); + return rc; + } + + dconf = kmalloc(sizeof(*dconf), GFP_KERNEL); + if (!dconf) + return -ENOMEM; + + /* read device config */ + rc = usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + GS_USB_BREQ_DEVICE_CONFIG, + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + 1, + intf->altsetting[0].desc.bInterfaceNumber, + dconf, + sizeof(*dconf), + 1000); + if (rc < 0) { + dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n", + rc); + + kfree(dconf); + + return rc; + } + + icount = dconf->icount+1; + + kfree(dconf); + + dev_info(&intf->dev, "Configuring for %d interfaces\n", icount); + + if (icount > GS_MAX_INTF) { + dev_err(&intf->dev, + "Driver cannot handle more that %d CAN interfaces\n", + GS_MAX_INTF); + return -EINVAL; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + init_usb_anchor(&dev->rx_submitted); + + atomic_set(&dev->active_channels, 0); + + usb_set_intfdata(intf, dev); + dev->udev = interface_to_usbdev(intf); + + for (i = 0; i < icount; i++) { + dev->canch[i] = gs_make_candev(i, intf); + if (IS_ERR_OR_NULL(dev->canch[i])) { + /* on failure destroy previously created candevs */ + icount = i; + for (i = 0; i < icount; i++) { + gs_destroy_candev(dev->canch[i]); + dev->canch[i] = NULL; + } + kfree(dev); + return rc; + } + dev->canch[i]->parent = dev; + } + + return 0; +} + +static void gs_usb_disconnect(struct usb_interface *intf) +{ + unsigned i; + struct gs_usb *dev = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + if (!dev) { + dev_err(&intf->dev, "Disconnect (nodata)\n"); + return; + } + + for (i = 0; i < GS_MAX_INTF; i++) { + struct gs_can *can = dev->canch[i]; + + if (!can) + continue; + + gs_destroy_candev(can); + } + + usb_kill_anchored_urbs(&dev->rx_submitted); +} + +static const struct usb_device_id gs_usb_table[] = { + {USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, gs_usb_table); + +static struct usb_driver gs_usb_driver = { + .name = "gs_usb", + .probe = gs_usb_probe, + .disconnect = gs_usb_disconnect, + .id_table = gs_usb_table, +}; + +module_usb_driver(gs_usb_driver); + +MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>"); +MODULE_DESCRIPTION( +"Socket CAN device driver for Geschwister Schneider Technologie-, " +"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c new file mode 100644 index 0000000..5e8b560 --- /dev/null +++ b/drivers/net/can/xilinx_can.c @@ -0,0 +1,1208 @@ +/* Xilinx CAN device driver + * + * Copyright (C) 2012 - 2014 Xilinx, Inc. + * Copyright (C) 2009 PetaLogix. All rights reserved. + * + * Description: + * This driver is developed for Axi CAN IP and for Zynq CANPS Controller. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> + +#define DRIVER_NAME "xilinx_can" + +/* CAN registers set */ +enum xcan_reg { + XCAN_SRR_OFFSET = 0x00, /* Software reset */ + XCAN_MSR_OFFSET = 0x04, /* Mode select */ + XCAN_BRPR_OFFSET = 0x08, /* Baud rate prescaler */ + XCAN_BTR_OFFSET = 0x0C, /* Bit timing */ + XCAN_ECR_OFFSET = 0x10, /* Error counter */ + XCAN_ESR_OFFSET = 0x14, /* Error status */ + XCAN_SR_OFFSET = 0x18, /* Status */ + XCAN_ISR_OFFSET = 0x1C, /* Interrupt status */ + XCAN_IER_OFFSET = 0x20, /* Interrupt enable */ + XCAN_ICR_OFFSET = 0x24, /* Interrupt clear */ + XCAN_TXFIFO_ID_OFFSET = 0x30,/* TX FIFO ID */ + XCAN_TXFIFO_DLC_OFFSET = 0x34, /* TX FIFO DLC */ + XCAN_TXFIFO_DW1_OFFSET = 0x38, /* TX FIFO Data Word 1 */ + XCAN_TXFIFO_DW2_OFFSET = 0x3C, /* TX FIFO Data Word 2 */ + XCAN_RXFIFO_ID_OFFSET = 0x50, /* RX FIFO ID */ + XCAN_RXFIFO_DLC_OFFSET = 0x54, /* RX FIFO DLC */ + XCAN_RXFIFO_DW1_OFFSET = 0x58, /* RX FIFO Data Word 1 */ + XCAN_RXFIFO_DW2_OFFSET = 0x5C, /* RX FIFO Data Word 2 */ +}; + +/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */ +#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */ +#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */ +#define XCAN_MSR_LBACK_MASK 0x00000002 /* Loop back mode select */ +#define XCAN_MSR_SLEEP_MASK 0x00000001 /* Sleep mode select */ +#define XCAN_BRPR_BRP_MASK 0x000000FF /* Baud rate prescaler */ +#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */ +#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */ +#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */ +#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */ +#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */ +#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */ +#define XCAN_ESR_BERR_MASK 0x00000008 /* Bit error */ +#define XCAN_ESR_STER_MASK 0x00000004 /* Stuff error */ +#define XCAN_ESR_FMER_MASK 0x00000002 /* Form error */ +#define XCAN_ESR_CRCER_MASK 0x00000001 /* CRC error */ +#define XCAN_SR_TXFLL_MASK 0x00000400 /* TX FIFO is full */ +#define XCAN_SR_ESTAT_MASK 0x00000180 /* Error status */ +#define XCAN_SR_ERRWRN_MASK 0x00000040 /* Error warning */ +#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */ +#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */ +#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */ +#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */ +#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */ +#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */ +#define XCAN_IXR_BSOFF_MASK 0x00000200 /* Bus off interrupt */ +#define XCAN_IXR_ERROR_MASK 0x00000100 /* Error interrupt */ +#define XCAN_IXR_RXNEMP_MASK 0x00000080 /* RX FIFO NotEmpty intr */ +#define XCAN_IXR_RXOFLW_MASK 0x00000040 /* RX FIFO Overflow intr */ +#define XCAN_IXR_RXOK_MASK 0x00000010 /* Message received intr */ +#define XCAN_IXR_TXFLL_MASK 0x00000004 /* Tx FIFO Full intr */ +#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */ +#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */ +#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */ +#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */ +#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */ +#define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */ +#define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */ +#define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */ + +#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\ + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \ + XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \ + XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK) + +/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */ +#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */ +#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */ +#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ +#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */ +#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */ +#define XCAN_ESR_REC_SHIFT 8 /* Rx Error Count */ + +/* CAN frame length constants */ +#define XCAN_FRAME_MAX_DATA_LEN 8 +#define XCAN_TIMEOUT (1 * HZ) + +/** + * struct xcan_priv - This definition define CAN driver instance + * @can: CAN private data structure. + * @tx_head: Tx CAN packets ready to send on the queue + * @tx_tail: Tx CAN packets successfully sended on the queue + * @tx_max: Maximum number packets the driver can send + * @napi: NAPI structure + * @read_reg: For reading data from CAN registers + * @write_reg: For writing data to CAN registers + * @dev: Network device data structure + * @reg_base: Ioremapped address to registers + * @irq_flags: For request_irq() + * @bus_clk: Pointer to struct clk + * @can_clk: Pointer to struct clk + */ +struct xcan_priv { + struct can_priv can; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_max; + struct napi_struct napi; + u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg); + void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg, + u32 val); + struct net_device *dev; + void __iomem *reg_base; + unsigned long irq_flags; + struct clk *bus_clk; + struct clk *can_clk; +}; + +/* CAN Bittiming constants as per Xilinx CAN specs */ +static const struct can_bittiming_const xcan_bittiming_const = { + .name = DRIVER_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +/** + * xcan_write_reg_le - Write a value to the device register little endian + * @priv: Driver private data structure + * @reg: Register offset + * @val: Value to write at the Register offset + * + * Write data to the paricular CAN register + */ +static void xcan_write_reg_le(const struct xcan_priv *priv, enum xcan_reg reg, + u32 val) +{ + iowrite32(val, priv->reg_base + reg); +} + +/** + * xcan_read_reg_le - Read a value from the device register little endian + * @priv: Driver private data structure + * @reg: Register offset + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static u32 xcan_read_reg_le(const struct xcan_priv *priv, enum xcan_reg reg) +{ + return ioread32(priv->reg_base + reg); +} + +/** + * xcan_write_reg_be - Write a value to the device register big endian + * @priv: Driver private data structure + * @reg: Register offset + * @val: Value to write at the Register offset + * + * Write data to the paricular CAN register + */ +static void xcan_write_reg_be(const struct xcan_priv *priv, enum xcan_reg reg, + u32 val) +{ + iowrite32be(val, priv->reg_base + reg); +} + +/** + * xcan_read_reg_be - Read a value from the device register big endian + * @priv: Driver private data structure + * @reg: Register offset + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static u32 xcan_read_reg_be(const struct xcan_priv *priv, enum xcan_reg reg) +{ + return ioread32be(priv->reg_base + reg); +} + +/** + * set_reset_mode - Resets the CAN device mode + * @ndev: Pointer to net_device structure + * + * This is the driver reset mode routine.The driver + * enters into configuration mode. + * + * Return: 0 on success and failure value on error + */ +static int set_reset_mode(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + unsigned long timeout; + + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + + timeout = jiffies + XCAN_TIMEOUT; + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) { + if (time_after(jiffies, timeout)) { + netdev_warn(ndev, "timed out for config mode\n"); + return -ETIMEDOUT; + } + usleep_range(500, 10000); + } + + return 0; +} + +/** + * xcan_set_bittiming - CAN set bit timing routine + * @ndev: Pointer to net_device structure + * + * This is the driver set bittiming routine. + * Return: 0 on success and failure value on error + */ +static int xcan_set_bittiming(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 btr0, btr1; + u32 is_config_mode; + + /* Check whether Xilinx CAN is in configuration mode. + * It cannot set bit timing if Xilinx CAN is not in configuration mode. + */ + is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) & + XCAN_SR_CONFIG_MASK; + if (!is_config_mode) { + netdev_alert(ndev, + "BUG! Cannot set bittiming - CAN is not in config mode\n"); + return -EPERM; + } + + /* Setting Baud Rate prescalar value in BRPR Register */ + btr0 = (bt->brp - 1); + + /* Setting Time Segment 1 in BTR Register */ + btr1 = (bt->prop_seg + bt->phase_seg1 - 1); + + /* Setting Time Segment 2 in BTR Register */ + btr1 |= (bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT; + + /* Setting Synchronous jump width in BTR Register */ + btr1 |= (bt->sjw - 1) << XCAN_BTR_SJW_SHIFT; + + priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0); + priv->write_reg(priv, XCAN_BTR_OFFSET, btr1); + + netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n", + priv->read_reg(priv, XCAN_BRPR_OFFSET), + priv->read_reg(priv, XCAN_BTR_OFFSET)); + + return 0; +} + +/** + * xcan_chip_start - This the drivers start routine + * @ndev: Pointer to net_device structure + * + * This is the drivers start routine. + * Based on the State of the CAN device it puts + * the CAN device into a proper mode. + * + * Return: 0 on success and failure value on error + */ +static int xcan_chip_start(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + u32 err, reg_msr, reg_sr_mask; + unsigned long timeout; + + /* Check if it is in reset mode */ + err = set_reset_mode(ndev); + if (err < 0) + return err; + + err = xcan_set_bittiming(ndev); + if (err < 0) + return err; + + /* Enable interrupts */ + priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL); + + /* Check whether it is loopback mode or normal mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + reg_msr = XCAN_MSR_LBACK_MASK; + reg_sr_mask = XCAN_SR_LBACK_MASK; + } else { + reg_msr = 0x0; + reg_sr_mask = XCAN_SR_NORMAL_MASK; + } + + priv->write_reg(priv, XCAN_MSR_OFFSET, reg_msr); + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK); + + timeout = jiffies + XCAN_TIMEOUT; + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & reg_sr_mask)) { + if (time_after(jiffies, timeout)) { + netdev_warn(ndev, + "timed out for correct mode\n"); + return -ETIMEDOUT; + } + } + netdev_dbg(ndev, "status:#x%08x\n", + priv->read_reg(priv, XCAN_SR_OFFSET)); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +/** + * xcan_do_set_mode - This sets the mode of the driver + * @ndev: Pointer to net_device structure + * @mode: Tells the mode of the driver + * + * This check the drivers state and calls the + * the corresponding modes to set. + * + * Return: 0 on success and failure value on error + */ +static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + ret = xcan_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "xcan_chip_start failed!\n"); + return ret; + } + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +/** + * xcan_start_xmit - Starts the transmission + * @skb: sk_buff pointer that contains data to be Txed + * @ndev: Pointer to net_device structure + * + * This function is invoked from upper layers to initiate transmission. This + * function uses the next available free txbuff and populates their fields to + * start the transmission. + * + * Return: 0 on success and failure value on error + */ +static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 id, dlc, data[2] = {0, 0}; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + /* Check if the TX buffer is full */ + if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) & + XCAN_SR_TXFLL_MASK)) { + netif_stop_queue(ndev); + netdev_err(ndev, "BUG!, TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CAN ID format */ + id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) & + XCAN_IDR_ID2_MASK; + id |= (((cf->can_id & CAN_EFF_MASK) >> + (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) << + XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK; + + /* The substibute remote TX request bit should be "1" + * for extended frames as in the Xilinx CAN datasheet + */ + id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Extended frames remote TX request */ + id |= XCAN_IDR_RTR_MASK; + } else { + /* Standard CAN ID format */ + id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) & + XCAN_IDR_ID1_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Standard frames remote TX request */ + id |= XCAN_IDR_SRR_MASK; + } + + dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT; + + if (cf->can_dlc > 0) + data[0] = be32_to_cpup((__be32 *)(cf->data + 0)); + if (cf->can_dlc > 4) + data[1] = be32_to_cpup((__be32 *)(cf->data + 4)); + + can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max); + priv->tx_head++; + + /* Write the Frame to Xilinx CAN TX FIFO */ + priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id); + /* If the CAN frame is RTR frame this write triggers tranmission */ + priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc); + if (!(cf->can_id & CAN_RTR_FLAG)) { + priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data[0]); + /* If the CAN frame is Standard/Extended frame this + * write triggers tranmission + */ + priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data[1]); + stats->tx_bytes += cf->can_dlc; + } + + /* Check if the TX buffer is full */ + if ((priv->tx_head - priv->tx_tail) == priv->tx_max) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +/** + * xcan_rx - Is called from CAN isr to complete the received + * frame processing + * @ndev: Pointer to net_device structure + * + * This function is invoked from the CAN isr(poll) to process the Rx frames. It + * does minimal processing and invokes "netif_receive_skb" to complete further + * processing. + * Return: 1 on success and 0 on failure. + */ +static int xcan_rx(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 id_xcan, dlc, data[2] = {0, 0}; + + skb = alloc_can_skb(ndev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + /* Read a frame from Xilinx zynq CANPS */ + id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET); + dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) >> + XCAN_DLCR_DLC_SHIFT; + + /* Change Xilinx CAN data length format to socketCAN data format */ + cf->can_dlc = get_can_dlc(dlc); + + /* Change Xilinx CAN ID format to socketCAN ID format */ + if (id_xcan & XCAN_IDR_IDE_MASK) { + /* The received frame is an Extended format frame */ + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3; + cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >> + XCAN_IDR_ID2_SHIFT; + cf->can_id |= CAN_EFF_FLAG; + if (id_xcan & XCAN_IDR_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* The received frame is a standard format frame */ + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> + XCAN_IDR_ID1_SHIFT; + if (id_xcan & XCAN_IDR_SRR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + if (!(id_xcan & XCAN_IDR_SRR_MASK)) { + data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET); + data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET); + + /* Change Xilinx CAN data format to socketCAN data format */ + if (cf->can_dlc > 0) + *(__be32 *)(cf->data) = cpu_to_be32(data[0]); + if (cf->can_dlc > 4) + *(__be32 *)(cf->data + 4) = cpu_to_be32(data[1]); + } + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_receive_skb(skb); + + return 1; +} + +/** + * xcan_err_interrupt - error frame Isr + * @ndev: net_device pointer + * @isr: interrupt status register value + * + * This is the CAN error interrupt and it will + * check the the type of error and forward the error + * frame to upper layers. + */ +static void xcan_err_interrupt(struct net_device *ndev, u32 isr) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 err_status, status, txerr = 0, rxerr = 0; + + skb = alloc_can_err_skb(ndev, &cf); + + err_status = priv->read_reg(priv, XCAN_ESR_OFFSET); + priv->write_reg(priv, XCAN_ESR_OFFSET, err_status); + txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK; + rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) & + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT); + status = priv->read_reg(priv, XCAN_SR_OFFSET); + + if (isr & XCAN_IXR_BSOFF_MASK) { + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + /* Leave device in Config Mode in bus-off state */ + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) { + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (rxerr > 127) ? + CAN_ERR_CRTL_RX_PASSIVE : + CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } else if (status & XCAN_SR_ERRWRN_MASK) { + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } + + /* Check for Arbitration lost interrupt */ + if (isr & XCAN_IXR_ARBLST_MASK) { + priv->can.can_stats.arbitration_lost++; + if (skb) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = CAN_ERR_LOSTARB_UNSPEC; + } + } + + /* Check for RX FIFO Overflow interrupt */ + if (isr & XCAN_IXR_RXOFLW_MASK) { + stats->rx_over_errors++; + stats->rx_errors++; + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + /* Check for error interrupt */ + if (isr & XCAN_IXR_ERROR_MASK) { + if (skb) { + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + } + + /* Check for Ack error interrupt */ + if (err_status & XCAN_ESR_ACKER_MASK) { + stats->tx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_ACK; + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + } + } + + /* Check for Bit error interrupt */ + if (err_status & XCAN_ESR_BERR_MASK) { + stats->tx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_BIT; + } + } + + /* Check for Stuff error interrupt */ + if (err_status & XCAN_ESR_STER_MASK) { + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_STUFF; + } + } + + /* Check for Form error interrupt */ + if (err_status & XCAN_ESR_FMER_MASK) { + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_FORM; + } + } + + /* Check for CRC error interrupt */ + if (err_status & XCAN_ESR_CRCER_MASK) { + stats->rx_errors++; + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL; + } + } + priv->can.can_stats.bus_error++; + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } + + netdev_dbg(ndev, "%s: error status register:0x%x\n", + __func__, priv->read_reg(priv, XCAN_ESR_OFFSET)); +} + +/** + * xcan_state_interrupt - It will check the state of the CAN device + * @ndev: net_device pointer + * @isr: interrupt status register value + * + * This will checks the state of the CAN device + * and puts the device into appropriate state. + */ +static void xcan_state_interrupt(struct net_device *ndev, u32 isr) +{ + struct xcan_priv *priv = netdev_priv(ndev); + + /* Check for Sleep interrupt if set put CAN device in sleep state */ + if (isr & XCAN_IXR_SLP_MASK) + priv->can.state = CAN_STATE_SLEEPING; + + /* Check for Wake up interrupt if set put CAN device in Active state */ + if (isr & XCAN_IXR_WKUP_MASK) + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +/** + * xcan_rx_poll - Poll routine for rx packets (NAPI) + * @napi: napi structure pointer + * @quota: Max number of rx packets to be processed. + * + * This is the poll routine for rx part. + * It will process the packets maximux quota value. + * + * Return: number of packets received + */ +static int xcan_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct xcan_priv *priv = netdev_priv(ndev); + u32 isr, ier; + int work_done = 0; + + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) { + if (isr & XCAN_IXR_RXOK_MASK) { + priv->write_reg(priv, XCAN_ICR_OFFSET, + XCAN_IXR_RXOK_MASK); + work_done += xcan_rx(ndev); + } else { + priv->write_reg(priv, XCAN_ICR_OFFSET, + XCAN_IXR_RXNEMP_MASK); + break; + } + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK); + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + } + + if (work_done) + can_led_event(ndev, CAN_LED_EVENT_RX); + + if (work_done < quota) { + napi_complete(napi); + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK); + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + } + return work_done; +} + +/** + * xcan_tx_interrupt - Tx Done Isr + * @ndev: net_device pointer + * @isr: Interrupt status register value + */ +static void xcan_tx_interrupt(struct net_device *ndev, u32 isr) +{ + struct xcan_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + + while ((priv->tx_head - priv->tx_tail > 0) && + (isr & XCAN_IXR_TXOK_MASK)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK); + can_get_echo_skb(ndev, priv->tx_tail % + priv->tx_max); + priv->tx_tail++; + stats->tx_packets++; + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + } + can_led_event(ndev, CAN_LED_EVENT_TX); + netif_wake_queue(ndev); +} + +/** + * xcan_interrupt - CAN Isr + * @irq: irq number + * @dev_id: device id poniter + * + * This is the xilinx CAN Isr. It checks for the type of interrupt + * and invokes the corresponding ISR. + * + * Return: + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise + */ +static irqreturn_t xcan_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct xcan_priv *priv = netdev_priv(ndev); + u32 isr, ier; + + /* Get the interrupt status from Xilinx CAN */ + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + if (!isr) + return IRQ_NONE; + + /* Check for the type of interrupt and Processing it */ + if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK | + XCAN_IXR_WKUP_MASK)); + xcan_state_interrupt(ndev, isr); + } + + /* Check for Tx interrupt and Processing it */ + if (isr & XCAN_IXR_TXOK_MASK) + xcan_tx_interrupt(ndev, isr); + + /* Check for the type of error interrupt and Processing it */ + if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) { + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK | + XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK | + XCAN_IXR_ARBLST_MASK)); + xcan_err_interrupt(ndev, isr); + } + + /* Check for the type of receive interrupt and Processing it */ + if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) { + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK); + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + napi_schedule(&priv->napi); + } + return IRQ_HANDLED; +} + +/** + * xcan_chip_stop - Driver stop routine + * @ndev: Pointer to net_device structure + * + * This is the drivers stop routine. It will disable the + * interrupts and put the device into configuration mode. + */ +static void xcan_chip_stop(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + u32 ier; + + /* Disable interrupts and leave the can in configuration mode */ + ier = priv->read_reg(priv, XCAN_IER_OFFSET); + ier &= ~XCAN_INTR_ALL; + priv->write_reg(priv, XCAN_IER_OFFSET, ier); + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + priv->can.state = CAN_STATE_STOPPED; +} + +/** + * xcan_open - Driver open routine + * @ndev: Pointer to net_device structure + * + * This is the driver open routine. + * Return: 0 on success and failure value on error + */ +static int xcan_open(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "irq allocation for CAN failed\n"); + goto err; + } + + ret = clk_prepare_enable(priv->can_clk); + if (ret) { + netdev_err(ndev, "unable to enable device clock\n"); + goto err_irq; + } + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) { + netdev_err(ndev, "unable to enable bus clock\n"); + goto err_can_clk; + } + + /* Set chip into reset mode */ + ret = set_reset_mode(ndev); + if (ret < 0) { + netdev_err(ndev, "mode resetting failed!\n"); + goto err_bus_clk; + } + + /* Common open */ + ret = open_candev(ndev); + if (ret) + goto err_bus_clk; + + ret = xcan_chip_start(ndev); + if (ret < 0) { + netdev_err(ndev, "xcan_chip_start failed!\n"); + goto err_candev; + } + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; + +err_candev: + close_candev(ndev); +err_bus_clk: + clk_disable_unprepare(priv->bus_clk); +err_can_clk: + clk_disable_unprepare(priv->can_clk); +err_irq: + free_irq(ndev->irq, ndev); +err: + return ret; +} + +/** + * xcan_close - Driver close routine + * @ndev: Pointer to net_device structure + * + * Return: 0 always + */ +static int xcan_close(struct net_device *ndev) +{ + struct xcan_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + xcan_chip_stop(ndev); + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + free_irq(ndev->irq, ndev); + close_candev(ndev); + + can_led_event(ndev, CAN_LED_EVENT_STOP); + + return 0; +} + +/** + * xcan_get_berr_counter - error counter routine + * @ndev: Pointer to net_device structure + * @bec: Pointer to can_berr_counter structure + * + * This is the driver error counter routine. + * Return: 0 on success and failure value on error + */ +static int xcan_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = clk_prepare_enable(priv->can_clk); + if (ret) + goto err; + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + goto err_clk; + + bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK; + bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) & + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT); + + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + + return 0; + +err_clk: + clk_disable_unprepare(priv->can_clk); +err: + return ret; +} + + +static const struct net_device_ops xcan_netdev_ops = { + .ndo_open = xcan_open, + .ndo_stop = xcan_close, + .ndo_start_xmit = xcan_start_xmit, +}; + +/** + * xcan_suspend - Suspend method for the driver + * @dev: Address of the platform_device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused xcan_suspend(struct device *dev) +{ + struct platform_device *pdev = dev_get_drvdata(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct xcan_priv *priv = netdev_priv(ndev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + } + + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->bus_clk); + clk_disable(priv->can_clk); + + return 0; +} + +/** + * xcan_resume - Resume from suspend + * @dev: Address of the platformdevice structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused xcan_resume(struct device *dev) +{ + struct platform_device *pdev = dev_get_drvdata(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct xcan_priv *priv = netdev_priv(ndev); + int ret; + + ret = clk_enable(priv->bus_clk); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + return ret; + } + ret = clk_enable(priv->can_clk); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + clk_disable_unprepare(priv->bus_clk); + return ret; + } + + priv->write_reg(priv, XCAN_MSR_OFFSET, 0); + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume); + +/** + * xcan_probe - Platform registration call + * @pdev: Handle to the platform device structure + * + * This function does all the memory allocation and registration for the CAN + * device. + * + * Return: 0 on success and failure value on error + */ +static int xcan_probe(struct platform_device *pdev) +{ + struct resource *res; /* IO mem resources */ + struct net_device *ndev; + struct xcan_priv *priv; + void __iomem *addr; + int ret, rx_max, tx_max; + + /* Get the virtual base address for the device */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) { + ret = PTR_ERR(addr); + goto err; + } + + ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", &tx_max); + if (ret < 0) + goto err; + + ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", &rx_max); + if (ret < 0) + goto err; + + /* Create a CAN device instance */ + ndev = alloc_candev(sizeof(struct xcan_priv), tx_max); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + priv->dev = ndev; + priv->can.bittiming_const = &xcan_bittiming_const; + priv->can.do_set_mode = xcan_do_set_mode; + priv->can.do_get_berr_counter = xcan_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_BERR_REPORTING; + priv->reg_base = addr; + priv->tx_max = tx_max; + + /* Get IRQ for the device */ + ndev->irq = platform_get_irq(pdev, 0); + ndev->flags |= IFF_ECHO; /* We support local echo */ + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &xcan_netdev_ops; + + /* Getting the CAN can_clk info */ + priv->can_clk = devm_clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(priv->can_clk)) { + dev_err(&pdev->dev, "Device clock not found.\n"); + ret = PTR_ERR(priv->can_clk); + goto err_free; + } + /* Check for type of CAN device */ + if (of_device_is_compatible(pdev->dev.of_node, + "xlnx,zynq-can-1.0")) { + priv->bus_clk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(priv->bus_clk)) { + dev_err(&pdev->dev, "bus clock not found\n"); + ret = PTR_ERR(priv->bus_clk); + goto err_free; + } + } else { + priv->bus_clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(priv->bus_clk)) { + dev_err(&pdev->dev, "bus clock not found\n"); + ret = PTR_ERR(priv->bus_clk); + goto err_free; + } + } + + ret = clk_prepare_enable(priv->can_clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable device clock\n"); + goto err_free; + } + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable bus clock\n"); + goto err_unprepare_disable_dev; + } + + priv->write_reg = xcan_write_reg_le; + priv->read_reg = xcan_read_reg_le; + + if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) { + priv->write_reg = xcan_write_reg_be; + priv->read_reg = xcan_read_reg_be; + } + + priv->can.clock.freq = clk_get_rate(priv->can_clk); + + netif_napi_add(ndev, &priv->napi, xcan_rx_poll, rx_max); + + ret = register_candev(ndev); + if (ret) { + dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret); + goto err_unprepare_disable_busclk; + } + + devm_can_led_init(ndev); + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); + netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n", + priv->reg_base, ndev->irq, priv->can.clock.freq, + priv->tx_max); + + return 0; + +err_unprepare_disable_busclk: + clk_disable_unprepare(priv->bus_clk); +err_unprepare_disable_dev: + clk_disable_unprepare(priv->can_clk); +err_free: + free_candev(ndev); +err: + return ret; +} + +/** + * xcan_remove - Unregister the device after releasing the resources + * @pdev: Handle to the platform device structure + * + * This function frees all the resources allocated to the device. + * Return: 0 always + */ +static int xcan_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct xcan_priv *priv = netdev_priv(ndev); + + if (set_reset_mode(ndev) < 0) + netdev_err(ndev, "mode resetting failed!\n"); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); + + return 0; +} + +/* Match table for OF platform binding */ +static struct of_device_id xcan_of_match[] = { + { .compatible = "xlnx,zynq-can-1.0", }, + { .compatible = "xlnx,axi-can-1.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, xcan_of_match); + +static struct platform_driver xcan_driver = { + .probe = xcan_probe, + .remove = xcan_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .pm = &xcan_dev_pm_ops, + .of_match_table = xcan_of_match, + }, +}; + +module_platform_driver(xcan_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xilinx Inc"); +MODULE_DESCRIPTION("Xilinx CAN interface"); diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 455d4c3..1d162cc 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -157,7 +157,7 @@ static void ax_reset_8390(struct net_device *dev) /* This check _should_not_ be necessary, omit eventually. */ while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) { - if (jiffies - reset_start_time > 2 * HZ / 100) { + if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) { netdev_warn(dev, "%s: did not complete.\n", __func__); break; } @@ -293,7 +293,7 @@ static void ax_block_output(struct net_device *dev, int count, dma_start = jiffies; while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) { - if (jiffies - dma_start > 2 * HZ / 100) { /* 20ms */ + if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */ netdev_warn(dev, "timeout waiting for Tx RDC.\n"); ax_reset_8390(dev); ax_NS8390_init(dev, 1); diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile index d4a187e..3eff2fd 100644 --- a/drivers/net/ethernet/altera/Makefile +++ b/drivers/net/ethernet/altera/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ALTERA_TSE) += altera_tse.o altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \ altera_msgdma.o altera_sgdma.o altera_utils.o +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/ethernet/altera/altera_msgdma.c b/drivers/net/ethernet/altera/altera_msgdma.c index 38c500f..0fb986b 100644 --- a/drivers/net/ethernet/altera/altera_msgdma.c +++ b/drivers/net/ethernet/altera/altera_msgdma.c @@ -37,16 +37,16 @@ void msgdma_start_rxdma(struct altera_tse_private *priv) void msgdma_reset(struct altera_tse_private *priv) { int counter; - struct msgdma_csr *txcsr = priv->tx_dma_csr; - struct msgdma_csr *rxcsr = priv->rx_dma_csr; /* Reset Rx mSGDMA */ - iowrite32(MSGDMA_CSR_STAT_MASK, &rxcsr->status); - iowrite32(MSGDMA_CSR_CTL_RESET, &rxcsr->control); + csrwr32(MSGDMA_CSR_STAT_MASK, priv->rx_dma_csr, + msgdma_csroffs(status)); + csrwr32(MSGDMA_CSR_CTL_RESET, priv->rx_dma_csr, + msgdma_csroffs(control)); counter = 0; while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) { - if (tse_bit_is_clear(&rxcsr->status, + if (tse_bit_is_clear(priv->rx_dma_csr, msgdma_csroffs(status), MSGDMA_CSR_STAT_RESETTING)) break; udelay(1); @@ -57,15 +57,18 @@ void msgdma_reset(struct altera_tse_private *priv) "TSE Rx mSGDMA resetting bit never cleared!\n"); /* clear all status bits */ - iowrite32(MSGDMA_CSR_STAT_MASK, &rxcsr->status); + csrwr32(MSGDMA_CSR_STAT_MASK, priv->rx_dma_csr, msgdma_csroffs(status)); /* Reset Tx mSGDMA */ - iowrite32(MSGDMA_CSR_STAT_MASK, &txcsr->status); - iowrite32(MSGDMA_CSR_CTL_RESET, &txcsr->control); + csrwr32(MSGDMA_CSR_STAT_MASK, priv->tx_dma_csr, + msgdma_csroffs(status)); + + csrwr32(MSGDMA_CSR_CTL_RESET, priv->tx_dma_csr, + msgdma_csroffs(control)); counter = 0; while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) { - if (tse_bit_is_clear(&txcsr->status, + if (tse_bit_is_clear(priv->tx_dma_csr, msgdma_csroffs(status), MSGDMA_CSR_STAT_RESETTING)) break; udelay(1); @@ -76,58 +79,58 @@ void msgdma_reset(struct altera_tse_private *priv) "TSE Tx mSGDMA resetting bit never cleared!\n"); /* clear all status bits */ - iowrite32(MSGDMA_CSR_STAT_MASK, &txcsr->status); + csrwr32(MSGDMA_CSR_STAT_MASK, priv->tx_dma_csr, msgdma_csroffs(status)); } void msgdma_disable_rxirq(struct altera_tse_private *priv) { - struct msgdma_csr *csr = priv->rx_dma_csr; - tse_clear_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR); + tse_clear_bit(priv->rx_dma_csr, msgdma_csroffs(control), + MSGDMA_CSR_CTL_GLOBAL_INTR); } void msgdma_enable_rxirq(struct altera_tse_private *priv) { - struct msgdma_csr *csr = priv->rx_dma_csr; - tse_set_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR); + tse_set_bit(priv->rx_dma_csr, msgdma_csroffs(control), + MSGDMA_CSR_CTL_GLOBAL_INTR); } void msgdma_disable_txirq(struct altera_tse_private *priv) { - struct msgdma_csr *csr = priv->tx_dma_csr; - tse_clear_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR); + tse_clear_bit(priv->tx_dma_csr, msgdma_csroffs(control), + MSGDMA_CSR_CTL_GLOBAL_INTR); } void msgdma_enable_txirq(struct altera_tse_private *priv) { - struct msgdma_csr *csr = priv->tx_dma_csr; - tse_set_bit(&csr->control, MSGDMA_CSR_CTL_GLOBAL_INTR); + tse_set_bit(priv->tx_dma_csr, msgdma_csroffs(control), + MSGDMA_CSR_CTL_GLOBAL_INTR); } void msgdma_clear_rxirq(struct altera_tse_private *priv) { - struct msgdma_csr *csr = priv->rx_dma_csr; - iowrite32(MSGDMA_CSR_STAT_IRQ, &csr->status); + csrwr32(MSGDMA_CSR_STAT_IRQ, priv->rx_dma_csr, msgdma_csroffs(status)); } void msgdma_clear_txirq(struct altera_tse_private *priv) { - struct msgdma_csr *csr = priv->tx_dma_csr; - iowrite32(MSGDMA_CSR_STAT_IRQ, &csr->status); + csrwr32(MSGDMA_CSR_STAT_IRQ, priv->tx_dma_csr, msgdma_csroffs(status)); } /* return 0 to indicate transmit is pending */ int msgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer) { - struct msgdma_extended_desc *desc = priv->tx_dma_desc; - - iowrite32(lower_32_bits(buffer->dma_addr), &desc->read_addr_lo); - iowrite32(upper_32_bits(buffer->dma_addr), &desc->read_addr_hi); - iowrite32(0, &desc->write_addr_lo); - iowrite32(0, &desc->write_addr_hi); - iowrite32(buffer->len, &desc->len); - iowrite32(0, &desc->burst_seq_num); - iowrite32(MSGDMA_DESC_TX_STRIDE, &desc->stride); - iowrite32(MSGDMA_DESC_CTL_TX_SINGLE, &desc->control); + csrwr32(lower_32_bits(buffer->dma_addr), priv->tx_dma_desc, + msgdma_descroffs(read_addr_lo)); + csrwr32(upper_32_bits(buffer->dma_addr), priv->tx_dma_desc, + msgdma_descroffs(read_addr_hi)); + csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(write_addr_lo)); + csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(write_addr_hi)); + csrwr32(buffer->len, priv->tx_dma_desc, msgdma_descroffs(len)); + csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(burst_seq_num)); + csrwr32(MSGDMA_DESC_TX_STRIDE, priv->tx_dma_desc, + msgdma_descroffs(stride)); + csrwr32(MSGDMA_DESC_CTL_TX_SINGLE, priv->tx_dma_desc, + msgdma_descroffs(control)); return 0; } @@ -136,16 +139,16 @@ u32 msgdma_tx_completions(struct altera_tse_private *priv) u32 ready = 0; u32 inuse; u32 status; - struct msgdma_csr *txcsr = priv->tx_dma_csr; /* Get number of sent descriptors */ - inuse = ioread32(&txcsr->rw_fill_level) & 0xffff; + inuse = csrrd32(priv->tx_dma_csr, msgdma_csroffs(rw_fill_level)) + & 0xffff; if (inuse) { /* Tx FIFO is not empty */ ready = priv->tx_prod - priv->tx_cons - inuse - 1; } else { /* Check for buffered last packet */ - status = ioread32(&txcsr->status); + status = csrrd32(priv->tx_dma_csr, msgdma_csroffs(status)); if (status & MSGDMA_CSR_STAT_BUSY) ready = priv->tx_prod - priv->tx_cons - 1; else @@ -159,7 +162,6 @@ u32 msgdma_tx_completions(struct altera_tse_private *priv) void msgdma_add_rx_desc(struct altera_tse_private *priv, struct tse_buffer *rxbuffer) { - struct msgdma_extended_desc *desc = priv->rx_dma_desc; u32 len = priv->rx_dma_buf_sz; dma_addr_t dma_addr = rxbuffer->dma_addr; u32 control = (MSGDMA_DESC_CTL_END_ON_EOP @@ -169,14 +171,16 @@ void msgdma_add_rx_desc(struct altera_tse_private *priv, | MSGDMA_DESC_CTL_TR_ERR_IRQ | MSGDMA_DESC_CTL_GO); - iowrite32(0, &desc->read_addr_lo); - iowrite32(0, &desc->read_addr_hi); - iowrite32(lower_32_bits(dma_addr), &desc->write_addr_lo); - iowrite32(upper_32_bits(dma_addr), &desc->write_addr_hi); - iowrite32(len, &desc->len); - iowrite32(0, &desc->burst_seq_num); - iowrite32(0x00010001, &desc->stride); - iowrite32(control, &desc->control); + csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(read_addr_lo)); + csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(read_addr_hi)); + csrwr32(lower_32_bits(dma_addr), priv->rx_dma_desc, + msgdma_descroffs(write_addr_lo)); + csrwr32(upper_32_bits(dma_addr), priv->rx_dma_desc, + msgdma_descroffs(write_addr_hi)); + csrwr32(len, priv->rx_dma_desc, msgdma_descroffs(len)); + csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(burst_seq_num)); + csrwr32(0x00010001, priv->rx_dma_desc, msgdma_descroffs(stride)); + csrwr32(control, priv->rx_dma_desc, msgdma_descroffs(control)); } /* status is returned on upper 16 bits, @@ -187,12 +191,13 @@ u32 msgdma_rx_status(struct altera_tse_private *priv) u32 rxstatus = 0; u32 pktlength; u32 pktstatus; - struct msgdma_csr *rxcsr = priv->rx_dma_csr; - struct msgdma_response *rxresp = priv->rx_dma_resp; - if (ioread32(&rxcsr->resp_fill_level) & 0xffff) { - pktlength = ioread32(&rxresp->bytes_transferred); - pktstatus = ioread32(&rxresp->status); + if (csrrd32(priv->rx_dma_csr, msgdma_csroffs(resp_fill_level)) + & 0xffff) { + pktlength = csrrd32(priv->rx_dma_resp, + msgdma_respoffs(bytes_transferred)); + pktstatus = csrrd32(priv->rx_dma_resp, + msgdma_respoffs(status)); rxstatus = pktstatus; rxstatus = rxstatus << 16; rxstatus |= (pktlength & 0xffff); diff --git a/drivers/net/ethernet/altera/altera_msgdmahw.h b/drivers/net/ethernet/altera/altera_msgdmahw.h index d7b59ba..e335626 100644 --- a/drivers/net/ethernet/altera/altera_msgdmahw.h +++ b/drivers/net/ethernet/altera/altera_msgdmahw.h @@ -17,15 +17,6 @@ #ifndef __ALTERA_MSGDMAHW_H__ #define __ALTERA_MSGDMAHW_H__ -/* mSGDMA standard descriptor format - */ -struct msgdma_desc { - u32 read_addr; /* data buffer source address */ - u32 write_addr; /* data buffer destination address */ - u32 len; /* the number of bytes to transfer per descriptor */ - u32 control; /* characteristics of the transfer */ -}; - /* mSGDMA extended descriptor format */ struct msgdma_extended_desc { @@ -159,6 +150,10 @@ struct msgdma_response { u32 status; }; +#define msgdma_respoffs(a) (offsetof(struct msgdma_response, a)) +#define msgdma_csroffs(a) (offsetof(struct msgdma_csr, a)) +#define msgdma_descroffs(a) (offsetof(struct msgdma_extended_desc, a)) + /* mSGDMA response register bit definitions */ #define MSGDMA_RESP_EARLY_TERM BIT(8) diff --git a/drivers/net/ethernet/altera/altera_sgdma.c b/drivers/net/ethernet/altera/altera_sgdma.c index dbd40e1..580553d 100644 --- a/drivers/net/ethernet/altera/altera_sgdma.c +++ b/drivers/net/ethernet/altera/altera_sgdma.c @@ -20,8 +20,8 @@ #include "altera_sgdmahw.h" #include "altera_sgdma.h" -static void sgdma_setup_descrip(struct sgdma_descrip *desc, - struct sgdma_descrip *ndesc, +static void sgdma_setup_descrip(struct sgdma_descrip __iomem *desc, + struct sgdma_descrip __iomem *ndesc, dma_addr_t ndesc_phys, dma_addr_t raddr, dma_addr_t waddr, @@ -31,17 +31,17 @@ static void sgdma_setup_descrip(struct sgdma_descrip *desc, int wfixed); static int sgdma_async_write(struct altera_tse_private *priv, - struct sgdma_descrip *desc); + struct sgdma_descrip __iomem *desc); static int sgdma_async_read(struct altera_tse_private *priv); static dma_addr_t sgdma_txphysaddr(struct altera_tse_private *priv, - struct sgdma_descrip *desc); + struct sgdma_descrip __iomem *desc); static dma_addr_t sgdma_rxphysaddr(struct altera_tse_private *priv, - struct sgdma_descrip *desc); + struct sgdma_descrip __iomem *desc); static int sgdma_txbusy(struct altera_tse_private *priv); @@ -79,7 +79,8 @@ int sgdma_initialize(struct altera_tse_private *priv) priv->rxdescphys = (dma_addr_t) 0; priv->txdescphys = (dma_addr_t) 0; - priv->rxdescphys = dma_map_single(priv->device, priv->rx_dma_desc, + priv->rxdescphys = dma_map_single(priv->device, + (void __force *)priv->rx_dma_desc, priv->rxdescmem, DMA_BIDIRECTIONAL); if (dma_mapping_error(priv->device, priv->rxdescphys)) { @@ -88,7 +89,8 @@ int sgdma_initialize(struct altera_tse_private *priv) return -EINVAL; } - priv->txdescphys = dma_map_single(priv->device, priv->tx_dma_desc, + priv->txdescphys = dma_map_single(priv->device, + (void __force *)priv->tx_dma_desc, priv->txdescmem, DMA_TO_DEVICE); if (dma_mapping_error(priv->device, priv->txdescphys)) { @@ -98,8 +100,8 @@ int sgdma_initialize(struct altera_tse_private *priv) } /* Initialize descriptor memory to all 0's, sync memory to cache */ - memset(priv->tx_dma_desc, 0, priv->txdescmem); - memset(priv->rx_dma_desc, 0, priv->rxdescmem); + memset_io(priv->tx_dma_desc, 0, priv->txdescmem); + memset_io(priv->rx_dma_desc, 0, priv->rxdescmem); dma_sync_single_for_device(priv->device, priv->txdescphys, priv->txdescmem, DMA_TO_DEVICE); @@ -126,22 +128,15 @@ void sgdma_uninitialize(struct altera_tse_private *priv) */ void sgdma_reset(struct altera_tse_private *priv) { - u32 *ptxdescripmem = priv->tx_dma_desc; - u32 txdescriplen = priv->txdescmem; - u32 *prxdescripmem = priv->rx_dma_desc; - u32 rxdescriplen = priv->rxdescmem; - struct sgdma_csr *ptxsgdma = priv->tx_dma_csr; - struct sgdma_csr *prxsgdma = priv->rx_dma_csr; - /* Initialize descriptor memory to 0 */ - memset(ptxdescripmem, 0, txdescriplen); - memset(prxdescripmem, 0, rxdescriplen); + memset_io(priv->tx_dma_desc, 0, priv->txdescmem); + memset_io(priv->rx_dma_desc, 0, priv->rxdescmem); - iowrite32(SGDMA_CTRLREG_RESET, &ptxsgdma->control); - iowrite32(0, &ptxsgdma->control); + csrwr32(SGDMA_CTRLREG_RESET, priv->tx_dma_csr, sgdma_csroffs(control)); + csrwr32(0, priv->tx_dma_csr, sgdma_csroffs(control)); - iowrite32(SGDMA_CTRLREG_RESET, &prxsgdma->control); - iowrite32(0, &prxsgdma->control); + csrwr32(SGDMA_CTRLREG_RESET, priv->rx_dma_csr, sgdma_csroffs(control)); + csrwr32(0, priv->rx_dma_csr, sgdma_csroffs(control)); } /* For SGDMA, interrupts remain enabled after initially enabling, @@ -167,14 +162,14 @@ void sgdma_disable_txirq(struct altera_tse_private *priv) void sgdma_clear_rxirq(struct altera_tse_private *priv) { - struct sgdma_csr *csr = priv->rx_dma_csr; - tse_set_bit(&csr->control, SGDMA_CTRLREG_CLRINT); + tse_set_bit(priv->rx_dma_csr, sgdma_csroffs(control), + SGDMA_CTRLREG_CLRINT); } void sgdma_clear_txirq(struct altera_tse_private *priv) { - struct sgdma_csr *csr = priv->tx_dma_csr; - tse_set_bit(&csr->control, SGDMA_CTRLREG_CLRINT); + tse_set_bit(priv->tx_dma_csr, sgdma_csroffs(control), + SGDMA_CTRLREG_CLRINT); } /* transmits buffer through SGDMA. Returns number of buffers @@ -184,11 +179,11 @@ void sgdma_clear_txirq(struct altera_tse_private *priv) */ int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer) { - int pktstx = 0; - struct sgdma_descrip *descbase = priv->tx_dma_desc; + struct sgdma_descrip __iomem *descbase = + (struct sgdma_descrip __iomem *)priv->tx_dma_desc; - struct sgdma_descrip *cdesc = &descbase[0]; - struct sgdma_descrip *ndesc = &descbase[1]; + struct sgdma_descrip __iomem *cdesc = &descbase[0]; + struct sgdma_descrip __iomem *ndesc = &descbase[1]; /* wait 'til the tx sgdma is ready for the next transmit request */ if (sgdma_txbusy(priv)) @@ -204,7 +199,7 @@ int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer) 0, /* read fixed */ SGDMA_CONTROL_WR_FIXED); /* Generate SOP */ - pktstx = sgdma_async_write(priv, cdesc); + sgdma_async_write(priv, cdesc); /* enqueue the request to the pending transmit queue */ queue_tx(priv, buffer); @@ -218,10 +213,10 @@ int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer) u32 sgdma_tx_completions(struct altera_tse_private *priv) { u32 ready = 0; - struct sgdma_descrip *desc = priv->tx_dma_desc; if (!sgdma_txbusy(priv) && - ((desc->control & SGDMA_CONTROL_HW_OWNED) == 0) && + ((csrrd8(priv->tx_dma_desc, sgdma_descroffs(control)) + & SGDMA_CONTROL_HW_OWNED) == 0) && (dequeue_tx(priv))) { ready = 1; } @@ -245,32 +240,31 @@ void sgdma_add_rx_desc(struct altera_tse_private *priv, */ u32 sgdma_rx_status(struct altera_tse_private *priv) { - struct sgdma_csr *csr = priv->rx_dma_csr; - struct sgdma_descrip *base = priv->rx_dma_desc; - struct sgdma_descrip *desc = NULL; - int pktsrx; - unsigned int rxstatus = 0; - unsigned int pktlength = 0; - unsigned int pktstatus = 0; + struct sgdma_descrip __iomem *base = + (struct sgdma_descrip __iomem *)priv->rx_dma_desc; + struct sgdma_descrip __iomem *desc = NULL; struct tse_buffer *rxbuffer = NULL; + unsigned int rxstatus = 0; - u32 sts = ioread32(&csr->status); + u32 sts = csrrd32(priv->rx_dma_csr, sgdma_csroffs(status)); desc = &base[0]; if (sts & SGDMA_STSREG_EOP) { + unsigned int pktlength = 0; + unsigned int pktstatus = 0; dma_sync_single_for_cpu(priv->device, priv->rxdescphys, priv->sgdmadesclen, DMA_FROM_DEVICE); - pktlength = desc->bytes_xferred; - pktstatus = desc->status & 0x3f; - rxstatus = pktstatus; + pktlength = csrrd16(desc, sgdma_descroffs(bytes_xferred)); + pktstatus = csrrd8(desc, sgdma_descroffs(status)); + rxstatus = pktstatus & ~SGDMA_STATUS_EOP; rxstatus = rxstatus << 16; rxstatus |= (pktlength & 0xffff); if (rxstatus) { - desc->status = 0; + csrwr8(0, desc, sgdma_descroffs(status)); rxbuffer = dequeue_rx(priv); if (rxbuffer == NULL) @@ -278,12 +272,12 @@ u32 sgdma_rx_status(struct altera_tse_private *priv) "sgdma rx and rx queue empty!\n"); /* Clear control */ - iowrite32(0, &csr->control); + csrwr32(0, priv->rx_dma_csr, sgdma_csroffs(control)); /* clear status */ - iowrite32(0xf, &csr->status); + csrwr32(0xf, priv->rx_dma_csr, sgdma_csroffs(status)); /* kick the rx sgdma after reaping this descriptor */ - pktsrx = sgdma_async_read(priv); + sgdma_async_read(priv); } else { /* If the SGDMA indicated an end of packet on recv, @@ -297,10 +291,11 @@ u32 sgdma_rx_status(struct altera_tse_private *priv) */ netdev_err(priv->dev, "SGDMA RX Error Info: %x, %x, %x\n", - sts, desc->status, rxstatus); + sts, csrrd8(desc, sgdma_descroffs(status)), + rxstatus); } } else if (sts == 0) { - pktsrx = sgdma_async_read(priv); + sgdma_async_read(priv); } return rxstatus; @@ -308,8 +303,8 @@ u32 sgdma_rx_status(struct altera_tse_private *priv) /* Private functions */ -static void sgdma_setup_descrip(struct sgdma_descrip *desc, - struct sgdma_descrip *ndesc, +static void sgdma_setup_descrip(struct sgdma_descrip __iomem *desc, + struct sgdma_descrip __iomem *ndesc, dma_addr_t ndesc_phys, dma_addr_t raddr, dma_addr_t waddr, @@ -319,27 +314,30 @@ static void sgdma_setup_descrip(struct sgdma_descrip *desc, int wfixed) { /* Clear the next descriptor as not owned by hardware */ - u32 ctrl = ndesc->control; + + u32 ctrl = csrrd8(ndesc, sgdma_descroffs(control)); ctrl &= ~SGDMA_CONTROL_HW_OWNED; - ndesc->control = ctrl; + csrwr8(ctrl, ndesc, sgdma_descroffs(control)); - ctrl = 0; ctrl = SGDMA_CONTROL_HW_OWNED; ctrl |= generate_eop; ctrl |= rfixed; ctrl |= wfixed; /* Channel is implicitly zero, initialized to 0 by default */ - - desc->raddr = raddr; - desc->waddr = waddr; - desc->next = lower_32_bits(ndesc_phys); - desc->control = ctrl; - desc->status = 0; - desc->rburst = 0; - desc->wburst = 0; - desc->bytes = length; - desc->bytes_xferred = 0; + csrwr32(lower_32_bits(raddr), desc, sgdma_descroffs(raddr)); + csrwr32(lower_32_bits(waddr), desc, sgdma_descroffs(waddr)); + + csrwr32(0, desc, sgdma_descroffs(pad1)); + csrwr32(0, desc, sgdma_descroffs(pad2)); + csrwr32(lower_32_bits(ndesc_phys), desc, sgdma_descroffs(next)); + + csrwr8(ctrl, desc, sgdma_descroffs(control)); + csrwr8(0, desc, sgdma_descroffs(status)); + csrwr8(0, desc, sgdma_descroffs(wburst)); + csrwr8(0, desc, sgdma_descroffs(rburst)); + csrwr16(length, desc, sgdma_descroffs(bytes)); + csrwr16(0, desc, sgdma_descroffs(bytes_xferred)); } /* If hardware is busy, don't restart async read. @@ -350,11 +348,11 @@ static void sgdma_setup_descrip(struct sgdma_descrip *desc, */ static int sgdma_async_read(struct altera_tse_private *priv) { - struct sgdma_csr *csr = priv->rx_dma_csr; - struct sgdma_descrip *descbase = priv->rx_dma_desc; - struct sgdma_descrip *cdesc = &descbase[0]; - struct sgdma_descrip *ndesc = &descbase[1]; + struct sgdma_descrip __iomem *descbase = + (struct sgdma_descrip __iomem *)priv->rx_dma_desc; + struct sgdma_descrip __iomem *cdesc = &descbase[0]; + struct sgdma_descrip __iomem *ndesc = &descbase[1]; struct tse_buffer *rxbuffer = NULL; if (!sgdma_rxbusy(priv)) { @@ -379,11 +377,13 @@ static int sgdma_async_read(struct altera_tse_private *priv) priv->sgdmadesclen, DMA_TO_DEVICE); - iowrite32(lower_32_bits(sgdma_rxphysaddr(priv, cdesc)), - &csr->next_descrip); + csrwr32(lower_32_bits(sgdma_rxphysaddr(priv, cdesc)), + priv->rx_dma_csr, + sgdma_csroffs(next_descrip)); - iowrite32((priv->rxctrlreg | SGDMA_CTRLREG_START), - &csr->control); + csrwr32((priv->rxctrlreg | SGDMA_CTRLREG_START), + priv->rx_dma_csr, + sgdma_csroffs(control)); return 1; } @@ -392,32 +392,32 @@ static int sgdma_async_read(struct altera_tse_private *priv) } static int sgdma_async_write(struct altera_tse_private *priv, - struct sgdma_descrip *desc) + struct sgdma_descrip __iomem *desc) { - struct sgdma_csr *csr = priv->tx_dma_csr; - if (sgdma_txbusy(priv)) return 0; /* clear control and status */ - iowrite32(0, &csr->control); - iowrite32(0x1f, &csr->status); + csrwr32(0, priv->tx_dma_csr, sgdma_csroffs(control)); + csrwr32(0x1f, priv->tx_dma_csr, sgdma_csroffs(status)); dma_sync_single_for_device(priv->device, priv->txdescphys, priv->sgdmadesclen, DMA_TO_DEVICE); - iowrite32(lower_32_bits(sgdma_txphysaddr(priv, desc)), - &csr->next_descrip); + csrwr32(lower_32_bits(sgdma_txphysaddr(priv, desc)), + priv->tx_dma_csr, + sgdma_csroffs(next_descrip)); - iowrite32((priv->txctrlreg | SGDMA_CTRLREG_START), - &csr->control); + csrwr32((priv->txctrlreg | SGDMA_CTRLREG_START), + priv->tx_dma_csr, + sgdma_csroffs(control)); return 1; } static dma_addr_t sgdma_txphysaddr(struct altera_tse_private *priv, - struct sgdma_descrip *desc) + struct sgdma_descrip __iomem *desc) { dma_addr_t paddr = priv->txdescmem_busaddr; uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->tx_dma_desc; @@ -426,7 +426,7 @@ sgdma_txphysaddr(struct altera_tse_private *priv, static dma_addr_t sgdma_rxphysaddr(struct altera_tse_private *priv, - struct sgdma_descrip *desc) + struct sgdma_descrip __iomem *desc) { dma_addr_t paddr = priv->rxdescmem_busaddr; uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->rx_dma_desc; @@ -515,8 +515,8 @@ queue_rx_peekhead(struct altera_tse_private *priv) */ static int sgdma_rxbusy(struct altera_tse_private *priv) { - struct sgdma_csr *csr = priv->rx_dma_csr; - return ioread32(&csr->status) & SGDMA_STSREG_BUSY; + return csrrd32(priv->rx_dma_csr, sgdma_csroffs(status)) + & SGDMA_STSREG_BUSY; } /* waits for the tx sgdma to finish it's current operation, returns 0 @@ -525,13 +525,14 @@ static int sgdma_rxbusy(struct altera_tse_private *priv) static int sgdma_txbusy(struct altera_tse_private *priv) { int delay = 0; - struct sgdma_csr *csr = priv->tx_dma_csr; /* if DMA is busy, wait for current transactino to finish */ - while ((ioread32(&csr->status) & SGDMA_STSREG_BUSY) && (delay++ < 100)) + while ((csrrd32(priv->tx_dma_csr, sgdma_csroffs(status)) + & SGDMA_STSREG_BUSY) && (delay++ < 100)) udelay(1); - if (ioread32(&csr->status) & SGDMA_STSREG_BUSY) { + if (csrrd32(priv->tx_dma_csr, sgdma_csroffs(status)) + & SGDMA_STSREG_BUSY) { netdev_err(priv->dev, "timeout waiting for tx dma\n"); return 1; } diff --git a/drivers/net/ethernet/altera/altera_sgdmahw.h b/drivers/net/ethernet/altera/altera_sgdmahw.h index ba3334f..85bc33b 100644 --- a/drivers/net/ethernet/altera/altera_sgdmahw.h +++ b/drivers/net/ethernet/altera/altera_sgdmahw.h @@ -19,16 +19,16 @@ /* SGDMA descriptor structure */ struct sgdma_descrip { - unsigned int raddr; /* address of data to be read */ - unsigned int pad1; - unsigned int waddr; - unsigned int pad2; - unsigned int next; - unsigned int pad3; - unsigned short bytes; - unsigned char rburst; - unsigned char wburst; - unsigned short bytes_xferred; /* 16 bits, bytes xferred */ + u32 raddr; /* address of data to be read */ + u32 pad1; + u32 waddr; + u32 pad2; + u32 next; + u32 pad3; + u16 bytes; + u8 rburst; + u8 wburst; + u16 bytes_xferred; /* 16 bits, bytes xferred */ /* bit 0: error * bit 1: length error @@ -39,7 +39,7 @@ struct sgdma_descrip { * bit 6: reserved * bit 7: status eop for recv case */ - unsigned char status; + u8 status; /* bit 0: eop * bit 1: read_fixed @@ -47,7 +47,7 @@ struct sgdma_descrip { * bits 3,4,5,6: Channel (always 0) * bit 7: hardware owned */ - unsigned char control; + u8 control; } __packed; @@ -101,6 +101,8 @@ struct sgdma_csr { u32 pad3[3]; }; +#define sgdma_csroffs(a) (offsetof(struct sgdma_csr, a)) +#define sgdma_descroffs(a) (offsetof(struct sgdma_descrip, a)) #define SGDMA_STSREG_ERR BIT(0) /* Error */ #define SGDMA_STSREG_EOP BIT(1) /* EOP */ diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h index 465c4aa..2adb24d 100644 --- a/drivers/net/ethernet/altera/altera_tse.h +++ b/drivers/net/ethernet/altera/altera_tse.h @@ -357,6 +357,8 @@ struct altera_tse_mac { u32 reserved5[42]; }; +#define tse_csroffs(a) (offsetof(struct altera_tse_mac, a)) + /* Transmit and Receive Command Registers Bit Definitions */ #define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC BIT(17) @@ -487,4 +489,49 @@ struct altera_tse_private { */ void altera_tse_set_ethtool_ops(struct net_device *); +static inline +u32 csrrd32(void __iomem *mac, size_t offs) +{ + void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs); + return readl(paddr); +} + +static inline +u16 csrrd16(void __iomem *mac, size_t offs) +{ + void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs); + return readw(paddr); +} + +static inline +u8 csrrd8(void __iomem *mac, size_t offs) +{ + void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs); + return readb(paddr); +} + +static inline +void csrwr32(u32 val, void __iomem *mac, size_t offs) +{ + void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs); + + writel(val, paddr); +} + +static inline +void csrwr16(u16 val, void __iomem *mac, size_t offs) +{ + void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs); + + writew(val, paddr); +} + +static inline +void csrwr8(u8 val, void __iomem *mac, size_t offs) +{ + void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs); + + writeb(val, paddr); +} + #endif /* __ALTERA_TSE_H__ */ diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c index d817e28..be72e1e 100644 --- a/drivers/net/ethernet/altera/altera_tse_ethtool.c +++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c @@ -96,54 +96,89 @@ static void tse_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 *buf) { struct altera_tse_private *priv = netdev_priv(dev); - struct altera_tse_mac *mac = priv->mac_dev; u64 ext; - buf[0] = ioread32(&mac->frames_transmitted_ok); - buf[1] = ioread32(&mac->frames_received_ok); - buf[2] = ioread32(&mac->frames_check_sequence_errors); - buf[3] = ioread32(&mac->alignment_errors); + buf[0] = csrrd32(priv->mac_dev, + tse_csroffs(frames_transmitted_ok)); + buf[1] = csrrd32(priv->mac_dev, + tse_csroffs(frames_received_ok)); + buf[2] = csrrd32(priv->mac_dev, + tse_csroffs(frames_check_sequence_errors)); + buf[3] = csrrd32(priv->mac_dev, + tse_csroffs(alignment_errors)); /* Extended aOctetsTransmittedOK counter */ - ext = (u64) ioread32(&mac->msb_octets_transmitted_ok) << 32; - ext |= ioread32(&mac->octets_transmitted_ok); + ext = (u64) csrrd32(priv->mac_dev, + tse_csroffs(msb_octets_transmitted_ok)) << 32; + + ext |= csrrd32(priv->mac_dev, + tse_csroffs(octets_transmitted_ok)); buf[4] = ext; /* Extended aOctetsReceivedOK counter */ - ext = (u64) ioread32(&mac->msb_octets_received_ok) << 32; - ext |= ioread32(&mac->octets_received_ok); + ext = (u64) csrrd32(priv->mac_dev, + tse_csroffs(msb_octets_received_ok)) << 32; + + ext |= csrrd32(priv->mac_dev, + tse_csroffs(octets_received_ok)); buf[5] = ext; - buf[6] = ioread32(&mac->tx_pause_mac_ctrl_frames); - buf[7] = ioread32(&mac->rx_pause_mac_ctrl_frames); - buf[8] = ioread32(&mac->if_in_errors); - buf[9] = ioread32(&mac->if_out_errors); - buf[10] = ioread32(&mac->if_in_ucast_pkts); - buf[11] = ioread32(&mac->if_in_multicast_pkts); - buf[12] = ioread32(&mac->if_in_broadcast_pkts); - buf[13] = ioread32(&mac->if_out_discards); - buf[14] = ioread32(&mac->if_out_ucast_pkts); - buf[15] = ioread32(&mac->if_out_multicast_pkts); - buf[16] = ioread32(&mac->if_out_broadcast_pkts); - buf[17] = ioread32(&mac->ether_stats_drop_events); + buf[6] = csrrd32(priv->mac_dev, + tse_csroffs(tx_pause_mac_ctrl_frames)); + buf[7] = csrrd32(priv->mac_dev, + tse_csroffs(rx_pause_mac_ctrl_frames)); + buf[8] = csrrd32(priv->mac_dev, + tse_csroffs(if_in_errors)); + buf[9] = csrrd32(priv->mac_dev, + tse_csroffs(if_out_errors)); + buf[10] = csrrd32(priv->mac_dev, + tse_csroffs(if_in_ucast_pkts)); + buf[11] = csrrd32(priv->mac_dev, + tse_csroffs(if_in_multicast_pkts)); + buf[12] = csrrd32(priv->mac_dev, + tse_csroffs(if_in_broadcast_pkts)); + buf[13] = csrrd32(priv->mac_dev, + tse_csroffs(if_out_discards)); + buf[14] = csrrd32(priv->mac_dev, + tse_csroffs(if_out_ucast_pkts)); + buf[15] = csrrd32(priv->mac_dev, + tse_csroffs(if_out_multicast_pkts)); + buf[16] = csrrd32(priv->mac_dev, + tse_csroffs(if_out_broadcast_pkts)); + buf[17] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_drop_events)); /* Extended etherStatsOctets counter */ - ext = (u64) ioread32(&mac->msb_ether_stats_octets) << 32; - ext |= ioread32(&mac->ether_stats_octets); + ext = (u64) csrrd32(priv->mac_dev, + tse_csroffs(msb_ether_stats_octets)) << 32; + ext |= csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_octets)); buf[18] = ext; - buf[19] = ioread32(&mac->ether_stats_pkts); - buf[20] = ioread32(&mac->ether_stats_undersize_pkts); - buf[21] = ioread32(&mac->ether_stats_oversize_pkts); - buf[22] = ioread32(&mac->ether_stats_pkts_64_octets); - buf[23] = ioread32(&mac->ether_stats_pkts_65to127_octets); - buf[24] = ioread32(&mac->ether_stats_pkts_128to255_octets); - buf[25] = ioread32(&mac->ether_stats_pkts_256to511_octets); - buf[26] = ioread32(&mac->ether_stats_pkts_512to1023_octets); - buf[27] = ioread32(&mac->ether_stats_pkts_1024to1518_octets); - buf[28] = ioread32(&mac->ether_stats_pkts_1519tox_octets); - buf[29] = ioread32(&mac->ether_stats_jabbers); - buf[30] = ioread32(&mac->ether_stats_fragments); + buf[19] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts)); + buf[20] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_undersize_pkts)); + buf[21] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_oversize_pkts)); + buf[22] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_64_octets)); + buf[23] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_65to127_octets)); + buf[24] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_128to255_octets)); + buf[25] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_256to511_octets)); + buf[26] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_512to1023_octets)); + buf[27] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_1024to1518_octets)); + buf[28] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_pkts_1519tox_octets)); + buf[29] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_jabbers)); + buf[30] = csrrd32(priv->mac_dev, + tse_csroffs(ether_stats_fragments)); } static int tse_sset_count(struct net_device *dev, int sset) @@ -178,7 +213,6 @@ static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs, { int i; struct altera_tse_private *priv = netdev_priv(dev); - u32 *tse_mac_regs = (u32 *)priv->mac_dev; u32 *buf = regbuf; /* Set version to a known value, so ethtool knows @@ -196,7 +230,7 @@ static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs, regs->version = 1; for (i = 0; i < TSE_NUM_REGS; i++) - buf[i] = ioread32(&tse_mac_regs[i]); + buf[i] = csrrd32(priv->mac_dev, i * 4); } static int tse_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index e44a4ae..7330681 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -100,29 +100,30 @@ static inline u32 tse_tx_avail(struct altera_tse_private *priv) */ static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { - struct altera_tse_mac *mac = (struct altera_tse_mac *)bus->priv; - unsigned int *mdio_regs = (unsigned int *)&mac->mdio_phy0; - u32 data; + struct net_device *ndev = bus->priv; + struct altera_tse_private *priv = netdev_priv(ndev); /* set MDIO address */ - iowrite32((mii_id & 0x1f), &mac->mdio_phy0_addr); + csrwr32((mii_id & 0x1f), priv->mac_dev, + tse_csroffs(mdio_phy0_addr)); /* get the data */ - data = ioread32(&mdio_regs[regnum]) & 0xffff; - return data; + return csrrd32(priv->mac_dev, + tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff; } static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) { - struct altera_tse_mac *mac = (struct altera_tse_mac *)bus->priv; - unsigned int *mdio_regs = (unsigned int *)&mac->mdio_phy0; + struct net_device *ndev = bus->priv; + struct altera_tse_private *priv = netdev_priv(ndev); /* set MDIO address */ - iowrite32((mii_id & 0x1f), &mac->mdio_phy0_addr); + csrwr32((mii_id & 0x1f), priv->mac_dev, + tse_csroffs(mdio_phy0_addr)); /* write the data */ - iowrite32((u32) value, &mdio_regs[regnum]); + csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4); return 0; } @@ -168,7 +169,7 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) for (i = 0; i < PHY_MAX_ADDR; i++) mdio->irq[i] = PHY_POLL; - mdio->priv = priv->mac_dev; + mdio->priv = dev; mdio->parent = priv->device; ret = of_mdiobus_register(mdio, mdio_node); @@ -563,7 +564,6 @@ static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int nopaged_len = skb_headlen(skb); enum netdev_tx ret = NETDEV_TX_OK; dma_addr_t dma_addr; - int txcomplete = 0; spin_lock_bh(&priv->tx_lock); @@ -599,7 +599,7 @@ static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev) dma_sync_single_for_device(priv->device, buffer->dma_addr, buffer->len, DMA_TO_DEVICE); - txcomplete = priv->dmaops->tx_buffer(priv, buffer); + priv->dmaops->tx_buffer(priv, buffer); skb_tx_timestamp(skb); @@ -698,7 +698,6 @@ static struct phy_device *connect_local_phy(struct net_device *dev) struct altera_tse_private *priv = netdev_priv(dev); struct phy_device *phydev = NULL; char phy_id_fmt[MII_BUS_ID_SIZE + 3]; - int ret; if (priv->phy_addr != POLL_PHY) { snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, @@ -712,6 +711,7 @@ static struct phy_device *connect_local_phy(struct net_device *dev) netdev_err(dev, "Could not attach to PHY\n"); } else { + int ret; phydev = phy_find_first(priv->mdio); if (phydev == NULL) { netdev_err(dev, "No PHY found\n"); @@ -791,7 +791,6 @@ static int init_phy(struct net_device *dev) static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr) { - struct altera_tse_mac *mac = priv->mac_dev; u32 msb; u32 lsb; @@ -799,8 +798,8 @@ static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr) lsb = ((addr[5] << 8) | addr[4]) & 0xffff; /* Set primary MAC address */ - iowrite32(msb, &mac->mac_addr_0); - iowrite32(lsb, &mac->mac_addr_1); + csrwr32(msb, priv->mac_dev, tse_csroffs(mac_addr_0)); + csrwr32(lsb, priv->mac_dev, tse_csroffs(mac_addr_1)); } /* MAC software reset. @@ -811,26 +810,26 @@ static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr) */ static int reset_mac(struct altera_tse_private *priv) { - void __iomem *cmd_cfg_reg = &priv->mac_dev->command_config; int counter; u32 dat; - dat = ioread32(cmd_cfg_reg); + dat = csrrd32(priv->mac_dev, tse_csroffs(command_config)); dat &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA); dat |= MAC_CMDCFG_SW_RESET | MAC_CMDCFG_CNT_RESET; - iowrite32(dat, cmd_cfg_reg); + csrwr32(dat, priv->mac_dev, tse_csroffs(command_config)); counter = 0; while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) { - if (tse_bit_is_clear(cmd_cfg_reg, MAC_CMDCFG_SW_RESET)) + if (tse_bit_is_clear(priv->mac_dev, tse_csroffs(command_config), + MAC_CMDCFG_SW_RESET)) break; udelay(1); } if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) { - dat = ioread32(cmd_cfg_reg); + dat = csrrd32(priv->mac_dev, tse_csroffs(command_config)); dat &= ~MAC_CMDCFG_SW_RESET; - iowrite32(dat, cmd_cfg_reg); + csrwr32(dat, priv->mac_dev, tse_csroffs(command_config)); return -1; } return 0; @@ -840,41 +839,57 @@ static int reset_mac(struct altera_tse_private *priv) */ static int init_mac(struct altera_tse_private *priv) { - struct altera_tse_mac *mac = priv->mac_dev; unsigned int cmd = 0; u32 frm_length; /* Setup Rx FIFO */ - iowrite32(priv->rx_fifo_depth - ALTERA_TSE_RX_SECTION_EMPTY, - &mac->rx_section_empty); - iowrite32(ALTERA_TSE_RX_SECTION_FULL, &mac->rx_section_full); - iowrite32(ALTERA_TSE_RX_ALMOST_EMPTY, &mac->rx_almost_empty); - iowrite32(ALTERA_TSE_RX_ALMOST_FULL, &mac->rx_almost_full); + csrwr32(priv->rx_fifo_depth - ALTERA_TSE_RX_SECTION_EMPTY, + priv->mac_dev, tse_csroffs(rx_section_empty)); + + csrwr32(ALTERA_TSE_RX_SECTION_FULL, priv->mac_dev, + tse_csroffs(rx_section_full)); + + csrwr32(ALTERA_TSE_RX_ALMOST_EMPTY, priv->mac_dev, + tse_csroffs(rx_almost_empty)); + + csrwr32(ALTERA_TSE_RX_ALMOST_FULL, priv->mac_dev, + tse_csroffs(rx_almost_full)); /* Setup Tx FIFO */ - iowrite32(priv->tx_fifo_depth - ALTERA_TSE_TX_SECTION_EMPTY, - &mac->tx_section_empty); - iowrite32(ALTERA_TSE_TX_SECTION_FULL, &mac->tx_section_full); - iowrite32(ALTERA_TSE_TX_ALMOST_EMPTY, &mac->tx_almost_empty); - iowrite32(ALTERA_TSE_TX_ALMOST_FULL, &mac->tx_almost_full); + csrwr32(priv->tx_fifo_depth - ALTERA_TSE_TX_SECTION_EMPTY, + priv->mac_dev, tse_csroffs(tx_section_empty)); + + csrwr32(ALTERA_TSE_TX_SECTION_FULL, priv->mac_dev, + tse_csroffs(tx_section_full)); + + csrwr32(ALTERA_TSE_TX_ALMOST_EMPTY, priv->mac_dev, + tse_csroffs(tx_almost_empty)); + + csrwr32(ALTERA_TSE_TX_ALMOST_FULL, priv->mac_dev, + tse_csroffs(tx_almost_full)); /* MAC Address Configuration */ tse_update_mac_addr(priv, priv->dev->dev_addr); /* MAC Function Configuration */ frm_length = ETH_HLEN + priv->dev->mtu + ETH_FCS_LEN; - iowrite32(frm_length, &mac->frm_length); - iowrite32(ALTERA_TSE_TX_IPG_LENGTH, &mac->tx_ipg_length); + csrwr32(frm_length, priv->mac_dev, tse_csroffs(frm_length)); + + csrwr32(ALTERA_TSE_TX_IPG_LENGTH, priv->mac_dev, + tse_csroffs(tx_ipg_length)); /* Disable RX/TX shift 16 for alignment of all received frames on 16-bit * start address */ - tse_set_bit(&mac->rx_cmd_stat, ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16); - tse_clear_bit(&mac->tx_cmd_stat, ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 | - ALTERA_TSE_TX_CMD_STAT_OMIT_CRC); + tse_set_bit(priv->mac_dev, tse_csroffs(rx_cmd_stat), + ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16); + + tse_clear_bit(priv->mac_dev, tse_csroffs(tx_cmd_stat), + ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 | + ALTERA_TSE_TX_CMD_STAT_OMIT_CRC); /* Set the MAC options */ - cmd = ioread32(&mac->command_config); + cmd = csrrd32(priv->mac_dev, tse_csroffs(command_config)); cmd &= ~MAC_CMDCFG_PAD_EN; /* No padding Removal on Receive */ cmd &= ~MAC_CMDCFG_CRC_FWD; /* CRC Removal */ cmd |= MAC_CMDCFG_RX_ERR_DISC; /* Automatically discard frames @@ -889,9 +904,10 @@ static int init_mac(struct altera_tse_private *priv) cmd &= ~MAC_CMDCFG_ETH_SPEED; cmd &= ~MAC_CMDCFG_ENA_10; - iowrite32(cmd, &mac->command_config); + csrwr32(cmd, priv->mac_dev, tse_csroffs(command_config)); - iowrite32(ALTERA_TSE_PAUSE_QUANTA, &mac->pause_quanta); + csrwr32(ALTERA_TSE_PAUSE_QUANTA, priv->mac_dev, + tse_csroffs(pause_quanta)); if (netif_msg_hw(priv)) dev_dbg(priv->device, @@ -904,15 +920,14 @@ static int init_mac(struct altera_tse_private *priv) */ static void tse_set_mac(struct altera_tse_private *priv, bool enable) { - struct altera_tse_mac *mac = priv->mac_dev; - u32 value = ioread32(&mac->command_config); + u32 value = csrrd32(priv->mac_dev, tse_csroffs(command_config)); if (enable) value |= MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA; else value &= ~(MAC_CMDCFG_TX_ENA | MAC_CMDCFG_RX_ENA); - iowrite32(value, &mac->command_config); + csrwr32(value, priv->mac_dev, tse_csroffs(command_config)); } /* Change the MTU @@ -942,13 +957,12 @@ static int tse_change_mtu(struct net_device *dev, int new_mtu) static void altera_tse_set_mcfilter(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); - struct altera_tse_mac *mac = priv->mac_dev; int i; struct netdev_hw_addr *ha; /* clear the hash filter */ for (i = 0; i < 64; i++) - iowrite32(0, &(mac->hash_table[i])); + csrwr32(0, priv->mac_dev, tse_csroffs(hash_table) + i * 4); netdev_for_each_mc_addr(ha, dev) { unsigned int hash = 0; @@ -964,7 +978,7 @@ static void altera_tse_set_mcfilter(struct net_device *dev) hash = (hash << 1) | xor_bit; } - iowrite32(1, &(mac->hash_table[hash])); + csrwr32(1, priv->mac_dev, tse_csroffs(hash_table) + hash * 4); } } @@ -972,12 +986,11 @@ static void altera_tse_set_mcfilter(struct net_device *dev) static void altera_tse_set_mcfilterall(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); - struct altera_tse_mac *mac = priv->mac_dev; int i; /* set the hash filter */ for (i = 0; i < 64; i++) - iowrite32(1, &(mac->hash_table[i])); + csrwr32(1, priv->mac_dev, tse_csroffs(hash_table) + i * 4); } /* Set or clear the multicast filter for this adaptor @@ -985,12 +998,12 @@ static void altera_tse_set_mcfilterall(struct net_device *dev) static void tse_set_rx_mode_hashfilter(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); - struct altera_tse_mac *mac = priv->mac_dev; spin_lock(&priv->mac_cfg_lock); if (dev->flags & IFF_PROMISC) - tse_set_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN); + tse_set_bit(priv->mac_dev, tse_csroffs(command_config), + MAC_CMDCFG_PROMIS_EN); if (dev->flags & IFF_ALLMULTI) altera_tse_set_mcfilterall(dev); @@ -1005,15 +1018,16 @@ static void tse_set_rx_mode_hashfilter(struct net_device *dev) static void tse_set_rx_mode(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); - struct altera_tse_mac *mac = priv->mac_dev; spin_lock(&priv->mac_cfg_lock); if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev) || !netdev_uc_empty(dev)) - tse_set_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN); + tse_set_bit(priv->mac_dev, tse_csroffs(command_config), + MAC_CMDCFG_PROMIS_EN); else - tse_clear_bit(&mac->command_config, MAC_CMDCFG_PROMIS_EN); + tse_clear_bit(priv->mac_dev, tse_csroffs(command_config), + MAC_CMDCFG_PROMIS_EN); spin_unlock(&priv->mac_cfg_lock); } @@ -1362,6 +1376,11 @@ static int altera_tse_probe(struct platform_device *pdev) of_property_read_bool(pdev->dev.of_node, "altr,has-hash-multicast-filter"); + /* Set hash filter to not set for now until the + * multicast filter receive issue is debugged + */ + priv->hash_filter = 0; + /* get supplemental address settings for this instance */ priv->added_unicast = of_property_read_bool(pdev->dev.of_node, @@ -1493,7 +1512,7 @@ static int altera_tse_remove(struct platform_device *pdev) return 0; } -struct altera_dmaops altera_dtype_sgdma = { +static const struct altera_dmaops altera_dtype_sgdma = { .altera_dtype = ALTERA_DTYPE_SGDMA, .dmamask = 32, .reset_dma = sgdma_reset, @@ -1512,7 +1531,7 @@ struct altera_dmaops altera_dtype_sgdma = { .start_rxdma = sgdma_start_rxdma, }; -struct altera_dmaops altera_dtype_msgdma = { +static const struct altera_dmaops altera_dtype_msgdma = { .altera_dtype = ALTERA_DTYPE_MSGDMA, .dmamask = 64, .reset_dma = msgdma_reset, diff --git a/drivers/net/ethernet/altera/altera_utils.c b/drivers/net/ethernet/altera/altera_utils.c index 70fa13f..d7eeb17 100644 --- a/drivers/net/ethernet/altera/altera_utils.c +++ b/drivers/net/ethernet/altera/altera_utils.c @@ -17,28 +17,28 @@ #include "altera_tse.h" #include "altera_utils.h" -void tse_set_bit(void __iomem *ioaddr, u32 bit_mask) +void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask) { - u32 value = ioread32(ioaddr); + u32 value = csrrd32(ioaddr, offs); value |= bit_mask; - iowrite32(value, ioaddr); + csrwr32(value, ioaddr, offs); } -void tse_clear_bit(void __iomem *ioaddr, u32 bit_mask) +void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask) { - u32 value = ioread32(ioaddr); + u32 value = csrrd32(ioaddr, offs); value &= ~bit_mask; - iowrite32(value, ioaddr); + csrwr32(value, ioaddr, offs); } -int tse_bit_is_set(void __iomem *ioaddr, u32 bit_mask) +int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask) { - u32 value = ioread32(ioaddr); + u32 value = csrrd32(ioaddr, offs); return (value & bit_mask) ? 1 : 0; } -int tse_bit_is_clear(void __iomem *ioaddr, u32 bit_mask) +int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask) { - u32 value = ioread32(ioaddr); + u32 value = csrrd32(ioaddr, offs); return (value & bit_mask) ? 0 : 1; } diff --git a/drivers/net/ethernet/altera/altera_utils.h b/drivers/net/ethernet/altera/altera_utils.h index ce1db36..baf100c 100644 --- a/drivers/net/ethernet/altera/altera_utils.h +++ b/drivers/net/ethernet/altera/altera_utils.h @@ -19,9 +19,9 @@ #ifndef __ALTERA_UTILS_H__ #define __ALTERA_UTILS_H__ -void tse_set_bit(void __iomem *ioaddr, u32 bit_mask); -void tse_clear_bit(void __iomem *ioaddr, u32 bit_mask); -int tse_bit_is_set(void __iomem *ioaddr, u32 bit_mask); -int tse_bit_is_clear(void __iomem *ioaddr, u32 bit_mask); +void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask); +void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask); +int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask); +int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask); #endif /* __ALTERA_UTILS_H__*/ diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c index b08101b..968b7bf 100644 --- a/drivers/net/ethernet/amd/ariadne.c +++ b/drivers/net/ethernet/amd/ariadne.c @@ -718,7 +718,6 @@ static int ariadne_init_one(struct zorro_dev *z, unsigned long mem_start = board + ARIADNE_RAM; struct resource *r1, *r2; struct net_device *dev; - struct ariadne_private *priv; u32 serial; int err; @@ -738,8 +737,6 @@ static int ariadne_init_one(struct zorro_dev *z, return -ENOMEM; } - priv = netdev_priv(dev); - r1->name = dev->name; r2->name = dev->name; diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index f66de13..25982b0 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -457,7 +457,7 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, struct sk_buff *skb; unsigned int p_index; u16 len, status; - struct rsb *rsb; + struct bcm_rsb *rsb; /* Determine how much we should process since last call */ p_index = rdma_readl(priv, RDMA_PROD_INDEX); @@ -479,10 +479,10 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, cb = &priv->rx_cbs[priv->rx_read_ptr]; skb = cb->skb; dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), - dma_unmap_len(cb, dma_len), DMA_FROM_DEVICE); + RX_BUF_LENGTH, DMA_FROM_DEVICE); /* Extract the Receive Status Block prepended */ - rsb = (struct rsb *)skb->data; + rsb = (struct bcm_rsb *)skb->data; len = (rsb->rx_status_len >> DESC_LEN_SHIFT) & DESC_LEN_MASK; status = (rsb->rx_status_len >> DESC_STATUS_SHIFT) & DESC_STATUS_MASK; @@ -759,7 +759,7 @@ static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id) static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *nskb; - struct tsb *tsb; + struct bcm_tsb *tsb; u32 csum_info; u8 ip_proto; u16 csum_start; @@ -777,7 +777,7 @@ static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) skb = nskb; } - tsb = (struct tsb *)skb_push(skb, sizeof(*tsb)); + tsb = (struct bcm_tsb *)skb_push(skb, sizeof(*tsb)); /* Zero-out TSB by default */ memset(tsb, 0, sizeof(*tsb)); @@ -821,6 +821,7 @@ static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb, struct bcm_sysport_cb *cb; struct netdev_queue *txq; struct dma_desc *desc; + unsigned int skb_len; dma_addr_t mapping; u32 len_status; u16 queue; @@ -848,10 +849,25 @@ static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb, } } - mapping = dma_map_single(kdev, skb->data, skb->len, DMA_TO_DEVICE); + /* The Ethernet switch we are interfaced with needs packets to be at + * least 64 bytes (including FCS) otherwise they will be discarded when + * they enter the switch port logic. When Broadcom tags are enabled, we + * need to make sure that packets are at least 68 bytes + * (including FCS and tag) because the length verification is done after + * the Broadcom tag is stripped off the ingress packet. + */ + if (skb_padto(skb, ETH_ZLEN + ENET_BRCM_TAG_LEN)) { + ret = NETDEV_TX_OK; + goto out; + } + + skb_len = skb->len < ETH_ZLEN + ENET_BRCM_TAG_LEN ? + ETH_ZLEN + ENET_BRCM_TAG_LEN : skb->len; + + mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE); if (dma_mapping_error(kdev, mapping)) { netif_err(priv, tx_err, dev, "DMA map failed at %p (len=%d)\n", - skb->data, skb->len); + skb->data, skb_len); ret = NETDEV_TX_OK; goto out; } @@ -860,14 +876,14 @@ static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb, cb = &ring->cbs[ring->curr_desc]; cb->skb = skb; dma_unmap_addr_set(cb, dma_addr, mapping); - dma_unmap_len_set(cb, dma_len, skb->len); + dma_unmap_len_set(cb, dma_len, skb_len); /* Fetch a descriptor entry from our pool */ desc = ring->desc_cpu; desc->addr_lo = lower_32_bits(mapping); len_status = upper_32_bits(mapping) & DESC_ADDR_HI_MASK; - len_status |= (skb->len << DESC_LEN_SHIFT); + len_status |= (skb_len << DESC_LEN_SHIFT); len_status |= (DESC_SOP | DESC_EOP | TX_STATUS_APP_CRC) << DESC_STATUS_SHIFT; if (skb->ip_summed == CHECKSUM_PARTIAL) @@ -959,15 +975,16 @@ static void bcm_sysport_adj_link(struct net_device *dev) if (!phydev->pause) cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE; - reg = umac_readl(priv, UMAC_CMD); - reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | + if (changed) { + reg = umac_readl(priv, UMAC_CMD); + reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | CMD_HD_EN | CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE); - reg |= cmd_bits; - umac_writel(priv, reg, UMAC_CMD); + reg |= cmd_bits; + umac_writel(priv, reg, UMAC_CMD); - if (changed) phy_print_status(priv->phydev); + } } static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, @@ -1227,6 +1244,12 @@ static inline void umac_enable_set(struct bcm_sysport_priv *priv, else reg &= ~(CMD_RX_EN | CMD_TX_EN); umac_writel(priv, reg, UMAC_CMD); + + /* UniMAC stops on a packet boundary, wait for a full-sized packet + * to be processed (1 msec). + */ + if (enable == 0) + usleep_range(1000, 2000); } static inline int umac_reset(struct bcm_sysport_priv *priv) @@ -1304,8 +1327,8 @@ static int bcm_sysport_open(struct net_device *dev) /* Read CRC forward */ priv->crc_fwd = !!(umac_readl(priv, UMAC_CMD) & CMD_CRC_FWD); - priv->phydev = of_phy_connect_fixed_link(dev, bcm_sysport_adj_link, - priv->phy_interface); + priv->phydev = of_phy_connect(dev, priv->phy_dn, bcm_sysport_adj_link, + 0, priv->phy_interface); if (!priv->phydev) { netdev_err(dev, "could not attach to PHY\n"); return -ENODEV; @@ -1528,6 +1551,19 @@ static int bcm_sysport_probe(struct platform_device *pdev) if (priv->phy_interface < 0) priv->phy_interface = PHY_INTERFACE_MODE_GMII; + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + if (of_phy_is_fixed_link(dn)) { + ret = of_phy_register_fixed_link(dn); + if (ret) { + dev_err(&pdev->dev, "failed to register fixed PHY\n"); + goto err; + } + + priv->phy_dn = dn; + } + /* Initialize netdevice members */ macaddr = of_get_mac_address(dn); if (!macaddr || !is_valid_ether_addr(macaddr)) { @@ -1548,8 +1584,8 @@ static int bcm_sysport_probe(struct platform_device *pdev) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; /* Set the needed headroom once and for all */ - BUILD_BUG_ON(sizeof(struct tsb) != 8); - dev->needed_headroom += sizeof(struct tsb); + BUILD_BUG_ON(sizeof(struct bcm_tsb) != 8); + dev->needed_headroom += sizeof(struct bcm_tsb); /* We are interfaced to a switch which handles the multicast * filtering for us, so we do not support programming any diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index a0441e7..281c082 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -29,16 +29,16 @@ /* Default RX buffer allocation size */ #define RX_BUF_LENGTH 2048 -/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528. +/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(4) + FCS(4) = 1526. * 1536 is multiple of 256 bytes */ -#define ENET_BRCM_TAG_LEN 6 -#define ENET_PAD 8 +#define ENET_BRCM_TAG_LEN 4 +#define ENET_PAD 10 #define UMAC_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \ ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD) /* Transmit status block */ -struct tsb { +struct bcm_tsb { u32 pcp_dei_vid; #define PCP_DEI_MASK 0xf #define VID_SHIFT 4 @@ -56,7 +56,7 @@ struct tsb { /* Receive status block uses the same * definitions as the DMA descriptor */ -struct rsb { +struct bcm_rsb { u32 rx_status_len; u32 brcm_egress_tag; }; @@ -656,6 +656,7 @@ struct bcm_sysport_priv { unsigned int rx_c_index; /* PHY device */ + struct device_node *phy_dn; struct phy_device *phydev; phy_interface_t phy_interface; int old_pause; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 4d8f8ab..4cab09d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 9261d53..d18441e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver * UDP CSUM errata workaround by Arik Gendelman diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 3448cc0..571427c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver * UDP CSUM errata workaround by Arik Gendelman diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 97ea542..51a952c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -12,7 +12,7 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Dmitry Kravkov * */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h index 804b8f6..c6939ec 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h @@ -12,7 +12,7 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Dmitry Kravkov * */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index af138f8..bd0600c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver * UDP CSUM errata workaround by Arik Gendelman diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h index f572ae1..8aafd9b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h @@ -6,8 +6,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> - * Written by: Vladislav Zolotarov <vladz@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> + * Written by: Vladislav Zolotarov * Based on the original idea of John Wright <john.wright@hp.com>. */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h index c2dfea7..bd90e50 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h @@ -7,9 +7,9 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir - * Modified by: Vladislav Zolotarov <vladz@broadcom.com> + * Modified by: Vladislav Zolotarov */ #ifndef BNX2X_INIT_H diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h index 8ab0dd9..5669ed2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h @@ -8,8 +8,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> - * Written by: Vladislav Zolotarov <vladz@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> + * Written by: Vladislav Zolotarov */ #ifndef BNX2X_INIT_OPS_H diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 9b6b3d7..4fef1e1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -2218,7 +2218,6 @@ int bnx2x_update_pfc(struct link_params *params, */ u32 val; struct bnx2x *bp = params->bp; - int bnx2x_status = 0; u8 bmac_loopback = (params->loopback_mode == LOOPBACK_BMAC); if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) @@ -2232,7 +2231,7 @@ int bnx2x_update_pfc(struct link_params *params, bnx2x_update_pfc_nig(params, vars, pfc_params); if (!vars->link_up) - return bnx2x_status; + return 0; DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n"); @@ -2246,7 +2245,7 @@ int bnx2x_update_pfc(struct link_params *params, == 0) { DP(NETIF_MSG_LINK, "About to update PFC in EMAC\n"); bnx2x_emac_enable(params, vars, 0); - return bnx2x_status; + return 0; } if (CHIP_IS_E2(bp)) bnx2x_update_pfc_bmac2(params, vars, bmac_loopback); @@ -2260,7 +2259,7 @@ int bnx2x_update_pfc(struct link_params *params, val = 1; REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val); } - return bnx2x_status; + return 0; } static int bnx2x_bmac1_enable(struct link_params *params, @@ -6473,7 +6472,6 @@ int bnx2x_test_link(struct link_params *params, struct link_vars *vars, static int bnx2x_link_initialize(struct link_params *params, struct link_vars *vars) { - int rc = 0; u8 phy_index, non_ext_phy; struct bnx2x *bp = params->bp; /* In case of external phy existence, the line speed would be the @@ -6546,7 +6544,7 @@ static int bnx2x_link_initialize(struct link_params *params, NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); - return rc; + return 0; } static void bnx2x_int_link_reset(struct bnx2x_phy *phy, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 3b0d431..ff2bdd8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver * UDP CSUM errata workaround by Arik Gendelman diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 31297266..736264b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -12,7 +12,7 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Vladislav Zolotarov * */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index 80f6c79..718ecd2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -12,7 +12,7 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Vladislav Zolotarov * */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 81cc2d9..a93c7af 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -12,9 +12,9 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> - * Written by: Shmulik Ravid <shmulikr@broadcom.com> - * Ariel Elior <ariele@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> + * Written by: Shmulik Ravid + * Ariel Elior <ariel.elior@qlogic.com> * */ #include "bnx2x.h" @@ -2576,7 +2576,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, ivi->vf = vfidx; ivi->qos = 0; - ivi->tx_rate = 10000; /* always 10G. TBA take from link struct */ + ivi->max_tx_rate = 10000; /* always 10G. TBA take from link struct */ + ivi->min_tx_rate = 0; ivi->spoofchk = 1; /*always enabled */ if (vf->state == VF_ENABLED) { /* mac and vlan are in vlan_mac objects */ @@ -2695,7 +2696,7 @@ out: bnx2x_unlock_vf_pf_channel(bp, vf, CHANNEL_TLV_PF_SET_MAC); } - return 0; + return rc; } int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index cd4d624..96c575e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -12,9 +12,9 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> - * Written by: Shmulik Ravid <shmulikr@broadcom.com> - * Ariel Elior <ariele@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> + * Written by: Shmulik Ravid + * Ariel Elior <ariel.elior@qlogic.com> */ #ifndef BNX2X_SRIOV_H #define BNX2X_SRIOV_H diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 3b75070..ca47665 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver * UDP CSUM errata workaround by Arik Gendelman diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index f358450..2beceae 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> * Written by: Eliezer Tamir * Based on code from Michael Chan's bnx2 driver * UDP CSUM errata workaround by Arik Gendelman diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index 0c067e8..d712d0d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -12,9 +12,9 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> - * Written by: Shmulik Ravid <shmulikr@broadcom.com> - * Ariel Elior <ariele@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> + * Written by: Shmulik Ravid + * Ariel Elior <ariel.elior@qlogic.com> */ #include "bnx2x.h" @@ -747,7 +747,7 @@ int bnx2x_vfpf_config_mac(struct bnx2x *bp, u8 *addr, u8 vf_qid, bool set) out: bnx2x_vfpf_finalize(bp, &req->first_tlv); - return 0; + return rc; } /* request pf to config rss table for vf queues*/ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h index c922b81..e21e706 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h @@ -12,8 +12,8 @@ * license other than the GPL, without Broadcom's express prior written * consent. * - * Maintained by: Eilon Greenstein <eilong@broadcom.com> - * Written by: Ariel Elior <ariele@broadcom.com> + * Maintained by: Ariel Elior <ariel.elior@qlogic.com> + * Written by: Ariel Elior <ariel.elior@qlogic.com> */ #ifndef VF_PF_IF_H #define VF_PF_IF_H diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 4608673..add8d85 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -298,6 +298,7 @@ int bcmgenet_mii_config(struct net_device *dev) static int bcmgenet_mii_probe(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); + struct device_node *dn = priv->pdev->dev.of_node; struct phy_device *phydev; unsigned int phy_flags; int ret; @@ -307,15 +308,19 @@ static int bcmgenet_mii_probe(struct net_device *dev) return 0; } - if (priv->phy_dn) - phydev = of_phy_connect(dev, priv->phy_dn, - bcmgenet_mii_setup, 0, - priv->phy_interface); - else - phydev = of_phy_connect_fixed_link(dev, - bcmgenet_mii_setup, - priv->phy_interface); + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + if (of_phy_is_fixed_link(dn)) { + ret = of_phy_register_fixed_link(dn); + if (ret) + return ret; + + priv->phy_dn = dn; + } + phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup, 0, + priv->phy_interface); if (!phydev) { pr_err("could not attach to PHY\n"); return -ENODEV; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 8c2314e..df2792d 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -3224,7 +3224,7 @@ static int tg3_nvram_read_using_eeprom(struct tg3 *tp, return 0; } -#define NVRAM_CMD_TIMEOUT 10000 +#define NVRAM_CMD_TIMEOUT 100 static int tg3_nvram_exec_cmd(struct tg3 *tp, u32 nvram_cmd) { @@ -11893,9 +11893,9 @@ static int tg3_get_eeprom_len(struct net_device *dev) static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) { struct tg3 *tp = netdev_priv(dev); - int ret; + int ret, cpmu_restore = 0; u8 *pd; - u32 i, offset, len, b_offset, b_count; + u32 i, offset, len, b_offset, b_count, cpmu_val = 0; __be32 val; if (tg3_flag(tp, NO_NVRAM)) @@ -11907,6 +11907,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, eeprom->magic = TG3_EEPROM_MAGIC; + /* Override clock, link aware and link idle modes */ + if (tg3_flag(tp, CPMU_PRESENT)) { + cpmu_val = tr32(TG3_CPMU_CTRL); + if (cpmu_val & (CPMU_CTRL_LINK_AWARE_MODE | + CPMU_CTRL_LINK_IDLE_MODE)) { + tw32(TG3_CPMU_CTRL, cpmu_val & + ~(CPMU_CTRL_LINK_AWARE_MODE | + CPMU_CTRL_LINK_IDLE_MODE)); + cpmu_restore = 1; + } + } + tg3_override_clk(tp); + if (offset & 3) { /* adjustments to start on required 4 byte boundary */ b_offset = offset & 3; @@ -11917,7 +11930,7 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, } ret = tg3_nvram_read_be32(tp, offset-b_offset, &val); if (ret) - return ret; + goto eeprom_done; memcpy(data, ((char *)&val) + b_offset, b_count); len -= b_count; offset += b_count; @@ -11929,10 +11942,20 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, for (i = 0; i < (len - (len & 3)); i += 4) { ret = tg3_nvram_read_be32(tp, offset + i, &val); if (ret) { + if (i) + i -= 4; eeprom->len += i; - return ret; + goto eeprom_done; } memcpy(pd + i, &val, 4); + if (need_resched()) { + if (signal_pending(current)) { + eeprom->len += i; + ret = -EINTR; + goto eeprom_done; + } + cond_resched(); + } } eeprom->len += i; @@ -11943,11 +11966,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, b_offset = offset + len - b_count; ret = tg3_nvram_read_be32(tp, b_offset, &val); if (ret) - return ret; + goto eeprom_done; memcpy(pd, &val, b_count); eeprom->len += b_count; } - return 0; + ret = 0; + +eeprom_done: + /* Restore clock, link aware and link idle modes */ + tg3_restore_clk(tp); + if (cpmu_restore) + tw32(TG3_CPMU_CTRL, cpmu_val); + + return ret; } static int tg3_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index e35c8e0..14f465f 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -43,6 +43,8 @@ #define ENIC_CQ_MAX (ENIC_WQ_MAX + ENIC_RQ_MAX) #define ENIC_INTR_MAX (ENIC_CQ_MAX + 2) +#define ENIC_AIC_LARGE_PKT_DIFF 3 + struct enic_msix_entry { int requested; char devname[IFNAMSIZ]; @@ -50,6 +52,33 @@ struct enic_msix_entry { void *devid; }; +/* Store only the lower range. Higher range is given by fw. */ +struct enic_intr_mod_range { + u32 small_pkt_range_start; + u32 large_pkt_range_start; +}; + +struct enic_intr_mod_table { + u32 rx_rate; + u32 range_percent; +}; + +#define ENIC_MAX_LINK_SPEEDS 3 +#define ENIC_LINK_SPEED_10G 10000 +#define ENIC_LINK_SPEED_4G 4000 +#define ENIC_LINK_40G_INDEX 2 +#define ENIC_LINK_10G_INDEX 1 +#define ENIC_LINK_4G_INDEX 0 +#define ENIC_RX_COALESCE_RANGE_END 125 +#define ENIC_AIC_TS_BREAK 100 + +struct enic_rx_coal { + u32 small_pkt_range_start; + u32 large_pkt_range_start; + u32 range_end; + u32 use_adaptive_rx_coalesce; +}; + /* priv_flags */ #define ENIC_SRIOV_ENABLED (1 << 0) @@ -85,13 +114,12 @@ struct enic { u32 msg_enable; spinlock_t devcmd_lock; u8 mac_addr[ETH_ALEN]; - u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN]; - u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN]; unsigned int flags; unsigned int priv_flags; unsigned int mc_count; unsigned int uc_count; u32 port_mtu; + struct enic_rx_coal rx_coalesce_setting; u32 rx_coalesce_usecs; u32 tx_coalesce_usecs; #ifdef CONFIG_PCI_IOV diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c index 4b6e569..3e27df5 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.c +++ b/drivers/net/ethernet/cisco/enic/enic_dev.c @@ -88,7 +88,7 @@ int enic_dev_packet_filter(struct enic *enic, int directed, int multicast, return err; } -int enic_dev_add_addr(struct enic *enic, u8 *addr) +int enic_dev_add_addr(struct enic *enic, const u8 *addr) { int err; @@ -99,7 +99,7 @@ int enic_dev_add_addr(struct enic *enic, u8 *addr) return err; } -int enic_dev_del_addr(struct enic *enic, u8 *addr) +int enic_dev_del_addr(struct enic *enic, const u8 *addr) { int err; diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.h b/drivers/net/ethernet/cisco/enic/enic_dev.h index 129b14a..36ea1ab 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.h +++ b/drivers/net/ethernet/cisco/enic/enic_dev.h @@ -45,8 +45,8 @@ int enic_dev_add_station_addr(struct enic *enic); int enic_dev_del_station_addr(struct enic *enic); int enic_dev_packet_filter(struct enic *enic, int directed, int multicast, int broadcast, int promisc, int allmulti); -int enic_dev_add_addr(struct enic *enic, u8 *addr); -int enic_dev_del_addr(struct enic *enic, u8 *addr); +int enic_dev_add_addr(struct enic *enic, const u8 *addr); +int enic_dev_del_addr(struct enic *enic, const u8 *addr); int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid); int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid); int enic_dev_notify_unset(struct enic *enic); diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 58a8c67..1882db2 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -79,6 +79,17 @@ static const struct enic_stat enic_rx_stats[] = { static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); +void enic_intr_coal_set_rx(struct enic *enic, u32 timer) +{ + int i; + int intr; + + for (i = 0; i < enic->rq_count; i++) { + intr = enic_msix_rq_intr(enic, i); + vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); + } +} + static int enic_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { @@ -178,9 +189,14 @@ static int enic_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd) { struct enic *enic = netdev_priv(netdev); + struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs; ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; + if (rxcoal->use_adaptive_rx_coalesce) + ecmd->use_adaptive_rx_coalesce = 1; + ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; + ecmd->rx_coalesce_usecs_high = rxcoal->range_end; return 0; } @@ -191,17 +207,31 @@ static int enic_set_coalesce(struct net_device *netdev, struct enic *enic = netdev_priv(netdev); u32 tx_coalesce_usecs; u32 rx_coalesce_usecs; + u32 rx_coalesce_usecs_low; + u32 rx_coalesce_usecs_high; + u32 coalesce_usecs_max; unsigned int i, intr; + struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; + coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, - vnic_dev_get_intr_coal_timer_max(enic->vdev)); + coalesce_usecs_max); rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, - vnic_dev_get_intr_coal_timer_max(enic->vdev)); + coalesce_usecs_max); + + rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, + coalesce_usecs_max); + rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, + coalesce_usecs_max); switch (vnic_dev_get_intr_mode(enic->vdev)) { case VNIC_DEV_INTR_MODE_INTX: if (tx_coalesce_usecs != rx_coalesce_usecs) return -EINVAL; + if (ecmd->use_adaptive_rx_coalesce || + ecmd->rx_coalesce_usecs_low || + ecmd->rx_coalesce_usecs_high) + return -EOPNOTSUPP; intr = enic_legacy_io_intr(); vnic_intr_coalescing_timer_set(&enic->intr[intr], @@ -210,6 +240,10 @@ static int enic_set_coalesce(struct net_device *netdev, case VNIC_DEV_INTR_MODE_MSI: if (tx_coalesce_usecs != rx_coalesce_usecs) return -EINVAL; + if (ecmd->use_adaptive_rx_coalesce || + ecmd->rx_coalesce_usecs_low || + ecmd->rx_coalesce_usecs_high) + return -EOPNOTSUPP; vnic_intr_coalescing_timer_set(&enic->intr[0], tx_coalesce_usecs); @@ -221,12 +255,27 @@ static int enic_set_coalesce(struct net_device *netdev, tx_coalesce_usecs); } - for (i = 0; i < enic->rq_count; i++) { - intr = enic_msix_rq_intr(enic, i); - vnic_intr_coalescing_timer_set(&enic->intr[intr], - rx_coalesce_usecs); + if (rxcoal->use_adaptive_rx_coalesce) { + if (!ecmd->use_adaptive_rx_coalesce) { + rxcoal->use_adaptive_rx_coalesce = 0; + enic_intr_coal_set_rx(enic, rx_coalesce_usecs); + } + } else { + if (ecmd->use_adaptive_rx_coalesce) + rxcoal->use_adaptive_rx_coalesce = 1; + else + enic_intr_coal_set_rx(enic, rx_coalesce_usecs); } + if (ecmd->rx_coalesce_usecs_high) { + if (rx_coalesce_usecs_high < + (rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) + return -EINVAL; + rxcoal->range_end = rx_coalesce_usecs_high; + rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; + rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + + ENIC_AIC_LARGE_PKT_DIFF; + } break; default: break; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 2945718..f32f828 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -38,6 +38,7 @@ #include <linux/rtnetlink.h> #include <linux/prefetch.h> #include <net/ip6_checksum.h> +#include <linux/ktime.h> #include "cq_enet_desc.h" #include "vnic_dev.h" @@ -72,6 +73,35 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); MODULE_DEVICE_TABLE(pci, enic_id_table); +#define ENIC_LARGE_PKT_THRESHOLD 1000 +#define ENIC_MAX_COALESCE_TIMERS 10 +/* Interrupt moderation table, which will be used to decide the + * coalescing timer values + * {rx_rate in Mbps, mapping percentage of the range} + */ +struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = { + {4000, 0}, + {4400, 10}, + {5060, 20}, + {5230, 30}, + {5540, 40}, + {5820, 50}, + {6120, 60}, + {6435, 70}, + {6745, 80}, + {7000, 90}, + {0xFFFFFFFF, 100} +}; + +/* This table helps the driver to pick different ranges for rx coalescing + * timer depending on the link speed. + */ +struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = { + {0, 0}, /* 0 - 4 Gbps */ + {0, 3}, /* 4 - 10 Gbps */ + {3, 6}, /* 10 - 40 Gbps */ +}; + int enic_is_dynamic(struct enic *enic) { return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN; @@ -586,8 +616,71 @@ static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev, return net_stats; } +static int enic_mc_sync(struct net_device *netdev, const u8 *mc_addr) +{ + struct enic *enic = netdev_priv(netdev); + + if (enic->mc_count == ENIC_MULTICAST_PERFECT_FILTERS) { + unsigned int mc_count = netdev_mc_count(netdev); + + netdev_warn(netdev, "Registering only %d out of %d multicast addresses\n", + ENIC_MULTICAST_PERFECT_FILTERS, mc_count); + + return -ENOSPC; + } + + enic_dev_add_addr(enic, mc_addr); + enic->mc_count++; + + return 0; +} + +static int enic_mc_unsync(struct net_device *netdev, const u8 *mc_addr) +{ + struct enic *enic = netdev_priv(netdev); + + enic_dev_del_addr(enic, mc_addr); + enic->mc_count--; + + return 0; +} + +static int enic_uc_sync(struct net_device *netdev, const u8 *uc_addr) +{ + struct enic *enic = netdev_priv(netdev); + + if (enic->uc_count == ENIC_UNICAST_PERFECT_FILTERS) { + unsigned int uc_count = netdev_uc_count(netdev); + + netdev_warn(netdev, "Registering only %d out of %d unicast addresses\n", + ENIC_UNICAST_PERFECT_FILTERS, uc_count); + + return -ENOSPC; + } + + enic_dev_add_addr(enic, uc_addr); + enic->uc_count++; + + return 0; +} + +static int enic_uc_unsync(struct net_device *netdev, const u8 *uc_addr) +{ + struct enic *enic = netdev_priv(netdev); + + enic_dev_del_addr(enic, uc_addr); + enic->uc_count--; + + return 0; +} + void enic_reset_addr_lists(struct enic *enic) { + struct net_device *netdev = enic->netdev; + + __dev_uc_unsync(netdev, NULL); + __dev_mc_unsync(netdev, NULL); + enic->mc_count = 0; enic->uc_count = 0; enic->flags = 0; @@ -654,112 +747,6 @@ static int enic_set_mac_address(struct net_device *netdev, void *p) return enic_dev_add_station_addr(enic); } -static void enic_update_multicast_addr_list(struct enic *enic) -{ - struct net_device *netdev = enic->netdev; - struct netdev_hw_addr *ha; - unsigned int mc_count = netdev_mc_count(netdev); - u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN]; - unsigned int i, j; - - if (mc_count > ENIC_MULTICAST_PERFECT_FILTERS) { - netdev_warn(netdev, "Registering only %d out of %d " - "multicast addresses\n", - ENIC_MULTICAST_PERFECT_FILTERS, mc_count); - mc_count = ENIC_MULTICAST_PERFECT_FILTERS; - } - - /* Is there an easier way? Trying to minimize to - * calls to add/del multicast addrs. We keep the - * addrs from the last call in enic->mc_addr and - * look for changes to add/del. - */ - - i = 0; - netdev_for_each_mc_addr(ha, netdev) { - if (i == mc_count) - break; - memcpy(mc_addr[i++], ha->addr, ETH_ALEN); - } - - for (i = 0; i < enic->mc_count; i++) { - for (j = 0; j < mc_count; j++) - if (ether_addr_equal(enic->mc_addr[i], mc_addr[j])) - break; - if (j == mc_count) - enic_dev_del_addr(enic, enic->mc_addr[i]); - } - - for (i = 0; i < mc_count; i++) { - for (j = 0; j < enic->mc_count; j++) - if (ether_addr_equal(mc_addr[i], enic->mc_addr[j])) - break; - if (j == enic->mc_count) - enic_dev_add_addr(enic, mc_addr[i]); - } - - /* Save the list to compare against next time - */ - - for (i = 0; i < mc_count; i++) - memcpy(enic->mc_addr[i], mc_addr[i], ETH_ALEN); - - enic->mc_count = mc_count; -} - -static void enic_update_unicast_addr_list(struct enic *enic) -{ - struct net_device *netdev = enic->netdev; - struct netdev_hw_addr *ha; - unsigned int uc_count = netdev_uc_count(netdev); - u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN]; - unsigned int i, j; - - if (uc_count > ENIC_UNICAST_PERFECT_FILTERS) { - netdev_warn(netdev, "Registering only %d out of %d " - "unicast addresses\n", - ENIC_UNICAST_PERFECT_FILTERS, uc_count); - uc_count = ENIC_UNICAST_PERFECT_FILTERS; - } - - /* Is there an easier way? Trying to minimize to - * calls to add/del unicast addrs. We keep the - * addrs from the last call in enic->uc_addr and - * look for changes to add/del. - */ - - i = 0; - netdev_for_each_uc_addr(ha, netdev) { - if (i == uc_count) - break; - memcpy(uc_addr[i++], ha->addr, ETH_ALEN); - } - - for (i = 0; i < enic->uc_count; i++) { - for (j = 0; j < uc_count; j++) - if (ether_addr_equal(enic->uc_addr[i], uc_addr[j])) - break; - if (j == uc_count) - enic_dev_del_addr(enic, enic->uc_addr[i]); - } - - for (i = 0; i < uc_count; i++) { - for (j = 0; j < enic->uc_count; j++) - if (ether_addr_equal(uc_addr[i], enic->uc_addr[j])) - break; - if (j == enic->uc_count) - enic_dev_add_addr(enic, uc_addr[i]); - } - - /* Save the list to compare against next time - */ - - for (i = 0; i < uc_count; i++) - memcpy(enic->uc_addr[i], uc_addr[i], ETH_ALEN); - - enic->uc_count = uc_count; -} - /* netif_tx_lock held, BHs disabled */ static void enic_set_rx_mode(struct net_device *netdev) { @@ -782,9 +769,9 @@ static void enic_set_rx_mode(struct net_device *netdev) } if (!promisc) { - enic_update_unicast_addr_list(enic); + __dev_uc_sync(netdev, enic_uc_sync, enic_uc_unsync); if (!allmulti) - enic_update_multicast_addr_list(enic); + __dev_mc_sync(netdev, enic_mc_sync, enic_mc_unsync); } } @@ -979,6 +966,15 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq) return 0; } +static void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size, + u32 pkt_len) +{ + if (ENIC_LARGE_PKT_THRESHOLD <= pkt_len) + pkt_size->large_pkt_bytes_cnt += pkt_len; + else + pkt_size->small_pkt_bytes_cnt += pkt_len; +} + static void enic_rq_indicate_buf(struct vnic_rq *rq, struct cq_desc *cq_desc, struct vnic_rq_buf *buf, int skipped, void *opaque) @@ -986,6 +982,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, struct enic *enic = vnic_dev_priv(rq->vdev); struct net_device *netdev = enic->netdev; struct sk_buff *skb; + struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; u8 type, color, eop, sop, ingress_port, vlan_stripped; u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof; @@ -1056,6 +1053,9 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, napi_gro_receive(&enic->napi[q_number], skb); else netif_receive_skb(skb); + if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) + enic_intr_update_pkt_size(&cq->pkt_size_counter, + bytes_written); } else { /* Buffer overflow @@ -1134,6 +1134,64 @@ static int enic_poll(struct napi_struct *napi, int budget) return rq_work_done; } +static void enic_set_int_moderation(struct enic *enic, struct vnic_rq *rq) +{ + unsigned int intr = enic_msix_rq_intr(enic, rq->index); + struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; + u32 timer = cq->tobe_rx_coal_timeval; + + if (cq->tobe_rx_coal_timeval != cq->cur_rx_coal_timeval) { + vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); + cq->cur_rx_coal_timeval = cq->tobe_rx_coal_timeval; + } +} + +static void enic_calc_int_moderation(struct enic *enic, struct vnic_rq *rq) +{ + struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting; + struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)]; + struct vnic_rx_bytes_counter *pkt_size_counter = &cq->pkt_size_counter; + int index; + u32 timer; + u32 range_start; + u32 traffic; + u64 delta; + ktime_t now = ktime_get(); + + delta = ktime_us_delta(now, cq->prev_ts); + if (delta < ENIC_AIC_TS_BREAK) + return; + cq->prev_ts = now; + + traffic = pkt_size_counter->large_pkt_bytes_cnt + + pkt_size_counter->small_pkt_bytes_cnt; + /* The table takes Mbps + * traffic *= 8 => bits + * traffic *= (10^6 / delta) => bps + * traffic /= 10^6 => Mbps + * + * Combining, traffic *= (8 / delta) + */ + + traffic <<= 3; + traffic = delta > UINT_MAX ? 0 : traffic / (u32)delta; + + for (index = 0; index < ENIC_MAX_COALESCE_TIMERS; index++) + if (traffic < mod_table[index].rx_rate) + break; + range_start = (pkt_size_counter->small_pkt_bytes_cnt > + pkt_size_counter->large_pkt_bytes_cnt << 1) ? + rx_coal->small_pkt_range_start : + rx_coal->large_pkt_range_start; + timer = range_start + ((rx_coal->range_end - range_start) * + mod_table[index].range_percent / 100); + /* Damping */ + cq->tobe_rx_coal_timeval = (timer + cq->tobe_rx_coal_timeval) >> 1; + + pkt_size_counter->large_pkt_bytes_cnt = 0; + pkt_size_counter->small_pkt_bytes_cnt = 0; +} + static int enic_poll_msix(struct napi_struct *napi, int budget) { struct net_device *netdev = napi->dev; @@ -1171,6 +1229,13 @@ static int enic_poll_msix(struct napi_struct *napi, int budget) if (err) work_done = work_to_do; + if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) + /* Call the function which refreshes + * the intr coalescing timer value based on + * the traffic. This is supported only in + * the case of MSI-x mode + */ + enic_calc_int_moderation(enic, &enic->rq[rq]); if (work_done < work_to_do) { @@ -1179,6 +1244,8 @@ static int enic_poll_msix(struct napi_struct *napi, int budget) */ napi_complete(napi); + if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) + enic_set_int_moderation(enic, &enic->rq[rq]); vnic_intr_unmask(&enic->intr[intr]); } @@ -1314,6 +1381,42 @@ static void enic_synchronize_irqs(struct enic *enic) } } +static void enic_set_rx_coal_setting(struct enic *enic) +{ + unsigned int speed; + int index = -1; + struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting; + + /* If intr mode is not MSIX, do not do adaptive coalescing */ + if (VNIC_DEV_INTR_MODE_MSIX != vnic_dev_get_intr_mode(enic->vdev)) { + netdev_info(enic->netdev, "INTR mode is not MSIX, Not initializing adaptive coalescing"); + return; + } + + /* 1. Read the link speed from fw + * 2. Pick the default range for the speed + * 3. Update it in enic->rx_coalesce_setting + */ + speed = vnic_dev_port_speed(enic->vdev); + if (ENIC_LINK_SPEED_10G < speed) + index = ENIC_LINK_40G_INDEX; + else if (ENIC_LINK_SPEED_4G < speed) + index = ENIC_LINK_10G_INDEX; + else + index = ENIC_LINK_4G_INDEX; + + rx_coal->small_pkt_range_start = mod_range[index].small_pkt_range_start; + rx_coal->large_pkt_range_start = mod_range[index].large_pkt_range_start; + rx_coal->range_end = ENIC_RX_COALESCE_RANGE_END; + + /* Start with the value provided by UCSM */ + for (index = 0; index < enic->rq_count; index++) + enic->cq[index].cur_rx_coal_timeval = + enic->config.intr_timer_usec; + + rx_coal->use_adaptive_rx_coalesce = 1; +} + static int enic_dev_notify_set(struct enic *enic) { int err; @@ -2231,6 +2334,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) enic->notify_timer.function = enic_notify_timer; enic->notify_timer.data = (unsigned long)enic; + enic_set_rx_coal_setting(enic); INIT_WORK(&enic->reset, enic_reset); INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); @@ -2250,6 +2354,9 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } enic->tx_coalesce_usecs = enic->config.intr_timer_usec; + /* rx coalesce time already got initialized. This gets used + * if adaptive coal is turned off + */ enic->rx_coalesce_usecs = enic->tx_coalesce_usecs; if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.h b/drivers/net/ethernet/cisco/enic/vnic_cq.h index 579315c..4e6aa65 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_cq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_cq.h @@ -50,6 +50,11 @@ struct vnic_cq_ctrl { u32 pad10; }; +struct vnic_rx_bytes_counter { + unsigned int small_pkt_bytes_cnt; + unsigned int large_pkt_bytes_cnt; +}; + struct vnic_cq { unsigned int index; struct vnic_dev *vdev; @@ -58,6 +63,10 @@ struct vnic_cq { unsigned int to_clean; unsigned int last_color; unsigned int interrupt_offset; + struct vnic_rx_bytes_counter pkt_size_counter; + unsigned int cur_rx_coal_timeval; + unsigned int tobe_rx_coal_timeval; + ktime_t prev_ts; }; static inline unsigned int vnic_cq_service(struct vnic_cq *cq, diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index 69dd925..e86a45c 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -657,7 +657,7 @@ int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, return err; } -int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) +int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr) { u64 a0 = 0, a1 = 0; int wait = 1000; @@ -674,7 +674,7 @@ int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) return err; } -int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr) +int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr) { u64 a0 = 0, a1 = 0; int wait = 1000; diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index e670029..1f3b301 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h @@ -95,8 +95,8 @@ int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats); int vnic_dev_hang_notify(struct vnic_dev *vdev); int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, int broadcast, int promisc, int allmulti); -int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr); -int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr); +int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr); +int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr); int vnic_dev_get_mac_addr(struct vnic_dev *vdev, u8 *mac_addr); int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr); int vnic_dev_notify_unset(struct vnic_dev *vdev); diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 31c3766..2e7c555 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -374,6 +374,7 @@ enum vf_state { #define BE_FLAGS_LINK_STATUS_INIT 1 #define BE_FLAGS_WORKER_SCHEDULED (1 << 3) #define BE_FLAGS_VLAN_PROMISC (1 << 4) +#define BE_FLAGS_MCAST_PROMISC (1 << 5) #define BE_FLAGS_NAPI_ENABLED (1 << 9) #define BE_FLAGS_QNQ_ASYNC_EVT_RCVD (1 << 11) #define BE_FLAGS_VXLAN_OFFLOADS (1 << 12) diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 7b59da2..f4ea349 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -119,22 +119,29 @@ static struct be_cmd_resp_hdr *be_decode_resp_hdr(u32 tag0, u32 tag1) return (void *)addr; } -static int be_mcc_compl_process(struct be_adapter *adapter, - struct be_mcc_compl *compl) +static bool be_skip_err_log(u8 opcode, u16 base_status, u16 addl_status) +{ + if (base_status == MCC_STATUS_NOT_SUPPORTED || + base_status == MCC_STATUS_ILLEGAL_REQUEST || + addl_status == MCC_ADDL_STATUS_TOO_MANY_INTERFACES || + (opcode == OPCODE_COMMON_WRITE_FLASHROM && + (base_status == MCC_STATUS_ILLEGAL_FIELD || + addl_status == MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH))) + return true; + else + return false; +} + +/* Place holder for all the async MCC cmds wherein the caller is not in a busy + * loop (has not issued be_mcc_notify_wait()) + */ +static void be_async_cmd_process(struct be_adapter *adapter, + struct be_mcc_compl *compl, + struct be_cmd_resp_hdr *resp_hdr) { - u16 compl_status, extd_status; - struct be_cmd_resp_hdr *resp_hdr; + enum mcc_base_status base_status = base_status(compl->status); u8 opcode = 0, subsystem = 0; - /* Just swap the status to host endian; mcc tag is opaquely copied - * from mcc_wrb */ - be_dws_le_to_cpu(compl, 4); - - compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) & - CQE_STATUS_COMPL_MASK; - - resp_hdr = be_decode_resp_hdr(compl->tag0, compl->tag1); - if (resp_hdr) { opcode = resp_hdr->opcode; subsystem = resp_hdr->subsystem; @@ -143,61 +150,86 @@ static int be_mcc_compl_process(struct be_adapter *adapter, if (opcode == OPCODE_LOWLEVEL_LOOPBACK_TEST && subsystem == CMD_SUBSYSTEM_LOWLEVEL) { complete(&adapter->et_cmd_compl); - return 0; + return; } - if (((opcode == OPCODE_COMMON_WRITE_FLASHROM) || - (opcode == OPCODE_COMMON_WRITE_OBJECT)) && - (subsystem == CMD_SUBSYSTEM_COMMON)) { - adapter->flash_status = compl_status; + if ((opcode == OPCODE_COMMON_WRITE_FLASHROM || + opcode == OPCODE_COMMON_WRITE_OBJECT) && + subsystem == CMD_SUBSYSTEM_COMMON) { + adapter->flash_status = compl->status; complete(&adapter->et_cmd_compl); + return; } - if (compl_status == MCC_STATUS_SUCCESS) { - if (((opcode == OPCODE_ETH_GET_STATISTICS) || - (opcode == OPCODE_ETH_GET_PPORT_STATS)) && - (subsystem == CMD_SUBSYSTEM_ETH)) { - be_parse_stats(adapter); - adapter->stats_cmd_sent = false; - } - if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES && - subsystem == CMD_SUBSYSTEM_COMMON) { + if ((opcode == OPCODE_ETH_GET_STATISTICS || + opcode == OPCODE_ETH_GET_PPORT_STATS) && + subsystem == CMD_SUBSYSTEM_ETH && + base_status == MCC_STATUS_SUCCESS) { + be_parse_stats(adapter); + adapter->stats_cmd_sent = false; + return; + } + + if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES && + subsystem == CMD_SUBSYSTEM_COMMON) { + if (base_status == MCC_STATUS_SUCCESS) { struct be_cmd_resp_get_cntl_addnl_attribs *resp = - (void *)resp_hdr; + (void *)resp_hdr; adapter->drv_stats.be_on_die_temperature = - resp->on_die_temperature; - } - } else { - if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES) + resp->on_die_temperature; + } else { adapter->be_get_temp_freq = 0; + } + return; + } +} + +static int be_mcc_compl_process(struct be_adapter *adapter, + struct be_mcc_compl *compl) +{ + enum mcc_base_status base_status; + enum mcc_addl_status addl_status; + struct be_cmd_resp_hdr *resp_hdr; + u8 opcode = 0, subsystem = 0; - if (compl_status == MCC_STATUS_NOT_SUPPORTED || - compl_status == MCC_STATUS_ILLEGAL_REQUEST) - goto done; + /* Just swap the status to host endian; mcc tag is opaquely copied + * from mcc_wrb */ + be_dws_le_to_cpu(compl, 4); - if (compl_status == MCC_STATUS_UNAUTHORIZED_REQUEST) { + base_status = base_status(compl->status); + addl_status = addl_status(compl->status); + + resp_hdr = be_decode_resp_hdr(compl->tag0, compl->tag1); + if (resp_hdr) { + opcode = resp_hdr->opcode; + subsystem = resp_hdr->subsystem; + } + + be_async_cmd_process(adapter, compl, resp_hdr); + + if (base_status != MCC_STATUS_SUCCESS && + !be_skip_err_log(opcode, base_status, addl_status)) { + + if (base_status == MCC_STATUS_UNAUTHORIZED_REQUEST) { dev_warn(&adapter->pdev->dev, "VF is not privileged to issue opcode %d-%d\n", opcode, subsystem); } else { - extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) & - CQE_STATUS_EXTD_MASK; dev_err(&adapter->pdev->dev, "opcode %d-%d failed:status %d-%d\n", - opcode, subsystem, compl_status, extd_status); - - if (extd_status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES) - return extd_status; + opcode, subsystem, base_status, addl_status); } } -done: - return compl_status; + return compl->status; } /* Link state evt is a string of bytes; no need for endian swapping */ static void be_async_link_state_process(struct be_adapter *adapter, - struct be_async_event_link_state *evt) + struct be_mcc_compl *compl) { + struct be_async_event_link_state *evt = + (struct be_async_event_link_state *)compl; + /* When link status changes, link speed must be re-queried from FW */ adapter->phy.link_speed = -1; @@ -220,10 +252,11 @@ static void be_async_link_state_process(struct be_adapter *adapter, /* Grp5 CoS Priority evt */ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter, - struct - be_async_event_grp5_cos_priority - *evt) + struct be_mcc_compl *compl) { + struct be_async_event_grp5_cos_priority *evt = + (struct be_async_event_grp5_cos_priority *)compl; + if (evt->valid) { adapter->vlan_prio_bmap = evt->available_priority_bmap; adapter->recommended_prio &= ~VLAN_PRIO_MASK; @@ -234,10 +267,11 @@ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter, /* Grp5 QOS Speed evt: qos_link_speed is in units of 10 Mbps */ static void be_async_grp5_qos_speed_process(struct be_adapter *adapter, - struct - be_async_event_grp5_qos_link_speed - *evt) + struct be_mcc_compl *compl) { + struct be_async_event_grp5_qos_link_speed *evt = + (struct be_async_event_grp5_qos_link_speed *)compl; + if (adapter->phy.link_speed >= 0 && evt->physical_port == adapter->port_num) adapter->phy.link_speed = le16_to_cpu(evt->qos_link_speed) * 10; @@ -245,10 +279,11 @@ static void be_async_grp5_qos_speed_process(struct be_adapter *adapter, /*Grp5 PVID evt*/ static void be_async_grp5_pvid_state_process(struct be_adapter *adapter, - struct - be_async_event_grp5_pvid_state - *evt) + struct be_mcc_compl *compl) { + struct be_async_event_grp5_pvid_state *evt = + (struct be_async_event_grp5_pvid_state *)compl; + if (evt->enabled) { adapter->pvid = le16_to_cpu(evt->tag) & VLAN_VID_MASK; dev_info(&adapter->pdev->dev, "LPVID: %d\n", adapter->pvid); @@ -258,26 +293,21 @@ static void be_async_grp5_pvid_state_process(struct be_adapter *adapter, } static void be_async_grp5_evt_process(struct be_adapter *adapter, - u32 trailer, struct be_mcc_compl *evt) + struct be_mcc_compl *compl) { - u8 event_type = 0; - - event_type = (trailer >> ASYNC_TRAILER_EVENT_TYPE_SHIFT) & - ASYNC_TRAILER_EVENT_TYPE_MASK; + u8 event_type = (compl->flags >> ASYNC_EVENT_TYPE_SHIFT) & + ASYNC_EVENT_TYPE_MASK; switch (event_type) { case ASYNC_EVENT_COS_PRIORITY: - be_async_grp5_cos_priority_process(adapter, - (struct be_async_event_grp5_cos_priority *)evt); - break; + be_async_grp5_cos_priority_process(adapter, compl); + break; case ASYNC_EVENT_QOS_SPEED: - be_async_grp5_qos_speed_process(adapter, - (struct be_async_event_grp5_qos_link_speed *)evt); - break; + be_async_grp5_qos_speed_process(adapter, compl); + break; case ASYNC_EVENT_PVID_STATE: - be_async_grp5_pvid_state_process(adapter, - (struct be_async_event_grp5_pvid_state *)evt); - break; + be_async_grp5_pvid_state_process(adapter, compl); + break; default: dev_warn(&adapter->pdev->dev, "Unknown grp5 event 0x%x!\n", event_type); @@ -286,13 +316,13 @@ static void be_async_grp5_evt_process(struct be_adapter *adapter, } static void be_async_dbg_evt_process(struct be_adapter *adapter, - u32 trailer, struct be_mcc_compl *cmp) + struct be_mcc_compl *cmp) { u8 event_type = 0; struct be_async_event_qnq *evt = (struct be_async_event_qnq *) cmp; - event_type = (trailer >> ASYNC_TRAILER_EVENT_TYPE_SHIFT) & - ASYNC_TRAILER_EVENT_TYPE_MASK; + event_type = (cmp->flags >> ASYNC_EVENT_TYPE_SHIFT) & + ASYNC_EVENT_TYPE_MASK; switch (event_type) { case ASYNC_DEBUG_EVENT_TYPE_QNQ: @@ -307,25 +337,33 @@ static void be_async_dbg_evt_process(struct be_adapter *adapter, } } -static inline bool is_link_state_evt(u32 trailer) +static inline bool is_link_state_evt(u32 flags) { - return ((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & - ASYNC_TRAILER_EVENT_CODE_MASK) == - ASYNC_EVENT_CODE_LINK_STATE; + return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) == + ASYNC_EVENT_CODE_LINK_STATE; } -static inline bool is_grp5_evt(u32 trailer) +static inline bool is_grp5_evt(u32 flags) { - return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & - ASYNC_TRAILER_EVENT_CODE_MASK) == - ASYNC_EVENT_CODE_GRP_5); + return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) == + ASYNC_EVENT_CODE_GRP_5; } -static inline bool is_dbg_evt(u32 trailer) +static inline bool is_dbg_evt(u32 flags) { - return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & - ASYNC_TRAILER_EVENT_CODE_MASK) == - ASYNC_EVENT_CODE_QNQ); + return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) == + ASYNC_EVENT_CODE_QNQ; +} + +static void be_mcc_event_process(struct be_adapter *adapter, + struct be_mcc_compl *compl) +{ + if (is_link_state_evt(compl->flags)) + be_async_link_state_process(adapter, compl); + else if (is_grp5_evt(compl->flags)) + be_async_grp5_evt_process(adapter, compl); + else if (is_dbg_evt(compl->flags)) + be_async_dbg_evt_process(adapter, compl); } static struct be_mcc_compl *be_mcc_compl_get(struct be_adapter *adapter) @@ -367,21 +405,13 @@ int be_process_mcc(struct be_adapter *adapter) struct be_mcc_obj *mcc_obj = &adapter->mcc_obj; spin_lock(&adapter->mcc_cq_lock); + while ((compl = be_mcc_compl_get(adapter))) { if (compl->flags & CQE_FLAGS_ASYNC_MASK) { - /* Interpret flags as an async trailer */ - if (is_link_state_evt(compl->flags)) - be_async_link_state_process(adapter, - (struct be_async_event_link_state *) compl); - else if (is_grp5_evt(compl->flags)) - be_async_grp5_evt_process(adapter, - compl->flags, compl); - else if (is_dbg_evt(compl->flags)) - be_async_dbg_evt_process(adapter, - compl->flags, compl); + be_mcc_event_process(adapter, compl); } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) { - status = be_mcc_compl_process(adapter, compl); - atomic_dec(&mcc_obj->q.used); + status = be_mcc_compl_process(adapter, compl); + atomic_dec(&mcc_obj->q.used); } be_mcc_compl_use(compl); num++; @@ -441,7 +471,9 @@ static int be_mcc_notify_wait(struct be_adapter *adapter) if (status == -EIO) goto out; - status = resp->status; + status = (resp->base_status | + ((resp->addl_status & CQE_ADDL_STATUS_MASK) << + CQE_ADDL_STATUS_SHIFT)); out: return status; } @@ -2300,7 +2332,7 @@ err_unlock: } int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - int offset) + u16 optype, int offset) { struct be_mcc_wrb *wrb; struct be_cmd_read_flash_crc *req; @@ -2319,7 +2351,7 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, OPCODE_COMMON_READ_FLASHROM, sizeof(*req), wrb, NULL); - req->params.op_type = cpu_to_le32(OPTYPE_REDBOOT); + req->params.op_type = cpu_to_le32(optype); req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT); req->params.offset = cpu_to_le32(offset); req->params.data_buf_size = cpu_to_le32(0x4); @@ -3562,33 +3594,47 @@ void be_reset_nic_desc(struct be_nic_res_desc *nic) nic->cq_count = 0xFFFF; nic->toe_conn_count = 0xFFFF; nic->eq_count = 0xFFFF; + nic->iface_count = 0xFFFF; nic->link_param = 0xFF; + nic->channel_id_param = cpu_to_le16(0xF000); nic->acpi_params = 0xFF; nic->wol_param = 0x0F; - nic->bw_min = 0xFFFFFFFF; + nic->tunnel_iface_count = 0xFFFF; + nic->direct_tenant_iface_count = 0xFFFF; nic->bw_max = 0xFFFFFFFF; } -int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain) +int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed, + u8 domain) { - if (lancer_chip(adapter)) { - struct be_nic_res_desc nic_desc; + struct be_nic_res_desc nic_desc; + u32 bw_percent; + u16 version = 0; - be_reset_nic_desc(&nic_desc); + if (BE3_chip(adapter)) + return be_cmd_set_qos(adapter, max_rate / 10, domain); + + be_reset_nic_desc(&nic_desc); + nic_desc.pf_num = adapter->pf_number; + nic_desc.vf_num = domain; + if (lancer_chip(adapter)) { nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V0; nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V0; nic_desc.flags = (1 << QUN_SHIFT) | (1 << IMM_SHIFT) | (1 << NOSV_SHIFT); - nic_desc.pf_num = adapter->pf_number; - nic_desc.vf_num = domain; - nic_desc.bw_max = cpu_to_le32(bps); - - return be_cmd_set_profile_config(adapter, &nic_desc, - RESOURCE_DESC_SIZE_V0, - 0, domain); + nic_desc.bw_max = cpu_to_le32(max_rate / 10); } else { - return be_cmd_set_qos(adapter, bps, domain); + version = 1; + nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V1; + nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V1; + nic_desc.flags = (1 << IMM_SHIFT) | (1 << NOSV_SHIFT); + bw_percent = max_rate ? (max_rate * 100) / link_speed : 100; + nic_desc.bw_max = cpu_to_le32(bw_percent); } + + return be_cmd_set_profile_config(adapter, &nic_desc, + nic_desc.hdr.desc_len, + version, domain); } int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op) diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 451f313..3e0a6b2 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -50,7 +50,7 @@ struct be_mcc_wrb { #define CQE_FLAGS_CONSUMED_MASK (1 << 27) /* Completion Status */ -enum { +enum mcc_base_status { MCC_STATUS_SUCCESS = 0, MCC_STATUS_FAILED = 1, MCC_STATUS_ILLEGAL_REQUEST = 2, @@ -60,12 +60,25 @@ enum { MCC_STATUS_NOT_SUPPORTED = 66 }; -#define MCC_ADDL_STS_INSUFFICIENT_RESOURCES 0x16 +/* Additional status */ +enum mcc_addl_status { + MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES = 0x16, + MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH = 0x4d, + MCC_ADDL_STATUS_TOO_MANY_INTERFACES = 0x4a +}; + +#define CQE_BASE_STATUS_MASK 0xFFFF +#define CQE_BASE_STATUS_SHIFT 0 /* bits 0 - 15 */ +#define CQE_ADDL_STATUS_MASK 0xFF +#define CQE_ADDL_STATUS_SHIFT 16 /* bits 16 - 31 */ -#define CQE_STATUS_COMPL_MASK 0xFFFF -#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */ -#define CQE_STATUS_EXTD_MASK 0xFFFF -#define CQE_STATUS_EXTD_SHIFT 16 /* bits 16 - 31 */ +#define base_status(status) \ + ((enum mcc_base_status) \ + (status > 0 ? (status & CQE_BASE_STATUS_MASK) : 0)) +#define addl_status(status) \ + ((enum mcc_addl_status) \ + (status > 0 ? (status >> CQE_ADDL_STATUS_SHIFT) & \ + CQE_ADDL_STATUS_MASK : 0)) struct be_mcc_compl { u32 status; /* dword 0 */ @@ -74,13 +87,13 @@ struct be_mcc_compl { u32 flags; /* dword 3 */ }; -/* When the async bit of mcc_compl is set, the last 4 bytes of - * mcc_compl is interpreted as follows: +/* When the async bit of mcc_compl flags is set, flags + * is interpreted as follows: */ -#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */ -#define ASYNC_TRAILER_EVENT_CODE_MASK 0xFF -#define ASYNC_TRAILER_EVENT_TYPE_SHIFT 16 -#define ASYNC_TRAILER_EVENT_TYPE_MASK 0xFF +#define ASYNC_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */ +#define ASYNC_EVENT_CODE_MASK 0xFF +#define ASYNC_EVENT_TYPE_SHIFT 16 +#define ASYNC_EVENT_TYPE_MASK 0xFF #define ASYNC_EVENT_CODE_LINK_STATE 0x1 #define ASYNC_EVENT_CODE_GRP_5 0x5 #define ASYNC_EVENT_QOS_SPEED 0x1 @@ -89,10 +102,6 @@ struct be_mcc_compl { #define ASYNC_EVENT_CODE_QNQ 0x6 #define ASYNC_DEBUG_EVENT_TYPE_QNQ 1 -struct be_async_event_trailer { - u32 code; -}; - enum { LINK_DOWN = 0x0, LINK_UP = 0x1 @@ -100,7 +109,7 @@ enum { #define LINK_STATUS_MASK 0x1 #define LOGICAL_LINK_STATUS_MASK 0x2 -/* When the event code of an async trailer is link-state, the mcc_compl +/* When the event code of compl->flags is link-state, the mcc_compl * must be interpreted as follows */ struct be_async_event_link_state { @@ -110,10 +119,10 @@ struct be_async_event_link_state { u8 port_speed; u8 port_fault; u8 rsvd0[7]; - struct be_async_event_trailer trailer; + u32 flags; } __packed; -/* When the event code of an async trailer is GRP-5 and event_type is QOS_SPEED +/* When the event code of compl->flags is GRP-5 and event_type is QOS_SPEED * the mcc_compl must be interpreted as follows */ struct be_async_event_grp5_qos_link_speed { @@ -121,10 +130,10 @@ struct be_async_event_grp5_qos_link_speed { u8 rsvd[5]; u16 qos_link_speed; u32 event_tag; - struct be_async_event_trailer trailer; + u32 flags; } __packed; -/* When the event code of an async trailer is GRP5 and event type is +/* When the event code of compl->flags is GRP5 and event type is * CoS-Priority, the mcc_compl must be interpreted as follows */ struct be_async_event_grp5_cos_priority { @@ -134,10 +143,10 @@ struct be_async_event_grp5_cos_priority { u8 valid; u8 rsvd0; u8 event_tag; - struct be_async_event_trailer trailer; + u32 flags; } __packed; -/* When the event code of an async trailer is GRP5 and event type is +/* When the event code of compl->flags is GRP5 and event type is * PVID state, the mcc_compl must be interpreted as follows */ struct be_async_event_grp5_pvid_state { @@ -146,7 +155,7 @@ struct be_async_event_grp5_pvid_state { u16 tag; u32 event_tag; u32 rsvd1; - struct be_async_event_trailer trailer; + u32 flags; } __packed; /* async event indicating outer VLAN tag in QnQ */ @@ -156,7 +165,7 @@ struct be_async_event_qnq { u16 vlan_tag; u32 event_tag; u8 rsvd1[4]; - struct be_async_event_trailer trailer; + u32 flags; } __packed; struct be_mcc_mailbox { @@ -258,8 +267,8 @@ struct be_cmd_resp_hdr { u8 opcode; /* dword 0 */ u8 subsystem; /* dword 0 */ u8 rsvd[2]; /* dword 0 */ - u8 status; /* dword 1 */ - u8 add_status; /* dword 1 */ + u8 base_status; /* dword 1 */ + u8 addl_status; /* dword 1 */ u8 rsvd1[2]; /* dword 1 */ u32 response_length; /* dword 2 */ u32 actual_resp_len; /* dword 3 */ @@ -1186,7 +1195,8 @@ struct be_cmd_read_flash_crc { struct flashrom_params params; u8 crc[4]; u8 rsvd[4]; -}; +} __packed; + /**************** Lancer Firmware Flash ************/ struct amap_lancer_write_obj_context { u8 write_length[24]; @@ -1891,16 +1901,20 @@ struct be_nic_res_desc { u16 cq_count; u16 toe_conn_count; u16 eq_count; - u32 rsvd5; + u16 vlan_id; + u16 iface_count; u32 cap_flags; u8 link_param; - u8 rsvd6[3]; + u8 rsvd6; + u16 channel_id_param; u32 bw_min; u32 bw_max; u8 acpi_params; u8 wol_param; u16 rsvd7; - u32 rsvd8[7]; + u16 tunnel_iface_count; + u16 direct_tenant_iface_count; + u32 rsvd8[6]; } __packed; /************ Multi-Channel type ***********/ @@ -2084,7 +2098,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd, u32 data_size, u32 data_offset, const char *obj_name, u32 *data_read, u32 *eof, u8 *addn_status); int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - int offset); + u16 optype, int offset); int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, struct be_dma_mem *nonemb_cmd); int be_cmd_fw_init(struct be_adapter *adapter); @@ -2101,7 +2115,8 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter, int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num, u8 loopback_type, u8 enable); int be_cmd_get_phy_info(struct be_adapter *adapter); -int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain); +int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, + u16 link_speed, u8 domain); void be_detect_error(struct be_adapter *adapter); int be_cmd_get_die_temperature(struct be_adapter *adapter); int be_cmd_get_cntl_attributes(struct be_adapter *adapter); diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index 3bd1985..8840c64 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -188,10 +188,14 @@ #define OPTYPE_FCOE_FW_ACTIVE 10 #define OPTYPE_FCOE_FW_BACKUP 11 #define OPTYPE_NCSI_FW 13 +#define OPTYPE_REDBOOT_DIR 18 +#define OPTYPE_REDBOOT_CONFIG 19 +#define OPTYPE_SH_PHY_FW 21 +#define OPTYPE_FLASHISM_JUMPVECTOR 22 +#define OPTYPE_UFI_DIR 23 #define OPTYPE_PHY_FW 99 #define TN_8022 13 -#define ILLEGAL_IOCTL_REQ 2 #define FLASHROM_OPER_PHY_FLASH 9 #define FLASHROM_OPER_PHY_SAVE 10 #define FLASHROM_OPER_FLASH 1 @@ -250,6 +254,9 @@ #define IMAGE_FIRMWARE_BACKUP_FCoE 178 #define IMAGE_FIRMWARE_BACKUP_COMP_FCoE 179 #define IMAGE_FIRMWARE_PHY 192 +#define IMAGE_REDBOOT_DIR 208 +#define IMAGE_REDBOOT_CONFIG 209 +#define IMAGE_UFI_DIR 210 #define IMAGE_BOOT_CODE 224 /************* Rx Packet Type Encoding **************/ @@ -534,7 +541,8 @@ struct flash_section_entry { u32 image_size; u32 cksum; u32 entry_point; - u32 rsvd0; + u16 optype; + u16 rsvd0; u32 rsvd1; u8 ver_data[32]; } __packed; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index dcc5e5c..6822b3d 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1111,7 +1111,8 @@ static int be_vid_config(struct be_adapter *adapter) status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num); if (status) { /* Set to VLAN promisc mode as setting VLAN filter failed */ - if (status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES) + if (addl_status(status) == + MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES) goto set_vlan_promisc; dev_err(&adapter->pdev->dev, "Setting HW VLAN filtering failed.\n"); @@ -1190,7 +1191,7 @@ ret: static void be_clear_promisc(struct be_adapter *adapter) { adapter->promiscuous = false; - adapter->flags &= ~BE_FLAGS_VLAN_PROMISC; + adapter->flags &= ~(BE_FLAGS_VLAN_PROMISC | BE_FLAGS_MCAST_PROMISC); be_cmd_rx_filter(adapter, IFF_PROMISC, OFF); } @@ -1215,10 +1216,8 @@ static void be_set_rx_mode(struct net_device *netdev) /* Enable multicast promisc if num configured exceeds what we support */ if (netdev->flags & IFF_ALLMULTI || - netdev_mc_count(netdev) > be_max_mc(adapter)) { - be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON); - goto done; - } + netdev_mc_count(netdev) > be_max_mc(adapter)) + goto set_mcast_promisc; if (netdev_uc_count(netdev) != adapter->uc_macs) { struct netdev_hw_addr *ha; @@ -1244,15 +1243,22 @@ static void be_set_rx_mode(struct net_device *netdev) } status = be_cmd_rx_filter(adapter, IFF_MULTICAST, ON); - - /* Set to MCAST promisc mode if setting MULTICAST address fails */ - if (status) { - dev_info(&adapter->pdev->dev, - "Exhausted multicast HW filters.\n"); - dev_info(&adapter->pdev->dev, - "Disabling HW multicast filtering.\n"); - be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON); + if (!status) { + if (adapter->flags & BE_FLAGS_MCAST_PROMISC) + adapter->flags &= ~BE_FLAGS_MCAST_PROMISC; + goto done; } + +set_mcast_promisc: + if (adapter->flags & BE_FLAGS_MCAST_PROMISC) + return; + + /* Set to MCAST promisc mode if setting MULTICAST address fails + * or if num configured exceeds what we support + */ + status = be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON); + if (!status) + adapter->flags |= BE_FLAGS_MCAST_PROMISC; done: return; } @@ -1302,7 +1308,8 @@ static int be_get_vf_config(struct net_device *netdev, int vf, return -EINVAL; vi->vf = vf; - vi->tx_rate = vf_cfg->tx_rate; + vi->max_tx_rate = vf_cfg->tx_rate; + vi->min_tx_rate = 0; vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK; vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT; memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN); @@ -1342,10 +1349,14 @@ static int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos) return status; } -static int be_set_vf_tx_rate(struct net_device *netdev, int vf, int rate) +static int be_set_vf_tx_rate(struct net_device *netdev, int vf, + int min_tx_rate, int max_tx_rate) { struct be_adapter *adapter = netdev_priv(netdev); - int status = 0; + struct device *dev = &adapter->pdev->dev; + int percent_rate, status = 0; + u16 link_speed = 0; + u8 link_status; if (!sriov_enabled(adapter)) return -EPERM; @@ -1353,18 +1364,50 @@ static int be_set_vf_tx_rate(struct net_device *netdev, int vf, int rate) if (vf >= adapter->num_vfs) return -EINVAL; - if (rate < 100 || rate > 10000) { - dev_err(&adapter->pdev->dev, - "tx rate must be between 100 and 10000 Mbps\n"); + if (min_tx_rate) return -EINVAL; + + if (!max_tx_rate) + goto config_qos; + + status = be_cmd_link_status_query(adapter, &link_speed, + &link_status, 0); + if (status) + goto err; + + if (!link_status) { + dev_err(dev, "TX-rate setting not allowed when link is down\n"); + status = -EPERM; + goto err; + } + + if (max_tx_rate < 100 || max_tx_rate > link_speed) { + dev_err(dev, "TX-rate must be between 100 and %d Mbps\n", + link_speed); + status = -EINVAL; + goto err; + } + + /* On Skyhawk the QOS setting must be done only as a % value */ + percent_rate = link_speed / 100; + if (skyhawk_chip(adapter) && (max_tx_rate % percent_rate)) { + dev_err(dev, "TX-rate must be a multiple of %d Mbps\n", + percent_rate); + status = -EINVAL; + goto err; } - status = be_cmd_config_qos(adapter, rate / 10, vf + 1); +config_qos: + status = be_cmd_config_qos(adapter, max_tx_rate, link_speed, vf + 1); if (status) - dev_err(&adapter->pdev->dev, - "tx rate %d on VF %d failed\n", rate, vf); - else - adapter->vf_cfg[vf].tx_rate = rate; + goto err; + + adapter->vf_cfg[vf].tx_rate = max_tx_rate; + return 0; + +err: + dev_err(dev, "TX-rate setting of %dMbps on VF%d failed\n", + max_tx_rate, vf); return status; } static int be_set_vf_link_state(struct net_device *netdev, int vf, @@ -3125,7 +3168,6 @@ static int be_vf_setup(struct be_adapter *adapter) struct be_vf_cfg *vf_cfg; int status, old_vfs, vf; u32 privileges; - u16 lnk_speed; old_vfs = pci_num_vf(adapter->pdev); if (old_vfs) { @@ -3181,16 +3223,9 @@ static int be_vf_setup(struct be_adapter *adapter) vf); } - /* BE3 FW, by default, caps VF TX-rate to 100mbps. - * Allow full available bandwidth - */ - if (BE3_chip(adapter) && !old_vfs) - be_cmd_config_qos(adapter, 1000, vf + 1); - - status = be_cmd_link_status_query(adapter, &lnk_speed, - NULL, vf + 1); - if (!status) - vf_cfg->tx_rate = lnk_speed; + /* Allow full available bandwidth */ + if (!old_vfs) + be_cmd_config_qos(adapter, 0, 0, vf + 1); if (!old_vfs) { be_cmd_enable_vf(adapter, vf + 1); @@ -3596,34 +3631,7 @@ static void be_netpoll(struct net_device *netdev) } #endif -#define FW_FILE_HDR_SIGN "ServerEngines Corp. " -static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "}; - -static bool be_flash_redboot(struct be_adapter *adapter, - const u8 *p, u32 img_start, int image_size, - int hdr_size) -{ - u32 crc_offset; - u8 flashed_crc[4]; - int status; - - crc_offset = hdr_size + img_start + image_size - 4; - - p += crc_offset; - - status = be_cmd_get_flash_crc(adapter, flashed_crc, (image_size - 4)); - if (status) { - dev_err(&adapter->pdev->dev, - "could not get crc from flash, not flashing redboot\n"); - return false; - } - - /*update redboot only if crc does not match*/ - if (!memcmp(flashed_crc, p, 4)) - return false; - else - return true; -} +static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "}; static bool phy_flashing_required(struct be_adapter *adapter) { @@ -3670,12 +3678,35 @@ static struct flash_section_info *get_fsec_info(struct be_adapter *adapter, return NULL; } +static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p, + u32 img_offset, u32 img_size, int hdr_size, + u16 img_optype, bool *crc_match) +{ + u32 crc_offset; + int status; + u8 crc[4]; + + status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_size - 4); + if (status) + return status; + + crc_offset = hdr_size + img_offset + img_size - 4; + + /* Skip flashing, if crc of flashed region matches */ + if (!memcmp(crc, p + crc_offset, 4)) + *crc_match = true; + else + *crc_match = false; + + return status; +} + static int be_flash(struct be_adapter *adapter, const u8 *img, struct be_dma_mem *flash_cmd, int optype, int img_size) { - u32 total_bytes = 0, flash_op, num_bytes = 0; - int status = 0; struct be_cmd_write_flashrom *req = flash_cmd->va; + u32 total_bytes, flash_op, num_bytes; + int status; total_bytes = img_size; while (total_bytes) { @@ -3699,14 +3730,11 @@ static int be_flash(struct be_adapter *adapter, const u8 *img, img += num_bytes; status = be_cmd_write_flashrom(adapter, flash_cmd, optype, flash_op, num_bytes); - if (status) { - if (status == ILLEGAL_IOCTL_REQ && - optype == OPTYPE_PHY_FW) - break; - dev_err(&adapter->pdev->dev, - "cmd to write to flash rom failed.\n"); + if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST && + optype == OPTYPE_PHY_FW) + break; + else if (status) return status; - } } return 0; } @@ -3716,12 +3744,13 @@ static int be_flash_BEx(struct be_adapter *adapter, const struct firmware *fw, struct be_dma_mem *flash_cmd, int num_of_images) { - int status = 0, i, filehdr_size = 0; int img_hdrs_size = (num_of_images * sizeof(struct image_hdr)); - const u8 *p = fw->data; - const struct flash_comp *pflashcomp; - int num_comp, redboot; + struct device *dev = &adapter->pdev->dev; struct flash_section_info *fsec = NULL; + int status, i, filehdr_size, num_comp; + const struct flash_comp *pflashcomp; + bool crc_match; + const u8 *p; struct flash_comp gen3_flash_types[] = { { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, OPTYPE_ISCSI_ACTIVE, @@ -3778,8 +3807,7 @@ static int be_flash_BEx(struct be_adapter *adapter, /* Get flash section info*/ fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw); if (!fsec) { - dev_err(&adapter->pdev->dev, - "Invalid Cookie. UFI corrupted ?\n"); + dev_err(dev, "Invalid Cookie. FW image may be corrupted\n"); return -1; } for (i = 0; i < num_comp; i++) { @@ -3795,25 +3823,32 @@ static int be_flash_BEx(struct be_adapter *adapter, continue; if (pflashcomp[i].optype == OPTYPE_REDBOOT) { - redboot = be_flash_redboot(adapter, fw->data, - pflashcomp[i].offset, - pflashcomp[i].size, - filehdr_size + - img_hdrs_size); - if (!redboot) + status = be_check_flash_crc(adapter, fw->data, + pflashcomp[i].offset, + pflashcomp[i].size, + filehdr_size + + img_hdrs_size, + OPTYPE_REDBOOT, &crc_match); + if (status) { + dev_err(dev, + "Could not get CRC for 0x%x region\n", + pflashcomp[i].optype); + continue; + } + + if (crc_match) continue; } - p = fw->data; - p += filehdr_size + pflashcomp[i].offset + img_hdrs_size; + p = fw->data + filehdr_size + pflashcomp[i].offset + + img_hdrs_size; if (p + pflashcomp[i].size > fw->data + fw->size) return -1; status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype, pflashcomp[i].size); if (status) { - dev_err(&adapter->pdev->dev, - "Flashing section type %d failed.\n", + dev_err(dev, "Flashing section type 0x%x failed\n", pflashcomp[i].img_type); return status; } @@ -3821,74 +3856,135 @@ static int be_flash_BEx(struct be_adapter *adapter, return 0; } +static u16 be_get_img_optype(struct flash_section_entry fsec_entry) +{ + u32 img_type = le32_to_cpu(fsec_entry.type); + u16 img_optype = le16_to_cpu(fsec_entry.optype); + + if (img_optype != 0xFFFF) + return img_optype; + + switch (img_type) { + case IMAGE_FIRMWARE_iSCSI: + img_optype = OPTYPE_ISCSI_ACTIVE; + break; + case IMAGE_BOOT_CODE: + img_optype = OPTYPE_REDBOOT; + break; + case IMAGE_OPTION_ROM_ISCSI: + img_optype = OPTYPE_BIOS; + break; + case IMAGE_OPTION_ROM_PXE: + img_optype = OPTYPE_PXE_BIOS; + break; + case IMAGE_OPTION_ROM_FCoE: + img_optype = OPTYPE_FCOE_BIOS; + break; + case IMAGE_FIRMWARE_BACKUP_iSCSI: + img_optype = OPTYPE_ISCSI_BACKUP; + break; + case IMAGE_NCSI: + img_optype = OPTYPE_NCSI_FW; + break; + case IMAGE_FLASHISM_JUMPVECTOR: + img_optype = OPTYPE_FLASHISM_JUMPVECTOR; + break; + case IMAGE_FIRMWARE_PHY: + img_optype = OPTYPE_SH_PHY_FW; + break; + case IMAGE_REDBOOT_DIR: + img_optype = OPTYPE_REDBOOT_DIR; + break; + case IMAGE_REDBOOT_CONFIG: + img_optype = OPTYPE_REDBOOT_CONFIG; + break; + case IMAGE_UFI_DIR: + img_optype = OPTYPE_UFI_DIR; + break; + default: + break; + } + + return img_optype; +} + static int be_flash_skyhawk(struct be_adapter *adapter, const struct firmware *fw, struct be_dma_mem *flash_cmd, int num_of_images) { - int status = 0, i, filehdr_size = 0; - int img_offset, img_size, img_optype, redboot; int img_hdrs_size = num_of_images * sizeof(struct image_hdr); - const u8 *p = fw->data; + struct device *dev = &adapter->pdev->dev; struct flash_section_info *fsec = NULL; + u32 img_offset, img_size, img_type; + int status, i, filehdr_size; + bool crc_match, old_fw_img; + u16 img_optype; + const u8 *p; filehdr_size = sizeof(struct flash_file_hdr_g3); fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw); if (!fsec) { - dev_err(&adapter->pdev->dev, - "Invalid Cookie. UFI corrupted ?\n"); + dev_err(dev, "Invalid Cookie. FW image may be corrupted\n"); return -1; } for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) { img_offset = le32_to_cpu(fsec->fsec_entry[i].offset); img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size); + img_type = le32_to_cpu(fsec->fsec_entry[i].type); + img_optype = be_get_img_optype(fsec->fsec_entry[i]); + old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF; - switch (le32_to_cpu(fsec->fsec_entry[i].type)) { - case IMAGE_FIRMWARE_iSCSI: - img_optype = OPTYPE_ISCSI_ACTIVE; - break; - case IMAGE_BOOT_CODE: - img_optype = OPTYPE_REDBOOT; - break; - case IMAGE_OPTION_ROM_ISCSI: - img_optype = OPTYPE_BIOS; - break; - case IMAGE_OPTION_ROM_PXE: - img_optype = OPTYPE_PXE_BIOS; - break; - case IMAGE_OPTION_ROM_FCoE: - img_optype = OPTYPE_FCOE_BIOS; - break; - case IMAGE_FIRMWARE_BACKUP_iSCSI: - img_optype = OPTYPE_ISCSI_BACKUP; - break; - case IMAGE_NCSI: - img_optype = OPTYPE_NCSI_FW; - break; - default: + if (img_optype == 0xFFFF) continue; + /* Don't bother verifying CRC if an old FW image is being + * flashed + */ + if (old_fw_img) + goto flash; + + status = be_check_flash_crc(adapter, fw->data, img_offset, + img_size, filehdr_size + + img_hdrs_size, img_optype, + &crc_match); + /* The current FW image on the card does not recognize the new + * FLASH op_type. The FW download is partially complete. + * Reboot the server now to enable FW image to recognize the + * new FLASH op_type. To complete the remaining process, + * download the same FW again after the reboot. + */ + if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST || + base_status(status) == MCC_STATUS_ILLEGAL_FIELD) { + dev_err(dev, "Flash incomplete. Reset the server\n"); + dev_err(dev, "Download FW image again after reset\n"); + return -EAGAIN; + } else if (status) { + dev_err(dev, "Could not get CRC for 0x%x region\n", + img_optype); + return -EFAULT; } - if (img_optype == OPTYPE_REDBOOT) { - redboot = be_flash_redboot(adapter, fw->data, - img_offset, img_size, - filehdr_size + - img_hdrs_size); - if (!redboot) - continue; - } + if (crc_match) + continue; - p = fw->data; - p += filehdr_size + img_offset + img_hdrs_size; +flash: + p = fw->data + filehdr_size + img_offset + img_hdrs_size; if (p + img_size > fw->data + fw->size) return -1; status = be_flash(adapter, p, flash_cmd, img_optype, img_size); - if (status) { - dev_err(&adapter->pdev->dev, - "Flashing section type %d failed.\n", - fsec->fsec_entry[i].type); - return status; + /* For old FW images ignore ILLEGAL_FIELD error or errors on + * UFI_DIR region + */ + if (old_fw_img && + (base_status(status) == MCC_STATUS_ILLEGAL_FIELD || + (img_optype == OPTYPE_UFI_DIR && + base_status(status) == MCC_STATUS_FAILED))) { + continue; + } else if (status) { + dev_err(dev, "Flashing section type 0x%x failed\n", + img_type); + return -EFAULT; } } return 0; @@ -4257,7 +4353,7 @@ static const struct net_device_ops be_netdev_ops = { .ndo_vlan_rx_kill_vid = be_vlan_rem_vid, .ndo_set_vf_mac = be_set_vf_mac, .ndo_set_vf_vlan = be_set_vf_vlan, - .ndo_set_vf_tx_rate = be_set_vf_tx_rate, + .ndo_set_vf_rate = be_set_vf_tx_rate, .ndo_get_vf_config = be_get_vf_config, .ndo_set_vf_link_state = be_set_vf_link_state, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -4952,6 +5048,12 @@ static void be_eeh_resume(struct pci_dev *pdev) if (status) goto err; + /* On some BE3 FW versions, after a HW reset, + * interrupts will remain disabled for each function. + * So, explicitly enable interrupts + */ + be_intr_set(adapter, true); + /* tell fw we're ready to fire cmds */ status = be_cmd_fw_init(adapter); if (status) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 8d69e43..802be17 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -54,6 +54,7 @@ #include <linux/of_net.h> #include <linux/regulator/consumer.h> #include <linux/if_vlan.h> +#include <linux/pinctrl/consumer.h> #include <asm/cacheflush.h> @@ -1255,6 +1256,49 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, return 0; } +static int fec_enet_clk_enable(struct net_device *ndev, bool enable) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + if (enable) { + ret = clk_prepare_enable(fep->clk_ahb); + if (ret) + return ret; + ret = clk_prepare_enable(fep->clk_ipg); + if (ret) + goto failed_clk_ipg; + if (fep->clk_enet_out) { + ret = clk_prepare_enable(fep->clk_enet_out); + if (ret) + goto failed_clk_enet_out; + } + if (fep->clk_ptp) { + ret = clk_prepare_enable(fep->clk_ptp); + if (ret) + goto failed_clk_ptp; + } + } else { + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); + if (fep->clk_enet_out) + clk_disable_unprepare(fep->clk_enet_out); + if (fep->clk_ptp) + clk_disable_unprepare(fep->clk_ptp); + } + + return 0; +failed_clk_ptp: + if (fep->clk_enet_out) + clk_disable_unprepare(fep->clk_enet_out); +failed_clk_enet_out: + clk_disable_unprepare(fep->clk_ipg); +failed_clk_ipg: + clk_disable_unprepare(fep->clk_ahb); + + return ret; +} + static int fec_enet_mii_probe(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); @@ -1364,7 +1408,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) * Reference Manual has an error on this, and gets fixed on i.MX6Q * document. */ - fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000); + fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) fep->phy_speed--; fep->phy_speed <<= 1; @@ -1773,6 +1817,11 @@ fec_enet_open(struct net_device *ndev) struct fec_enet_private *fep = netdev_priv(ndev); int ret; + pinctrl_pm_select_default_state(&fep->pdev->dev); + ret = fec_enet_clk_enable(ndev, true); + if (ret) + return ret; + /* I should reset the ring buffers here, but I don't yet know * a simple way to do that. */ @@ -1811,6 +1860,8 @@ fec_enet_close(struct net_device *ndev) phy_disconnect(fep->phy_dev); } + fec_enet_clk_enable(ndev, false); + pinctrl_pm_select_sleep_state(&fep->pdev->dev); fec_enet_free_buffers(ndev); return 0; @@ -2114,6 +2165,9 @@ fec_probe(struct platform_device *pdev) fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; #endif + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); fep->hwp = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(fep->hwp)) { @@ -2164,26 +2218,10 @@ fec_probe(struct platform_device *pdev) fep->bufdesc_ex = 0; } - ret = clk_prepare_enable(fep->clk_ahb); + ret = fec_enet_clk_enable(ndev, true); if (ret) goto failed_clk; - ret = clk_prepare_enable(fep->clk_ipg); - if (ret) - goto failed_clk_ipg; - - if (fep->clk_enet_out) { - ret = clk_prepare_enable(fep->clk_enet_out); - if (ret) - goto failed_clk_enet_out; - } - - if (fep->clk_ptp) { - ret = clk_prepare_enable(fep->clk_ptp); - if (ret) - goto failed_clk_ptp; - } - fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); if (!IS_ERR(fep->reg_phy)) { ret = regulator_enable(fep->reg_phy); @@ -2225,6 +2263,8 @@ fec_probe(struct platform_device *pdev) /* Carrier starts down, phylib will bring it up */ netif_carrier_off(ndev); + fec_enet_clk_enable(ndev, false); + pinctrl_pm_select_sleep_state(&pdev->dev); ret = register_netdev(ndev); if (ret) @@ -2244,15 +2284,7 @@ failed_init: if (fep->reg_phy) regulator_disable(fep->reg_phy); failed_regulator: - if (fep->clk_ptp) - clk_disable_unprepare(fep->clk_ptp); -failed_clk_ptp: - if (fep->clk_enet_out) - clk_disable_unprepare(fep->clk_enet_out); -failed_clk_enet_out: - clk_disable_unprepare(fep->clk_ipg); -failed_clk_ipg: - clk_disable_unprepare(fep->clk_ahb); + fec_enet_clk_enable(ndev, false); failed_clk: failed_ioremap: free_netdev(ndev); @@ -2272,14 +2304,9 @@ fec_drv_remove(struct platform_device *pdev) del_timer_sync(&fep->time_keep); if (fep->reg_phy) regulator_disable(fep->reg_phy); - if (fep->clk_ptp) - clk_disable_unprepare(fep->clk_ptp); if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); - if (fep->clk_enet_out) - clk_disable_unprepare(fep->clk_enet_out); - clk_disable_unprepare(fep->clk_ipg); - clk_disable_unprepare(fep->clk_ahb); + fec_enet_clk_enable(ndev, false); free_netdev(ndev); return 0; @@ -2296,12 +2323,8 @@ fec_suspend(struct device *dev) fec_stop(ndev); netif_device_detach(ndev); } - if (fep->clk_ptp) - clk_disable_unprepare(fep->clk_ptp); - if (fep->clk_enet_out) - clk_disable_unprepare(fep->clk_enet_out); - clk_disable_unprepare(fep->clk_ipg); - clk_disable_unprepare(fep->clk_ahb); + fec_enet_clk_enable(ndev, false); + pinctrl_pm_select_sleep_state(&fep->pdev->dev); if (fep->reg_phy) regulator_disable(fep->reg_phy); @@ -2322,25 +2345,10 @@ fec_resume(struct device *dev) return ret; } - ret = clk_prepare_enable(fep->clk_ahb); + pinctrl_pm_select_default_state(&fep->pdev->dev); + ret = fec_enet_clk_enable(ndev, true); if (ret) - goto failed_clk_ahb; - - ret = clk_prepare_enable(fep->clk_ipg); - if (ret) - goto failed_clk_ipg; - - if (fep->clk_enet_out) { - ret = clk_prepare_enable(fep->clk_enet_out); - if (ret) - goto failed_clk_enet_out; - } - - if (fep->clk_ptp) { - ret = clk_prepare_enable(fep->clk_ptp); - if (ret) - goto failed_clk_ptp; - } + goto failed_clk; if (netif_running(ndev)) { fec_restart(ndev, fep->full_duplex); @@ -2349,14 +2357,7 @@ fec_resume(struct device *dev) return 0; -failed_clk_ptp: - if (fep->clk_enet_out) - clk_disable_unprepare(fep->clk_enet_out); -failed_clk_enet_out: - clk_disable_unprepare(fep->clk_ipg); -failed_clk_ipg: - clk_disable_unprepare(fep->clk_ahb); -failed_clk_ahb: +failed_clk: if (fep->reg_phy) regulator_disable(fep->reg_phy); return ret; diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index dc80db4..cfaf17b 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -792,10 +792,6 @@ static int fs_init_phy(struct net_device *dev) phydev = of_phy_connect(dev, fep->fpi->phy_node, &fs_adjust_link, 0, iface); if (!phydev) { - phydev = of_phy_connect_fixed_link(dev, &fs_adjust_link, - iface); - } - if (!phydev) { dev_err(&dev->dev, "Could not attach to PHY\n"); return -ENODEV; } @@ -1029,9 +1025,16 @@ static int fs_enet_probe(struct platform_device *ofdev) fpi->use_napi = 1; fpi->napi_weight = 17; fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); - if ((!fpi->phy_node) && (!of_get_property(ofdev->dev.of_node, "fixed-link", - NULL))) - goto out_free_fpi; + if (!fpi->phy_node && of_phy_is_fixed_link(ofdev->dev.of_node)) { + err = of_phy_register_fixed_link(ofdev->dev.of_node); + if (err) + goto out_free_fpi; + + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + fpi->phy_node = ofdev->dev.of_node; + } if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) { phy_connection_type = of_get_property(ofdev->dev.of_node, diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index e2d4247..2826740 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -889,6 +889,17 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) priv->phy_node = of_parse_phandle(np, "phy-handle", 0); + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + if (of_phy_is_fixed_link(np)) { + err = of_phy_register_fixed_link(np); + if (err) + goto err_grp_init; + + priv->phy_node = np; + } + /* Find the TBI PHY. If it's not there, we don't support SGMII */ priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0); @@ -1660,9 +1671,6 @@ static int init_phy(struct net_device *dev) priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0, interface); - if (!priv->phydev) - priv->phydev = of_phy_connect_fixed_link(dev, &adjust_link, - interface); if (!priv->phydev) { dev_err(&dev->dev, "could not attach to PHY\n"); return -ENODEV; diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index c8299c3..fab39e2 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -1728,9 +1728,6 @@ static int init_phy(struct net_device *dev) phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0, priv->phy_interface); - if (!phydev) - phydev = of_phy_connect_fixed_link(dev, &adjust_link, - priv->phy_interface); if (!phydev) { dev_err(&dev->dev, "Could not attach to PHY\n"); return -ENODEV; @@ -3790,6 +3787,17 @@ static int ucc_geth_probe(struct platform_device* ofdev) ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0); + if (!ug_info->phy_node) { + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + if (of_phy_is_fixed_link(np)) { + err = of_phy_register_fixed_link(np); + if (err) + return err; + } + ug_info->phy_node = np; + } /* Find the TBI PHY node. If it's not there, we don't support SGMII */ ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0); diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 538903b..a0b418e 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -28,6 +28,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/device.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/tcp.h> @@ -3273,7 +3274,7 @@ static int ehea_probe_adapter(struct platform_device *dev) return -EINVAL; } - adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); + adapter = devm_kzalloc(&dev->dev, sizeof(*adapter), GFP_KERNEL); if (!adapter) { ret = -ENOMEM; dev_err(&dev->dev, "no mem for ehea_adapter\n"); @@ -3359,7 +3360,6 @@ out_kill_eq: out_free_ad: list_del(&adapter->list); - kfree(adapter); out: ehea_update_firmware_handles(); @@ -3386,7 +3386,6 @@ static int ehea_remove(struct platform_device *dev) ehea_destroy_eq(adapter->neq); ehea_remove_adapter_mr(adapter); list_del(&adapter->list); - kfree(adapter); ehea_update_firmware_handles(); diff --git a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c index 9b03033b..a0820f7 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c @@ -103,12 +103,14 @@ out_nomem: static void hw_queue_dtor(struct hw_queue *queue) { - int pages_per_kpage = PAGE_SIZE / queue->pagesize; + int pages_per_kpage; int i, nr_pages; if (!queue || !queue->queue_pages) return; + pages_per_kpage = PAGE_SIZE / queue->pagesize; + nr_pages = queue->queue_length / queue->pagesize; for (i = 0; i < nr_pages; i += pages_per_kpage) diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index c1d3fdb..e9b07cc 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -4877,10 +4877,10 @@ void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, * since the test for a multicast frame will test positive on * a broadcast frame. */ - if ((mac_addr[0] == (u8) 0xff) && (mac_addr[1] == (u8) 0xff)) + if (is_broadcast_ether_addr(mac_addr)) /* Broadcast packet */ stats->bprc++; - else if (*mac_addr & 0x01) + else if (is_multicast_ether_addr(mac_addr)) /* Multicast packet */ stats->mprc++; diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c index a5f6b11..08f22f3 100644 --- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c +++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c @@ -1365,6 +1365,7 @@ static const struct e1000_mac_operations es2_mac_ops = { .setup_led = e1000e_setup_led_generic, .config_collision_dist = e1000e_config_collision_dist_generic, .rar_set = e1000e_rar_set_generic, + .rar_get_count = e1000e_rar_get_count_generic, }; static const struct e1000_phy_operations es2_phy_ops = { diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index e0aa7f1..218481e 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -1896,6 +1896,7 @@ static const struct e1000_mac_operations e82571_mac_ops = { .config_collision_dist = e1000e_config_collision_dist_generic, .read_mac_addr = e1000_read_mac_addr_82571, .rar_set = e1000e_rar_set_generic, + .rar_get_count = e1000e_rar_get_count_generic, }; static const struct e1000_phy_operations e82_phy_ops_igp = { diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index e27e609..7785240 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -391,6 +391,8 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca); * 25MHz 46-bit 2^46 / 10^9 / 3600 = 19.55 hours */ #define E1000_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 60 * 4) +#define E1000_MAX_82574_SYSTIM_REREADS 50 +#define E1000_82574_SYSTIM_EPSILON (1ULL << 35ULL) /* hardware capability, feature, and workaround flags */ #define FLAG_HAS_AMT (1 << 0) @@ -573,35 +575,8 @@ static inline u32 __er32(struct e1000_hw *hw, unsigned long reg) #define er32(reg) __er32(hw, E1000_##reg) -/** - * __ew32_prepare - prepare to write to MAC CSR register on certain parts - * @hw: pointer to the HW structure - * - * When updating the MAC CSR registers, the Manageability Engine (ME) could - * be accessing the registers at the same time. Normally, this is handled in - * h/w by an arbiter but on some parts there is a bug that acknowledges Host - * accesses later than it should which could result in the register to have - * an incorrect value. Workaround this by checking the FWSM register which - * has bit 24 set while ME is accessing MAC CSR registers, wait if it is set - * and try again a number of times. - **/ -static inline s32 __ew32_prepare(struct e1000_hw *hw) -{ - s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT; - - while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i) - udelay(50); - - return i; -} - -static inline void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val) -{ - if (hw->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA) - __ew32_prepare(hw); - - writel(val, hw->hw_addr + reg); -} +s32 __ew32_prepare(struct e1000_hw *hw); +void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val); #define ew32(reg, val) __ew32(hw, E1000_##reg, (val)) diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index 6b3de5f..72f5475 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -469,8 +469,9 @@ struct e1000_mac_operations { s32 (*setup_led)(struct e1000_hw *); void (*write_vfta)(struct e1000_hw *, u32, u32); void (*config_collision_dist)(struct e1000_hw *); - void (*rar_set)(struct e1000_hw *, u8 *, u32); + int (*rar_set)(struct e1000_hw *, u8 *, u32); s32 (*read_mac_addr)(struct e1000_hw *); + u32 (*rar_get_count)(struct e1000_hw *); }; /* When to use various PHY register access functions: diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 5f55395..8894ab8 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -139,8 +139,9 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link); static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw); static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw); static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw); -static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index); -static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index); +static int e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index); +static int e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index); +static u32 e1000_rar_get_count_pch_lpt(struct e1000_hw *hw); static s32 e1000_k1_workaround_lv(struct e1000_hw *hw); static void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate); static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force); @@ -704,6 +705,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw) mac->ops.rar_set = e1000_rar_set_pch_lpt; mac->ops.setup_physical_interface = e1000_setup_copper_link_pch_lpt; + mac->ops.rar_get_count = e1000_rar_get_count_pch_lpt; } /* Enable PCS Lock-loss workaround for ICH8 */ @@ -1635,9 +1637,9 @@ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw) u32 fwsm; fwsm = er32(FWSM); - return ((fwsm & E1000_ICH_FWSM_FW_VALID) && + return (fwsm & E1000_ICH_FWSM_FW_VALID) && ((fwsm & E1000_FWSM_MODE_MASK) == - (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT))); + (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)); } /** @@ -1668,7 +1670,7 @@ static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw) * contain the MAC address but RAR[1-6] are reserved for manageability (ME). * Use SHRA[0-3] in place of those reserved for ME. **/ -static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) +static int e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; @@ -1690,7 +1692,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) e1e_flush(); ew32(RAH(index), rar_high); e1e_flush(); - return; + return 0; } /* RAR[1-6] are owned by manageability. Skip those and program the @@ -1713,7 +1715,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) /* verify the register updates */ if ((er32(SHRAL(index - 1)) == rar_low) && (er32(SHRAH(index - 1)) == rar_high)) - return; + return 0; e_dbg("SHRA[%d] might be locked by ME - FWSM=0x%8.8x\n", (index - 1), er32(FWSM)); @@ -1721,6 +1723,43 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) out: e_dbg("Failed to write receive address at index %d\n", index); + return -E1000_ERR_CONFIG; +} + +/** + * e1000_rar_get_count_pch_lpt - Get the number of available SHRA + * @hw: pointer to the HW structure + * + * Get the number of available receive registers that the Host can + * program. SHRA[0-10] are the shared receive address registers + * that are shared between the Host and manageability engine (ME). + * ME can reserve any number of addresses and the host needs to be + * able to tell how many available registers it has access to. + **/ +static u32 e1000_rar_get_count_pch_lpt(struct e1000_hw *hw) +{ + u32 wlock_mac; + u32 num_entries; + + wlock_mac = er32(FWSM) & E1000_FWSM_WLOCK_MAC_MASK; + wlock_mac >>= E1000_FWSM_WLOCK_MAC_SHIFT; + + switch (wlock_mac) { + case 0: + /* All SHRA[0..10] and RAR[0] available */ + num_entries = hw->mac.rar_entry_count; + break; + case 1: + /* Only RAR[0] available */ + num_entries = 1; + break; + default: + /* SHRA[0..(wlock_mac - 1)] available + RAR[0] */ + num_entries = wlock_mac + 1; + break; + } + + return num_entries; } /** @@ -1734,7 +1773,7 @@ out: * contain the MAC address. SHRA[0-10] are the shared receive address * registers that are shared between the Host and manageability engine (ME). **/ -static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index) +static int e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; u32 wlock_mac; @@ -1756,7 +1795,7 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index) e1e_flush(); ew32(RAH(index), rar_high); e1e_flush(); - return; + return 0; } /* The manageability engine (ME) can lock certain SHRAR registers that @@ -1788,12 +1827,13 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index) /* verify the register updates */ if ((er32(SHRAL_PCH_LPT(index - 1)) == rar_low) && (er32(SHRAH_PCH_LPT(index - 1)) == rar_high)) - return; + return 0; } } out: e_dbg("Failed to write receive address at index %d\n", index); + return -E1000_ERR_CONFIG; } /** @@ -4977,6 +5017,7 @@ static const struct e1000_mac_operations ich8_mac_ops = { /* id_led_init dependent on mac type */ .config_collision_dist = e1000e_config_collision_dist_generic, .rar_set = e1000e_rar_set_generic, + .rar_get_count = e1000e_rar_get_count_generic, }; static const struct e1000_phy_operations ich8_phy_ops = { diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c index baa0a46..8c386f3a 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.c +++ b/drivers/net/ethernet/intel/e1000e/mac.c @@ -211,6 +211,11 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) return 0; } +u32 e1000e_rar_get_count_generic(struct e1000_hw *hw) +{ + return hw->mac.rar_entry_count; +} + /** * e1000e_rar_set_generic - Set receive address register * @hw: pointer to the HW structure @@ -220,7 +225,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) * Sets the receive address array register at index to the address passed * in by addr. **/ -void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) +int e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; @@ -244,6 +249,8 @@ void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) e1e_flush(); ew32(RAH(index), rar_high); e1e_flush(); + + return 0; } /** diff --git a/drivers/net/ethernet/intel/e1000e/mac.h b/drivers/net/ethernet/intel/e1000e/mac.h index 4e81c28..0513d90 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.h +++ b/drivers/net/ethernet/intel/e1000e/mac.h @@ -61,7 +61,8 @@ void e1000e_update_adaptive(struct e1000_hw *hw); void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value); void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw); -void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index); +u32 e1000e_rar_get_count_generic(struct e1000_hw *hw); +int e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index); void e1000e_config_collision_dist_generic(struct e1000_hw *hw); #endif diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index e4207ef..201cc93 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -124,6 +124,36 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = { }; /** + * __ew32_prepare - prepare to write to MAC CSR register on certain parts + * @hw: pointer to the HW structure + * + * When updating the MAC CSR registers, the Manageability Engine (ME) could + * be accessing the registers at the same time. Normally, this is handled in + * h/w by an arbiter but on some parts there is a bug that acknowledges Host + * accesses later than it should which could result in the register to have + * an incorrect value. Workaround this by checking the FWSM register which + * has bit 24 set while ME is accessing MAC CSR registers, wait if it is set + * and try again a number of times. + **/ +s32 __ew32_prepare(struct e1000_hw *hw) +{ + s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT; + + while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i) + udelay(50); + + return i; +} + +void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val) +{ + if (hw->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA) + __ew32_prepare(hw); + + writel(val, hw->hw_addr + reg); +} + +/** * e1000_regdump - register printout routine * @hw: pointer to the HW structure * @reginfo: pointer to the register info table @@ -3311,9 +3341,11 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - unsigned int rar_entries = hw->mac.rar_entry_count; + unsigned int rar_entries; int count = 0; + rar_entries = hw->mac.ops.rar_get_count(hw); + /* save a rar entry for our hardware address */ rar_entries--; @@ -3332,9 +3364,13 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev) * combining */ netdev_for_each_uc_addr(ha, netdev) { + int rval; + if (!rar_entries) break; - hw->mac.ops.rar_set(hw, ha->addr, rar_entries--); + rval = hw->mac.ops.rar_set(hw, ha->addr, rar_entries--); + if (rval < 0) + return -ENOMEM; count++; } } @@ -4093,12 +4129,37 @@ static cycle_t e1000e_cyclecounter_read(const struct cyclecounter *cc) struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter, cc); struct e1000_hw *hw = &adapter->hw; - cycle_t systim; + cycle_t systim, systim_next; /* latch SYSTIMH on read of SYSTIML */ systim = (cycle_t)er32(SYSTIML); systim |= (cycle_t)er32(SYSTIMH) << 32; + if ((hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82583)) { + u64 incvalue, time_delta, rem, temp; + int i; + + /* errata for 82574/82583 possible bad bits read from SYSTIMH/L + * check to see that the time is incrementing at a reasonable + * rate and is a multiple of incvalue + */ + incvalue = er32(TIMINCA) & E1000_TIMINCA_INCVALUE_MASK; + for (i = 0; i < E1000_MAX_82574_SYSTIM_REREADS; i++) { + /* latch SYSTIMH on read of SYSTIML */ + systim_next = (cycle_t)er32(SYSTIML); + systim_next |= (cycle_t)er32(SYSTIMH) << 32; + + time_delta = systim_next - systim; + temp = time_delta; + rem = do_div(temp, incvalue); + + systim = systim_next; + + if ((time_delta < E1000_82574_SYSTIM_EPSILON) && + (rem == 0)) + break; + } + } return systim; } @@ -4499,7 +4560,7 @@ static void e1000e_update_phy_task(struct work_struct *work) e1000_get_phy_info(hw); /* Enable EEE on 82579 after link up */ - if (hw->phy.type == e1000_phy_82579) + if (hw->phy.type >= e1000_phy_82579) e1000_set_eee_pchlan(hw); } diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index a46571c..581898f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -97,10 +97,6 @@ #define STRINGIFY(foo) #foo #define XSTRINGIFY(bar) STRINGIFY(bar) -#ifndef ARCH_HAS_PREFETCH -#define prefetch(X) -#endif - #define I40E_RX_DESC(R, i) \ ((ring_is_16byte_desc_enabled(R)) \ ? (union i40e_32byte_rx_desc *) \ @@ -538,6 +534,15 @@ static inline bool i40e_rx_is_programming_status(u64 qw) (qw >> I40E_RX_PROG_STATUS_DESC_LENGTH_SHIFT); } +/** + * i40e_get_fd_cnt_all - get the total FD filter space available + * @pf: pointer to the pf struct + **/ +static inline int i40e_get_fd_cnt_all(struct i40e_pf *pf) +{ + return pf->hw.fdir_shared_filter_count + pf->fdir_pf_filter_count; +} + /* needed by i40e_ethtool.c */ int i40e_up(struct i40e_vsi *vsi); void i40e_down(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 34415d3..ba2811b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -587,8 +587,7 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi); hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo; - if (hw->aq.api_maj_ver != I40E_FW_API_VERSION_MAJOR || - hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) { + if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) { ret_code = I40E_ERR_FIRMWARE_API_VERSION; goto init_adminq_free_arq; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 22eefda..2329e2f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -133,7 +133,11 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, **/ bool i40e_check_asq_alive(struct i40e_hw *hw) { - return !!(rd32(hw, hw->aq.asq.len) & I40E_PF_ATQLEN_ATQENABLE_MASK); + if (hw->aq.asq.len) + return !!(rd32(hw, hw->aq.asq.len) & + I40E_PF_ATQLEN_ATQENABLE_MASK); + else + return false; } /** @@ -789,6 +793,9 @@ void i40e_clear_pxe_mode(struct i40e_hw *hw) { u32 reg; + if (i40e_check_asq_alive(hw)) + i40e_aq_clear_pxe_mode(hw, NULL); + /* Clear single descriptor fetch/write-back mode */ reg = rd32(hw, I40E_GLLAN_RCTL_0); @@ -907,6 +914,33 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink) /* Admin command wrappers */ /** + * i40e_aq_clear_pxe_mode + * @hw: pointer to the hw struct + * @cmd_details: pointer to command details structure or NULL + * + * Tell the firmware that the driver is taking over from PXE + **/ +i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details) +{ + i40e_status status; + struct i40e_aq_desc desc; + struct i40e_aqc_clear_pxe *cmd = + (struct i40e_aqc_clear_pxe *)&desc.params.raw; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_clear_pxe_mode); + + cmd->rx_cnt = 0x2; + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + wr32(hw, I40E_GLLAN_RCTL_0, 0x1); + + return status; +} + +/** * i40e_aq_set_link_restart_an * @hw: pointer to the hw struct * @cmd_details: pointer to command details structure or NULL @@ -1915,6 +1949,12 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, } } + /* Software override ensuring FCoE is disabled if npar or mfp + * mode because it is not supported in these modes. + */ + if (p->npar_enable || p->mfp_mode_1) + p->fcoe = false; + /* additional HW specific goodies that might * someday be HW version specific */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index f629294..354181d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -112,7 +112,6 @@ static struct i40e_stats i40e_gstrings_stats[] = { I40E_PF_STAT("rx_oversize", stats.rx_oversize), I40E_PF_STAT("rx_jabber", stats.rx_jabber), I40E_PF_STAT("VF_admin_queue_requests", vf_aq_requests), - I40E_PF_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), I40E_PF_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared), /* LPI stats */ I40E_PF_STAT("tx_lpi_status", stats.tx_lpi_status), @@ -122,8 +121,9 @@ static struct i40e_stats i40e_gstrings_stats[] = { }; #define I40E_QUEUE_STATS_LEN(n) \ - ((((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs + \ - ((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs) * 2) + (((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs \ + * 2 /* Tx and Rx together */ \ + * (sizeof(struct i40e_queue_stats) / sizeof(u64))) #define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats) #define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats) #define I40E_VSI_STATS_LEN(n) (I40E_NETDEV_STATS_LEN + \ @@ -633,6 +633,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_ring *tx_ring, *rx_ring; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; int i = 0; @@ -650,8 +651,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, } rcu_read_lock(); for (j = 0; j < vsi->num_queue_pairs; j++) { - struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[j]); - struct i40e_ring *rx_ring; + tx_ring = ACCESS_ONCE(vsi->tx_rings[j]); if (!tx_ring) continue; @@ -1009,14 +1009,13 @@ static int i40e_get_coalesce(struct net_device *netdev, ec->rx_max_coalesced_frames_irq = vsi->work_limit; if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) - ec->rx_coalesce_usecs = 1; - else - ec->rx_coalesce_usecs = vsi->rx_itr_setting; + ec->use_adaptive_rx_coalesce = 1; if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) - ec->tx_coalesce_usecs = 1; - else - ec->tx_coalesce_usecs = vsi->tx_itr_setting; + ec->use_adaptive_tx_coalesce = 1; + + ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC; + ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC; return 0; } @@ -1035,37 +1034,27 @@ static int i40e_set_coalesce(struct net_device *netdev, if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) vsi->work_limit = ec->tx_max_coalesced_frames_irq; - switch (ec->rx_coalesce_usecs) { - case 0: - vsi->rx_itr_setting = 0; - break; - case 1: - vsi->rx_itr_setting = (I40E_ITR_DYNAMIC | - ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); - break; - default: - if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1))) - return -EINVAL; + if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && + (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) vsi->rx_itr_setting = ec->rx_coalesce_usecs; - break; - } + else + return -EINVAL; - switch (ec->tx_coalesce_usecs) { - case 0: - vsi->tx_itr_setting = 0; - break; - case 1: - vsi->tx_itr_setting = (I40E_ITR_DYNAMIC | - ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); - break; - default: - if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1))) - return -EINVAL; + if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && + (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) vsi->tx_itr_setting = ec->tx_coalesce_usecs; - break; - } + else + return -EINVAL; + + if (ec->use_adaptive_rx_coalesce) + vsi->rx_itr_setting |= I40E_ITR_DYNAMIC; + else + vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC; + + if (ec->use_adaptive_tx_coalesce) + vsi->tx_itr_setting |= I40E_ITR_DYNAMIC; + else + vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; vector = vsi->base_vector; for (i = 0; i < vsi->num_q_vectors; i++, vector++) { @@ -1142,8 +1131,7 @@ static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf, int cnt = 0; /* report total rule count */ - cmd->data = pf->hw.fdir_shared_filter_count + - pf->fdir_pf_filter_count; + cmd->data = i40e_get_fd_cnt_all(pf); hlist_for_each_entry_safe(rule, node2, &pf->fdir_filter_list, fdir_node) { @@ -1177,10 +1165,6 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, struct i40e_fdir_filter *rule = NULL; struct hlist_node *node2; - /* report total rule count */ - cmd->data = pf->hw.fdir_shared_filter_count + - pf->fdir_pf_filter_count; - hlist_for_each_entry_safe(rule, node2, &pf->fdir_filter_list, fdir_node) { if (fsp->location <= rule->fd_id) @@ -1231,6 +1215,8 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, break; case ETHTOOL_GRXCLSRLCNT: cmd->rule_cnt = pf->fdir_pf_active_filters; + /* report total rule count */ + cmd->data = i40e_get_fd_cnt_all(pf); ret = 0; break; case ETHTOOL_GRXCLSRULE: @@ -1299,16 +1285,12 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) case UDP_V4_FLOW: switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: - hena &= - ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); + hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | + ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): - hena |= - (((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); + hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | + ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4)); break; default: return -EINVAL; @@ -1317,16 +1299,12 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) case UDP_V6_FLOW: switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: - hena &= - ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); + hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | + ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): - hena |= - (((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); + hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | + ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6)); break; default: return -EINVAL; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e399f9b..cef3db4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -356,6 +356,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct( struct rtnl_link_stats64 *stats) { struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_ring *tx_ring, *rx_ring; struct i40e_vsi *vsi = np->vsi; struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi); int i; @@ -368,7 +369,6 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct( rcu_read_lock(); for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *tx_ring, *rx_ring; u64 bytes, packets; unsigned int start; @@ -2415,6 +2415,7 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) **/ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) { + struct i40e_ring *tx_ring, *rx_ring; u16 qoffset, qcount; int i, n; @@ -2428,8 +2429,8 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) qoffset = vsi->tc_config.tc_info[n].qoffset; qcount = vsi->tc_config.tc_info[n].qcount; for (i = qoffset; i < (qoffset + qcount); i++) { - struct i40e_ring *rx_ring = vsi->rx_rings[i]; - struct i40e_ring *tx_ring = vsi->tx_rings[i]; + rx_ring = vsi->rx_rings[i]; + tx_ring = vsi->tx_rings[i]; rx_ring->dcb_tc = n; tx_ring->dcb_tc = n; } @@ -2567,7 +2568,6 @@ static void i40e_enable_misc_int_causes(struct i40e_hw *hw) I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK | I40E_PFINT_ICR0_ENA_GPIO_MASK | I40E_PFINT_ICR0_ENA_TIMESYNC_MASK | - I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK | I40E_PFINT_ICR0_ENA_HMC_ERR_MASK | I40E_PFINT_ICR0_ENA_VFLR_MASK | I40E_PFINT_ICR0_ENA_ADMINQ_MASK; @@ -4333,7 +4333,7 @@ int i40e_vsi_open(struct i40e_vsi *vsi) dev_driver_string(&pf->pdev->dev)); err = i40e_vsi_request_irq(vsi, int_name); } else { - err = EINVAL; + err = -EINVAL; goto err_setup_rx; } @@ -4711,8 +4711,7 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) (pf->flags & I40E_FLAG_FD_SB_ENABLED)) return; fcnt_prog = i40e_get_current_fd_count(pf); - fcnt_avail = pf->hw.fdir_shared_filter_count + - pf->fdir_pf_filter_count; + fcnt_avail = i40e_get_fd_cnt_all(pf); if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) { if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) { @@ -5368,8 +5367,10 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) * because the reset will make them disappear. */ ret = i40e_pf_reset(hw); - if (ret) + if (ret) { dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret); + goto end_core_reset; + } pf->pfr_count++; if (test_bit(__I40E_DOWN, &pf->state)) @@ -5948,14 +5949,12 @@ static void i40e_vsi_clear_rings(struct i40e_vsi *vsi) **/ static int i40e_alloc_rings(struct i40e_vsi *vsi) { + struct i40e_ring *tx_ring, *rx_ring; struct i40e_pf *pf = vsi->back; int i; /* Set basic values in the rings to be used later during open() */ for (i = 0; i < vsi->alloc_queue_pairs; i++) { - struct i40e_ring *tx_ring; - struct i40e_ring *rx_ring; - /* allocate space for both Tx and Rx in one shot */ tx_ring = kzalloc(sizeof(struct i40e_ring) * 2, GFP_KERNEL); if (!tx_ring) @@ -6368,6 +6367,10 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_FLAG_MSIX_ENABLED | I40E_FLAG_RX_1BUF_ENABLED; + /* Set default ITR */ + pf->rx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_RX_DEF; + pf->tx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF; + /* Depending on PF configurations, it is possible that the RSS * maximum might end up larger than the available queues */ @@ -6720,7 +6723,7 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_set_features = i40e_set_features, .ndo_set_vf_mac = i40e_ndo_set_vf_mac, .ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan, - .ndo_set_vf_tx_rate = i40e_ndo_set_vf_bw, + .ndo_set_vf_rate = i40e_ndo_set_vf_bw, .ndo_get_vf_config = i40e_ndo_get_vf_config, .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, #ifdef CONFIG_I40E_VXLAN @@ -6832,7 +6835,6 @@ static void i40e_vsi_delete(struct i40e_vsi *vsi) return; i40e_aq_delete_element(&vsi->back->hw, vsi->seid, NULL); - return; } /** @@ -7572,8 +7574,6 @@ void i40e_veb_release(struct i40e_veb *veb) i40e_aq_delete_element(&pf->hw, veb->seid, NULL); i40e_veb_clear(veb); - - return; } /** @@ -8054,7 +8054,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) } pf->queues_left = queues_left; - return; } /** @@ -8364,6 +8363,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } +#ifdef CONFIG_PCI_IOV /* prep for VF support */ if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) && (pf->flags & I40E_FLAG_MSIX_ENABLED) && @@ -8386,6 +8386,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err); } } +#endif /* CONFIG_PCI_IOV */ pfs_found++; diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index d351832..57172f9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -74,6 +74,8 @@ i40e_status i40e_aq_set_phy_reset(struct i40e_hw *hw, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, u16 vsi_id, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_link_restart_an(struct i40e_hw *hw, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 1fedc7a..101f439 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -48,7 +48,6 @@ I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) #define I40E_PRTTSYN_CTL1_TSYNTYPE_V2 (0x2 << \ I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) -#define I40E_PTP_TX_TIMEOUT (HZ * 15) /** * i40e_ptp_read - Read the PHC time from the device diff --git a/drivers/net/ethernet/intel/i40e/i40e_register.h b/drivers/net/ethernet/intel/i40e/i40e_register.h index 1d40f42..25c9286 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_register.h +++ b/drivers/net/ethernet/intel/i40e/i40e_register.h @@ -1340,8 +1340,6 @@ #define I40E_PFINT_ICR0_GPIO_MASK (0x1 << I40E_PFINT_ICR0_GPIO_SHIFT) #define I40E_PFINT_ICR0_TIMESYNC_SHIFT 23 #define I40E_PFINT_ICR0_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_TIMESYNC_SHIFT) -#define I40E_PFINT_ICR0_STORM_DETECT_SHIFT 24 -#define I40E_PFINT_ICR0_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_STORM_DETECT_SHIFT) #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT 25 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT) #define I40E_PFINT_ICR0_HMC_ERR_SHIFT 26 @@ -1367,8 +1365,6 @@ #define I40E_PFINT_ICR0_ENA_GPIO_MASK (0x1 << I40E_PFINT_ICR0_ENA_GPIO_SHIFT) #define I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT 23 #define I40E_PFINT_ICR0_ENA_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT) -#define I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT 24 -#define I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT) #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT 25 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT) #define I40E_PFINT_ICR0_ENA_HMC_ERR_SHIFT 26 diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index ece7ae9..5a22187 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -24,6 +24,7 @@ * ******************************************************************************/ +#include <linux/prefetch.h> #include "i40e.h" #include "i40e_prototype.h" @@ -183,7 +184,6 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, struct iphdr *ip; bool err = false; int ret; - int i; static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -199,21 +199,17 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, ip->saddr = fd_data->src_ip[0]; udp->source = fd_data->src_port; - for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP; - i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) { - fd_data->pctype = i; - ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); - - if (ret) { - dev_info(&pf->pdev->dev, - "Filter command send failed for PCTYPE %d (ret = %d)\n", - fd_data->pctype, ret); - err = true; - } else { - dev_info(&pf->pdev->dev, - "Filter OK for PCTYPE %d (ret = %d)\n", - fd_data->pctype, ret); - } + fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; + ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); + if (ret) { + dev_info(&pf->pdev->dev, + "Filter command send failed for PCTYPE %d (ret = %d)\n", + fd_data->pctype, ret); + err = true; + } else { + dev_info(&pf->pdev->dev, + "Filter OK for PCTYPE %d (ret = %d)\n", + fd_data->pctype, ret); } return err ? -EOPNOTSUPP : 0; @@ -262,7 +258,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, } } - fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN; + fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); if (ret) { @@ -455,9 +451,7 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, /* filter programming failed most likely due to table full */ fcnt_prog = i40e_get_current_fd_count(pf); - fcnt_avail = pf->hw.fdir_shared_filter_count + - pf->fdir_pf_filter_count; - + fcnt_avail = i40e_get_fd_cnt_all(pf); /* If ATR is running fcnt_prog can quickly change, * if we are very close to full, it makes sense to disable * FD ATR/SB and then re-enable it when there is room. diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index d534969..d5e3f54 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -27,7 +27,7 @@ #ifndef _I40E_TXRX_H_ #define _I40E_TXRX_H_ -/* Interrupt Throttling and Rate Limiting (storm control) Goodies */ +/* Interrupt Throttling and Rate Limiting Goodies */ #define I40E_MAX_ITR 0x0FF0 /* reg uses 2 usec resolution */ #define I40E_MIN_ITR 0x0004 /* reg uses 2 usec resolution */ @@ -69,16 +69,11 @@ enum i40e_dyn_idx_t { /* Supported RSS offloads */ #define I40E_DEFAULT_RSS_HENA ( \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | \ ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | \ diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index c4df8ba..71f9718 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -866,18 +866,14 @@ struct i40e_filter_program_desc { /* Packet Classifier Types for filters */ enum i40e_filter_pctype { - /* Note: Values 0-28 are reserved for future use */ - I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP = 29, - I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP = 30, + /* Note: Values 0-30 are reserved for future use */ I40E_FILTER_PCTYPE_NONF_IPV4_UDP = 31, - I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN = 32, + /* Note: Value 32 is reserved for future use */ I40E_FILTER_PCTYPE_NONF_IPV4_TCP = 33, I40E_FILTER_PCTYPE_NONF_IPV4_SCTP = 34, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER = 35, I40E_FILTER_PCTYPE_FRAG_IPV4 = 36, - /* Note: Values 37-38 are reserved for future use */ - I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP = 39, - I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP = 40, + /* Note: Values 37-40 are reserved for future use */ I40E_FILTER_PCTYPE_NONF_IPV6_UDP = 41, I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN = 42, I40E_FILTER_PCTYPE_NONF_IPV6_TCP = 43, @@ -1020,8 +1016,8 @@ struct i40e_hw_port_stats { u64 mac_short_packet_dropped; /* mspdc */ u64 checksum_error; /* xec */ /* EEE LPI */ - bool tx_lpi_status; - bool rx_lpi_status; + u32 tx_lpi_status; + u32 rx_lpi_status; u64 tx_lpi_count; /* etlpic */ u64 rx_lpi_count; /* erlpic */ }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 82e7abf..4e7634c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -354,6 +354,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_idx, rx_ctx.tphhead_ena = 1; rx_ctx.lrxqthresh = 2; rx_ctx.crcstrip = 1; + rx_ctx.prefena = 1; /* clear the context in the HMC */ ret = i40e_clear_lan_rx_queue_context(hw, pf_queue_id); @@ -842,6 +843,10 @@ void i40e_free_vfs(struct i40e_pf *pf) kfree(pf->vf); pf->vf = NULL; + /* This check is for when the driver is unloaded while VFs are + * assigned. Setting the number of VFs to 0 through sysfs is caught + * before this function ever gets called. + */ if (!i40e_vfs_are_assigned(pf)) { pci_disable_sriov(pf->pdev); /* Acknowledge VFLR for all VFS. Without this, VFs will fail to @@ -978,7 +983,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) if (num_vfs) return i40e_pci_sriov_enable(pdev, num_vfs); - i40e_free_vfs(pf); + if (!i40e_vfs_are_assigned(pf)) { + i40e_free_vfs(pf); + } else { + dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n"); + return -EINVAL; + } return 0; } @@ -2188,6 +2198,8 @@ error_pvid: return ret; } +#define I40E_BW_CREDIT_DIVISOR 50 /* 50Mbps per BW credit */ +#define I40E_MAX_BW_INACTIVE_ACCUM 4 /* device can accumulate 4 credits max */ /** * i40e_ndo_set_vf_bw * @netdev: network interface device structure @@ -2196,7 +2208,8 @@ error_pvid: * * configure vf tx rate **/ -int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate) +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; @@ -2212,6 +2225,12 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate) goto error; } + if (min_tx_rate) { + dev_err(&pf->pdev->dev, "Invalid min tx rate (%d) (greater than 0) specified for vf %d.\n", + min_tx_rate, vf_id); + return -EINVAL; + } + vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_index]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { @@ -2234,23 +2253,29 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate) break; } - if (tx_rate > speed) { - dev_err(&pf->pdev->dev, "Invalid tx rate %d specified for vf %d.", - tx_rate, vf->vf_id); + if (max_tx_rate > speed) { + dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for vf %d.", + max_tx_rate, vf->vf_id); ret = -EINVAL; goto error; } + if ((max_tx_rate < 50) && (max_tx_rate > 0)) { + dev_warn(&pf->pdev->dev, "Setting max Tx rate to minimum usable value of 50Mbps.\n"); + max_tx_rate = 50; + } + /* Tx rate credits are in values of 50Mbps, 0 is disabled*/ - ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, tx_rate / 50, 0, - NULL); + ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, + max_tx_rate / I40E_BW_CREDIT_DIVISOR, + I40E_MAX_BW_INACTIVE_ACCUM, NULL); if (ret) { - dev_err(&pf->pdev->dev, "Unable to set tx rate, error code %d.\n", + dev_err(&pf->pdev->dev, "Unable to set max tx rate, error code %d.\n", ret); ret = -EIO; goto error; } - vf->tx_rate = tx_rate; + vf->tx_rate = max_tx_rate; error: return ret; } @@ -2292,7 +2317,8 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN); - ivi->tx_rate = vf->tx_rate; + ivi->max_tx_rate = vf->tx_rate; + ivi->min_tx_rate = 0; ivi->vlan = le16_to_cpu(vsi->info.pvid) & I40E_VLAN_MASK; ivi->qos = (le16_to_cpu(vsi->info.pvid) & I40E_PRIORITY_MASK) >> I40E_VLAN_PRIORITY_SHIFT; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index ba3d1f8..5a559be 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -116,7 +116,8 @@ void i40e_vc_notify_vf_reset(struct i40e_vf *vf); int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac); int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos); -int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate); +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate); int i40e_ndo_get_vf_config(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link); diff --git a/drivers/net/ethernet/intel/i40evf/Makefile b/drivers/net/ethernet/intel/i40evf/Makefile index e09be37..3a42383 100644 --- a/drivers/net/ethernet/intel/i40evf/Makefile +++ b/drivers/net/ethernet/intel/i40evf/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel Ethernet Controller XL710 Family Linux Virtual Function Driver -# Copyright(c) 2013 Intel Corporation. +# Copyright(c) 2013 - 2014 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <http://www.gnu.org/licenses/>. +# # The full GNU General Public License is included in this distribution in # the file called "COPYING". # diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index c79df25..68b4aac 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index 7d24be5..e3472c6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index 6e61766..89d9209 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -676,7 +679,6 @@ struct i40e_aqc_add_get_update_vsi { #define I40E_AQ_VSI_TYPE_PF 0x2 #define I40E_AQ_VSI_TYPE_EMP_MNG 0x3 #define I40E_AQ_VSI_FLAG_CASCADED_PV 0x4 -#define I40E_AQ_VSI_FLAG_CLOUD_VSI 0x8 __le32 addr_high; __le32 addr_low; }; @@ -1038,7 +1040,9 @@ struct i40e_aqc_set_vsi_promiscuous_modes { #define I40E_AQC_SET_VSI_PROMISC_VLAN 0x10 __le16 seid; #define I40E_AQC_VSI_PROM_CMD_SEID_MASK 0x3FF - u8 reserved[10]; + __le16 vlan_tag; +#define I40E_AQC_SET_VSI_VLAN_VALID 0x8000 + u8 reserved[8]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_set_vsi_promiscuous_modes); @@ -1931,19 +1935,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); /* Add Udp Tunnel command and completion (direct 0x0B00) */ struct i40e_aqc_add_udp_tunnel { __le16 udp_port; - u8 header_len; /* in DWords, 1 to 15 */ + u8 reserved0[3]; u8 protocol_type; -#define I40E_AQC_TUNNEL_TYPE_TEREDO 0x0 -#define I40E_AQC_TUNNEL_TYPE_VXLAN 0x2 -#define I40E_AQC_TUNNEL_TYPE_NGE 0x3 - u8 variable_udp_length; -#define I40E_AQC_TUNNEL_FIXED_UDP_LENGTH 0x0 -#define I40E_AQC_TUNNEL_VARIABLE_UDP_LENGTH 0x1 - u8 udp_key_index; -#define I40E_AQC_TUNNEL_KEY_INDEX_VXLAN 0x0 -#define I40E_AQC_TUNNEL_KEY_INDEX_NGE 0x1 -#define I40E_AQC_TUNNEL_KEY_INDEX_PROPRIETARY_UDP 0x2 - u8 reserved[10]; +#define I40E_AQC_TUNNEL_TYPE_VXLAN 0x00 +#define I40E_AQC_TUNNEL_TYPE_NGE 0x01 +#define I40E_AQC_TUNNEL_TYPE_TEREDO 0x10 + u8 reserved1[10]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_add_udp_tunnel); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_alloc.h b/drivers/net/ethernet/intel/i40evf/i40e_alloc.h index d8654fb..8e6a6dd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_alloc.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_alloc.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index ae08437..ea0f200 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -130,7 +133,11 @@ void i40evf_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, **/ bool i40evf_check_asq_alive(struct i40e_hw *hw) { - return !!(rd32(hw, hw->aq.asq.len) & I40E_PF_ATQLEN_ATQENABLE_MASK); + if (hw->aq.asq.len) + return !!(rd32(hw, hw->aq.asq.len) & + I40E_PF_ATQLEN_ATQENABLE_MASK); + else + return false; } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40e_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_hmc.h index cb97b3e..9d90651 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_hmc.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_hmc.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h index 775fcb2..d6f7622 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h index 622f373..21a91b1 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h index 97ab8c2..849edcc 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_register.h b/drivers/net/ethernet/intel/i40evf/i40e_register.h index 30af953..7977205 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_register.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_register.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -1337,8 +1340,6 @@ #define I40E_PFINT_ICR0_GPIO_MASK (0x1 << I40E_PFINT_ICR0_GPIO_SHIFT) #define I40E_PFINT_ICR0_TIMESYNC_SHIFT 23 #define I40E_PFINT_ICR0_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_TIMESYNC_SHIFT) -#define I40E_PFINT_ICR0_STORM_DETECT_SHIFT 24 -#define I40E_PFINT_ICR0_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_STORM_DETECT_SHIFT) #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT 25 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT) #define I40E_PFINT_ICR0_HMC_ERR_SHIFT 26 @@ -1364,8 +1365,6 @@ #define I40E_PFINT_ICR0_ENA_GPIO_MASK (0x1 << I40E_PFINT_ICR0_ENA_GPIO_SHIFT) #define I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT 23 #define I40E_PFINT_ICR0_ENA_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT) -#define I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT 24 -#define I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT) #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT 25 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT) #define I40E_PFINT_ICR0_ENA_HMC_ERR_SHIFT 26 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_status.h b/drivers/net/ethernet/intel/i40evf/i40e_status.h index 7c08cc2..7fa7a41 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_status.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_status.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index b9f50f4..82d6844 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 10bf49e..af639d8 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -24,7 +27,7 @@ #ifndef _I40E_TXRX_H_ #define _I40E_TXRX_H_ -/* Interrupt Throttling and Rate Limiting (storm control) Goodies */ +/* Interrupt Throttling and Rate Limiting Goodies */ #define I40E_MAX_ITR 0x0FF0 /* reg uses 2 usec resolution */ #define I40E_MIN_ITR 0x0004 /* reg uses 2 usec resolution */ @@ -66,16 +69,11 @@ enum i40e_dyn_idx_t { /* Supported RSS offloads */ #define I40E_DEFAULT_RSS_HENA ( \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | \ ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | \ ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | \ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 51a6dee..67082f7 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -101,15 +104,6 @@ enum i40e_debug_mask { I40E_DEBUG_ALL = 0xFFFFFFFF }; -/* PCI Bus Info */ -#define I40E_PCI_LINK_WIDTH_1 0x10 -#define I40E_PCI_LINK_WIDTH_2 0x20 -#define I40E_PCI_LINK_WIDTH_4 0x40 -#define I40E_PCI_LINK_WIDTH_8 0x80 -#define I40E_PCI_LINK_SPEED_2500 0x1 -#define I40E_PCI_LINK_SPEED_5000 0x2 -#define I40E_PCI_LINK_SPEED_8000 0x3 - /* These are structs for managing the hardware information and the operations. * The structures of function pointers are filled out at init time when we * know for sure exactly which hardware we're working with. This gives us the @@ -872,18 +866,14 @@ struct i40e_filter_program_desc { /* Packet Classifier Types for filters */ enum i40e_filter_pctype { - /* Note: Values 0-28 are reserved for future use */ - I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP = 29, - I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP = 30, + /* Note: Values 0-30 are reserved for future use */ I40E_FILTER_PCTYPE_NONF_IPV4_UDP = 31, - I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN = 32, + /* Note: Value 32 is reserved for future use */ I40E_FILTER_PCTYPE_NONF_IPV4_TCP = 33, I40E_FILTER_PCTYPE_NONF_IPV4_SCTP = 34, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER = 35, I40E_FILTER_PCTYPE_FRAG_IPV4 = 36, - /* Note: Values 37-38 are reserved for future use */ - I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP = 39, - I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP = 40, + /* Note: Values 37-40 are reserved for future use */ I40E_FILTER_PCTYPE_NONF_IPV6_UDP = 41, I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN = 42, I40E_FILTER_PCTYPE_NONF_IPV6_TCP = 43, @@ -1026,8 +1016,8 @@ struct i40e_hw_port_stats { u64 mac_short_packet_dropped; /* mspdc */ u64 checksum_error; /* xec */ /* EEE LPI */ - bool tx_lpi_status; - bool rx_lpi_status; + u32 tx_lpi_status; + u32 rx_lpi_status; u64 tx_lpi_count; /* etlpic */ u64 rx_lpi_count; /* erlpic */ }; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index ccf45d0..1ef5b31 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -1,7 +1,7 @@ /******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver - * Copyright(c) 2013 Intel Corporation. + * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 807807d..2913bc3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index dbc8986..9cd381a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -56,10 +59,12 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = { }; #define I40EVF_GLOBAL_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats) -#define I40EVF_QUEUE_STATS_LEN \ +#define I40EVF_QUEUE_STATS_LEN(_dev) \ (((struct i40evf_adapter *) \ - netdev_priv(netdev))->vsi_res->num_queue_pairs * 4) -#define I40EVF_STATS_LEN (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN) + netdev_priv(_dev))->vsi_res->num_queue_pairs \ + * 2 * (sizeof(struct i40e_queue_stats) / sizeof(u64))) +#define I40EVF_STATS_LEN(_dev) \ + (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev)) /** * i40evf_get_settings - Get Link Speed and Duplex settings @@ -75,7 +80,7 @@ static int i40evf_get_settings(struct net_device *netdev, /* In the future the VF will be able to query the PF for * some information - for now use a dummy value */ - ecmd->supported = SUPPORTED_10000baseT_Full; + ecmd->supported = 0; ecmd->autoneg = AUTONEG_DISABLE; ecmd->transceiver = XCVR_DUMMY1; ecmd->port = PORT_NONE; @@ -94,9 +99,9 @@ static int i40evf_get_settings(struct net_device *netdev, static int i40evf_get_sset_count(struct net_device *netdev, int sset) { if (sset == ETH_SS_STATS) - return I40EVF_STATS_LEN; + return I40EVF_STATS_LEN(netdev); else - return -ENOTSUPP; + return -EINVAL; } /** @@ -290,14 +295,13 @@ static int i40evf_get_coalesce(struct net_device *netdev, ec->rx_max_coalesced_frames = vsi->work_limit; if (ITR_IS_DYNAMIC(vsi->rx_itr_setting)) - ec->rx_coalesce_usecs = 1; - else - ec->rx_coalesce_usecs = vsi->rx_itr_setting; + ec->use_adaptive_rx_coalesce = 1; if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) - ec->tx_coalesce_usecs = 1; - else - ec->tx_coalesce_usecs = vsi->tx_itr_setting; + ec->use_adaptive_tx_coalesce = 1; + + ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC; + ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC; return 0; } @@ -318,40 +322,34 @@ static int i40evf_set_coalesce(struct net_device *netdev, struct i40e_q_vector *q_vector; int i; - if (ec->tx_max_coalesced_frames || ec->rx_max_coalesced_frames) - vsi->work_limit = ec->tx_max_coalesced_frames; + if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) + vsi->work_limit = ec->tx_max_coalesced_frames_irq; - switch (ec->rx_coalesce_usecs) { - case 0: - vsi->rx_itr_setting = 0; - break; - case 1: - vsi->rx_itr_setting = (I40E_ITR_DYNAMIC - | ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); - break; - default: - if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1))) - return -EINVAL; + if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && + (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) vsi->rx_itr_setting = ec->rx_coalesce_usecs; - break; - } - switch (ec->tx_coalesce_usecs) { - case 0: - vsi->tx_itr_setting = 0; - break; - case 1: - vsi->tx_itr_setting = (I40E_ITR_DYNAMIC - | ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); - break; - default: - if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1))) - return -EINVAL; + else + return -EINVAL; + + if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && + (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) vsi->tx_itr_setting = ec->tx_coalesce_usecs; - break; - } + else if (ec->use_adaptive_tx_coalesce) + vsi->tx_itr_setting = (I40E_ITR_DYNAMIC | + ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); + else + return -EINVAL; + + if (ec->use_adaptive_rx_coalesce) + vsi->rx_itr_setting |= I40E_ITR_DYNAMIC; + else + vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC; + + if (ec->use_adaptive_tx_coalesce) + vsi->tx_itr_setting |= I40E_ITR_DYNAMIC; + else + vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) { q_vector = adapter->q_vector[i]; @@ -678,7 +676,7 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, return 0; } -static struct ethtool_ops i40evf_ethtool_ops = { +static const struct ethtool_ops i40evf_ethtool_ops = { .get_settings = i40evf_get_settings, .get_drvinfo = i40evf_get_drvinfo, .get_link = ethtool_op_get_link, diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 6edd581..632c2b3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -690,7 +693,6 @@ static void i40evf_del_vlan(struct i40evf_adapter *adapter, u16 vlan) f->remove = true; adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER; } - return; } /** @@ -843,7 +845,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev) list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) { bool found = false; - if (f->macaddr[0] & 0x01) { + if (is_multicast_ether_addr(f->macaddr)) { netdev_for_each_mc_addr(mca, netdev) { if (ether_addr_equal(mca->addr, f->macaddr)) { found = true; @@ -1029,30 +1031,21 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors) * Right now, we simply care about how many we'll get; we'll * set them up later while requesting irq's. */ - while (vectors >= vector_threshold) { - err = pci_enable_msix(adapter->pdev, adapter->msix_entries, - vectors); - if (!err) /* Success in acquiring all requested vectors. */ - break; - else if (err < 0) - vectors = 0; /* Nasty failure, quit now */ - else /* err == number of vectors we should try again with */ - vectors = err; - } - - if (vectors < vector_threshold) { + err = pci_enable_msix_range(adapter->pdev, adapter->msix_entries, + vector_threshold, vectors); + if (err < 0) { dev_err(&adapter->pdev->dev, "Unable to allocate MSI-X interrupts.\n"); kfree(adapter->msix_entries); adapter->msix_entries = NULL; - err = -EIO; - } else { - /* Adjust for only the vectors we'll use, which is minimum - * of max_msix_q_vectors + NONQ_VECS, or the number of - * vectors we were allocated. - */ - adapter->num_msix_vectors = vectors; + return err; } - return err; + + /* Adjust for only the vectors we'll use, which is minimum + * of max_msix_q_vectors + NONQ_VECS, or the number of + * vectors we were allocated. + */ + adapter->num_msix_vectors = err; + return 0; } /** @@ -1238,8 +1231,6 @@ void i40evf_reset_interrupt_capability(struct i40evf_adapter *adapter) pci_disable_msix(adapter->pdev); kfree(adapter->msix_entries); adapter->msix_entries = NULL; - - return; } /** @@ -1853,8 +1844,6 @@ void i40evf_reinit_locked(struct i40evf_adapter *adapter) WARN_ON(in_interrupt()); - adapter->state = __I40EVF_RESETTING; - i40evf_down(adapter); /* allocate transmit descriptors */ @@ -2119,8 +2108,10 @@ static void i40evf_init_task(struct work_struct *work) adapter->vsi.back = adapter; adapter->vsi.base_vector = 1; adapter->vsi.work_limit = I40E_DEFAULT_IRQ_WORK; - adapter->vsi.rx_itr_setting = I40E_ITR_DYNAMIC; - adapter->vsi.tx_itr_setting = I40E_ITR_DYNAMIC; + adapter->vsi.rx_itr_setting = (I40E_ITR_DYNAMIC | + ITR_REG_TO_USEC(I40E_ITR_RX_DEF)); + adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC | + ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); adapter->vsi.netdev = adapter->netdev; if (!adapter->netdev_registered) { @@ -2162,7 +2153,6 @@ err: return; /* do not reschedule */ } schedule_delayed_work(&adapter->init_task, HZ * 3); - return; } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index e294f01..7f80bb4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -12,6 +12,9 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c index 92bcdbe..e8280d0 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -798,5 +798,4 @@ etrack_id: fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT) | eeprom_verl; } - return; } diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 424f16c..c1bb64d 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -2204,16 +2204,10 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw) void igb_power_up_phy_copper(struct e1000_hw *hw) { u16 mii_reg = 0; - u16 power_reg = 0; /* The PHY will retain its settings across a power down/up cycle */ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); mii_reg &= ~MII_CR_POWER_DOWN; - if (hw->phy.type == e1000_phy_i210) { - hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg); - power_reg &= ~GS40G_CS_POWER_DOWN; - hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg); - } hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); } @@ -2227,18 +2221,10 @@ void igb_power_up_phy_copper(struct e1000_hw *hw) void igb_power_down_phy_copper(struct e1000_hw *hw) { u16 mii_reg = 0; - u16 power_reg = 0; /* The PHY will retain its settings across a power down/up cycle */ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); mii_reg |= MII_CR_POWER_DOWN; - - /* i210 Phy requires an additional bit for power up/down */ - if (hw->phy.type == e1000_phy_i210) { - hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg); - power_reg |= GS40G_CS_POWER_DOWN; - hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg); - } hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); usleep_range(1000, 2000); } diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index fe921e2..7af4ffa 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -151,7 +151,6 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw); #define GS40G_MAC_LB 0x4140 #define GS40G_MAC_SPEED_1G 0X0006 #define GS40G_COPPER_SPEC 0x0010 -#define GS40G_CS_POWER_DOWN 0x0002 #define GS40G_LINE_LB 0x4000 /* SFP modules ID memory locations */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index bfcda8a..ea2868b 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -169,7 +169,7 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter); static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac); static int igb_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos); -static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate); +static int igb_ndo_set_vf_bw(struct net_device *, int, int, int); static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); static int igb_ndo_get_vf_config(struct net_device *netdev, int vf, @@ -2084,7 +2084,7 @@ static const struct net_device_ops igb_netdev_ops = { .ndo_vlan_rx_kill_vid = igb_vlan_rx_kill_vid, .ndo_set_vf_mac = igb_ndo_set_vf_mac, .ndo_set_vf_vlan = igb_ndo_set_vf_vlan, - .ndo_set_vf_tx_rate = igb_ndo_set_vf_bw, + .ndo_set_vf_rate = igb_ndo_set_vf_bw, .ndo_set_vf_spoofchk = igb_ndo_set_vf_spoofchk, .ndo_get_vf_config = igb_ndo_get_vf_config, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -2139,7 +2139,6 @@ void igb_set_fw_version(struct igb_adapter *adapter) } break; } - return; } /** @@ -7879,7 +7878,8 @@ static void igb_check_vf_rate_limit(struct igb_adapter *adapter) } } -static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) +static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, + int min_tx_rate, int max_tx_rate) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -7888,15 +7888,19 @@ static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) if (hw->mac.type != e1000_82576) return -EOPNOTSUPP; + if (min_tx_rate) + return -EINVAL; + actual_link_speed = igb_link_mbps(adapter->link_speed); if ((vf >= adapter->vfs_allocated_count) || (!(rd32(E1000_STATUS) & E1000_STATUS_LU)) || - (tx_rate < 0) || (tx_rate > actual_link_speed)) + (max_tx_rate < 0) || + (max_tx_rate > actual_link_speed)) return -EINVAL; adapter->vf_rate_link_speed = actual_link_speed; - adapter->vf_data[vf].tx_rate = (u16)tx_rate; - igb_set_vf_rate_limit(hw, vf, tx_rate, actual_link_speed); + adapter->vf_data[vf].tx_rate = (u16)max_tx_rate; + igb_set_vf_rate_limit(hw, vf, max_tx_rate, actual_link_speed); return 0; } @@ -7936,7 +7940,8 @@ static int igb_ndo_get_vf_config(struct net_device *netdev, return -EINVAL; ivi->vf = vf; memcpy(&ivi->mac, adapter->vf_data[vf].vf_mac_addresses, ETH_ALEN); - ivi->tx_rate = adapter->vf_data[vf].tx_rate; + ivi->max_tx_rate = adapter->vf_data[vf].tx_rate; + ivi->min_tx_rate = 0; ivi->vlan = adapter->vf_data[vf].pf_vlan; ivi->qos = adapter->vf_data[vf].pf_qos; ivi->spoofchk = adapter->vf_data[vf].spoofchk_enabled; diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index f58170b..7d4e855 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -119,7 +119,6 @@ static int igbvf_set_settings(struct net_device *netdev, static void igbvf_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { - return; } static int igbvf_set_pauseparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index c688c8a..ac9f214 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -362,7 +362,7 @@ struct ixgbe_ring_container { for (pos = (head).ring; pos != NULL; pos = pos->next) #define MAX_RX_PACKET_BUFFERS ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) \ - ? 8 : 1) + ? 8 : 1) #define MAX_TX_PACKET_BUFFERS MAX_RX_PACKET_BUFFERS /* MAX_Q_VECTORS of these are allocated, @@ -957,6 +957,7 @@ static inline struct netdev_queue *txring_txq(const struct ixgbe_ring *ring) } void ixgbe_ptp_init(struct ixgbe_adapter *adapter); +void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter); void ixgbe_ptp_stop(struct ixgbe_adapter *adapter); void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter); void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index 1c52e47..1560933 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -41,10 +41,10 @@ #define IXGBE_82598_RX_PB_SIZE 512 static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, - u8 *eeprom_data); + u8 *eeprom_data); /** * ixgbe_set_pcie_completion_timeout - set pci-e completion timeout @@ -140,7 +140,7 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw) phy->ops.setup_link = &ixgbe_setup_phy_link_tnx; phy->ops.check_link = &ixgbe_check_phy_link_tnx; phy->ops.get_firmware_version = - &ixgbe_get_phy_firmware_version_tnx; + &ixgbe_get_phy_firmware_version_tnx; break; case ixgbe_phy_nl: phy->ops.reset = &ixgbe_reset_phy_nl; @@ -156,8 +156,8 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw) /* Check to see if SFP+ module is supported */ ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, - &list_offset, - &data_offset); + &list_offset, + &data_offset); if (ret_val != 0) { ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED; goto out; @@ -219,8 +219,8 @@ static s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw) * Determines the link capabilities by reading the AUTOC register. **/ static s32 ixgbe_get_link_capabilities_82598(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, - bool *autoneg) + ixgbe_link_speed *speed, + bool *autoneg) { s32 status = 0; u32 autoc = 0; @@ -473,7 +473,7 @@ out: * Restarts the link. Performs autonegotiation if needed. **/ static s32 ixgbe_start_mac_link_82598(struct ixgbe_hw *hw, - bool autoneg_wait_to_complete) + bool autoneg_wait_to_complete) { u32 autoc_reg; u32 links_reg; @@ -555,8 +555,8 @@ static s32 ixgbe_validate_link_ready(struct ixgbe_hw *hw) * Reads the links register to determine if link is up and the current speed **/ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, bool *link_up, - bool link_up_wait_to_complete) + ixgbe_link_speed *speed, bool *link_up, + bool link_up_wait_to_complete) { u32 links_reg; u32 i; @@ -572,7 +572,7 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, hw->phy.ops.read_reg(hw, 0xC79F, MDIO_MMD_PMAPMD, &link_reg); hw->phy.ops.read_reg(hw, 0xC79F, MDIO_MMD_PMAPMD, &link_reg); hw->phy.ops.read_reg(hw, 0xC00C, MDIO_MMD_PMAPMD, - &adapt_comp_reg); + &adapt_comp_reg); if (link_up_wait_to_complete) { for (i = 0; i < IXGBE_LINK_UP_TIME; i++) { if ((link_reg & 1) && @@ -584,11 +584,11 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, } msleep(100); hw->phy.ops.read_reg(hw, 0xC79F, - MDIO_MMD_PMAPMD, - &link_reg); + MDIO_MMD_PMAPMD, + &link_reg); hw->phy.ops.read_reg(hw, 0xC00C, - MDIO_MMD_PMAPMD, - &adapt_comp_reg); + MDIO_MMD_PMAPMD, + &adapt_comp_reg); } } else { if ((link_reg & 1) && ((adapt_comp_reg & 1) == 0)) @@ -661,7 +661,7 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw, /* Set KX4/KX support according to speed requested */ else if (link_mode == IXGBE_AUTOC_LMS_KX4_AN || - link_mode == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) { + link_mode == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) { autoc &= ~IXGBE_AUTOC_KX4_KX_SUPP_MASK; if (speed & IXGBE_LINK_SPEED_10GB_FULL) autoc |= IXGBE_AUTOC_KX4_SUPP; @@ -694,14 +694,14 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw, * Sets the link speed in the AUTOC register in the MAC and restarts link. **/ static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) { s32 status; /* Setup the PHY according to input speed */ status = hw->phy.ops.setup_link_speed(hw, speed, - autoneg_wait_to_complete); + autoneg_wait_to_complete); /* Set up MAC */ ixgbe_start_mac_link_82598(hw, autoneg_wait_to_complete); @@ -740,28 +740,28 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw) if (analog_val & IXGBE_ATLAS_PDN_TX_REG_EN) { /* Enable Tx Atlas so packets can be transmitted again */ hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_LPBK, - &analog_val); + &analog_val); analog_val &= ~IXGBE_ATLAS_PDN_TX_REG_EN; hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_LPBK, - analog_val); + analog_val); hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_10G, - &analog_val); + &analog_val); analog_val &= ~IXGBE_ATLAS_PDN_TX_10G_QL_ALL; hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_10G, - analog_val); + analog_val); hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_1G, - &analog_val); + &analog_val); analog_val &= ~IXGBE_ATLAS_PDN_TX_1G_QL_ALL; hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_1G, - analog_val); + analog_val); hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_AN, - &analog_val); + &analog_val); analog_val &= ~IXGBE_ATLAS_PDN_TX_AN_QL_ALL; hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_AN, - analog_val); + analog_val); } /* Reset PHY */ @@ -960,7 +960,7 @@ static s32 ixgbe_clear_vfta_82598(struct ixgbe_hw *hw) for (vlanbyte = 0; vlanbyte < 4; vlanbyte++) for (offset = 0; offset < hw->mac.vft_size; offset++) IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(vlanbyte, offset), - 0); + 0); return 0; } @@ -978,7 +978,7 @@ static s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val) u32 atlas_ctl; IXGBE_WRITE_REG(hw, IXGBE_ATLASCTL, - IXGBE_ATLASCTL_WRITE_CMD | (reg << 8)); + IXGBE_ATLASCTL_WRITE_CMD | (reg << 8)); IXGBE_WRITE_FLUSH(hw); udelay(10); atlas_ctl = IXGBE_READ_REG(hw, IXGBE_ATLASCTL); @@ -1278,8 +1278,6 @@ static void ixgbe_set_rxpba_82598(struct ixgbe_hw *hw, int num_pb, /* Setup Tx packet buffer sizes */ for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), IXGBE_TXPBSIZE_40KB); - - return; } static struct ixgbe_mac_operations mac_ops_82598 = { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index f32b3dd..bc7c924 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -48,17 +48,17 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw); static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, bool autoneg_wait_to_complete); static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); static s32 ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw); static s32 ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data); @@ -96,9 +96,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw) if ((mac->ops.get_media_type(hw) == ixgbe_media_type_fiber) && !ixgbe_mng_enabled(hw)) { mac->ops.disable_tx_laser = - &ixgbe_disable_tx_laser_multispeed_fiber; + &ixgbe_disable_tx_laser_multispeed_fiber; mac->ops.enable_tx_laser = - &ixgbe_enable_tx_laser_multispeed_fiber; + &ixgbe_enable_tx_laser_multispeed_fiber; mac->ops.flap_tx_laser = &ixgbe_flap_tx_laser_multispeed_fiber; } else { mac->ops.disable_tx_laser = NULL; @@ -132,13 +132,13 @@ static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw) hw->phy.ops.reset = NULL; ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset, - &data_offset); + &data_offset); if (ret_val != 0) goto setup_sfp_out; /* PHY config will finish before releasing the semaphore */ ret_val = hw->mac.ops.acquire_swfw_sync(hw, - IXGBE_GSSR_MAC_CSR_SM); + IXGBE_GSSR_MAC_CSR_SM); if (ret_val != 0) { ret_val = IXGBE_ERR_SWFW_SYNC; goto setup_sfp_out; @@ -334,7 +334,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw) phy->ops.check_link = &ixgbe_check_phy_link_tnx; phy->ops.setup_link = &ixgbe_setup_phy_link_tnx; phy->ops.get_firmware_version = - &ixgbe_get_phy_firmware_version_tnx; + &ixgbe_get_phy_firmware_version_tnx; break; default: break; @@ -352,7 +352,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw) * Determines the link capabilities by reading the AUTOC register. **/ static s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, + ixgbe_link_speed *speed, bool *autoneg) { s32 status = 0; @@ -543,7 +543,7 @@ static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw) * Restarts the link. Performs autonegotiation if needed. **/ static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, - bool autoneg_wait_to_complete) + bool autoneg_wait_to_complete) { u32 autoc_reg; u32 links_reg; @@ -672,8 +672,8 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw) * Set the link speed in the AUTOC register and restarts link. **/ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) { s32 status = 0; ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; @@ -820,8 +820,8 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, */ if (speedcnt > 1) status = ixgbe_setup_mac_link_multispeed_fiber(hw, - highest_link_speed, - autoneg_wait_to_complete); + highest_link_speed, + autoneg_wait_to_complete); out: /* Set autoneg_advertised value based on input link speed */ @@ -1009,8 +1009,8 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, if (speed & IXGBE_LINK_SPEED_1GB_FULL) autoc |= IXGBE_AUTOC_KX_SUPP; } else if ((pma_pmd_1g == IXGBE_AUTOC_1G_SFI) && - (link_mode == IXGBE_AUTOC_LMS_1G_LINK_NO_AN || - link_mode == IXGBE_AUTOC_LMS_1G_AN)) { + (link_mode == IXGBE_AUTOC_LMS_1G_LINK_NO_AN || + link_mode == IXGBE_AUTOC_LMS_1G_AN)) { /* Switch from 1G SFI to 10G SFI if requested */ if ((speed == IXGBE_LINK_SPEED_10GB_FULL) && (pma_pmd_10g_serial == IXGBE_AUTOC2_10G_SFI)) { @@ -1018,7 +1018,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, autoc |= IXGBE_AUTOC_LMS_10G_SERIAL; } } else if ((pma_pmd_10g_serial == IXGBE_AUTOC2_10G_SFI) && - (link_mode == IXGBE_AUTOC_LMS_10G_SERIAL)) { + (link_mode == IXGBE_AUTOC_LMS_10G_SERIAL)) { /* Switch from 10G SFI to 1G SFI if requested */ if ((speed == IXGBE_LINK_SPEED_1GB_FULL) && (pma_pmd_1g == IXGBE_AUTOC_1G_SFI)) { @@ -1051,7 +1051,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, } if (!(links_reg & IXGBE_LINKS_KX_AN_COMP)) { status = - IXGBE_ERR_AUTONEG_NOT_COMPLETE; + IXGBE_ERR_AUTONEG_NOT_COMPLETE; hw_dbg(hw, "Autoneg did not complete.\n"); } } @@ -1074,14 +1074,14 @@ out: * Restarts link on PHY and MAC based on settings passed in. **/ static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) { s32 status; /* Setup the PHY according to input speed */ status = hw->phy.ops.setup_link_speed(hw, speed, - autoneg_wait_to_complete); + autoneg_wait_to_complete); /* Set up MAC */ ixgbe_start_mac_link_82599(hw, autoneg_wait_to_complete); @@ -1224,7 +1224,7 @@ mac_reset_top: (hw->mac.orig_autoc2 & IXGBE_AUTOC2_UPPER_MASK)) { autoc2 &= ~IXGBE_AUTOC2_UPPER_MASK; autoc2 |= (hw->mac.orig_autoc2 & - IXGBE_AUTOC2_UPPER_MASK); + IXGBE_AUTOC2_UPPER_MASK); IXGBE_WRITE_REG(hw, IXGBE_AUTOC2, autoc2); } } @@ -1246,7 +1246,7 @@ mac_reset_top: /* Add the SAN MAC address to the RAR only if it's a valid address */ if (is_valid_ether_addr(hw->mac.san_addr)) { hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1, - hw->mac.san_addr, 0, IXGBE_RAH_AV); + hw->mac.san_addr, 0, IXGBE_RAH_AV); /* Save the SAN MAC RAR index */ hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1; @@ -1257,7 +1257,7 @@ mac_reset_top: /* Store the alternative WWNN/WWPN prefix */ hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix, - &hw->mac.wwpn_prefix); + &hw->mac.wwpn_prefix); reset_hw_out: return status; @@ -1271,6 +1271,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) { int i; u32 fdirctrl = IXGBE_READ_REG(hw, IXGBE_FDIRCTRL); + fdirctrl &= ~IXGBE_FDIRCTRL_INIT_DONE; /* @@ -1284,8 +1285,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) udelay(10); } if (i >= IXGBE_FDIRCMD_CMD_POLL) { - hw_dbg(hw, "Flow Director previous command isn't complete, " - "aborting table re-initialization.\n"); + hw_dbg(hw, "Flow Director previous command isn't complete, aborting table re-initialization.\n"); return IXGBE_ERR_FDIR_REINIT_FAILED; } @@ -1299,12 +1299,12 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) * - write 0 to bit 8 of FDIRCMD register */ IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, - (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) | - IXGBE_FDIRCMD_CLEARHT)); + (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) | + IXGBE_FDIRCMD_CLEARHT)); IXGBE_WRITE_FLUSH(hw); IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD, - (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) & - ~IXGBE_FDIRCMD_CLEARHT)); + (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) & + ~IXGBE_FDIRCMD_CLEARHT)); IXGBE_WRITE_FLUSH(hw); /* * Clear FDIR Hash register to clear any leftover hashes @@ -1319,7 +1319,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw) /* Poll init-done after we write FDIRCTRL register */ for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) { if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) & - IXGBE_FDIRCTRL_INIT_DONE) + IXGBE_FDIRCTRL_INIT_DONE) break; usleep_range(1000, 2000); } @@ -1368,7 +1368,7 @@ static void ixgbe_fdir_enable_82599(struct ixgbe_hw *hw, u32 fdirctrl) IXGBE_WRITE_FLUSH(hw); for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) { if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) & - IXGBE_FDIRCTRL_INIT_DONE) + IXGBE_FDIRCTRL_INIT_DONE) break; usleep_range(1000, 2000); } @@ -1453,7 +1453,7 @@ do { \ bucket_hash ^= hi_hash_dword >> n; \ else if (IXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \ sig_hash ^= hi_hash_dword << (16 - n); \ -} while (0); +} while (0) /** * ixgbe_atr_compute_sig_hash_82599 - Compute the signature hash @@ -1529,9 +1529,9 @@ static u32 ixgbe_atr_compute_sig_hash_82599(union ixgbe_atr_hash_dword input, * @queue: queue index to direct traffic to **/ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, - union ixgbe_atr_hash_dword input, - union ixgbe_atr_hash_dword common, - u8 queue) + union ixgbe_atr_hash_dword input, + union ixgbe_atr_hash_dword common, + u8 queue) { u64 fdirhashcmd; u32 fdircmd; @@ -1555,7 +1555,7 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, /* configure FDIRCMD register */ fdircmd = IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE | - IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN; + IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN; fdircmd |= input.formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT; fdircmd |= (u32)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT; @@ -1579,7 +1579,7 @@ do { \ bucket_hash ^= lo_hash_dword >> n; \ if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \ bucket_hash ^= hi_hash_dword >> n; \ -} while (0); +} while (0) /** * ixgbe_atr_compute_perfect_hash_82599 - Compute the perfect filter hash @@ -1651,6 +1651,7 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input, static u32 ixgbe_get_fdirtcpm_82599(union ixgbe_atr_input *input_mask) { u32 mask = ntohs(input_mask->formatted.dst_port); + mask <<= IXGBE_FDIRTCPM_DPORTM_SHIFT; mask |= ntohs(input_mask->formatted.src_port); mask = ((mask & 0x55555555) << 1) | ((mask & 0xAAAAAAAA) >> 1); @@ -1885,7 +1886,7 @@ static s32 ixgbe_read_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 *val) u32 core_ctl; IXGBE_WRITE_REG(hw, IXGBE_CORECTL, IXGBE_CORECTL_WRITE_CMD | - (reg << 8)); + (reg << 8)); IXGBE_WRITE_FLUSH(hw); udelay(10); core_ctl = IXGBE_READ_REG(hw, IXGBE_CORECTL); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index bdc5581..4e5385a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -41,7 +41,7 @@ static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw); static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw); static void ixgbe_standby_eeprom(struct ixgbe_hw *hw); static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data, - u16 count); + u16 count); static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count); static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec); static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec); @@ -485,7 +485,7 @@ s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw) * Reads the part number string from the EEPROM. **/ s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, - u32 pba_num_size) + u32 pba_num_size) { s32 ret_val; u16 data; @@ -818,9 +818,8 @@ s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw) eeprom->address_bits = 16; else eeprom->address_bits = 8; - hw_dbg(hw, "Eeprom params: type = %d, size = %d, address bits: " - "%d\n", eeprom->type, eeprom->word_size, - eeprom->address_bits); + hw_dbg(hw, "Eeprom params: type = %d, size = %d, address bits: %d\n", + eeprom->type, eeprom->word_size, eeprom->address_bits); } return 0; @@ -1392,8 +1391,7 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) } if (i == timeout) { - hw_dbg(hw, "Driver can't access the Eeprom - SMBI Semaphore " - "not granted.\n"); + hw_dbg(hw, "Driver can't access the Eeprom - SMBI Semaphore not granted.\n"); /* * this release is particularly important because our attempts * above to get the semaphore may have succeeded, and if there @@ -1438,14 +1436,12 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) * was not granted because we don't have access to the EEPROM */ if (i >= timeout) { - hw_dbg(hw, "SWESMBI Software EEPROM semaphore " - "not granted.\n"); + hw_dbg(hw, "SWESMBI Software EEPROM semaphore not granted.\n"); ixgbe_release_eeprom_semaphore(hw); status = IXGBE_ERR_EEPROM; } } else { - hw_dbg(hw, "Software semaphore SMBI between device drivers " - "not granted.\n"); + hw_dbg(hw, "Software semaphore SMBI between device drivers not granted.\n"); } return status; @@ -1487,7 +1483,7 @@ static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw) */ for (i = 0; i < IXGBE_EEPROM_MAX_RETRY_SPI; i += 5) { ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_RDSR_OPCODE_SPI, - IXGBE_EEPROM_OPCODE_BITS); + IXGBE_EEPROM_OPCODE_BITS); spi_stat_reg = (u8)ixgbe_shift_in_eeprom_bits(hw, 8); if (!(spi_stat_reg & IXGBE_EEPROM_STATUS_RDY_SPI)) break; @@ -1536,7 +1532,7 @@ static void ixgbe_standby_eeprom(struct ixgbe_hw *hw) * @count: number of bits to shift out **/ static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data, - u16 count) + u16 count) { u32 eec; u32 mask; @@ -1740,7 +1736,7 @@ u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw) * caller does not need checksum_val, the value can be NULL. **/ s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, - u16 *checksum_val) + u16 *checksum_val) { s32 status; u16 checksum; @@ -1813,7 +1809,7 @@ s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw) * Puts an ethernet address into a receive address register. **/ s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, - u32 enable_addr) + u32 enable_addr) { u32 rar_low, rar_high; u32 rar_entries = hw->mac.num_rar_entries; @@ -2057,7 +2053,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, if (hw->addr_ctrl.mta_in_use > 0) IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, - IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type); + IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type); hw_dbg(hw, "ixgbe_update_mc_addr_list_generic Complete\n"); return 0; @@ -2075,7 +2071,7 @@ s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw) if (a->mta_in_use > 0) IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE | - hw->mac.mc_filter_type); + hw->mac.mc_filter_type); return 0; } @@ -2663,8 +2659,7 @@ s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw) /* For informational purposes only */ if (i >= IXGBE_MAX_SECRX_POLL) - hw_dbg(hw, "Rx unit being enabled before security " - "path fully disabled. Continuing with init.\n"); + hw_dbg(hw, "Rx unit being enabled before security path fully disabled. Continuing with init.\n"); return 0; @@ -2791,7 +2786,7 @@ out: * get and set mac_addr routines. **/ static s32 ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw, - u16 *san_mac_offset) + u16 *san_mac_offset) { s32 ret_val; @@ -2837,7 +2832,7 @@ s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr) hw->mac.ops.set_lan_id(hw); /* apply the port offset to the address offset */ (hw->bus.func) ? (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT1_OFFSET) : - (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT0_OFFSET); + (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT0_OFFSET); for (i = 0; i < 3; i++) { ret_val = hw->eeprom.ops.read(hw, san_mac_offset, &san_mac_data); @@ -3077,7 +3072,7 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan) * Turn on/off specified VLAN in the VLAN filter table. **/ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, - bool vlan_on) + bool vlan_on) { s32 regindex; u32 bitindex; @@ -3199,9 +3194,9 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, * Ignore it. */ vfta_changed = false; } - } - else + } else { IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0); + } } if (vfta_changed) @@ -3301,7 +3296,7 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, * block to check the support for the alternative WWNN/WWPN prefix support. **/ s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix, - u16 *wwpn_prefix) + u16 *wwpn_prefix) { u16 offset, caps; u16 alt_san_mac_blk_offset; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index d15ff2e..2ae5d4b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -39,7 +39,7 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw); s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw); s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw); s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num, - u32 pba_num_size); + u32 pba_num_size); s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr); enum ixgbe_bus_width ixgbe_convert_bus_width(u16 link_status); enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status); @@ -61,16 +61,16 @@ s32 ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data); s32 ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, - u16 *data); + u16 *data); s32 ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw); s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw, - u16 *checksum_val); + u16 *checksum_val); s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw); s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, - u32 enable_addr); + u32 enable_addr); s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index); s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw); s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, @@ -92,13 +92,13 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq); s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw); s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, - u32 vind, bool vlan_on); + u32 vind, bool vlan_on); s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw); s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, - bool *link_up, bool link_up_wait_to_complete); + ixgbe_link_speed *speed, + bool *link_up, bool link_up_wait_to_complete); s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix, - u16 *wwpn_prefix); + u16 *wwpn_prefix); s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *, u32 *reg_val); s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index e055e00..a689ee0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -267,7 +267,7 @@ void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *cfg, int direction, u8 *map) * Configure dcb settings and enable dcb mode. */ s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw, - struct ixgbe_dcb_config *dcb_config) + struct ixgbe_dcb_config *dcb_config) { s32 ret = 0; u8 pfc_en; @@ -389,7 +389,6 @@ static void ixgbe_dcb_read_rtrup2tc_82599(struct ixgbe_hw *hw, u8 *map) for (i = 0; i < MAX_USER_PRIORITY; i++) map[i] = IXGBE_RTRUP2TC_UP_MASK & (reg >> (i * IXGBE_RTRUP2TC_UP_SHIFT)); - return; } void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h index d5a1e3d..90c3702 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h @@ -31,17 +31,17 @@ /* DCB register definitions */ #define IXGBE_RTTDCS_TDPAC 0x00000001 /* 0 Round Robin, - * 1 WSP - Weighted Strict Priority - */ + * 1 WSP - Weighted Strict Priority + */ #define IXGBE_RTTDCS_VMPAC 0x00000002 /* 0 Round Robin, - * 1 WRR - Weighted Round Robin - */ + * 1 WRR - Weighted Round Robin + */ #define IXGBE_RTTDCS_TDRM 0x00000010 /* Transmit Recycle Mode */ #define IXGBE_RTTDCS_ARBDIS 0x00000040 /* DCB arbiter disable */ #define IXGBE_RTTDCS_BDPM 0x00400000 /* Bypass Data Pipe - must clear! */ #define IXGBE_RTTDCS_BPBFSM 0x00800000 /* Bypass PB Free Space - must - * clear! - */ + * clear! + */ #define IXGBE_RTTDCS_SPEED_CHG 0x80000000 /* Link speed change */ /* Receive UP2TC mapping */ @@ -56,11 +56,11 @@ #define IXGBE_RTRPT4C_LSP 0x80000000 /* LSP enable bit */ #define IXGBE_RDRXCTL_MPBEN 0x00000010 /* DMA config for multiple packet - * buffers enable - */ + * buffers enable + */ #define IXGBE_RDRXCTL_MCEN 0x00000040 /* DMA config for multiple cores - * (RSS) enable - */ + * (RSS) enable + */ /* RTRPCS Bit Masks */ #define IXGBE_RTRPCS_RRM 0x00000002 /* Receive Recycle Mode enable */ @@ -81,8 +81,8 @@ /* RTTPCS Bit Masks */ #define IXGBE_RTTPCS_TPPAC 0x00000020 /* 0 Round Robin, - * 1 SP - Strict Priority - */ + * 1 SP - Strict Priority + */ #define IXGBE_RTTPCS_ARBDIS 0x00000040 /* Arbiter disable */ #define IXGBE_RTTPCS_TPRM 0x00000100 /* Transmit Recycle Mode enable */ #define IXGBE_RTTPCS_ARBD_SHIFT 22 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index edd89a1..5172b6b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -192,8 +192,8 @@ static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev, } static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc, - u8 prio, u8 bwg_id, u8 bw_pct, - u8 up_map) + u8 prio, u8 bwg_id, u8 bw_pct, + u8 up_map) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -210,7 +210,7 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc, } static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, - u8 bw_pct) + u8 bw_pct) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -218,8 +218,8 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, } static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc, - u8 prio, u8 bwg_id, u8 bw_pct, - u8 up_map) + u8 prio, u8 bwg_id, u8 bw_pct, + u8 up_map) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -236,7 +236,7 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc, } static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, - u8 bw_pct) + u8 bw_pct) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -244,8 +244,8 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, } static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, - u8 *prio, u8 *bwg_id, u8 *bw_pct, - u8 *up_map) + u8 *prio, u8 *bwg_id, u8 *bw_pct, + u8 *up_map) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -256,7 +256,7 @@ static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, } static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, - u8 *bw_pct) + u8 *bw_pct) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -264,8 +264,8 @@ static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, } static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc, - u8 *prio, u8 *bwg_id, u8 *bw_pct, - u8 *up_map) + u8 *prio, u8 *bwg_id, u8 *bw_pct, + u8 *up_map) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -276,7 +276,7 @@ static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc, } static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, - u8 *bw_pct) + u8 *bw_pct) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -284,7 +284,7 @@ static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, } static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority, - u8 setting) + u8 setting) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -295,7 +295,7 @@ static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority, } static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority, - u8 *setting) + u8 *setting) { struct ixgbe_adapter *adapter = netdev_priv(netdev); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c index 472b0f4..5e2c1e3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c @@ -253,8 +253,7 @@ void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter) **/ void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter) { - if (adapter->ixgbe_dbg_adapter) - debugfs_remove_recursive(adapter->ixgbe_dbg_adapter); + debugfs_remove_recursive(adapter->ixgbe_dbg_adapter); adapter->ixgbe_dbg_adapter = NULL; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 31d7268..cc70de2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -141,8 +141,8 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = { sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \ / sizeof(u64)) #define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + \ - IXGBE_PB_STATS_LEN + \ - IXGBE_QUEUE_STATS_LEN) + IXGBE_PB_STATS_LEN + \ + IXGBE_QUEUE_STATS_LEN) static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = { "Register test (offline)", "Eeprom test (offline)", @@ -152,7 +152,7 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = { #define IXGBE_TEST_LEN sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN static int ixgbe_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) + struct ethtool_cmd *ecmd) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -311,7 +311,7 @@ static int ixgbe_get_settings(struct net_device *netdev, } static int ixgbe_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) + struct ethtool_cmd *ecmd) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -368,7 +368,7 @@ static int ixgbe_set_settings(struct net_device *netdev, } static void ixgbe_get_pauseparam(struct net_device *netdev, - struct ethtool_pauseparam *pause) + struct ethtool_pauseparam *pause) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -390,7 +390,7 @@ static void ixgbe_get_pauseparam(struct net_device *netdev, } static int ixgbe_set_pauseparam(struct net_device *netdev, - struct ethtool_pauseparam *pause) + struct ethtool_pauseparam *pause) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -450,7 +450,7 @@ static int ixgbe_get_regs_len(struct net_device *netdev) #define IXGBE_GET_STAT(_A_, _R_) _A_->stats._R_ static void ixgbe_get_regs(struct net_device *netdev, - struct ethtool_regs *regs, void *p) + struct ethtool_regs *regs, void *p) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -812,7 +812,7 @@ static int ixgbe_get_eeprom_len(struct net_device *netdev) } static int ixgbe_get_eeprom(struct net_device *netdev, - struct ethtool_eeprom *eeprom, u8 *bytes) + struct ethtool_eeprom *eeprom, u8 *bytes) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; @@ -918,7 +918,7 @@ err: } static void ixgbe_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) + struct ethtool_drvinfo *drvinfo) { struct ixgbe_adapter *adapter = netdev_priv(netdev); u32 nvm_track_id; @@ -940,7 +940,7 @@ static void ixgbe_get_drvinfo(struct net_device *netdev, } static void ixgbe_get_ringparam(struct net_device *netdev, - struct ethtool_ringparam *ring) + struct ethtool_ringparam *ring) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_ring *tx_ring = adapter->tx_ring[0]; @@ -953,7 +953,7 @@ static void ixgbe_get_ringparam(struct net_device *netdev, } static int ixgbe_set_ringparam(struct net_device *netdev, - struct ethtool_ringparam *ring) + struct ethtool_ringparam *ring) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_ring *temp_ring; @@ -1082,7 +1082,7 @@ static int ixgbe_get_sset_count(struct net_device *netdev, int sset) } static void ixgbe_get_ethtool_stats(struct net_device *netdev, - struct ethtool_stats *stats, u64 *data) + struct ethtool_stats *stats, u64 *data) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct rtnl_link_stats64 temp; @@ -1110,7 +1110,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } data[i] = (ixgbe_gstrings_stats[i].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } for (j = 0; j < netdev->num_tx_queues; j++) { ring = adapter->tx_ring[j]; @@ -1180,7 +1180,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, - u8 *data) + u8 *data) { char *p = (char *)data; int i; @@ -1357,8 +1357,7 @@ static bool reg_pattern_test(struct ixgbe_adapter *adapter, u64 *data, int reg, ixgbe_write_reg(&adapter->hw, reg, test_pattern[pat] & write); val = ixgbe_read_reg(&adapter->hw, reg); if (val != (test_pattern[pat] & write & mask)) { - e_err(drv, "pattern test reg %04X failed: got " - "0x%08X expected 0x%08X\n", + e_err(drv, "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n", reg, val, (test_pattern[pat] & write & mask)); *data = reg; ixgbe_write_reg(&adapter->hw, reg, before); @@ -1382,8 +1381,8 @@ static bool reg_set_and_check(struct ixgbe_adapter *adapter, u64 *data, int reg, ixgbe_write_reg(&adapter->hw, reg, write & mask); val = ixgbe_read_reg(&adapter->hw, reg); if ((write & mask) != (val & mask)) { - e_err(drv, "set/check reg %04X test failed: got 0x%08X " - "expected 0x%08X\n", reg, (val & mask), (write & mask)); + e_err(drv, "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", + reg, (val & mask), (write & mask)); *data = reg; ixgbe_write_reg(&adapter->hw, reg, before); return true; @@ -1430,8 +1429,8 @@ static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data) ixgbe_write_reg(&adapter->hw, IXGBE_STATUS, toggle); after = ixgbe_read_reg(&adapter->hw, IXGBE_STATUS) & toggle; if (value != after) { - e_err(drv, "failed STATUS register test got: 0x%08X " - "expected: 0x%08X\n", after, value); + e_err(drv, "failed STATUS register test got: 0x%08X expected: 0x%08X\n", + after, value); *data = 1; return 1; } @@ -1533,10 +1532,10 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data) return -1; } } else if (!request_irq(irq, ixgbe_test_intr, IRQF_PROBE_SHARED, - netdev->name, netdev)) { + netdev->name, netdev)) { shared_int = false; } else if (request_irq(irq, ixgbe_test_intr, IRQF_SHARED, - netdev->name, netdev)) { + netdev->name, netdev)) { *data = 1; return -1; } @@ -1563,9 +1562,9 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data) */ adapter->test_icr = 0; IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, - ~mask & 0x00007FFF); + ~mask & 0x00007FFF); IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, - ~mask & 0x00007FFF); + ~mask & 0x00007FFF); IXGBE_WRITE_FLUSH(&adapter->hw); usleep_range(10000, 20000); @@ -1587,7 +1586,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data) IXGBE_WRITE_FLUSH(&adapter->hw); usleep_range(10000, 20000); - if (!(adapter->test_icr &mask)) { + if (!(adapter->test_icr & mask)) { *data = 4; break; } @@ -1602,9 +1601,9 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data) */ adapter->test_icr = 0; IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, - ~mask & 0x00007FFF); + ~mask & 0x00007FFF); IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, - ~mask & 0x00007FFF); + ~mask & 0x00007FFF); IXGBE_WRITE_FLUSH(&adapter->hw); usleep_range(10000, 20000); @@ -1964,7 +1963,7 @@ out: } static void ixgbe_diag_test(struct net_device *netdev, - struct ethtool_test *eth_test, u64 *data) + struct ethtool_test *eth_test, u64 *data) { struct ixgbe_adapter *adapter = netdev_priv(netdev); bool if_running = netif_running(netdev); @@ -1987,10 +1986,7 @@ static void ixgbe_diag_test(struct net_device *netdev, int i; for (i = 0; i < adapter->num_vfs; i++) { if (adapter->vfinfo[i].clear_to_send) { - netdev_warn(netdev, "%s", - "offline diagnostic is not " - "supported when VFs are " - "present\n"); + netdev_warn(netdev, "offline diagnostic is not supported when VFs are present\n"); data[0] = 1; data[1] = 1; data[2] = 1; @@ -2037,8 +2033,7 @@ static void ixgbe_diag_test(struct net_device *netdev, * loopback diagnostic. */ if (adapter->flags & (IXGBE_FLAG_SRIOV_ENABLED | IXGBE_FLAG_VMDQ_ENABLED)) { - e_info(hw, "Skip MAC loopback diagnostic in VT " - "mode\n"); + e_info(hw, "Skip MAC loopback diagnostic in VT mode\n"); data[3] = 0; goto skip_loopback; } @@ -2078,7 +2073,7 @@ skip_ol_tests: } static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter, - struct ethtool_wolinfo *wol) + struct ethtool_wolinfo *wol) { struct ixgbe_hw *hw = &adapter->hw; int retval = 0; @@ -2094,12 +2089,12 @@ static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter, } static void ixgbe_get_wol(struct net_device *netdev, - struct ethtool_wolinfo *wol) + struct ethtool_wolinfo *wol) { struct ixgbe_adapter *adapter = netdev_priv(netdev); wol->supported = WAKE_UCAST | WAKE_MCAST | - WAKE_BCAST | WAKE_MAGIC; + WAKE_BCAST | WAKE_MAGIC; wol->wolopts = 0; if (ixgbe_wol_exclusion(adapter, wol) || @@ -2181,7 +2176,7 @@ static int ixgbe_set_phys_id(struct net_device *netdev, } static int ixgbe_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec) { struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -2222,8 +2217,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter) adapter->rx_itr_setting > IXGBE_MIN_RSC_ITR) { if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)) { adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED; - e_info(probe, "rx-usecs value high enough " - "to re-enable RSC\n"); + e_info(probe, "rx-usecs value high enough to re-enable RSC\n"); return true; } /* if interrupt rate is too high then disable RSC */ @@ -2236,7 +2230,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter) } static int ixgbe_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_q_vector *q_vector; @@ -2421,9 +2415,11 @@ static int ixgbe_get_rss_hash_opts(struct ixgbe_adapter *adapter, switch (cmd->flow_type) { case TCP_V4_FLOW: cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fallthrough */ case UDP_V4_FLOW: if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV4_UDP) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fallthrough */ case SCTP_V4_FLOW: case AH_ESP_V4_FLOW: case AH_V4_FLOW: @@ -2433,9 +2429,11 @@ static int ixgbe_get_rss_hash_opts(struct ixgbe_adapter *adapter, break; case TCP_V6_FLOW: cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fallthrough */ case UDP_V6_FLOW: if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV6_UDP) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fallthrough */ case SCTP_V6_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: @@ -2787,8 +2785,7 @@ static int ixgbe_set_rss_hash_opt(struct ixgbe_adapter *adapter, if ((flags2 & UDP_RSS_FLAGS) && !(adapter->flags2 & UDP_RSS_FLAGS)) - e_warn(drv, "enabling UDP RSS: fragmented packets" - " may arrive out of order to the stack above\n"); + e_warn(drv, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); adapter->flags2 = flags2; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 2067d39..2d9451e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -1113,8 +1113,8 @@ static void ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter) err = pci_enable_msi(adapter->pdev); if (err) { netif_printk(adapter, hw, KERN_DEBUG, adapter->netdev, - "Unable to allocate MSI interrupt, " - "falling back to legacy. Error: %d\n", err); + "Unable to allocate MSI interrupt, falling back to legacy. Error: %d\n", + err); return; } adapter->flags |= IXGBE_FLAG_MSI_ENABLED; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 8089ea9..ea11e2c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5659,6 +5659,17 @@ err_setup_tx: return err; } +static void ixgbe_close_suspend(struct ixgbe_adapter *adapter) +{ + ixgbe_ptp_suspend(adapter); + + ixgbe_down(adapter); + ixgbe_free_irq(adapter); + + ixgbe_free_all_tx_resources(adapter); + ixgbe_free_all_rx_resources(adapter); +} + /** * ixgbe_close - Disables a network interface * @netdev: network interface device structure @@ -5676,14 +5687,10 @@ static int ixgbe_close(struct net_device *netdev) ixgbe_ptp_stop(adapter); - ixgbe_down(adapter); - ixgbe_free_irq(adapter); + ixgbe_close_suspend(adapter); ixgbe_fdir_filter_exit(adapter); - ixgbe_free_all_tx_resources(adapter); - ixgbe_free_all_rx_resources(adapter); - ixgbe_release_hw_control(adapter); return 0; @@ -5750,12 +5757,8 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake) netif_device_detach(netdev); rtnl_lock(); - if (netif_running(netdev)) { - ixgbe_down(adapter); - ixgbe_free_irq(adapter); - ixgbe_free_all_tx_resources(adapter); - ixgbe_free_all_rx_resources(adapter); - } + if (netif_running(netdev)) + ixgbe_close_suspend(adapter); rtnl_unlock(); ixgbe_clear_interrupt_scheme(adapter); @@ -6087,7 +6090,7 @@ static void ixgbe_fdir_reinit_subtask(struct ixgbe_adapter *adapter) if (ixgbe_reinit_fdir_tables_82599(hw) == 0) { for (i = 0; i < adapter->num_tx_queues; i++) set_bit(__IXGBE_TX_FDIR_INIT_DONE, - &(adapter->tx_ring[i]->state)); + &(adapter->tx_ring[i]->state)); /* re-enable flow director interrupts */ IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_FLOW_DIR); } else { @@ -7926,7 +7929,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_do_ioctl = ixgbe_ioctl, .ndo_set_vf_mac = ixgbe_ndo_set_vf_mac, .ndo_set_vf_vlan = ixgbe_ndo_set_vf_vlan, - .ndo_set_vf_tx_rate = ixgbe_ndo_set_vf_bw, + .ndo_set_vf_rate = ixgbe_ndo_set_vf_bw, .ndo_set_vf_spoofchk = ixgbe_ndo_set_vf_spoofchk, .ndo_get_vf_config = ixgbe_ndo_get_vf_config, .ndo_get_stats64 = ixgbe_get_stats64, @@ -8387,7 +8390,7 @@ skip_sriov: if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present) e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n", hw->mac.type, hw->phy.type, hw->phy.sfp_type, - part_str); + part_str); else e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n", hw->mac.type, hw->phy.type, part_str); @@ -8449,8 +8452,8 @@ skip_sriov: ixgbe_dbg_adapter_init(adapter); - /* Need link setup for MNG FW, else wait for IXGBE_UP */ - if (ixgbe_mng_enabled(hw) && hw->mac.ops.setup_link) + /* setup link for SFP devices with MNG FW, else wait for IXGBE_UP */ + if (ixgbe_mng_enabled(hw) && ixgbe_is_sfp(hw) && hw->mac.ops.setup_link) hw->mac.ops.setup_link(hw, IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, true); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c index f5c6af2..1918e0a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c @@ -223,7 +223,7 @@ out: * received an ack to that message within delay * timeout period **/ static s32 ixgbe_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, - u16 mbx_id) + u16 mbx_id) { struct ixgbe_mbx_info *mbx = &hw->mbx; s32 ret_val = IXGBE_ERR_MBX; @@ -269,7 +269,7 @@ static s32 ixgbe_check_for_msg_pf(struct ixgbe_hw *hw, u16 vf_number) u32 vf_bit = vf_number % 16; if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFREQ_VF1 << vf_bit, - index)) { + index)) { ret_val = 0; hw->mbx.stats.reqs++; } @@ -291,7 +291,7 @@ static s32 ixgbe_check_for_ack_pf(struct ixgbe_hw *hw, u16 vf_number) u32 vf_bit = vf_number % 16; if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFACK_VF1 << vf_bit, - index)) { + index)) { ret_val = 0; hw->mbx.stats.acks++; } @@ -366,7 +366,7 @@ static s32 ixgbe_obtain_mbx_lock_pf(struct ixgbe_hw *hw, u16 vf_number) * returns SUCCESS if it successfully copied message into the buffer **/ static s32 ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, - u16 vf_number) + u16 vf_number) { s32 ret_val; u16 i; @@ -407,7 +407,7 @@ out_no_write: * a message due to a VF request so no polling for message is needed. **/ static s32 ixgbe_read_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size, - u16 vf_number) + u16 vf_number) { s32 ret_val; u16 i; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h index a9b9ad6..a5cb755 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h @@ -54,11 +54,11 @@ * Message ACK's are the value or'd with 0xF0000000 */ #define IXGBE_VT_MSGTYPE_ACK 0x80000000 /* Messages below or'd with - * this are the ACK */ + * this are the ACK */ #define IXGBE_VT_MSGTYPE_NACK 0x40000000 /* Messages below or'd with - * this are the NACK */ + * this are the NACK */ #define IXGBE_VT_MSGTYPE_CTS 0x20000000 /* Indicates that VF is still - clear to send requests */ + clear to send requests */ #define IXGBE_VT_MSGINFO_SHIFT 16 /* bits 23:16 are used for exra info for certain messages */ #define IXGBE_VT_MSGINFO_MASK (0xFF << IXGBE_VT_MSGINFO_SHIFT) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index a76af8e2..ff68b7a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -67,7 +67,7 @@ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) if (mdio45_probe(&hw->phy.mdio, phy_addr) == 0) { ixgbe_get_phy_id(hw); hw->phy.type = - ixgbe_get_phy_type_from_id(hw->phy.id); + ixgbe_get_phy_type_from_id(hw->phy.id); if (hw->phy.type == ixgbe_phy_unknown) { hw->phy.ops.read_reg(hw, @@ -136,12 +136,12 @@ static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw) u16 phy_id_low = 0; status = hw->phy.ops.read_reg(hw, MDIO_DEVID1, MDIO_MMD_PMAPMD, - &phy_id_high); + &phy_id_high); if (status == 0) { hw->phy.id = (u32)(phy_id_high << 16); status = hw->phy.ops.read_reg(hw, MDIO_DEVID2, MDIO_MMD_PMAPMD, - &phy_id_low); + &phy_id_low); hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK); hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK); } @@ -318,7 +318,7 @@ s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, * @phy_data: Pointer to read data from PHY register **/ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u16 *phy_data) + u32 device_type, u16 *phy_data) { s32 status; u16 gssr; @@ -421,7 +421,7 @@ s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, * @phy_data: Data to write to the PHY register **/ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u16 phy_data) + u32 device_type, u16 phy_data) { s32 status; u16 gssr; @@ -548,8 +548,8 @@ s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) * @speed: new link speed **/ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) { /* @@ -582,8 +582,8 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, * Determines the link capabilities by reading the AUTOC register. */ s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, - bool *autoneg) + ixgbe_link_speed *speed, + bool *autoneg) { s32 status = IXGBE_ERR_LINK_SETUP; u16 speed_ability; @@ -592,7 +592,7 @@ s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, *autoneg = true; status = hw->phy.ops.read_reg(hw, MDIO_SPEED, MDIO_MMD_PMAPMD, - &speed_ability); + &speed_ability); if (status == 0) { if (speed_ability & MDIO_SPEED_10G) @@ -806,11 +806,11 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) /* reset the PHY and poll for completion */ hw->phy.ops.write_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS, - (phy_data | MDIO_CTRL1_RESET)); + (phy_data | MDIO_CTRL1_RESET)); for (i = 0; i < 100; i++) { hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS, - &phy_data); + &phy_data); if ((phy_data & MDIO_CTRL1_RESET) == 0) break; usleep_range(10000, 20000); @@ -824,7 +824,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) /* Get init offsets */ ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset, - &data_offset); + &data_offset); if (ret_val != 0) goto out; @@ -838,7 +838,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) if (ret_val) goto err_eeprom; control = (eword & IXGBE_CONTROL_MASK_NL) >> - IXGBE_CONTROL_SHIFT_NL; + IXGBE_CONTROL_SHIFT_NL; edata = eword & IXGBE_DATA_MASK_NL; switch (control) { case IXGBE_DELAY_NL: @@ -859,7 +859,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw) if (ret_val) goto err_eeprom; hw->phy.ops.write_reg(hw, phy_offset, - MDIO_MMD_PMAPMD, eword); + MDIO_MMD_PMAPMD, eword); hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword, phy_offset); data_offset++; @@ -1010,10 +1010,10 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = - ixgbe_sfp_type_da_cu_core0; + ixgbe_sfp_type_da_cu_core0; else hw->phy.sfp_type = - ixgbe_sfp_type_da_cu_core1; + ixgbe_sfp_type_da_cu_core1; } else if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE) { hw->phy.ops.read_i2c_eeprom( hw, IXGBE_SFF_CABLE_SPEC_COMP, @@ -1035,10 +1035,10 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) IXGBE_SFF_10GBASELR_CAPABLE)) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = - ixgbe_sfp_type_srlr_core0; + ixgbe_sfp_type_srlr_core0; else hw->phy.sfp_type = - ixgbe_sfp_type_srlr_core1; + ixgbe_sfp_type_srlr_core1; } else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = @@ -1087,15 +1087,15 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) goto err_read_i2c_eeprom; status = hw->phy.ops.read_i2c_eeprom(hw, - IXGBE_SFF_VENDOR_OUI_BYTE1, - &oui_bytes[1]); + IXGBE_SFF_VENDOR_OUI_BYTE1, + &oui_bytes[1]); if (status != 0) goto err_read_i2c_eeprom; status = hw->phy.ops.read_i2c_eeprom(hw, - IXGBE_SFF_VENDOR_OUI_BYTE2, - &oui_bytes[2]); + IXGBE_SFF_VENDOR_OUI_BYTE2, + &oui_bytes[2]); if (status != 0) goto err_read_i2c_eeprom; @@ -1403,8 +1403,8 @@ err_read_i2c_eeprom: * so it returns the offsets to the phy init sequence block. **/ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, - u16 *list_offset, - u16 *data_offset) + u16 *list_offset, + u16 *data_offset) { u16 sfp_id; u16 sfp_type = hw->phy.sfp_type; @@ -1493,11 +1493,11 @@ err_phy: * Performs byte read operation to SFP module's EEPROM over I2C interface. **/ s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 *eeprom_data) + u8 *eeprom_data) { return hw->phy.ops.read_i2c_byte(hw, byte_offset, - IXGBE_I2C_EEPROM_DEV_ADDR, - eeprom_data); + IXGBE_I2C_EEPROM_DEV_ADDR, + eeprom_data); } /** @@ -1525,11 +1525,11 @@ s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, * Performs byte write operation to SFP module's EEPROM over I2C interface. **/ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 eeprom_data) + u8 eeprom_data) { return hw->phy.ops.write_i2c_byte(hw, byte_offset, - IXGBE_I2C_EEPROM_DEV_ADDR, - eeprom_data); + IXGBE_I2C_EEPROM_DEV_ADDR, + eeprom_data); } /** @@ -1542,7 +1542,7 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, * a specified device address. **/ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 *data) + u8 dev_addr, u8 *data) { s32 status = 0; u32 max_retry = 10; @@ -1631,7 +1631,7 @@ read_byte_out: * a specified device address. **/ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 data) + u8 dev_addr, u8 data) { s32 status = 0; u32 max_retry = 1; @@ -2046,7 +2046,7 @@ s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw) /* Check that the LASI temp alarm status was triggered */ hw->phy.ops.read_reg(hw, IXGBE_TN_LASI_STATUS_REG, - MDIO_MMD_PMAPMD, &phy_data); + MDIO_MMD_PMAPMD, &phy_data); if (!(phy_data & IXGBE_TN_LASI_STATUS_TEMP_ALARM)) goto out; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index 0bb047f..54071ed 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -114,47 +114,47 @@ s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw); s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw); s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw); s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u16 *phy_data); + u32 device_type, u16 *phy_data); s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u16 phy_data); + u32 device_type, u16 phy_data); s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data); s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data); s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw); s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, - bool *autoneg); + ixgbe_link_speed *speed, + bool *autoneg); bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw); /* PHY specific */ s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, - bool *link_up); + ixgbe_link_speed *speed, + bool *link_up); s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw); s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw, - u16 *firmware_version); + u16 *firmware_version); s32 ixgbe_get_phy_firmware_version_generic(struct ixgbe_hw *hw, - u16 *firmware_version); + u16 *firmware_version); s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw); s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw); s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw); s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, - u16 *list_offset, - u16 *data_offset); + u16 *list_offset, + u16 *data_offset); s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw); s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 *data); + u8 dev_addr, u8 *data); s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 data); + u8 dev_addr, u8 data); s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 *eeprom_data); + u8 *eeprom_data); s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *sff8472_data); s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 eeprom_data); + u8 eeprom_data); #endif /* _IXGBE_PHY_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 8902ae6..b3266b7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -334,7 +334,7 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp, } /** - * ixgbe_ptp_enable + * ixgbe_ptp_feature_enable * @ptp: the ptp clock structure * @rq: the requested feature to change * @on: whether to enable or disable the feature @@ -342,8 +342,8 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp, * enable (or disable) ancillary features of the phc subsystem. * our driver only supports the PPS feature on the X540 */ -static int ixgbe_ptp_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, int on) +static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) { struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); @@ -570,9 +570,9 @@ int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) } /** - * ixgbe_ptp_set_ts_config - control hardware time stamping - * @adapter: pointer to adapter struct - * @ifreq: ioctl data + * ixgbe_ptp_set_timestamp_mode - setup the hardware for the requested mode + * @adapter: the private ixgbe adapter structure + * @config: the hwtstamp configuration requested * * Outgoing time stamping can be enabled and disabled. Play nice and * disable it when requested, although it shouldn't cause any overhead @@ -590,25 +590,25 @@ int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) * packets, regardless of the type specified in the register, only use V2 * Event mode. This more accurately tells the user what the hardware is going * to do anyways. + * + * Note: this may modify the hwtstamp configuration towards a more general + * mode, if required to support the specifically requested mode. */ -int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) +static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, + struct hwtstamp_config *config) { struct ixgbe_hw *hw = &adapter->hw; - struct hwtstamp_config config; u32 tsync_tx_ctl = IXGBE_TSYNCTXCTL_ENABLED; u32 tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED; u32 tsync_rx_mtrl = PTP_EV_PORT << 16; bool is_l2 = false; u32 regval; - if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) - return -EFAULT; - /* reserved for future extensions */ - if (config.flags) + if (config->flags) return -EINVAL; - switch (config.tx_type) { + switch (config->tx_type) { case HWTSTAMP_TX_OFF: tsync_tx_ctl = 0; case HWTSTAMP_TX_ON: @@ -617,7 +617,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) return -ERANGE; } - switch (config.rx_filter) { + switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: tsync_rx_ctl = 0; tsync_rx_mtrl = 0; @@ -641,7 +641,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2; is_l2 = true; - config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_ALL: @@ -652,7 +652,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) * Delay_Req messages and hardware does not support * timestamping all packets => return error */ - config.rx_filter = HWTSTAMP_FILTER_NONE; + config->rx_filter = HWTSTAMP_FILTER_NONE; return -ERANGE; } @@ -671,7 +671,6 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) else IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588), 0); - /* enable/disable TX */ regval = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL); regval &= ~IXGBE_TSYNCTXCTL_ENABLED; @@ -693,6 +692,29 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH); regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH); + return 0; +} + +/** + * ixgbe_ptp_set_ts_config - user entry point for timestamp mode + * @adapter: pointer to adapter struct + * @ifreq: ioctl data + * + * Set hardware to requested mode. If unsupported, return an error with no + * changes. Otherwise, store the mode for future reference. + */ +int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) +{ + struct hwtstamp_config config; + int err; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + err = ixgbe_ptp_set_timestamp_mode(adapter, &config); + if (err) + return err; + /* save these settings for future reference */ memcpy(&adapter->tstamp_config, &config, sizeof(adapter->tstamp_config)); @@ -790,9 +812,13 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) * ixgbe_ptp_reset * @adapter: the ixgbe private board structure * - * When the MAC resets, all timesync features are reset. This function should be - * called to re-enable the PTP clock structure. It will re-init the timecounter - * structure based on the kernel time as well as setup the cycle counter data. + * When the MAC resets, all the hardware bits for timesync are reset. This + * function is used to re-enable the device for PTP based on current settings. + * We do lose the current clock time, so just reset the cyclecounter to the + * system real clock time. + * + * This function will maintain hwtstamp_config settings, and resets the SDP + * output if it was enabled. */ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter) { @@ -804,8 +830,8 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000); IXGBE_WRITE_FLUSH(hw); - /* Reset the saved tstamp_config */ - memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config)); + /* reset the hardware timestamping mode */ + ixgbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); ixgbe_ptp_start_cyclecounter(adapter); @@ -825,16 +851,23 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter) } /** - * ixgbe_ptp_init + * ixgbe_ptp_create_clock * @adapter: the ixgbe private adapter structure * - * This function performs the required steps for enabling ptp - * support. If ptp support has already been loaded it simply calls the - * cyclecounter init routine and exits. + * This function performs setup of the user entry point function table and + * initializes the PTP clock device, which is used to access the clock-like + * features of the PTP core. It will be called by ixgbe_ptp_init, only if + * there isn't already a clock device (such as after a suspend/resume cycle, + * where the clock device wasn't destroyed). */ -void ixgbe_ptp_init(struct ixgbe_adapter *adapter) +static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) { struct net_device *netdev = adapter->netdev; + long err; + + /* do nothing if we already have a clock device */ + if (!IS_ERR_OR_NULL(adapter->ptp_clock)) + return 0; switch (adapter->hw.mac.type) { case ixgbe_mac_X540: @@ -851,7 +884,7 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter) adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.gettime = ixgbe_ptp_gettime; adapter->ptp_caps.settime = ixgbe_ptp_settime; - adapter->ptp_caps.enable = ixgbe_ptp_enable; + adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; break; case ixgbe_mac_82599EB: snprintf(adapter->ptp_caps.name, @@ -867,24 +900,57 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter) adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.gettime = ixgbe_ptp_gettime; adapter->ptp_caps.settime = ixgbe_ptp_settime; - adapter->ptp_caps.enable = ixgbe_ptp_enable; + adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; break; default: adapter->ptp_clock = NULL; - return; + return -EOPNOTSUPP; } - spin_lock_init(&adapter->tmreg_lock); - INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work); - adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps, &adapter->pdev->dev); if (IS_ERR(adapter->ptp_clock)) { + err = PTR_ERR(adapter->ptp_clock); adapter->ptp_clock = NULL; e_dev_err("ptp_clock_register failed\n"); + return err; } else e_dev_info("registered PHC device on %s\n", netdev->name); + /* set default timestamp mode to disabled here. We do this in + * create_clock instead of init, because we don't want to override the + * previous settings during a resume cycle. + */ + adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF; + + return 0; +} + +/** + * ixgbe_ptp_init + * @adapter: the ixgbe private adapter structure + * + * This function performs the required steps for enabling PTP + * support. If PTP support has already been loaded it simply calls the + * cyclecounter init routine and exits. + */ +void ixgbe_ptp_init(struct ixgbe_adapter *adapter) +{ + /* initialize the spin lock first since we can't control when a user + * will call the entry functions once we have initialized the clock + * device + */ + spin_lock_init(&adapter->tmreg_lock); + + /* obtain a PTP device, or re-use an existing device */ + if (ixgbe_ptp_create_clock(adapter)) + return; + + /* we have a clock so we can initialize work now */ + INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work); + + /* reset the PTP related hardware bits */ ixgbe_ptp_reset(adapter); /* enter the IXGBE_PTP_RUNNING state */ @@ -894,28 +960,45 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter) } /** - * ixgbe_ptp_stop - disable ptp device and stop the overflow check - * @adapter: pointer to adapter struct + * ixgbe_ptp_suspend - stop PTP work items + * @ adapter: pointer to adapter struct * - * this function stops the ptp support, and cancels the delayed work. + * this function suspends PTP activity, and prevents more PTP work from being + * generated, but does not destroy the PTP clock device. */ -void ixgbe_ptp_stop(struct ixgbe_adapter *adapter) +void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter) { /* Leave the IXGBE_PTP_RUNNING state. */ if (!test_and_clear_bit(__IXGBE_PTP_RUNNING, &adapter->state)) return; - /* stop the PPS signal */ - adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED; - ixgbe_ptp_setup_sdp(adapter); + /* since this might be called in suspend, we don't clear the state, + * but simply reset the auxiliary PPS signal control register + */ + IXGBE_WRITE_REG(&adapter->hw, IXGBE_TSAUXC, 0x0); + /* ensure that we cancel any pending PTP Tx work item in progress */ cancel_work_sync(&adapter->ptp_tx_work); if (adapter->ptp_tx_skb) { dev_kfree_skb_any(adapter->ptp_tx_skb); adapter->ptp_tx_skb = NULL; clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state); } +} + +/** + * ixgbe_ptp_stop - close the PTP device + * @adapter: pointer to adapter struct + * + * completely destroy the PTP device, should only be called when the device is + * being fully closed. + */ +void ixgbe_ptp_stop(struct ixgbe_adapter *adapter) +{ + /* first, suspend PTP activity */ + ixgbe_ptp_suspend(adapter); + /* disable the PTP clock device */ if (adapter->ptp_clock) { ptp_clock_unregister(adapter->ptp_clock); adapter->ptp_clock = NULL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index a01417c..16b3a1c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1129,9 +1129,9 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos) adapter->vfinfo[vf].vlan_count--; adapter->vfinfo[vf].pf_vlan = 0; adapter->vfinfo[vf].pf_qos = 0; - } + } out: - return err; + return err; } static int ixgbe_link_mbps(struct ixgbe_adapter *adapter) @@ -1222,7 +1222,8 @@ void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter) } } -int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) +int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate, + int max_tx_rate) { struct ixgbe_adapter *adapter = netdev_priv(netdev); int link_speed; @@ -1240,13 +1241,16 @@ int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) if (link_speed != 10000) return -EINVAL; + if (min_tx_rate) + return -EINVAL; + /* rate limit cannot be less than 10Mbs or greater than link speed */ - if (tx_rate && ((tx_rate <= 10) || (tx_rate > link_speed))) + if (max_tx_rate && ((max_tx_rate <= 10) || (max_tx_rate > link_speed))) return -EINVAL; /* store values */ adapter->vf_rate_link_speed = link_speed; - adapter->vfinfo[vf].tx_rate = tx_rate; + adapter->vfinfo[vf].tx_rate = max_tx_rate; /* update hardware configuration */ ixgbe_set_vf_rate_limit(adapter, vf); @@ -1288,7 +1292,8 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev, return -EINVAL; ivi->vf = vf; memcpy(&ivi->mac, adapter->vfinfo[vf].vf_mac_addresses, ETH_ALEN); - ivi->tx_rate = adapter->vfinfo[vf].tx_rate; + ivi->max_tx_rate = adapter->vfinfo[vf].tx_rate; + ivi->min_tx_rate = 0; ivi->vlan = adapter->vfinfo[vf].pf_vlan; ivi->qos = adapter->vfinfo[vf].pf_qos; ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index cea6401..32c26d5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -44,7 +44,8 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter); int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac); int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan, u8 qos); -int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate); +int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate, + int max_tx_rate); int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); int ixgbe_ndo_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 551d608..9a89f98 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -160,7 +160,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_MAX_EITR 0x00000FF8 #define IXGBE_MIN_EITR 8 #define IXGBE_EITR(_i) (((_i) <= 23) ? (0x00820 + ((_i) * 4)) : \ - (0x012300 + (((_i) - 24) * 4))) + (0x012300 + (((_i) - 24) * 4))) #define IXGBE_EITR_ITR_INT_MASK 0x00000FF8 #define IXGBE_EITR_LLI_MOD 0x00008000 #define IXGBE_EITR_CNT_WDIS 0x80000000 @@ -213,7 +213,7 @@ struct ixgbe_thermal_sensor_data { * 64-127: 0x0D014 + (n-64)*0x40 */ #define IXGBE_SRRCTL(_i) (((_i) <= 15) ? (0x02100 + ((_i) * 4)) : \ - (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \ + (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \ (0x0D014 + (((_i) - 64) * 0x40)))) /* * Rx DCA Control Register: @@ -222,11 +222,11 @@ struct ixgbe_thermal_sensor_data { * 64-127: 0x0D00C + (n-64)*0x40 */ #define IXGBE_DCA_RXCTRL(_i) (((_i) <= 15) ? (0x02200 + ((_i) * 4)) : \ - (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \ + (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \ (0x0D00C + (((_i) - 64) * 0x40)))) #define IXGBE_RDRXCTL 0x02F00 #define IXGBE_RXPBSIZE(_i) (0x03C00 + ((_i) * 4)) - /* 8 of these 0x03C00 - 0x03C1C */ + /* 8 of these 0x03C00 - 0x03C1C */ #define IXGBE_RXCTRL 0x03000 #define IXGBE_DROPEN 0x03D04 #define IXGBE_RXPBSIZE_SHIFT 10 @@ -239,14 +239,14 @@ struct ixgbe_thermal_sensor_data { /* Multicast Table Array - 128 entries */ #define IXGBE_MTA(_i) (0x05200 + ((_i) * 4)) #define IXGBE_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \ - (0x0A200 + ((_i) * 8))) + (0x0A200 + ((_i) * 8))) #define IXGBE_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \ - (0x0A204 + ((_i) * 8))) + (0x0A204 + ((_i) * 8))) #define IXGBE_MPSAR_LO(_i) (0x0A600 + ((_i) * 8)) #define IXGBE_MPSAR_HI(_i) (0x0A604 + ((_i) * 8)) /* Packet split receive type */ #define IXGBE_PSRTYPE(_i) (((_i) <= 15) ? (0x05480 + ((_i) * 4)) : \ - (0x0EA00 + ((_i) * 4))) + (0x0EA00 + ((_i) * 4))) /* array of 4096 1-bit vlan filters */ #define IXGBE_VFTA(_i) (0x0A000 + ((_i) * 4)) /*array of 4096 4-bit vlan vmdq indices */ @@ -696,7 +696,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_RQSMR(_i) (0x02300 + ((_i) * 4)) #define IXGBE_TQSMR(_i) (((_i) <= 7) ? (0x07300 + ((_i) * 4)) : \ - (0x08600 + ((_i) * 4))) + (0x08600 + ((_i) * 4))) #define IXGBE_TQSM(_i) (0x08600 + ((_i) * 4)) #define IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */ @@ -820,7 +820,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_GCR_EXT_VT_MODE_32 0x00000002 #define IXGBE_GCR_EXT_VT_MODE_64 0x00000003 #define IXGBE_GCR_EXT_SRIOV (IXGBE_GCR_EXT_MSIX_EN | \ - IXGBE_GCR_EXT_VT_MODE_64) + IXGBE_GCR_EXT_VT_MODE_64) /* Time Sync Registers */ #define IXGBE_TSYNCRXCTL 0x05188 /* Rx Time Sync Control register - RW */ @@ -1396,10 +1396,10 @@ enum { #define IXGBE_EIMC_OTHER IXGBE_EICR_OTHER /* INT Cause Active */ #define IXGBE_EIMS_ENABLE_MASK ( \ - IXGBE_EIMS_RTX_QUEUE | \ - IXGBE_EIMS_LSC | \ - IXGBE_EIMS_TCP_TIMER | \ - IXGBE_EIMS_OTHER) + IXGBE_EIMS_RTX_QUEUE | \ + IXGBE_EIMS_LSC | \ + IXGBE_EIMS_TCP_TIMER | \ + IXGBE_EIMS_OTHER) /* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */ #define IXGBE_IMIR_PORT_IM_EN 0x00010000 /* TCP port enable */ @@ -2161,18 +2161,18 @@ enum { /* Masks to determine if packets should be dropped due to frame errors */ #define IXGBE_RXD_ERR_FRAME_ERR_MASK ( \ - IXGBE_RXD_ERR_CE | \ - IXGBE_RXD_ERR_LE | \ - IXGBE_RXD_ERR_PE | \ - IXGBE_RXD_ERR_OSE | \ - IXGBE_RXD_ERR_USE) + IXGBE_RXD_ERR_CE | \ + IXGBE_RXD_ERR_LE | \ + IXGBE_RXD_ERR_PE | \ + IXGBE_RXD_ERR_OSE | \ + IXGBE_RXD_ERR_USE) #define IXGBE_RXDADV_ERR_FRAME_ERR_MASK ( \ - IXGBE_RXDADV_ERR_CE | \ - IXGBE_RXDADV_ERR_LE | \ - IXGBE_RXDADV_ERR_PE | \ - IXGBE_RXDADV_ERR_OSE | \ - IXGBE_RXDADV_ERR_USE) + IXGBE_RXDADV_ERR_CE | \ + IXGBE_RXDADV_ERR_LE | \ + IXGBE_RXDADV_ERR_PE | \ + IXGBE_RXDADV_ERR_OSE | \ + IXGBE_RXDADV_ERR_USE) /* Multicast bit mask */ #define IXGBE_MCSTCTRL_MFE 0x4 @@ -2393,9 +2393,9 @@ struct ixgbe_adv_tx_context_desc { #define IXGBE_ADVTXD_CC 0x00000080 /* Check Context */ #define IXGBE_ADVTXD_POPTS_SHIFT 8 /* Adv desc POPTS shift */ #define IXGBE_ADVTXD_POPTS_IXSM (IXGBE_TXD_POPTS_IXSM << \ - IXGBE_ADVTXD_POPTS_SHIFT) + IXGBE_ADVTXD_POPTS_SHIFT) #define IXGBE_ADVTXD_POPTS_TXSM (IXGBE_TXD_POPTS_TXSM << \ - IXGBE_ADVTXD_POPTS_SHIFT) + IXGBE_ADVTXD_POPTS_SHIFT) #define IXGBE_ADVTXD_POPTS_ISCO_1ST 0x00000000 /* 1st TSO of iSCSI PDU */ #define IXGBE_ADVTXD_POPTS_ISCO_MDL 0x00000800 /* Middle TSO of iSCSI PDU */ #define IXGBE_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */ @@ -2435,10 +2435,10 @@ typedef u32 ixgbe_link_speed; #define IXGBE_LINK_SPEED_1GB_FULL 0x0020 #define IXGBE_LINK_SPEED_10GB_FULL 0x0080 #define IXGBE_LINK_SPEED_82598_AUTONEG (IXGBE_LINK_SPEED_1GB_FULL | \ - IXGBE_LINK_SPEED_10GB_FULL) + IXGBE_LINK_SPEED_10GB_FULL) #define IXGBE_LINK_SPEED_82599_AUTONEG (IXGBE_LINK_SPEED_100_FULL | \ - IXGBE_LINK_SPEED_1GB_FULL | \ - IXGBE_LINK_SPEED_10GB_FULL) + IXGBE_LINK_SPEED_1GB_FULL | \ + IXGBE_LINK_SPEED_10GB_FULL) /* Physical layer type */ @@ -2840,7 +2840,7 @@ struct ixgbe_hw; /* iterator type for walking multicast address lists */ typedef u8* (*ixgbe_mc_addr_itr) (struct ixgbe_hw *hw, u8 **mc_addr_ptr, - u32 *vmdq); + u32 *vmdq); /* Function pointer table */ struct ixgbe_eeprom_operations { @@ -2887,7 +2887,7 @@ struct ixgbe_mac_operations { s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool); s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *, - bool *); + bool *); /* Packet Buffer Manipulation */ void (*set_rxpba)(struct ixgbe_hw *, int, u32, int); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 188a597..40dd798 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -81,7 +81,7 @@ static s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw, bool autoneg_wait_to_complete) { return hw->phy.ops.setup_link_speed(hw, speed, - autoneg_wait_to_complete); + autoneg_wait_to_complete); } /** @@ -155,7 +155,7 @@ mac_reset_top: /* Add the SAN MAC address to the RAR only if it's a valid address */ if (is_valid_ether_addr(hw->mac.san_addr)) { hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1, - hw->mac.san_addr, 0, IXGBE_RAH_AV); + hw->mac.san_addr, 0, IXGBE_RAH_AV); /* Save the SAN MAC RAR index */ hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1; @@ -166,7 +166,7 @@ mac_reset_top: /* Store the alternative WWNN/WWPN prefix */ hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix, - &hw->mac.wwpn_prefix); + &hw->mac.wwpn_prefix); reset_hw_out: return status; @@ -237,9 +237,9 @@ static s32 ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw) eec = IXGBE_READ_REG(hw, IXGBE_EEC); eeprom_size = (u16)((eec & IXGBE_EEC_SIZE) >> - IXGBE_EEC_SIZE_SHIFT); + IXGBE_EEC_SIZE_SHIFT); eeprom->word_size = 1 << (eeprom_size + - IXGBE_EEPROM_WORD_SIZE_SHIFT); + IXGBE_EEPROM_WORD_SIZE_SHIFT); hw_dbg(hw, "Eeprom params: type = %d, size = %d\n", eeprom->type, eeprom->word_size); @@ -712,8 +712,7 @@ static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw) udelay(50); } } else { - hw_dbg(hw, "Software semaphore SMBI between device drivers " - "not granted.\n"); + hw_dbg(hw, "Software semaphore SMBI between device drivers not granted.\n"); } return status; @@ -813,7 +812,7 @@ static struct ixgbe_mac_operations mac_ops_X540 = { .clear_hw_cntrs = &ixgbe_clear_hw_cntrs_generic, .get_media_type = &ixgbe_get_media_type_X540, .get_supported_physical_layer = - &ixgbe_get_supported_physical_layer_X540, + &ixgbe_get_supported_physical_layer_X540, .enable_rx_dma = &ixgbe_enable_rx_dma_generic, .get_mac_addr = &ixgbe_get_mac_addr_generic, .get_san_mac_addr = &ixgbe_get_san_mac_addr_generic, diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 6e664d9..b78378c 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -2027,14 +2027,14 @@ jme_fill_tx_map(struct pci_dev *pdev, return 0; } -static void jme_drop_tx_map(struct jme_adapter *jme, int startidx, int endidx) +static void jme_drop_tx_map(struct jme_adapter *jme, int startidx, int count) { struct jme_ring *txring = &(jme->txring[0]); struct jme_buffer_info *txbi = txring->bufinf, *ctxbi; int mask = jme->tx_ring_mask; int j; - for (j = startidx ; j < endidx ; ++j) { + for (j = 0 ; j < count ; j++) { ctxbi = txbi + ((startidx + j + 2) & (mask)); pci_unmap_page(jme->pdev, ctxbi->mapping, @@ -2069,7 +2069,7 @@ jme_map_tx_skb(struct jme_adapter *jme, struct sk_buff *skb, int idx) skb_frag_page(frag), frag->page_offset, skb_frag_size(frag), hidma); if (ret) { - jme_drop_tx_map(jme, idx, idx+i); + jme_drop_tx_map(jme, idx, i); goto out; } @@ -2081,7 +2081,7 @@ jme_map_tx_skb(struct jme_adapter *jme, struct sk_buff *skb, int idx) ret = jme_fill_tx_map(jme->pdev, ctxdesc, ctxbi, virt_to_page(skb->data), offset_in_page(skb->data), len, hidma); if (ret) - jme_drop_tx_map(jme, idx, idx+i); + jme_drop_tx_map(jme, idx, i); out: return ret; @@ -2269,7 +2269,7 @@ jme_start_xmit(struct sk_buff *skb, struct net_device *netdev) } if (jme_fill_tx_desc(jme, skb, idx)) - return NETDEV_TX_BUSY; + return NETDEV_TX_OK; jwrite32(jme, JME_TXCS, jme->reg_txcs | TXCS_SELECT_QUEUE0 | diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index df1d1b9..b151a94 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -42,6 +42,7 @@ #include <linux/dma-mapping.h> #include <linux/in.h> #include <linux/ip.h> +#include <net/tso.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/etherdevice.h> @@ -179,10 +180,18 @@ static char mv643xx_eth_driver_version[] = "1.4"; * Misc definitions. */ #define DEFAULT_RX_QUEUE_SIZE 128 -#define DEFAULT_TX_QUEUE_SIZE 256 +#define DEFAULT_TX_QUEUE_SIZE 512 #define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES) +#define TSO_HEADER_SIZE 128 +/* Max number of allowed TCP segments for software TSO */ +#define MV643XX_MAX_TSO_SEGS 100 +#define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) + +#define IS_TSO_HEADER(txq, addr) \ + ((addr >= txq->tso_hdrs_dma) && \ + (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE)) /* * RX/TX descriptors. */ @@ -250,6 +259,7 @@ struct tx_desc { #define GEN_TCP_UDP_CHECKSUM 0x00020000 #define UDP_FRAME 0x00010000 #define MAC_HDR_EXTRA_4_BYTES 0x00008000 +#define GEN_TCP_UDP_CHK_FULL 0x00000400 #define MAC_HDR_EXTRA_8_BYTES 0x00000200 #define TX_IHL_SHIFT 11 @@ -345,6 +355,12 @@ struct tx_queue { int tx_curr_desc; int tx_used_desc; + int tx_stop_threshold; + int tx_wake_threshold; + + char *tso_hdrs; + dma_addr_t tso_hdrs_dma; + struct tx_desc *tx_desc_area; dma_addr_t tx_desc_dma; int tx_desc_area_size; @@ -491,7 +507,7 @@ static void txq_maybe_wake(struct tx_queue *txq) if (netif_tx_queue_stopped(nq)) { __netif_tx_lock(nq, smp_processor_id()); - if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1) + if (txq->tx_desc_count <= txq->tx_wake_threshold) netif_tx_wake_queue(nq); __netif_tx_unlock(nq); } @@ -661,6 +677,198 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb) return 0; } +static inline __be16 sum16_as_be(__sum16 sum) +{ + return (__force __be16)sum; +} + +static int skb_tx_csum(struct mv643xx_eth_private *mp, struct sk_buff *skb, + u16 *l4i_chk, u32 *command, int length) +{ + int ret; + u32 cmd = 0; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + int hdr_len; + int tag_bytes; + + BUG_ON(skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_8021Q)); + + hdr_len = (void *)ip_hdr(skb) - (void *)skb->data; + tag_bytes = hdr_len - ETH_HLEN; + + if (length - hdr_len > mp->shared->tx_csum_limit || + unlikely(tag_bytes & ~12)) { + ret = skb_checksum_help(skb); + if (!ret) + goto no_csum; + return ret; + } + + if (tag_bytes & 4) + cmd |= MAC_HDR_EXTRA_4_BYTES; + if (tag_bytes & 8) + cmd |= MAC_HDR_EXTRA_8_BYTES; + + cmd |= GEN_TCP_UDP_CHECKSUM | GEN_TCP_UDP_CHK_FULL | + GEN_IP_V4_CHECKSUM | + ip_hdr(skb)->ihl << TX_IHL_SHIFT; + + /* TODO: Revisit this. With the usage of GEN_TCP_UDP_CHK_FULL + * it seems we don't need to pass the initial checksum. */ + switch (ip_hdr(skb)->protocol) { + case IPPROTO_UDP: + cmd |= UDP_FRAME; + *l4i_chk = 0; + break; + case IPPROTO_TCP: + *l4i_chk = 0; + break; + default: + WARN(1, "protocol not supported"); + } + } else { +no_csum: + /* Errata BTS #50, IHL must be 5 if no HW checksum */ + cmd |= 5 << TX_IHL_SHIFT; + } + *command = cmd; + return 0; +} + +static inline int +txq_put_data_tso(struct net_device *dev, struct tx_queue *txq, + struct sk_buff *skb, char *data, int length, + bool last_tcp, bool is_last) +{ + int tx_index; + u32 cmd_sts; + struct tx_desc *desc; + + tx_index = txq->tx_curr_desc++; + if (txq->tx_curr_desc == txq->tx_ring_size) + txq->tx_curr_desc = 0; + desc = &txq->tx_desc_area[tx_index]; + + desc->l4i_chk = 0; + desc->byte_cnt = length; + desc->buf_ptr = dma_map_single(dev->dev.parent, data, + length, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, desc->buf_ptr))) { + WARN(1, "dma_map_single failed!\n"); + return -ENOMEM; + } + + cmd_sts = BUFFER_OWNED_BY_DMA; + if (last_tcp) { + /* last descriptor in the TCP packet */ + cmd_sts |= ZERO_PADDING | TX_LAST_DESC; + /* last descriptor in SKB */ + if (is_last) + cmd_sts |= TX_ENABLE_INTERRUPT; + } + desc->cmd_sts = cmd_sts; + return 0; +} + +static inline void +txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length) +{ + struct mv643xx_eth_private *mp = txq_to_mp(txq); + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int tx_index; + struct tx_desc *desc; + int ret; + u32 cmd_csum = 0; + u16 l4i_chk = 0; + + tx_index = txq->tx_curr_desc; + desc = &txq->tx_desc_area[tx_index]; + + ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_csum, length); + if (ret) + WARN(1, "failed to prepare checksum!"); + + /* Should we set this? Can't use the value from skb_tx_csum() + * as it's not the correct initial L4 checksum to use. */ + desc->l4i_chk = 0; + + desc->byte_cnt = hdr_len; + desc->buf_ptr = txq->tso_hdrs_dma + + txq->tx_curr_desc * TSO_HEADER_SIZE; + desc->cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA | TX_FIRST_DESC | + GEN_CRC; + + txq->tx_curr_desc++; + if (txq->tx_curr_desc == txq->tx_ring_size) + txq->tx_curr_desc = 0; +} + +static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb, + struct net_device *dev) +{ + struct mv643xx_eth_private *mp = txq_to_mp(txq); + int total_len, data_left, ret; + int desc_count = 0; + struct tso_t tso; + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + + /* Count needed descriptors */ + if ((txq->tx_desc_count + tso_count_descs(skb)) >= txq->tx_ring_size) { + netdev_dbg(dev, "not enough descriptors for TSO!\n"); + return -EBUSY; + } + + /* Initialize the TSO handler, and prepare the first payload */ + tso_start(skb, &tso); + + total_len = skb->len - hdr_len; + while (total_len > 0) { + char *hdr; + + data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); + total_len -= data_left; + desc_count++; + + /* prepare packet headers: MAC + IP + TCP */ + hdr = txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE; + tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); + txq_put_hdr_tso(skb, txq, data_left); + + while (data_left > 0) { + int size; + desc_count++; + + size = min_t(int, tso.size, data_left); + ret = txq_put_data_tso(dev, txq, skb, tso.data, size, + size == data_left, + total_len == 0); + if (ret) + goto err_release; + data_left -= size; + tso_build_data(skb, &tso, size); + } + } + + __skb_queue_tail(&txq->tx_skb, skb); + skb_tx_timestamp(skb); + + /* clear TX_END status */ + mp->work_tx_end &= ~(1 << txq->index); + + /* ensure all descriptors are written before poking hardware */ + wmb(); + txq_enable(txq); + txq->tx_desc_count += desc_count; + return 0; +err_release: + /* TODO: Release all used data descriptors; header descriptors must not + * be DMA-unmapped. + */ + return ret; +} + static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) { struct mv643xx_eth_private *mp = txq_to_mp(txq); @@ -671,8 +879,10 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) skb_frag_t *this_frag; int tx_index; struct tx_desc *desc; + void *addr; this_frag = &skb_shinfo(skb)->frags[frag]; + addr = page_address(this_frag->page.p) + this_frag->page_offset; tx_index = txq->tx_curr_desc++; if (txq->tx_curr_desc == txq->tx_ring_size) txq->tx_curr_desc = 0; @@ -692,19 +902,13 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) desc->l4i_chk = 0; desc->byte_cnt = skb_frag_size(this_frag); - desc->buf_ptr = skb_frag_dma_map(mp->dev->dev.parent, - this_frag, 0, - skb_frag_size(this_frag), - DMA_TO_DEVICE); + desc->buf_ptr = dma_map_single(mp->dev->dev.parent, addr, + desc->byte_cnt, DMA_TO_DEVICE); } } -static inline __be16 sum16_as_be(__sum16 sum) -{ - return (__force __be16)sum; -} - -static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) +static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb, + struct net_device *dev) { struct mv643xx_eth_private *mp = txq_to_mp(txq); int nr_frags = skb_shinfo(skb)->nr_frags; @@ -712,54 +916,22 @@ static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) struct tx_desc *desc; u32 cmd_sts; u16 l4i_chk; - int length; + int length, ret; - cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA; + cmd_sts = 0; l4i_chk = 0; - if (skb->ip_summed == CHECKSUM_PARTIAL) { - int hdr_len; - int tag_bytes; - - BUG_ON(skb->protocol != htons(ETH_P_IP) && - skb->protocol != htons(ETH_P_8021Q)); - - hdr_len = (void *)ip_hdr(skb) - (void *)skb->data; - tag_bytes = hdr_len - ETH_HLEN; - if (skb->len - hdr_len > mp->shared->tx_csum_limit || - unlikely(tag_bytes & ~12)) { - if (skb_checksum_help(skb) == 0) - goto no_csum; - dev_kfree_skb_any(skb); - return 1; - } - - if (tag_bytes & 4) - cmd_sts |= MAC_HDR_EXTRA_4_BYTES; - if (tag_bytes & 8) - cmd_sts |= MAC_HDR_EXTRA_8_BYTES; - - cmd_sts |= GEN_TCP_UDP_CHECKSUM | - GEN_IP_V4_CHECKSUM | - ip_hdr(skb)->ihl << TX_IHL_SHIFT; - - switch (ip_hdr(skb)->protocol) { - case IPPROTO_UDP: - cmd_sts |= UDP_FRAME; - l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check)); - break; - case IPPROTO_TCP: - l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check)); - break; - default: - BUG(); - } - } else { -no_csum: - /* Errata BTS #50, IHL must be 5 if no HW checksum */ - cmd_sts |= 5 << TX_IHL_SHIFT; + if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { + if (net_ratelimit()) + netdev_err(dev, "tx queue full?!\n"); + return -EBUSY; } + ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_sts, skb->len); + if (ret) + return ret; + cmd_sts |= TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA; + tx_index = txq->tx_curr_desc++; if (txq->tx_curr_desc == txq->tx_ring_size) txq->tx_curr_desc = 0; @@ -801,7 +973,7 @@ no_csum: static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - int length, queue; + int length, queue, ret; struct tx_queue *txq; struct netdev_queue *nq; @@ -810,30 +982,26 @@ static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) nq = netdev_get_tx_queue(dev, queue); if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { - txq->tx_dropped++; netdev_printk(KERN_DEBUG, dev, "failed to linearize skb with tiny unaligned fragment\n"); return NETDEV_TX_BUSY; } - if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { - if (net_ratelimit()) - netdev_err(dev, "tx queue full?!\n"); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - length = skb->len; - if (!txq_submit_skb(txq, skb)) { - int entries_left; - + if (skb_is_gso(skb)) + ret = txq_submit_tso(txq, skb, dev); + else + ret = txq_submit_skb(txq, skb, dev); + if (!ret) { txq->tx_bytes += length; txq->tx_packets++; - entries_left = txq->tx_ring_size - txq->tx_desc_count; - if (entries_left < MAX_SKB_FRAGS + 1) + if (txq->tx_desc_count >= txq->tx_stop_threshold) netif_tx_stop_queue(nq); + } else { + txq->tx_dropped++; + dev_kfree_skb_any(skb); } return NETDEV_TX_OK; @@ -907,14 +1075,9 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) mp->dev->stats.tx_errors++; } - if (cmd_sts & TX_FIRST_DESC) { + if (!IS_TSO_HEADER(txq, desc->buf_ptr)) dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr, desc->byte_cnt, DMA_TO_DEVICE); - } else { - dma_unmap_page(mp->dev->dev.parent, desc->buf_ptr, - desc->byte_cnt, DMA_TO_DEVICE); - } - dev_kfree_skb(skb); } @@ -1010,8 +1173,9 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq) /* mii management interface *************************************************/ -static void mv643xx_adjust_pscr(struct mv643xx_eth_private *mp) +static void mv643xx_eth_adjust_link(struct net_device *dev) { + struct mv643xx_eth_private *mp = netdev_priv(dev); u32 pscr = rdlp(mp, PORT_SERIAL_CONTROL); u32 autoneg_disable = FORCE_LINK_PASS | DISABLE_AUTO_NEG_SPEED_GMII | @@ -1387,7 +1551,7 @@ mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) ret = phy_ethtool_sset(mp->phy, cmd); if (!ret) - mv643xx_adjust_pscr(mp); + mv643xx_eth_adjust_link(dev); return ret; } @@ -1456,7 +1620,11 @@ mv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er) return -EINVAL; mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096; - mp->tx_ring_size = er->tx_pending < 4096 ? er->tx_pending : 4096; + mp->tx_ring_size = clamp_t(unsigned int, er->tx_pending, + MV643XX_MAX_SKB_DESCS * 2, 4096); + if (mp->tx_ring_size != er->tx_pending) + netdev_warn(dev, "TX queue size set to %u (requested %u)\n", + mp->tx_ring_size, er->tx_pending); if (netif_running(dev)) { mv643xx_eth_stop(dev); @@ -1832,6 +2000,13 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) txq->tx_ring_size = mp->tx_ring_size; + /* A queue must always have room for at least one skb. + * Therefore, stop the queue when the free entries reaches + * the maximum number of descriptors per skb. + */ + txq->tx_stop_threshold = txq->tx_ring_size - MV643XX_MAX_SKB_DESCS; + txq->tx_wake_threshold = txq->tx_stop_threshold / 2; + txq->tx_desc_count = 0; txq->tx_curr_desc = 0; txq->tx_used_desc = 0; @@ -1871,6 +2046,15 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) nexti * sizeof(struct tx_desc); } + /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ + txq->tso_hdrs = dma_alloc_coherent(mp->dev->dev.parent, + txq->tx_ring_size * TSO_HEADER_SIZE, + &txq->tso_hdrs_dma, GFP_KERNEL); + if (txq->tso_hdrs == NULL) { + dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, + txq->tx_desc_area, txq->tx_desc_dma); + return -ENOMEM; + } skb_queue_head_init(&txq->tx_skb); return 0; @@ -1891,6 +2075,10 @@ static void txq_deinit(struct tx_queue *txq) else dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, txq->tx_desc_area, txq->tx_desc_dma); + if (txq->tso_hdrs) + dma_free_coherent(mp->dev->dev.parent, + txq->tx_ring_size * TSO_HEADER_SIZE, + txq->tso_hdrs, txq->tso_hdrs_dma); } @@ -2303,7 +2491,7 @@ static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ret = phy_mii_ioctl(mp->phy, ifr, cmd); if (!ret) - mv643xx_adjust_pscr(mp); + mv643xx_eth_adjust_link(dev); return ret; } @@ -2678,6 +2866,7 @@ static void set_params(struct mv643xx_eth_private *mp, struct mv643xx_eth_platform_data *pd) { struct net_device *dev = mp->dev; + unsigned int tx_ring_size; if (is_valid_ether_addr(pd->mac_addr)) memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); @@ -2692,22 +2881,22 @@ static void set_params(struct mv643xx_eth_private *mp, mp->rxq_count = pd->rx_queue_count ? : 1; - mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE; + tx_ring_size = DEFAULT_TX_QUEUE_SIZE; if (pd->tx_queue_size) - mp->tx_ring_size = pd->tx_queue_size; + tx_ring_size = pd->tx_queue_size; + + mp->tx_ring_size = clamp_t(unsigned int, tx_ring_size, + MV643XX_MAX_SKB_DESCS * 2, 4096); + if (mp->tx_ring_size != tx_ring_size) + netdev_warn(dev, "TX queue size set to %u (requested %u)\n", + mp->tx_ring_size, tx_ring_size); + mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_size = pd->tx_sram_size; mp->txq_count = pd->tx_queue_count ? : 1; } -static void mv643xx_eth_adjust_link(struct net_device *dev) -{ - struct mv643xx_eth_private *mp = netdev_priv(dev); - - mv643xx_adjust_pscr(mp); -} - static struct phy_device *phy_scan(struct mv643xx_eth_private *mp, int phy_addr) { @@ -2921,11 +3110,14 @@ static int mv643xx_eth_probe(struct platform_device *pdev) dev->watchdog_timeo = 2 * HZ; dev->base_addr = 0; - dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM; - dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM; - dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM; + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + dev->vlan_features = dev->features; + + dev->features |= NETIF_F_RXCSUM; + dev->hw_features = dev->features; dev->priv_flags |= IFF_UNICAST_FLT; + dev->gso_max_segs = MV643XX_MAX_TSO_SEGS; SET_NETDEV_DEV(dev, &pdev->dev); diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 9d5ced2..fc2fb25 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -195,11 +195,10 @@ static int orion_mdio_probe(struct platform_device *pdev) return -ENODEV; } - bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev)); - if (!bus) { - dev_err(&pdev->dev, "Cannot allocate MDIO bus\n"); + bus = devm_mdiobus_alloc_size(&pdev->dev, + sizeof(struct orion_mdio_dev)); + if (!bus) return -ENOMEM; - } bus->name = "orion_mdio_bus"; bus->read = orion_mdio_read; @@ -208,11 +207,10 @@ static int orion_mdio_probe(struct platform_device *pdev) dev_name(&pdev->dev)); bus->parent = &pdev->dev; - bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!bus->irq) { - mdiobus_free(bus); + bus->irq = devm_kmalloc_array(&pdev->dev, PHY_MAX_ADDR, sizeof(int), + GFP_KERNEL); + if (!bus->irq) return -ENOMEM; - } for (i = 0; i < PHY_MAX_ADDR; i++) bus->irq[i] = PHY_POLL; @@ -264,8 +262,6 @@ static int orion_mdio_probe(struct platform_device *pdev) out_mdio: if (!IS_ERR(dev->clk)) clk_disable_unprepare(dev->clk); - kfree(bus->irq); - mdiobus_free(bus); return ret; } @@ -276,8 +272,6 @@ static int orion_mdio_remove(struct platform_device *pdev) writel(0, dev->regs + MVMDIO_ERR_INT_MASK); mdiobus_unregister(bus); - kfree(bus->irq); - mdiobus_free(bus); if (!IS_ERR(dev->clk)) clk_disable_unprepare(dev->clk); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 72bc47f..45beca1 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -23,6 +23,7 @@ #include <net/ip.h> #include <net/ipv6.h> #include <linux/io.h> +#include <net/tso.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_mdio.h> @@ -218,9 +219,6 @@ #define MVNETA_RX_COAL_PKTS 32 #define MVNETA_RX_COAL_USEC 100 -/* Napi polling weight */ -#define MVNETA_RX_POLL_WEIGHT 64 - /* The two bytes Marvell header. Either contains a special value used * by Marvell switches when a specific hardware mode is enabled (not * supported by this driver) or is filled automatically by zeroes on @@ -244,12 +242,20 @@ #define MVNETA_TX_MTU_MAX 0x3ffff +/* TSO header size */ +#define TSO_HEADER_SIZE 128 + /* Max number of Rx descriptors */ #define MVNETA_MAX_RXD 128 /* Max number of Tx descriptors */ #define MVNETA_MAX_TXD 532 +/* Max number of allowed TCP segments for software TSO */ +#define MVNETA_MAX_TSO_SEGS 100 + +#define MVNETA_MAX_SKB_DESCS (MVNETA_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) + /* descriptor aligned size */ #define MVNETA_DESC_ALIGNED_SIZE 32 @@ -258,6 +264,10 @@ ETH_HLEN + ETH_FCS_LEN, \ MVNETA_CPU_D_CACHE_LINE_SIZE) +#define IS_TSO_HEADER(txq, addr) \ + ((addr >= txq->tso_hdrs_phys) && \ + (addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE)) + #define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD) struct mvneta_pcpu_stats { @@ -279,9 +289,6 @@ struct mvneta_port { u32 cause_rx_tx; struct napi_struct napi; - /* Napi weight */ - int weight; - /* Core clock */ struct clk *clk; u8 mcast_count[256]; @@ -390,6 +397,8 @@ struct mvneta_tx_queue { * descriptor ring */ int count; + int tx_stop_threshold; + int tx_wake_threshold; /* Array of transmitted skb */ struct sk_buff **tx_skb; @@ -413,6 +422,12 @@ struct mvneta_tx_queue { /* Index of the next TX DMA descriptor to process */ int next_desc_to_proc; + + /* DMA buffers for TSO headers */ + char *tso_hdrs; + + /* DMA address of TSO headers */ + dma_addr_t tso_hdrs_phys; }; struct mvneta_rx_queue { @@ -441,7 +456,10 @@ struct mvneta_rx_queue { int next_desc_to_proc; }; -static int rxq_number = 8; +/* The hardware supports eight (8) rx queues, but we are only allowing + * the first one to be used. Therefore, let's just allocate one queue. + */ +static int rxq_number = 1; static int txq_number = 8; static int rxq_def; @@ -1277,11 +1295,12 @@ static void mvneta_txq_bufs_free(struct mvneta_port *pp, mvneta_txq_inc_get(txq); + if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr)) + dma_unmap_single(pp->dev->dev.parent, + tx_desc->buf_phys_addr, + tx_desc->data_size, DMA_TO_DEVICE); if (!skb) continue; - - dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr, - tx_desc->data_size, DMA_TO_DEVICE); dev_kfree_skb_any(skb); } } @@ -1302,7 +1321,7 @@ static void mvneta_txq_done(struct mvneta_port *pp, txq->count -= tx_done; if (netif_tx_queue_stopped(nq)) { - if (txq->size - txq->count >= MAX_SKB_FRAGS + 1) + if (txq->count <= txq->tx_wake_threshold) netif_tx_wake_queue(nq); } } @@ -1519,14 +1538,134 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, return rx_done; } +static inline void +mvneta_tso_put_hdr(struct sk_buff *skb, + struct mvneta_port *pp, struct mvneta_tx_queue *txq) +{ + struct mvneta_tx_desc *tx_desc; + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + + txq->tx_skb[txq->txq_put_index] = NULL; + tx_desc = mvneta_txq_next_desc_get(txq); + tx_desc->data_size = hdr_len; + tx_desc->command = mvneta_skb_tx_csum(pp, skb); + tx_desc->command |= MVNETA_TXD_F_DESC; + tx_desc->buf_phys_addr = txq->tso_hdrs_phys + + txq->txq_put_index * TSO_HEADER_SIZE; + mvneta_txq_inc_put(txq); +} + +static inline int +mvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq, + struct sk_buff *skb, char *data, int size, + bool last_tcp, bool is_last) +{ + struct mvneta_tx_desc *tx_desc; + + tx_desc = mvneta_txq_next_desc_get(txq); + tx_desc->data_size = size; + tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, data, + size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, + tx_desc->buf_phys_addr))) { + mvneta_txq_desc_put(txq); + return -ENOMEM; + } + + tx_desc->command = 0; + txq->tx_skb[txq->txq_put_index] = NULL; + + if (last_tcp) { + /* last descriptor in the TCP packet */ + tx_desc->command = MVNETA_TXD_L_DESC; + + /* last descriptor in SKB */ + if (is_last) + txq->tx_skb[txq->txq_put_index] = skb; + } + mvneta_txq_inc_put(txq); + return 0; +} + +static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, + struct mvneta_tx_queue *txq) +{ + int total_len, data_left; + int desc_count = 0; + struct mvneta_port *pp = netdev_priv(dev); + struct tso_t tso; + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int i; + + /* Count needed descriptors */ + if ((txq->count + tso_count_descs(skb)) >= txq->size) + return 0; + + if (skb_headlen(skb) < (skb_transport_offset(skb) + tcp_hdrlen(skb))) { + pr_info("*** Is this even possible???!?!?\n"); + return 0; + } + + /* Initialize the TSO handler, and prepare the first payload */ + tso_start(skb, &tso); + + total_len = skb->len - hdr_len; + while (total_len > 0) { + char *hdr; + + data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); + total_len -= data_left; + desc_count++; + + /* prepare packet headers: MAC + IP + TCP */ + hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE; + tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); + + mvneta_tso_put_hdr(skb, pp, txq); + + while (data_left > 0) { + int size; + desc_count++; + + size = min_t(int, tso.size, data_left); + + if (mvneta_tso_put_data(dev, txq, skb, + tso.data, size, + size == data_left, + total_len == 0)) + goto err_release; + data_left -= size; + + tso_build_data(skb, &tso, size); + } + } + + return desc_count; + +err_release: + /* Release all used data descriptors; header descriptors must not + * be DMA-unmapped. + */ + for (i = desc_count - 1; i >= 0; i--) { + struct mvneta_tx_desc *tx_desc = txq->descs + i; + if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr)) + dma_unmap_single(pp->dev->dev.parent, + tx_desc->buf_phys_addr, + tx_desc->data_size, + DMA_TO_DEVICE); + mvneta_txq_desc_put(txq); + } + return 0; +} + /* Handle tx fragmentation processing */ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb, struct mvneta_tx_queue *txq) { struct mvneta_tx_desc *tx_desc; - int i; + int i, nr_frags = skb_shinfo(skb)->nr_frags; - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + for (i = 0; i < nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; void *addr = page_address(frag->page.p) + frag->page_offset; @@ -1543,20 +1682,16 @@ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb, goto error; } - if (i == (skb_shinfo(skb)->nr_frags - 1)) { + if (i == nr_frags - 1) { /* Last descriptor */ tx_desc->command = MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD; - txq->tx_skb[txq->txq_put_index] = skb; - - mvneta_txq_inc_put(txq); } else { /* Descriptor in the middle: Not First, Not Last */ tx_desc->command = 0; - txq->tx_skb[txq->txq_put_index] = NULL; - mvneta_txq_inc_put(txq); } + mvneta_txq_inc_put(txq); } return 0; @@ -1584,15 +1719,18 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev) u16 txq_id = skb_get_queue_mapping(skb); struct mvneta_tx_queue *txq = &pp->txqs[txq_id]; struct mvneta_tx_desc *tx_desc; - struct netdev_queue *nq; int frags = 0; u32 tx_cmd; if (!netif_running(dev)) goto out; + if (skb_is_gso(skb)) { + frags = mvneta_tx_tso(skb, dev, txq); + goto out; + } + frags = skb_shinfo(skb)->nr_frags + 1; - nq = netdev_get_tx_queue(dev, txq_id); /* Get a descriptor for the first part of the packet */ tx_desc = mvneta_txq_next_desc_get(txq); @@ -1635,15 +1773,16 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev) } } - txq->count += frags; - mvneta_txq_pend_desc_add(pp, txq, frags); - - if (txq->size - txq->count < MAX_SKB_FRAGS + 1) - netif_tx_stop_queue(nq); - out: if (frags > 0) { struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); + struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); + + txq->count += frags; + mvneta_txq_pend_desc_add(pp, txq, frags); + + if (txq->count >= txq->tx_stop_threshold) + netif_tx_stop_queue(nq); u64_stats_update_begin(&stats->syncp); stats->tx_packets++; @@ -2003,7 +2142,7 @@ static void mvneta_tx_reset(struct mvneta_port *pp) { int queue; - /* free the skb's in the hal tx ring */ + /* free the skb's in the tx ring */ for (queue = 0; queue < txq_number; queue++) mvneta_txq_done_force(pp, &pp->txqs[queue]); @@ -2081,6 +2220,14 @@ static int mvneta_txq_init(struct mvneta_port *pp, { txq->size = pp->tx_ring_size; + /* A queue must always have room for at least one skb. + * Therefore, stop the queue when the free entries reaches + * the maximum number of descriptors per skb. + */ + txq->tx_stop_threshold = txq->size - MVNETA_MAX_SKB_DESCS; + txq->tx_wake_threshold = txq->tx_stop_threshold / 2; + + /* Allocate memory for TX descriptors */ txq->descs = dma_alloc_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, @@ -2109,6 +2256,18 @@ static int mvneta_txq_init(struct mvneta_port *pp, txq->descs, txq->descs_phys); return -ENOMEM; } + + /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ + txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent, + txq->size * TSO_HEADER_SIZE, + &txq->tso_hdrs_phys, GFP_KERNEL); + if (txq->tso_hdrs == NULL) { + kfree(txq->tx_skb); + dma_free_coherent(pp->dev->dev.parent, + txq->size * MVNETA_DESC_ALIGNED_SIZE, + txq->descs, txq->descs_phys); + return -ENOMEM; + } mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal); return 0; @@ -2120,6 +2279,10 @@ static void mvneta_txq_deinit(struct mvneta_port *pp, { kfree(txq->tx_skb); + if (txq->tso_hdrs) + dma_free_coherent(pp->dev->dev.parent, + txq->size * TSO_HEADER_SIZE, + txq->tso_hdrs, txq->tso_hdrs_phys); if (txq->descs) dma_free_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, @@ -2279,24 +2442,28 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu) return 0; /* The interface is running, so we have to force a - * reallocation of the RXQs + * reallocation of the queues */ mvneta_stop_dev(pp); mvneta_cleanup_txqs(pp); mvneta_cleanup_rxqs(pp); - pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu); + pp->pkt_size = MVNETA_RX_PKT_SIZE(dev->mtu); pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ret = mvneta_setup_rxqs(pp); if (ret) { - netdev_err(pp->dev, "unable to setup rxqs after MTU change\n"); + netdev_err(dev, "unable to setup rxqs after MTU change\n"); return ret; } - mvneta_setup_txqs(pp); + ret = mvneta_setup_txqs(pp); + if (ret) { + netdev_err(dev, "unable to setup txqs after MTU change\n"); + return ret; + } mvneta_start_dev(pp); mvneta_port_up(pp); @@ -2323,22 +2490,19 @@ static void mvneta_get_mac_addr(struct mvneta_port *pp, unsigned char *addr) static int mvneta_set_mac_addr(struct net_device *dev, void *addr) { struct mvneta_port *pp = netdev_priv(dev); - u8 *mac = addr + 2; - int i; - - if (netif_running(dev)) - return -EBUSY; + struct sockaddr *sockaddr = addr; + int ret; + ret = eth_prepare_mac_addr_change(dev, addr); + if (ret < 0) + return ret; /* Remove previous address table entry */ mvneta_mac_addr_set(pp, dev->dev_addr, -1); /* Set new addr in hw */ - mvneta_mac_addr_set(pp, mac, rxq_def); - - /* Set addr in the device */ - for (i = 0; i < ETH_ALEN; i++) - dev->dev_addr[i] = mac[i]; + mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def); + eth_commit_mac_addr_change(dev, addr); return 0; } @@ -2433,8 +2597,6 @@ static int mvneta_open(struct net_device *dev) struct mvneta_port *pp = netdev_priv(dev); int ret; - mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def); - pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu); pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); @@ -2600,8 +2762,12 @@ static int mvneta_ethtool_set_ringparam(struct net_device *dev, return -EINVAL; pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ? ring->rx_pending : MVNETA_MAX_RXD; - pp->tx_ring_size = ring->tx_pending < MVNETA_MAX_TXD ? - ring->tx_pending : MVNETA_MAX_TXD; + + pp->tx_ring_size = clamp_t(u16, ring->tx_pending, + MVNETA_MAX_SKB_DESCS * 2, MVNETA_MAX_TXD); + if (pp->tx_ring_size != ring->tx_pending) + netdev_warn(dev, "TX queue size set to %u (requested %u)\n", + pp->tx_ring_size, ring->tx_pending); if (netif_running(dev)) { mvneta_stop(dev); @@ -2638,7 +2804,7 @@ const struct ethtool_ops mvneta_eth_tool_ops = { }; /* Initialize hw */ -static int mvneta_init(struct mvneta_port *pp, int phy_addr) +static int mvneta_init(struct device *dev, struct mvneta_port *pp) { int queue; @@ -2648,8 +2814,8 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr) /* Set port default values */ mvneta_defaults_set(pp); - pp->txqs = kzalloc(txq_number * sizeof(struct mvneta_tx_queue), - GFP_KERNEL); + pp->txqs = devm_kcalloc(dev, txq_number, sizeof(struct mvneta_tx_queue), + GFP_KERNEL); if (!pp->txqs) return -ENOMEM; @@ -2661,12 +2827,10 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr) txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS; } - pp->rxqs = kzalloc(rxq_number * sizeof(struct mvneta_rx_queue), - GFP_KERNEL); - if (!pp->rxqs) { - kfree(pp->txqs); + pp->rxqs = devm_kcalloc(dev, rxq_number, sizeof(struct mvneta_rx_queue), + GFP_KERNEL); + if (!pp->rxqs) return -ENOMEM; - } /* Create Rx descriptor rings */ for (queue = 0; queue < rxq_number; queue++) { @@ -2680,12 +2844,6 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr) return 0; } -static void mvneta_deinit(struct mvneta_port *pp) -{ - kfree(pp->txqs); - kfree(pp->rxqs); -} - /* platform glue : initialize decoding windows */ static void mvneta_conf_mbus_windows(struct mvneta_port *pp, const struct mbus_dram_target_info *dram) @@ -2768,7 +2926,6 @@ static int mvneta_probe(struct platform_device *pdev) struct resource *res; struct device_node *dn = pdev->dev.of_node; struct device_node *phy_node; - u32 phy_addr; struct mvneta_port *pp; struct net_device *dev; const char *dt_mac_addr; @@ -2797,9 +2954,22 @@ static int mvneta_probe(struct platform_device *pdev) phy_node = of_parse_phandle(dn, "phy", 0); if (!phy_node) { - dev_err(&pdev->dev, "no associated PHY\n"); - err = -ENODEV; - goto err_free_irq; + if (!of_phy_is_fixed_link(dn)) { + dev_err(&pdev->dev, "no PHY specified\n"); + err = -ENODEV; + goto err_free_irq; + } + + err = of_phy_register_fixed_link(dn); + if (err < 0) { + dev_err(&pdev->dev, "cannot register fixed PHY\n"); + goto err_free_irq; + } + + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + phy_node = dn; } phy_mode = of_get_phy_mode(dn); @@ -2816,8 +2986,6 @@ static int mvneta_probe(struct platform_device *pdev) dev->ethtool_ops = &mvneta_eth_tool_ops; pp = netdev_priv(dev); - - pp->weight = MVNETA_RX_POLL_WEIGHT; pp->phy_node = phy_node; pp->phy_interface = phy_mode; @@ -2864,33 +3032,32 @@ static int mvneta_probe(struct platform_device *pdev) pp->dev = dev; SET_NETDEV_DEV(dev, &pdev->dev); - err = mvneta_init(pp, phy_addr); - if (err < 0) { - dev_err(&pdev->dev, "can't init eth hal\n"); + err = mvneta_init(&pdev->dev, pp); + if (err < 0) goto err_free_stats; - } err = mvneta_port_power_up(pp, phy_mode); if (err < 0) { dev_err(&pdev->dev, "can't power up port\n"); - goto err_deinit; + goto err_free_stats; } dram_target_info = mv_mbus_dram_info(); if (dram_target_info) mvneta_conf_mbus_windows(pp, dram_target_info); - netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight); + netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT); - dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; - dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM; - dev->vlan_features |= NETIF_F_SG | NETIF_F_IP_CSUM; + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + dev->hw_features |= dev->features; + dev->vlan_features |= dev->features; dev->priv_flags |= IFF_UNICAST_FLT; + dev->gso_max_segs = MVNETA_MAX_TSO_SEGS; err = register_netdev(dev); if (err < 0) { dev_err(&pdev->dev, "failed to register\n"); - goto err_deinit; + goto err_free_stats; } netdev_info(dev, "Using %s mac address %pM\n", mac_from, @@ -2900,8 +3067,6 @@ static int mvneta_probe(struct platform_device *pdev) return 0; -err_deinit: - mvneta_deinit(pp); err_free_stats: free_percpu(pp->stats); err_clk: @@ -2920,7 +3085,6 @@ static int mvneta_remove(struct platform_device *pdev) struct mvneta_port *pp = netdev_priv(dev); unregister_netdev(dev); - mvneta_deinit(pp); clk_disable_unprepare(pp->clk); free_percpu(pp->stats); irq_dispose_mapping(dev->irq); diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 357dcb0..a89e464 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1262,12 +1262,12 @@ static struct mlx4_cmd_info cmd_info[] = { }, { .opcode = MLX4_CMD_UPDATE_QP, - .has_inbox = false, + .has_inbox = true, .has_outbox = false, .out_is_imm = false, .encode_slave_id = false, .verify = NULL, - .wrapper = mlx4_CMD_EPERM_wrapper + .wrapper = mlx4_UPDATE_QP_wrapper }, { .opcode = MLX4_CMD_GET_OP_REQ, @@ -1887,9 +1887,9 @@ void mlx4_master_comm_channel(struct work_struct *work) if (toggle != slt) { if (master->slave_state[slave].comm_toggle != slt) { - printk(KERN_INFO "slave %d out of sync. read toggle %d, state toggle %d. Resynching.\n", - slave, slt, - master->slave_state[slave].comm_toggle); + pr_info("slave %d out of sync. read toggle %d, state toggle %d. Resynching.\n", + slave, slt, + master->slave_state[slave].comm_toggle); master->slave_state[slave].comm_toggle = slt; } @@ -2486,11 +2486,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in ivf->mac[4] = ((s_info->mac >> (1*8)) & 0xff); ivf->mac[5] = ((s_info->mac) & 0xff); - ivf->vlan = s_info->default_vlan; - ivf->qos = s_info->default_qos; - ivf->tx_rate = s_info->tx_rate; - ivf->spoofchk = s_info->spoofchk; - ivf->linkstate = s_info->link_state; + ivf->vlan = s_info->default_vlan; + ivf->qos = s_info->default_qos; + ivf->max_tx_rate = s_info->tx_rate; + ivf->min_tx_rate = 0; + ivf->spoofchk = s_info->spoofchk; + ivf->linkstate = s_info->link_state; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 263a1c7..c021c96 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -1121,7 +1121,7 @@ static int mlx4_en_set_channels(struct net_device *dev, { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; - int port_up; + int port_up = 0; int err = 0; if (channel->other_count || channel->combined_count || diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a56f601..38e9a4c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -132,8 +132,7 @@ MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)"); static bool use_prio; module_param_named(use_prio, use_prio, bool, 0444); -MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports " - "(0/1, default 0)"); +MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports (deprecated)"); int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG); module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444); @@ -290,7 +289,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.log_num_macs = log_num_mac; dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS; - dev->caps.log_num_prios = use_prio ? 3 : 0; for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.port_type[i] = MLX4_PORT_TYPE_NONE; @@ -358,7 +356,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_ADDR] = (1 << dev->caps.log_num_macs) * (1 << dev->caps.log_num_vlans) * - (1 << dev->caps.log_num_prios) * dev->caps.num_ports; dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_EXCH] = MLX4_NUM_FEXCH; @@ -2596,7 +2593,7 @@ static void __mlx4_remove_one(struct pci_dev *pdev) /* in SRIOV it is not allowed to unload the pf's * driver while there are alive vf's */ if (mlx4_is_master(dev) && mlx4_how_many_lives_vf(dev)) - printk(KERN_ERR "Removing PF when there are assigned VF's !!!\n"); + pr_warn("Removing PF when there are assigned VF's !!!\n"); mlx4_stop_sense(dev); mlx4_unregister_device(dev); @@ -2767,22 +2764,26 @@ static struct pci_driver mlx4_driver = { static int __init mlx4_verify_params(void) { if ((log_num_mac < 0) || (log_num_mac > 7)) { - pr_warning("mlx4_core: bad num_mac: %d\n", log_num_mac); + pr_warn("mlx4_core: bad num_mac: %d\n", log_num_mac); return -1; } if (log_num_vlan != 0) - pr_warning("mlx4_core: log_num_vlan - obsolete module param, using %d\n", - MLX4_LOG_NUM_VLANS); + pr_warn("mlx4_core: log_num_vlan - obsolete module param, using %d\n", + MLX4_LOG_NUM_VLANS); + + if (use_prio != 0) + pr_warn("mlx4_core: use_prio - obsolete module param, ignored\n"); if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) { - pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg); + pr_warn("mlx4_core: bad log_mtts_per_seg: %d\n", + log_mtts_per_seg); return -1; } /* Check if module param for ports type has legal combination */ if (port_type_array[0] == false && port_type_array[1] == true) { - printk(KERN_WARNING "Module parameter configuration ETH/IB is not supported. Switching to default configuration IB/IB\n"); + pr_warn("Module parameter configuration ETH/IB is not supported. Switching to default configuration IB/IB\n"); port_type_array[0] = true; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 52c1e7d..9dd1b30 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1196,6 +1196,12 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd); +int mlx4_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd); + int mlx4_PROMISC_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 64fb3e6..3e04ea1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -954,8 +954,7 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr, mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) { err = PTR_ERR(mailbox); - printk(KERN_WARNING "mlx4_ib: mlx4_alloc_cmd_mailbox failed (%d)\n", - err); + pr_warn("mlx4_ib: mlx4_alloc_cmd_mailbox failed (%d)\n", err); return; } @@ -964,8 +963,7 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr, (dev->caps.num_mpts - 1)); mlx4_free_cmd_mailbox(dev, mailbox); if (err) { - printk(KERN_WARNING "mlx4_ib: mlx4_HW2SW_MPT failed (%d)\n", - err); + pr_warn("mlx4_ib: mlx4_HW2SW_MPT failed (%d)\n", err); return; } fmr->mr.enabled = MLX4_MPT_EN_SW; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 9bdb6ae..1d3234a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -389,6 +389,41 @@ err_icm: EXPORT_SYMBOL_GPL(mlx4_qp_alloc); +#define MLX4_UPDATE_QP_SUPPORTED_ATTRS MLX4_UPDATE_QP_SMAC +int mlx4_update_qp(struct mlx4_dev *dev, struct mlx4_qp *qp, + enum mlx4_update_qp_attr attr, + struct mlx4_update_qp_params *params) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_update_qp_context *cmd; + u64 pri_addr_path_mask = 0; + int err = 0; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + cmd = (struct mlx4_update_qp_context *)mailbox->buf; + + if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) + return -EINVAL; + + if (attr & MLX4_UPDATE_QP_SMAC) { + pri_addr_path_mask |= 1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX; + cmd->qp_context.pri_path.grh_mylmc = params->smac_index; + } + + cmd->primary_addr_path_mask = cpu_to_be64(pri_addr_path_mask); + + err = mlx4_cmd(dev, mailbox->dma, qp->qpn & 0xffffff, 0, + MLX4_CMD_UPDATE_QP, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_update_qp); + void mlx4_qp_remove(struct mlx4_dev *dev, struct mlx4_qp *qp) { struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index a95df9d..dd821b3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -962,7 +962,7 @@ static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave, ret = alloc_srq_tr(id); break; case RES_MAC: - printk(KERN_ERR "implementation missing\n"); + pr_err("implementation missing\n"); return NULL; case RES_COUNTER: ret = alloc_counter_tr(id); @@ -1056,10 +1056,10 @@ static int remove_mtt_ok(struct res_mtt *res, int order) { if (res->com.state == RES_MTT_BUSY || atomic_read(&res->ref_count)) { - printk(KERN_DEBUG "%s-%d: state %s, ref_count %d\n", - __func__, __LINE__, - mtt_states_str(res->com.state), - atomic_read(&res->ref_count)); + pr_devel("%s-%d: state %s, ref_count %d\n", + __func__, __LINE__, + mtt_states_str(res->com.state), + atomic_read(&res->ref_count)); return -EBUSY; } else if (res->com.state != RES_MTT_ALLOCATED) return -EPERM; @@ -3895,6 +3895,60 @@ static int add_eth_header(struct mlx4_dev *dev, int slave, } +#define MLX4_UPD_QP_PATH_MASK_SUPPORTED (1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX) +int mlx4_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd_info) +{ + int err; + u32 qpn = vhcr->in_modifier & 0xffffff; + struct res_qp *rqp; + u64 mac; + unsigned port; + u64 pri_addr_path_mask; + struct mlx4_update_qp_context *cmd; + int smac_index; + + cmd = (struct mlx4_update_qp_context *)inbox->buf; + + pri_addr_path_mask = be64_to_cpu(cmd->primary_addr_path_mask); + if (cmd->qp_mask || cmd->secondary_addr_path_mask || + (pri_addr_path_mask & ~MLX4_UPD_QP_PATH_MASK_SUPPORTED)) + return -EPERM; + + /* Just change the smac for the QP */ + err = get_res(dev, slave, qpn, RES_QP, &rqp); + if (err) { + mlx4_err(dev, "Updating qpn 0x%x for slave %d rejected\n", qpn, slave); + return err; + } + + port = (rqp->sched_queue >> 6 & 1) + 1; + smac_index = cmd->qp_context.pri_path.grh_mylmc; + err = mac_find_smac_ix_in_slave(dev, slave, port, + smac_index, &mac); + if (err) { + mlx4_err(dev, "Failed to update qpn 0x%x, MAC is invalid. smac_ix: %d\n", + qpn, smac_index); + goto err_mac; + } + + err = mlx4_cmd(dev, inbox->dma, + vhcr->in_modifier, 0, + MLX4_CMD_UPDATE_QP, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) { + mlx4_err(dev, "Failed to update qpn on qpn 0x%x, command failed\n", qpn); + goto err_mac; + } + +err_mac: + put_res(dev, slave, qpn, RES_QP); + return err; +} + int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 13767eb..66d4ab7 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -26,6 +26,8 @@ #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> #include "ks8851.h" @@ -85,6 +87,8 @@ union ks8851_tx_hdr { * @eeprom_size: Companion eeprom size in Bytes, 0 if no eeprom * @eeprom: 93CX6 EEPROM state for accessing on-board EEPROM. * @vdd_reg: Optional regulator supplying the chip + * @vdd_io: Optional digital power supply for IO + * @gpio: Optional reset_n gpio * * The @lock ensures that the chip is protected when certain operations are * in progress. When the read or write packet transfer is in progress, most @@ -133,6 +137,8 @@ struct ks8851_net { struct eeprom_93cx6 eeprom; struct regulator *vdd_reg; + struct regulator *vdd_io; + int gpio; }; static int msg_enable; @@ -1404,6 +1410,7 @@ static int ks8851_probe(struct spi_device *spi) struct ks8851_net *ks; int ret; unsigned cider; + int gpio; ndev = alloc_etherdev(sizeof(struct ks8851_net)); if (!ndev) @@ -1417,20 +1424,53 @@ static int ks8851_probe(struct spi_device *spi) ks->spidev = spi; ks->tx_space = 6144; - ks->vdd_reg = regulator_get_optional(&spi->dev, "vdd"); - if (IS_ERR(ks->vdd_reg)) { - ret = PTR_ERR(ks->vdd_reg); - if (ret == -EPROBE_DEFER) - goto err_reg; - } else { - ret = regulator_enable(ks->vdd_reg); + gpio = of_get_named_gpio_flags(spi->dev.of_node, "reset-gpios", + 0, NULL); + if (gpio == -EPROBE_DEFER) { + ret = gpio; + goto err_gpio; + } + + ks->gpio = gpio; + if (gpio_is_valid(gpio)) { + ret = devm_gpio_request_one(&spi->dev, gpio, + GPIOF_OUT_INIT_LOW, "ks8851_rst_n"); if (ret) { - dev_err(&spi->dev, "regulator enable fail: %d\n", - ret); - goto err_reg_en; + dev_err(&spi->dev, "reset gpio request failed\n"); + goto err_gpio; } } + ks->vdd_io = devm_regulator_get(&spi->dev, "vdd-io"); + if (IS_ERR(ks->vdd_io)) { + ret = PTR_ERR(ks->vdd_io); + goto err_reg_io; + } + + ret = regulator_enable(ks->vdd_io); + if (ret) { + dev_err(&spi->dev, "regulator vdd_io enable fail: %d\n", + ret); + goto err_reg_io; + } + + ks->vdd_reg = devm_regulator_get(&spi->dev, "vdd"); + if (IS_ERR(ks->vdd_reg)) { + ret = PTR_ERR(ks->vdd_reg); + goto err_reg; + } + + ret = regulator_enable(ks->vdd_reg); + if (ret) { + dev_err(&spi->dev, "regulator vdd enable fail: %d\n", + ret); + goto err_reg; + } + + if (gpio_is_valid(gpio)) { + usleep_range(10000, 11000); + gpio_set_value(gpio, 1); + } mutex_init(&ks->lock); spin_lock_init(&ks->statelock); @@ -1527,13 +1567,14 @@ err_netdev: free_irq(ndev->irq, ks); err_irq: + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, 0); err_id: - if (!IS_ERR(ks->vdd_reg)) - regulator_disable(ks->vdd_reg); -err_reg_en: - if (!IS_ERR(ks->vdd_reg)) - regulator_put(ks->vdd_reg); + regulator_disable(ks->vdd_reg); err_reg: + regulator_disable(ks->vdd_io); +err_reg_io: +err_gpio: free_netdev(ndev); return ret; } @@ -1547,18 +1588,24 @@ static int ks8851_remove(struct spi_device *spi) unregister_netdev(priv->netdev); free_irq(spi->irq, priv); - if (!IS_ERR(priv->vdd_reg)) { - regulator_disable(priv->vdd_reg); - regulator_put(priv->vdd_reg); - } + if (gpio_is_valid(priv->gpio)) + gpio_set_value(priv->gpio, 0); + regulator_disable(priv->vdd_reg); + regulator_disable(priv->vdd_io); free_netdev(priv->netdev); return 0; } +static const struct of_device_id ks8851_match_table[] = { + { .compatible = "micrel,ks8851" }, + { } +}; + static struct spi_driver ks8851_driver = { .driver = { .name = "ks8851", + .of_match_table = ks8851_match_table, .owner = THIS_MODULE, .pm = &ks8851_pm_ops, }, diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 4b9592c1..064a48d 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -4930,7 +4930,7 @@ static void netdev_tx_timeout(struct net_device *dev) * Only reset the hardware if time between calls is long * enough. */ - if (jiffies - last_reset <= dev->watchdog_timeo) + if (time_before_eq(jiffies, last_reset + dev->watchdog_timeo)) hw_priv = NULL; } @@ -7072,6 +7072,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id) dev = alloc_etherdev(sizeof(struct dev_priv)); if (!dev) goto pcidev_init_reg_err; + SET_NETDEV_DEV(dev, &pdev->dev); info->netdev[i] = dev; priv = netdev_priv(dev); diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c index 089b713..2bbd01f 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-config.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c @@ -120,7 +120,6 @@ __vxge_hw_device_register_poll(void __iomem *reg, u64 mask, u32 max_millis) { u64 val64; u32 i = 0; - enum vxge_hw_status ret = VXGE_HW_FAIL; udelay(10); @@ -139,7 +138,7 @@ __vxge_hw_device_register_poll(void __iomem *reg, u64 mask, u32 max_millis) mdelay(1); } while (++i <= max_millis); - return ret; + return VXGE_HW_FAIL; } static inline enum vxge_hw_status @@ -1682,12 +1681,10 @@ enum vxge_hw_status vxge_hw_driver_stats_get( struct __vxge_hw_device *hldev, struct vxge_hw_device_stats_sw_info *sw_stats) { - enum vxge_hw_status status = VXGE_HW_OK; - memcpy(sw_stats, &hldev->stats.sw_dev_info_stats, sizeof(struct vxge_hw_device_stats_sw_info)); - return status; + return VXGE_HW_OK; } /* @@ -3228,7 +3225,6 @@ enum vxge_hw_status vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask) { struct vxge_hw_vpmgmt_reg __iomem *vpmgmt_reg; - enum vxge_hw_status status = VXGE_HW_OK; int i = 0, j = 0; for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) { @@ -3241,7 +3237,7 @@ vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask) return VXGE_HW_FAIL; } } - return status; + return VXGE_HW_OK; } /* * vxge_hw_mgmt_reg_Write - Write Titan register. @@ -3979,7 +3975,6 @@ __vxge_hw_vpath_mgmt_read( { u32 i, mtu = 0, max_pyld = 0; u64 val64; - enum vxge_hw_status status = VXGE_HW_OK; for (i = 0; i < VXGE_HW_MAC_MAX_MAC_PORT_ID; i++) { @@ -4009,7 +4004,7 @@ __vxge_hw_vpath_mgmt_read( else VXGE_HW_DEVICE_LINK_STATE_SET(vpath->hldev, VXGE_HW_LINK_DOWN); - return status; + return VXGE_HW_OK; } /* @@ -4039,14 +4034,13 @@ static enum vxge_hw_status __vxge_hw_vpath_reset(struct __vxge_hw_device *hldev, u32 vp_id) { u64 val64; - enum vxge_hw_status status = VXGE_HW_OK; val64 = VXGE_HW_CMN_RSTHDLR_CFG0_SW_RESET_VPATH(1 << (16 - vp_id)); __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn(val64, 0, 32), &hldev->common_reg->cmn_rsthdlr_cfg0); - return status; + return VXGE_HW_OK; } /* @@ -4227,7 +4221,6 @@ static enum vxge_hw_status __vxge_hw_vpath_mac_configure(struct __vxge_hw_device *hldev, u32 vp_id) { u64 val64; - enum vxge_hw_status status = VXGE_HW_OK; struct __vxge_hw_virtualpath *vpath; struct vxge_hw_vp_config *vp_config; struct vxge_hw_vpath_reg __iomem *vp_reg; @@ -4283,7 +4276,7 @@ __vxge_hw_vpath_mac_configure(struct __vxge_hw_device *hldev, u32 vp_id) writeq(val64, &vp_reg->rxmac_vcfg1); } - return status; + return VXGE_HW_OK; } /* @@ -4295,7 +4288,6 @@ static enum vxge_hw_status __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) { u64 val64; - enum vxge_hw_status status = VXGE_HW_OK; struct __vxge_hw_virtualpath *vpath; struct vxge_hw_vpath_reg __iomem *vp_reg; struct vxge_hw_vp_config *config; @@ -4545,7 +4537,7 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) val64 |= VXGE_HW_TIM_WRKLD_CLC_CNT_RX_TX(3); writeq(val64, &vp_reg->tim_wrkld_clc); - return status; + return VXGE_HW_OK; } /* diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index d107bcb..7a0dead 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -2122,7 +2122,7 @@ static int vxge_open_vpaths(struct vxgedev *vdev) static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo) { fifo->interrupt_count++; - if (jiffies > fifo->jiffies + HZ / 100) { + if (time_before(fifo->jiffies + HZ / 100, jiffies)) { struct __vxge_hw_fifo *hw_fifo = fifo->handle; fifo->jiffies = jiffies; @@ -2150,7 +2150,7 @@ static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo) static void adaptive_coalesce_rx_interrupts(struct vxge_ring *ring) { ring->interrupt_count++; - if (jiffies > ring->jiffies + HZ / 100) { + if (time_before(ring->jiffies + HZ / 100, jiffies)) { struct __vxge_hw_ring *hw_ring = ring->handle; ring->jiffies = jiffies; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig index a588ffd..44c8be1 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig +++ b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig @@ -4,7 +4,7 @@ config PCH_GBE tristate "OKI SEMICONDUCTOR IOH(ML7223/ML7831) GbE" - depends on PCI && (X86 || COMPILE_TEST) + depends on PCI && (X86_32 || COMPILE_TEST) select MII select PTP_1588_CLOCK_PCH ---help--- diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 6e7527e..41abe60 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1319,6 +1319,7 @@ struct qlcnic_eswitch { #define QL_STATUS_INVALID_PARAM -1 #define MAX_BW 100 /* % of link speed */ +#define MIN_BW 1 /* % of link speed */ #define MAX_VLAN_ID 4095 #define MIN_VLAN_ID 2 #define DEFAULT_MAC_LEARN 1 diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index c9e8574..851cb4a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -620,7 +620,7 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter) struct hlist_node *n; struct hlist_head *head; int i; - unsigned long time; + unsigned long expires; u8 cmd; for (i = 0; i < adapter->fhash.fbucket_size; i++) { @@ -628,8 +628,8 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter) hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { cmd = tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL; - time = tmp_fil->ftime; - if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) { + expires = tmp_fil->ftime + QLCNIC_FILTER_AGE * HZ; + if (time_before(expires, jiffies)) { qlcnic_sre_macaddr_change(adapter, tmp_fil->faddr, tmp_fil->vlan_id, @@ -647,8 +647,8 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter) hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { - time = tmp_fil->ftime; - if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) { + expires = tmp_fil->ftime + QLCNIC_FILTER_AGE * HZ; + if (time_before(expires, jiffies)) { spin_lock_bh(&adapter->rx_mac_learn_lock); adapter->rx_fhash.fnum--; hlist_del(&tmp_fil->fnode); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index f0a2853..f06ba90b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -525,7 +525,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { #endif #ifdef CONFIG_QLCNIC_SRIOV .ndo_set_vf_mac = qlcnic_sriov_set_vf_mac, - .ndo_set_vf_tx_rate = qlcnic_sriov_set_vf_tx_rate, + .ndo_set_vf_rate = qlcnic_sriov_set_vf_tx_rate, .ndo_get_vf_config = qlcnic_sriov_get_vf_config, .ndo_set_vf_vlan = qlcnic_sriov_set_vf_vlan, .ndo_set_vf_spoofchk = qlcnic_sriov_set_vf_spoofchk, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index 335b50f..4677b2e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h @@ -233,7 +233,7 @@ bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *, void qlcnic_sriov_pf_reset(struct qlcnic_adapter *); int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *); int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *); -int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); +int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int, int); int qlcnic_sriov_get_vf_config(struct net_device *, int , struct ifla_vf_info *); int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 498fa63..2bdd9de 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -201,6 +201,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) sriov->vf_info[i].vp = vp; vp->vlan_mode = QLC_GUEST_VLAN_MODE; vp->max_tx_bw = MAX_BW; + vp->min_tx_bw = MIN_BW; vp->spoofchk = false; random_ether_addr(vp->mac); dev_info(&adapter->pdev->dev, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 6d2f72f..a29538b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -1848,7 +1848,8 @@ int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) return 0; } -int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) +int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, + int min_tx_rate, int max_tx_rate) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_sriov *sriov = adapter->ahw->sriov; @@ -1863,35 +1864,52 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) if (vf >= sriov->num_vfs) return -EINVAL; - if (tx_rate >= 10000 || tx_rate < 100) { + vf_info = &sriov->vf_info[vf]; + vp = vf_info->vp; + vpid = vp->handle; + + if (!min_tx_rate) + min_tx_rate = QLC_VF_MIN_TX_RATE; + + if (max_tx_rate && + (max_tx_rate >= 10000 || max_tx_rate < min_tx_rate)) { netdev_err(netdev, - "Invalid Tx rate, allowed range is [%d - %d]", - QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE); + "Invalid max Tx rate, allowed range is [%d - %d]", + min_tx_rate, QLC_VF_MAX_TX_RATE); return -EINVAL; } - if (tx_rate == 0) - tx_rate = 10000; + if (!max_tx_rate) + max_tx_rate = 10000; - vf_info = &sriov->vf_info[vf]; - vp = vf_info->vp; - vpid = vp->handle; + if (min_tx_rate && + (min_tx_rate > max_tx_rate || min_tx_rate < QLC_VF_MIN_TX_RATE)) { + netdev_err(netdev, + "Invalid min Tx rate, allowed range is [%d - %d]", + QLC_VF_MIN_TX_RATE, max_tx_rate); + return -EINVAL; + } if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid)) return -EIO; - nic_info.max_tx_bw = tx_rate / 100; + nic_info.max_tx_bw = max_tx_rate / 100; + nic_info.min_tx_bw = min_tx_rate / 100; nic_info.bit_offsets = BIT_0; if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid)) return -EIO; } - vp->max_tx_bw = tx_rate / 100; + vp->max_tx_bw = max_tx_rate / 100; + netdev_info(netdev, + "Setting Max Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", + max_tx_rate, vp->max_tx_bw, vf); + vp->min_tx_bw = min_tx_rate / 100; netdev_info(netdev, - "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", - tx_rate, vp->max_tx_bw, vf); + "Setting Min Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", + min_tx_rate, vp->min_tx_bw, vf); return 0; } @@ -1990,9 +2008,13 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, ivi->qos = vp->qos; ivi->spoofchk = vp->spoofchk; if (vp->max_tx_bw == MAX_BW) - ivi->tx_rate = 0; + ivi->max_tx_rate = 0; + else + ivi->max_tx_rate = vp->max_tx_bw * 100; + if (vp->min_tx_bw == MIN_BW) + ivi->min_tx_rate = 0; else - ivi->tx_rate = vp->max_tx_bw * 100; + ivi->min_tx_rate = vp->min_tx_bw * 100; ivi->vf = vf; return 0; diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 32d969e..89b83e5 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -156,13 +156,15 @@ void efx_nic_fini_interrupt(struct efx_nic *efx) efx->net_dev->rx_cpu_rmap = NULL; #endif - /* Disable MSI/MSI-X interrupts */ - efx_for_each_channel(channel, efx) - free_irq(channel->irq, &efx->msi_context[channel->channel]); - - /* Disable legacy interrupt */ - if (efx->legacy_irq) + if (EFX_INT_MODE_USE_MSI(efx)) { + /* Disable MSI/MSI-X interrupts */ + efx_for_each_channel(channel, efx) + free_irq(channel->irq, + &efx->msi_context[channel->channel]); + } else { + /* Disable legacy interrupt */ free_irq(efx->legacy_irq, efx); + } } /* Register dump */ diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c index 9a9205e..43d2e64 100644 --- a/drivers/net/ethernet/sfc/siena_sriov.c +++ b/drivers/net/ethernet/sfc/siena_sriov.c @@ -1633,7 +1633,8 @@ int efx_sriov_get_vf_config(struct net_device *net_dev, int vf_i, ivi->vf = vf_i; ether_addr_copy(ivi->mac, vf->addr.mac_addr); - ivi->tx_rate = 0; + ivi->max_tx_rate = 0; + ivi->min_tx_rate = 0; tci = ntohs(vf->addr.tci); ivi->vlan = tci & VLAN_VID_MASK; ivi->qos = (tci >> VLAN_PRIO_SHIFT) & 0x7; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index c963394..c62e67f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -322,9 +322,7 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, return -EBUSY; } cmd->transceiver = XCVR_INTERNAL; - spin_lock_irq(&priv->lock); rc = phy_ethtool_gset(phy, cmd); - spin_unlock_irq(&priv->lock); return rc; } @@ -431,8 +429,6 @@ stmmac_get_pauseparam(struct net_device *netdev, if (priv->pcs) /* FIXME */ return; - spin_lock(&priv->lock); - pause->rx_pause = 0; pause->tx_pause = 0; pause->autoneg = priv->phydev->autoneg; @@ -442,7 +438,6 @@ stmmac_get_pauseparam(struct net_device *netdev, if (priv->flow_ctrl & FLOW_TX) pause->tx_pause = 1; - spin_unlock(&priv->lock); } static int @@ -457,8 +452,6 @@ stmmac_set_pauseparam(struct net_device *netdev, if (priv->pcs) /* FIXME */ return -EOPNOTSUPP; - spin_lock(&priv->lock); - if (pause->rx_pause) new_pause |= FLOW_RX; if (pause->tx_pause) @@ -473,7 +466,6 @@ stmmac_set_pauseparam(struct net_device *netdev, } else priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex, priv->flow_ctrl, priv->pause); - spin_unlock(&priv->lock); return ret; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 93cf4f6..057a120 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1704,7 +1704,7 @@ static int stmmac_open(struct net_device *dev) if (ret) { pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret); - goto phy_error; + return ret; } } @@ -1753,7 +1753,7 @@ static int stmmac_open(struct net_device *dev) } /* Request the IRQ lines */ - if (priv->lpi_irq != -ENXIO) { + if (priv->lpi_irq > 0) { ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED, dev->name, dev); if (unlikely(ret < 0)) { @@ -1779,8 +1779,6 @@ init_error: dma_desc_error: if (priv->phydev) phy_disconnect(priv->phydev); -phy_error: - clk_disable_unprepare(priv->stmmac_clk); return ret; } @@ -1815,7 +1813,7 @@ static int stmmac_release(struct net_device *dev) free_irq(dev->irq, dev); if (priv->wol_irq != dev->irq) free_irq(priv->wol_irq, dev); - if (priv->lpi_irq != -ENXIO) + if (priv->lpi_irq > 0) free_irq(priv->lpi_irq, dev); /* Stop TX/RX DMA and clear the descriptors */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 46aef510..ea7a65b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -237,10 +237,12 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) /* Get the MAC information */ priv->dev->irq = platform_get_irq_byname(pdev, "macirq"); - if (priv->dev->irq == -ENXIO) { - pr_err("%s: ERROR: MAC IRQ configuration " - "information not found\n", __func__); - return -ENXIO; + if (priv->dev->irq < 0) { + if (priv->dev->irq != -EPROBE_DEFER) { + netdev_err(priv->dev, + "MAC IRQ configuration information not found\n"); + } + return priv->dev->irq; } /* @@ -252,10 +254,15 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) * so the driver will continue to use the mac irq (ndev->irq) */ priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); - if (priv->wol_irq == -ENXIO) + if (priv->wol_irq < 0) { + if (priv->wol_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; priv->wol_irq = priv->dev->irq; + } priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); + if (priv->lpi_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; platform_set_drvdata(pdev, priv->dev); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index e3d8710..ff380da 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -143,13 +143,13 @@ do { \ u32 i; \ for (i = 0; i < priv->num_irqs; i++) \ enable_irq(priv->irqs_table[i]); \ - } while (0); + } while (0) #define cpsw_disable_irq(priv) \ do { \ u32 i; \ for (i = 0; i < priv->num_irqs; i++) \ disable_irq_nosync(priv->irqs_table[i]); \ - } while (0); + } while (0) #define cpsw_slave_index(priv) \ ((priv->data.dual_emac) ? priv->emac_port : \ @@ -1808,25 +1808,25 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, return -EINVAL; if (of_property_read_u32(node, "slaves", &prop)) { - pr_err("Missing slaves property in the DT.\n"); + dev_err(&pdev->dev, "Missing slaves property in the DT.\n"); return -EINVAL; } data->slaves = prop; if (of_property_read_u32(node, "active_slave", &prop)) { - pr_err("Missing active_slave property in the DT.\n"); + dev_err(&pdev->dev, "Missing active_slave property in the DT.\n"); return -EINVAL; } data->active_slave = prop; if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { - pr_err("Missing cpts_clock_mult property in the DT.\n"); + dev_err(&pdev->dev, "Missing cpts_clock_mult property in the DT.\n"); return -EINVAL; } data->cpts_clock_mult = prop; if (of_property_read_u32(node, "cpts_clock_shift", &prop)) { - pr_err("Missing cpts_clock_shift property in the DT.\n"); + dev_err(&pdev->dev, "Missing cpts_clock_shift property in the DT.\n"); return -EINVAL; } data->cpts_clock_shift = prop; @@ -1838,31 +1838,31 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, return -ENOMEM; if (of_property_read_u32(node, "cpdma_channels", &prop)) { - pr_err("Missing cpdma_channels property in the DT.\n"); + dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n"); return -EINVAL; } data->channels = prop; if (of_property_read_u32(node, "ale_entries", &prop)) { - pr_err("Missing ale_entries property in the DT.\n"); + dev_err(&pdev->dev, "Missing ale_entries property in the DT.\n"); return -EINVAL; } data->ale_entries = prop; if (of_property_read_u32(node, "bd_ram_size", &prop)) { - pr_err("Missing bd_ram_size property in the DT.\n"); + dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n"); return -EINVAL; } data->bd_ram_size = prop; if (of_property_read_u32(node, "rx_descs", &prop)) { - pr_err("Missing rx_descs property in the DT.\n"); + dev_err(&pdev->dev, "Missing rx_descs property in the DT.\n"); return -EINVAL; } data->rx_descs = prop; if (of_property_read_u32(node, "mac_control", &prop)) { - pr_err("Missing mac_control property in the DT.\n"); + dev_err(&pdev->dev, "Missing mac_control property in the DT.\n"); return -EINVAL; } data->mac_control = prop; @@ -1876,7 +1876,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, ret = of_platform_populate(node, NULL, NULL, &pdev->dev); /* We do not want to force this, as in some cases may not have child */ if (ret) - pr_warn("Doesn't have any child node\n"); + dev_warn(&pdev->dev, "Doesn't have any child node\n"); for_each_child_of_node(node, slave_node) { struct cpsw_slave_data *slave_data = data->slave_data + i; @@ -1893,7 +1893,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, parp = of_get_property(slave_node, "phy_id", &lenp); if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) { - pr_err("Missing slave[%d] phy_id property\n", i); + dev_err(&pdev->dev, "Missing slave[%d] phy_id property\n", i); return -EINVAL; } mdio_node = of_find_node_by_phandle(be32_to_cpup(parp)); @@ -1913,18 +1913,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, slave_data->phy_if = of_get_phy_mode(slave_node); if (slave_data->phy_if < 0) { - pr_err("Missing or malformed slave[%d] phy-mode property\n", - i); + dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n", + i); return slave_data->phy_if; } if (data->dual_emac) { if (of_property_read_u32(slave_node, "dual_emac_res_vlan", &prop)) { - pr_err("Missing dual_emac_res_vlan in DT.\n"); + dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n"); slave_data->dual_emac_res_vlan = i+1; - pr_err("Using %d as Reserved VLAN for %d slave\n", - slave_data->dual_emac_res_vlan, i); + dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n", + slave_data->dual_emac_res_vlan, i); } else { slave_data->dual_emac_res_vlan = prop; } @@ -1948,7 +1948,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, ndev = alloc_etherdev(sizeof(struct cpsw_priv)); if (!ndev) { - pr_err("cpsw: error allocating net_device\n"); + dev_err(&pdev->dev, "cpsw: error allocating net_device\n"); return -ENOMEM; } @@ -1964,10 +1964,10 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, if (is_valid_ether_addr(data->slave_data[1].mac_addr)) { memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr, ETH_ALEN); - pr_info("cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr); + dev_info(&pdev->dev, "cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr); } else { random_ether_addr(priv_sl2->mac_addr); - pr_info("cpsw: Random MACID = %pM\n", priv_sl2->mac_addr); + dev_info(&pdev->dev, "cpsw: Random MACID = %pM\n", priv_sl2->mac_addr); } memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN); @@ -2005,7 +2005,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, SET_NETDEV_DEV(ndev, &pdev->dev); ret = register_netdev(ndev); if (ret) { - pr_err("cpsw: error registering net device\n"); + dev_err(&pdev->dev, "cpsw: error registering net device\n"); free_netdev(ndev); ret = -ENODEV; } @@ -2027,7 +2027,7 @@ static int cpsw_probe(struct platform_device *pdev) ndev = alloc_etherdev(sizeof(struct cpsw_priv)); if (!ndev) { - pr_err("error allocating net_device\n"); + dev_err(&pdev->dev, "error allocating net_device\n"); return -ENOMEM; } @@ -2042,7 +2042,7 @@ static int cpsw_probe(struct platform_device *pdev) priv->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL); priv->irq_enabled = true; if (!priv->cpts) { - pr_err("error allocating cpts\n"); + dev_err(&pdev->dev, "error allocating cpts\n"); goto clean_ndev_ret; } @@ -2055,7 +2055,7 @@ static int cpsw_probe(struct platform_device *pdev) pinctrl_pm_select_default_state(&pdev->dev); if (cpsw_probe_dt(&priv->data, pdev)) { - pr_err("cpsw: platform data missing\n"); + dev_err(&pdev->dev, "cpsw: platform data missing\n"); ret = -ENODEV; goto clean_runtime_disable_ret; } @@ -2063,10 +2063,10 @@ static int cpsw_probe(struct platform_device *pdev) if (is_valid_ether_addr(data->slave_data[0].mac_addr)) { memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN); - pr_info("Detected MACID = %pM\n", priv->mac_addr); + dev_info(&pdev->dev, "Detected MACID = %pM\n", priv->mac_addr); } else { eth_random_addr(priv->mac_addr); - pr_info("Random MACID = %pM\n", priv->mac_addr); + dev_info(&pdev->dev, "Random MACID = %pM\n", priv->mac_addr); } memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 88ef270..539dbde 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -158,9 +158,9 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr, int bitmap_size; struct cpdma_desc_pool *pool; - pool = kzalloc(sizeof(*pool), GFP_KERNEL); + pool = devm_kzalloc(dev, sizeof(*pool), GFP_KERNEL); if (!pool) - return NULL; + goto fail; spin_lock_init(&pool->lock); @@ -170,7 +170,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr, pool->num_desc = size / pool->desc_size; bitmap_size = (pool->num_desc / BITS_PER_LONG) * sizeof(long); - pool->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + pool->bitmap = devm_kzalloc(dev, bitmap_size, GFP_KERNEL); if (!pool->bitmap) goto fail; @@ -187,10 +187,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr, if (pool->iomap) return pool; - fail: - kfree(pool->bitmap); - kfree(pool); return NULL; } @@ -203,7 +200,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool) spin_lock_irqsave(&pool->lock, flags); WARN_ON(pool->used_desc); - kfree(pool->bitmap); if (pool->cpumap) { dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap, pool->phys); @@ -211,7 +207,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool) iounmap(pool->iomap); } spin_unlock_irqrestore(&pool->lock, flags); - kfree(pool); } static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool, @@ -276,7 +271,7 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) { struct cpdma_ctlr *ctlr; - ctlr = kzalloc(sizeof(*ctlr), GFP_KERNEL); + ctlr = devm_kzalloc(params->dev, sizeof(*ctlr), GFP_KERNEL); if (!ctlr) return NULL; @@ -468,7 +463,6 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr) cpdma_desc_pool_destroy(ctlr->pool); spin_unlock_irqrestore(&ctlr->lock, flags); - kfree(ctlr); return ret; } EXPORT_SYMBOL_GPL(cpdma_ctlr_destroy); @@ -507,21 +501,22 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, cpdma_handler_fn handler) { struct cpdma_chan *chan; - int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4; + int offset = (chan_num % CPDMA_MAX_CHANNELS) * 4; unsigned long flags; if (__chan_linear(chan_num) >= ctlr->num_chan) return NULL; - ret = -ENOMEM; - chan = kzalloc(sizeof(*chan), GFP_KERNEL); + chan = devm_kzalloc(ctlr->dev, sizeof(*chan), GFP_KERNEL); if (!chan) - goto err_chan_alloc; + return ERR_PTR(-ENOMEM); spin_lock_irqsave(&ctlr->lock, flags); - ret = -EBUSY; - if (ctlr->channels[chan_num]) - goto err_chan_busy; + if (ctlr->channels[chan_num]) { + spin_unlock_irqrestore(&ctlr->lock, flags); + devm_kfree(ctlr->dev, chan); + return ERR_PTR(-EBUSY); + } chan->ctlr = ctlr; chan->state = CPDMA_STATE_IDLE; @@ -551,12 +546,6 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, ctlr->channels[chan_num] = chan; spin_unlock_irqrestore(&ctlr->lock, flags); return chan; - -err_chan_busy: - spin_unlock_irqrestore(&ctlr->lock, flags); - kfree(chan); -err_chan_alloc: - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(cpdma_chan_create); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index e76eae5..f32d730 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1865,7 +1865,6 @@ static int davinci_emac_probe(struct platform_device *pdev) struct emac_priv *priv; unsigned long hw_ram_addr; struct emac_platform_data *pdata; - struct device *emac_dev; struct cpdma_params dma_params; struct clk *emac_clk; unsigned long emac_bus_frequency; @@ -1911,7 +1910,6 @@ static int davinci_emac_probe(struct platform_device *pdev) priv->coal_intvl = 0; priv->bus_freq_mhz = (u32)(emac_bus_frequency / 1000000); - emac_dev = &ndev->dev; /* Get EMAC platform data */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->emac_base_phys = res->start + pdata->ctrl_reg_offset; @@ -1930,7 +1928,7 @@ static int davinci_emac_probe(struct platform_device *pdev) hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset; memset(&dma_params, 0, sizeof(dma_params)); - dma_params.dev = emac_dev; + dma_params.dev = &pdev->dev; dma_params.dmaregs = priv->emac_base; dma_params.rxthresh = priv->emac_base + 0x120; dma_params.rxfree = priv->emac_base + 0x140; @@ -1994,7 +1992,7 @@ static int davinci_emac_probe(struct platform_device *pdev) if (netif_msg_probe(priv)) { - dev_notice(emac_dev, "DaVinci EMAC Probe found device "\ + dev_notice(&pdev->dev, "DaVinci EMAC Probe found device " "(regs: %p, irq: %d)\n", (void *)priv->emac_base_phys, ndev->irq); } diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 34e97ec..735dc53 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -303,7 +303,7 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data, return -EINVAL; if (of_property_read_u32(node, "bus_freq", &prop)) { - pr_err("Missing bus_freq property in the DT.\n"); + dev_err(&pdev->dev, "Missing bus_freq property in the DT.\n"); return -EINVAL; } data->bus_freq = prop; diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c index 7e1c91d..5eca9bb 100644 --- a/drivers/net/ethernet/tile/tilegx.c +++ b/drivers/net/ethernet/tile/tilegx.c @@ -2192,7 +2192,6 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac) { int ret; int i; - int nz_addr = 0; struct net_device *dev; struct tile_net_priv *priv; @@ -2212,7 +2211,6 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac) /* Initialize "priv". */ priv = netdev_priv(dev); - memset(priv, 0, sizeof(*priv)); priv->dev = dev; priv->channel = -1; priv->loopify_channel = -1; @@ -2223,15 +2221,10 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac) * be done before the device is opened. If the MAC is all zeroes, * we use a random address, since we're probably on the simulator. */ - for (i = 0; i < 6; i++) - nz_addr |= mac[i]; - - if (nz_addr) { - memcpy(dev->dev_addr, mac, ETH_ALEN); - dev->addr_len = 6; - } else { + if (!is_zero_ether_addr(mac)) + ether_addr_copy(dev->dev_addr, mac); + else eth_hw_addr_random(dev); - } /* Register the network device. */ ret = register_netdev(dev); diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index fa193c4..4ef818a 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -75,7 +75,7 @@ int temac_indirect_busywait(struct temac_local *lp) long end = jiffies + 2; while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) { - if (end - jiffies <= 0) { + if (time_before_eq(end, jiffies)) { WARN_ON(1); return -ETIMEDOUT; } diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 64b4639..d4abf47 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -22,7 +22,7 @@ int axienet_mdio_wait_until_ready(struct axienet_local *lp) long end = jiffies + 2; while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) & XAE_MDIO_MCR_READY_MASK)) { - if (end - jiffies <= 0) { + if (time_before_eq(end, jiffies)) { WARN_ON(1); return -ETIMEDOUT; } diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 0d87c67..8c4aed3 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -702,7 +702,7 @@ static int xemaclite_mdio_wait(struct net_local *lp) */ while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) & XEL_MDIOCTRL_MDIOSTS_MASK) { - if (end - jiffies <= 0) { + if (time_before_eq(end, jiffies)) { WARN_ON(1); return -ETIMEDOUT; } diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 4b7df5a..6cc37c1 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -791,6 +791,7 @@ enum ndis_per_pkt_info_type { IEEE_8021Q_INFO, ORIGINAL_PKTINFO, PACKET_CANCEL_ID, + NBL_HASH_VALUE = PACKET_CANCEL_ID, ORIGINAL_NET_BUFLIST, CACHED_NET_BUFLIST, SHORT_PKT_PADINFO, @@ -937,6 +938,9 @@ struct ndis_tcp_lso_info { #define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ sizeof(struct ndis_tcp_lso_info)) +#define NDIS_HASH_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ + sizeof(u32)) + /* Format of Information buffer passed in a SetRequest for the OID */ /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ struct rndis_config_parameter_info { diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 2e967a7..4fd71b7 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -224,9 +224,11 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1) return 0; - if (netvsc_set_hash(&hash, skb)) + if (netvsc_set_hash(&hash, skb)) { q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % ndev->real_num_tx_queues; + skb_set_hash(skb, hash, PKT_HASH_TYPE_L3); + } return q_idx; } @@ -384,6 +386,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) struct ndis_tcp_lso_info *lso_info; int hdr_offset; u32 net_trans_info; + u32 hash; /* We will atmost need two pages to describe the rndis @@ -402,9 +405,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) packet = kzalloc(sizeof(struct hv_netvsc_packet) + (num_data_pgs * sizeof(struct hv_page_buffer)) + sizeof(struct rndis_message) + - NDIS_VLAN_PPI_SIZE + - NDIS_CSUM_PPI_SIZE + - NDIS_LSO_PPI_SIZE, GFP_ATOMIC); + NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + + NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE, GFP_ATOMIC); if (!packet) { /* out of memory, drop packet */ netdev_err(net, "unable to allocate hv_netvsc_packet\n"); @@ -443,6 +445,14 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); + hash = skb_get_hash_raw(skb); + if (hash != 0 && net->real_num_tx_queues > 1) { + rndis_msg_size += NDIS_HASH_PPI_SIZE; + ppi = init_ppi_data(rndis_msg, NDIS_HASH_PPI_SIZE, + NBL_HASH_VALUE); + *(u32 *)((void *)ppi + ppi->ppi_offset) = hash; + } + if (isvlan) { struct ndis_pkt_8021q_info *vlan; diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c index b8d2217..27d8320 100644 --- a/drivers/net/ieee802154/fakelb.c +++ b/drivers/net/ieee802154/fakelb.c @@ -26,6 +26,7 @@ #include <linux/timer.h> #include <linux/platform_device.h> #include <linux/netdevice.h> +#include <linux/device.h> #include <linux/spinlock.h> #include <net/mac802154.h> #include <net/wpan-phy.h> @@ -228,7 +229,8 @@ static int fakelb_probe(struct platform_device *pdev) int err = -ENOMEM; int i; - priv = kzalloc(sizeof(struct fakelb_priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(struct fakelb_priv), + GFP_KERNEL); if (!priv) goto err_alloc; @@ -248,7 +250,6 @@ static int fakelb_probe(struct platform_device *pdev) err_slave: list_for_each_entry(dp, &priv->list, list) fakelb_del(dp); - kfree(priv); err_alloc: return err; } @@ -260,7 +261,6 @@ static int fakelb_remove(struct platform_device *pdev) list_for_each_entry_safe(dp, temp, &priv->list, list) fakelb_del(dp); - kfree(priv); return 0; } diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 2900af0..998bb89 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -510,10 +510,8 @@ static void via_hw_init(struct via_ircc_cb *self) */ static int via_ircc_read_dongle_id(int iobase) { - int dongle_id = 9; /* Default to IBM */ - IRDA_ERROR("via-ircc: dongle probing not supported, please specify dongle_id module parameter.\n"); - return dongle_id; + return 9; /* Default to IBM */ } /* @@ -926,7 +924,6 @@ static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase) static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self) { int iobase; - int ret = TRUE; u8 Tx_status; IRDA_DEBUG(3, "%s()\n", __func__); @@ -983,7 +980,7 @@ F01_E*/ // Tell the network layer, that we can accept more frames netif_wake_queue(self->netdev); //F01 } - return ret; + return TRUE; } /* diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index e03707d..eee9106 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -33,6 +33,7 @@ #include <linux/workqueue.h> #include <net/rtnetlink.h> #include <net/xfrm.h> +#include <linux/netpoll.h> #define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) @@ -44,9 +45,10 @@ struct macvlan_port { struct sk_buff_head bc_queue; struct work_struct bc_work; bool passthru; - int count; }; +#define MACVLAN_PORT_IS_EMPTY(port) list_empty(&port->vlans) + struct macvlan_skb_cb { const struct macvlan_dev *src; }; @@ -356,12 +358,26 @@ xmit_world: return dev_queue_xmit(skb); } +static inline netdev_tx_t macvlan_netpoll_send_skb(struct macvlan_dev *vlan, struct sk_buff *skb) +{ +#ifdef CONFIG_NET_POLL_CONTROLLER + if (vlan->netpoll) + netpoll_send_skb(vlan->netpoll, skb); +#else + BUG(); +#endif + return NETDEV_TX_OK; +} + static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned int len = skb->len; int ret; - const struct macvlan_dev *vlan = netdev_priv(dev); + struct macvlan_dev *vlan = netdev_priv(dev); + + if (unlikely(netpoll_tx_running(dev))) + return macvlan_netpoll_send_skb(vlan, skb); if (vlan->fwd_priv) { skb->dev = vlan->lowerdev; @@ -493,42 +509,58 @@ hash_del: return 0; } -static int macvlan_set_mac_address(struct net_device *dev, void *p) +static int macvlan_sync_address(struct net_device *dev, unsigned char *addr) { struct macvlan_dev *vlan = netdev_priv(dev); struct net_device *lowerdev = vlan->lowerdev; - struct sockaddr *addr = p; int err; - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - if (!(dev->flags & IFF_UP)) { /* Just copy in the new address */ - memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + ether_addr_copy(dev->dev_addr, addr); } else { /* Rehash and update the device filters */ - if (macvlan_addr_busy(vlan->port, addr->sa_data)) + if (macvlan_addr_busy(vlan->port, addr)) return -EBUSY; - err = dev_uc_add(lowerdev, addr->sa_data); - if (err) - return err; + if (!vlan->port->passthru) { + err = dev_uc_add(lowerdev, addr); + if (err) + return err; - dev_uc_del(lowerdev, dev->dev_addr); + dev_uc_del(lowerdev, dev->dev_addr); + } - macvlan_hash_change_addr(vlan, addr->sa_data); + macvlan_hash_change_addr(vlan, addr); } return 0; } +static int macvlan_set_mac_address(struct net_device *dev, void *p) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (vlan->mode == MACVLAN_MODE_PASSTHRU) { + dev_set_mac_address(vlan->lowerdev, addr); + return 0; + } + + return macvlan_sync_address(dev, addr->sa_data); +} + static void macvlan_change_rx_flags(struct net_device *dev, int change) { struct macvlan_dev *vlan = netdev_priv(dev); struct net_device *lowerdev = vlan->lowerdev; - if (change & IFF_ALLMULTI) - dev_set_allmulti(lowerdev, dev->flags & IFF_ALLMULTI ? 1 : -1); + if (dev->flags & IFF_UP) { + if (change & IFF_ALLMULTI) + dev_set_allmulti(lowerdev, dev->flags & IFF_ALLMULTI ? 1 : -1); + } } static void macvlan_set_mac_lists(struct net_device *dev) @@ -584,6 +616,11 @@ static struct lock_class_key macvlan_netdev_addr_lock_key; #define MACVLAN_STATE_MASK \ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) +static int macvlan_get_nest_level(struct net_device *dev) +{ + return ((struct macvlan_dev *)netdev_priv(dev))->nest_level; +} + static void macvlan_set_lockdep_class_one(struct net_device *dev, struct netdev_queue *txq, void *_unused) @@ -594,8 +631,9 @@ static void macvlan_set_lockdep_class_one(struct net_device *dev, static void macvlan_set_lockdep_class(struct net_device *dev) { - lockdep_set_class(&dev->addr_list_lock, - &macvlan_netdev_addr_lock_key); + lockdep_set_class_and_subclass(&dev->addr_list_lock, + &macvlan_netdev_addr_lock_key, + macvlan_get_nest_level(dev)); netdev_for_each_tx_queue(dev, macvlan_set_lockdep_class_one, NULL); } @@ -628,8 +666,7 @@ static void macvlan_uninit(struct net_device *dev) free_percpu(vlan->pcpu_stats); - port->count -= 1; - if (!port->count) + if (MACVLAN_PORT_IS_EMPTY(port)) macvlan_port_destroy(port->dev); } @@ -766,6 +803,50 @@ static netdev_features_t macvlan_fix_features(struct net_device *dev, return features; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void macvlan_dev_poll_controller(struct net_device *dev) +{ + return; +} + +static int macvlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + struct net_device *real_dev = vlan->lowerdev; + struct netpoll *netpoll; + int err = 0; + + netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL); + err = -ENOMEM; + if (!netpoll) + goto out; + + err = __netpoll_setup(netpoll, real_dev); + if (err) { + kfree(netpoll); + goto out; + } + + vlan->netpoll = netpoll; + +out: + return err; +} + +static void macvlan_dev_netpoll_cleanup(struct net_device *dev) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + struct netpoll *netpoll = vlan->netpoll; + + if (!netpoll) + return; + + vlan->netpoll = NULL; + + __netpoll_free_async(netpoll); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + static const struct ethtool_ops macvlan_ethtool_ops = { .get_link = ethtool_op_get_link, .get_settings = macvlan_ethtool_get_settings, @@ -790,6 +871,12 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_fdb_add = macvlan_fdb_add, .ndo_fdb_del = macvlan_fdb_del, .ndo_fdb_dump = ndo_dflt_fdb_dump, + .ndo_get_lock_subclass = macvlan_get_nest_level, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = macvlan_dev_poll_controller, + .ndo_netpoll_setup = macvlan_dev_netpoll_setup, + .ndo_netpoll_cleanup = macvlan_dev_netpoll_cleanup, +#endif }; void macvlan_common_setup(struct net_device *dev) @@ -922,6 +1009,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, vlan->dev = dev; vlan->port = port; vlan->set_features = MACVLAN_FEATURES; + vlan->nest_level = dev_get_nest_level(lowerdev, netif_is_macvlan) + 1; vlan->mode = MACVLAN_MODE_VEPA; if (data && data[IFLA_MACVLAN_MODE]) @@ -931,13 +1019,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]); if (vlan->mode == MACVLAN_MODE_PASSTHRU) { - if (port->count) + if (!MACVLAN_PORT_IS_EMPTY(port)) return -EINVAL; port->passthru = true; eth_hw_addr_inherit(dev, lowerdev); } - port->count += 1; err = register_netdevice(dev); if (err < 0) goto destroy_port; @@ -955,8 +1042,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, unregister_netdev: unregister_netdevice(dev); destroy_port: - port->count -= 1; - if (!port->count) + if (MACVLAN_PORT_IS_EMPTY(port)) macvlan_port_destroy(lowerdev); return err; @@ -1098,6 +1184,18 @@ static int macvlan_device_event(struct notifier_block *unused, dev_set_mtu(vlan->dev, dev->mtu); } break; + case NETDEV_CHANGEADDR: + if (!port->passthru) + return NOTIFY_DONE; + + vlan = list_first_entry_or_null(&port->vlans, + struct macvlan_dev, + list); + + if (macvlan_sync_address(vlan->dev, dev->dev_addr)) + return NOTIFY_BAD; + + break; case NETDEV_UNREGISTER: /* twiddle thumbs on netns device moves */ if (dev->reg_state != NETREG_UNREGISTERING) diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index ba55adf..d60d875 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -21,6 +21,7 @@ #include <linux/phy_fixed.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/of.h> #define MII_REGS_NUM 29 @@ -31,7 +32,7 @@ struct fixed_mdio_bus { }; struct fixed_phy { - int id; + int addr; u16 regs[MII_REGS_NUM]; struct phy_device *phydev; struct fixed_phy_status status; @@ -104,8 +105,8 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) if (fp->status.asym_pause) lpa |= LPA_PAUSE_ASYM; - fp->regs[MII_PHYSID1] = fp->id >> 16; - fp->regs[MII_PHYSID2] = fp->id; + fp->regs[MII_PHYSID1] = 0; + fp->regs[MII_PHYSID2] = 0; fp->regs[MII_BMSR] = bmsr; fp->regs[MII_BMCR] = bmcr; @@ -115,7 +116,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) return 0; } -static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) +static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) { struct fixed_mdio_bus *fmb = bus->priv; struct fixed_phy *fp; @@ -124,7 +125,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) return -1; list_for_each_entry(fp, &fmb->phys, node) { - if (fp->id == phy_id) { + if (fp->addr == phy_addr) { /* Issue callback if user registered it. */ if (fp->link_update) { fp->link_update(fp->phydev->attached_dev, @@ -138,7 +139,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) return 0xFFFF; } -static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num, +static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, u16 val) { return 0; @@ -160,7 +161,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev, return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { - if (fp->id == phydev->phy_id) { + if (fp->addr == phydev->addr) { fp->link_update = link_update; fp->phydev = phydev; return 0; @@ -171,7 +172,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -int fixed_phy_add(unsigned int irq, int phy_id, +int fixed_phy_add(unsigned int irq, int phy_addr, struct fixed_phy_status *status) { int ret; @@ -184,9 +185,9 @@ int fixed_phy_add(unsigned int irq, int phy_id, memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); - fmb->irqs[phy_id] = irq; + fmb->irqs[phy_addr] = irq; - fp->id = phy_id; + fp->addr = phy_addr; fp->status = *status; ret = fixed_phy_update_regs(fp); @@ -203,6 +204,66 @@ err_regs: } EXPORT_SYMBOL_GPL(fixed_phy_add); +void fixed_phy_del(int phy_addr) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp, *tmp; + + list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { + if (fp->addr == phy_addr) { + list_del(&fp->node); + kfree(fp); + return; + } + } +} +EXPORT_SYMBOL_GPL(fixed_phy_del); + +static int phy_fixed_addr; +static DEFINE_SPINLOCK(phy_fixed_addr_lock); + +int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct phy_device *phy; + int phy_addr; + int ret; + + /* Get the next available PHY address, up to PHY_MAX_ADDR */ + spin_lock(&phy_fixed_addr_lock); + if (phy_fixed_addr == PHY_MAX_ADDR) { + spin_unlock(&phy_fixed_addr_lock); + return -ENOSPC; + } + phy_addr = phy_fixed_addr++; + spin_unlock(&phy_fixed_addr_lock); + + ret = fixed_phy_add(PHY_POLL, phy_addr, status); + if (ret < 0) + return ret; + + phy = get_phy_device(fmb->mii_bus, phy_addr, false); + if (!phy || IS_ERR(phy)) { + fixed_phy_del(phy_addr); + return -EINVAL; + } + + of_node_get(np); + phy->dev.of_node = np; + + ret = phy_device_register(phy); + if (ret) { + phy_device_free(phy); + of_node_put(np); + fixed_phy_del(phy_addr); + return ret; + } + + return 0; +} + static int __init fixed_mdio_bus_init(void) { struct fixed_mdio_bus *fmb = &platform_fmb; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index a628496..2e58aa5 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -300,6 +300,12 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) if (IS_ERR(phydev) || phydev == NULL) return phydev; + /* + * For DT, see if the auto-probed phy has a correspoding child + * in the bus node, and set the of_node pointer in this case. + */ + of_mdiobus_link_phydev(bus, phydev); + err = phy_device_register(phydev); if (err) { phy_device_free(phydev); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a972056..3bc079a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -715,7 +715,7 @@ void phy_state_machine(struct work_struct *work) struct delayed_work *dwork = to_delayed_work(work); struct phy_device *phydev = container_of(dwork, struct phy_device, state_queue); - int needs_aneg = 0, do_suspend = 0; + bool needs_aneg = false, do_suspend = false, do_resume = false; int err = 0; mutex_lock(&phydev->lock); @@ -727,7 +727,7 @@ void phy_state_machine(struct work_struct *work) case PHY_PENDING: break; case PHY_UP: - needs_aneg = 1; + needs_aneg = true; phydev->link_timeout = PHY_AN_TIMEOUT; @@ -757,7 +757,7 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); } else if (0 == phydev->link_timeout--) - needs_aneg = 1; + needs_aneg = true; break; case PHY_NOLINK: err = phy_read_status(phydev); @@ -791,7 +791,7 @@ void phy_state_machine(struct work_struct *work) netif_carrier_on(phydev->attached_dev); } else { if (0 == phydev->link_timeout--) - needs_aneg = 1; + needs_aneg = true; } phydev->adjust_link(phydev->attached_dev); @@ -827,7 +827,7 @@ void phy_state_machine(struct work_struct *work) phydev->link = 0; netif_carrier_off(phydev->attached_dev); phydev->adjust_link(phydev->attached_dev); - do_suspend = 1; + do_suspend = true; } break; case PHY_RESUMING: @@ -876,6 +876,7 @@ void phy_state_machine(struct work_struct *work) } phydev->adjust_link(phydev->attached_dev); } + do_resume = true; break; } @@ -883,9 +884,10 @@ void phy_state_machine(struct work_struct *work) if (needs_aneg) err = phy_start_aneg(phydev); - - if (do_suspend) + else if (do_suspend) phy_suspend(phydev); + else if (do_resume) + phy_resume(phydev); if (err < 0) phy_error(phydev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 466ae3e..35d753d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -33,6 +33,7 @@ #include <linux/mdio.h> #include <linux/io.h> #include <linux/uaccess.h> +#include <linux/of.h> #include <asm/irq.h> @@ -614,8 +615,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, err = phy_init_hw(phydev); if (err) phy_detach(phydev); - - phy_resume(phydev); + else + phy_resume(phydev); return err; } @@ -1072,9 +1073,6 @@ int genphy_config_init(struct phy_device *phydev) int val; u32 features; - /* For now, I'll claim that the generic driver supports - * all possible port types - */ features = (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | SUPPORTED_BNC); @@ -1107,8 +1105,8 @@ int genphy_config_init(struct phy_device *phydev) features |= SUPPORTED_1000baseT_Half; } - phydev->supported = features; - phydev->advertising = features; + phydev->supported &= features; + phydev->advertising &= features; return 0; } @@ -1169,6 +1167,38 @@ static int gen10g_resume(struct phy_device *phydev) return 0; } +static void of_set_phy_supported(struct phy_device *phydev) +{ + struct device_node *node = phydev->dev.of_node; + u32 max_speed; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return; + + if (!node) + return; + + if (!of_property_read_u32(node, "max-speed", &max_speed)) { + /* The default values for phydev->supported are provided by the PHY + * driver "features" member, we want to reset to sane defaults fist + * before supporting higher speeds. + */ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return; + + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + } +} + /** * phy_probe - probe and init a PHY device * @dev: device to probe and init @@ -1203,7 +1233,8 @@ static int phy_probe(struct device *dev) * or both of these values */ phydev->supported = phydrv->features; - phydev->advertising = phydrv->features; + of_set_phy_supported(phydev); + phydev->advertising = phydev->supported; /* Set the state to READY by default */ phydev->state = PHY_READY; @@ -1296,7 +1327,9 @@ static struct phy_driver genphy_driver[] = { .name = "Generic PHY", .soft_reset = genphy_soft_reset, .config_init = genphy_config_init, - .features = 0, + .features = PHY_GBIT_FEATURES | SUPPORTED_MII | + SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC, .config_aneg = genphy_config_aneg, .aneg_done = genphy_aneg_done, .read_status = genphy_read_status, diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index e3923eb..91d6c12 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -757,7 +757,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = get_filter(argp, &code); if (err >= 0) { - struct sock_fprog fprog = { + struct sock_fprog_kern fprog = { .len = err, .filter = code, }; @@ -778,7 +778,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = get_filter(argp, &code); if (err >= 0) { - struct sock_fprog fprog = { + struct sock_fprog_kern fprog = { .len = err, .filter = code, }; diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 0180531..1aff970 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) nf_reset(skb); skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(skb, NULL); ip_send_check(iph); ip_local_out(skb); diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 767fe61..9a9ce8d 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -968,7 +968,7 @@ static void team_port_disable(struct team *team, static void __team_compute_features(struct team *team) { struct team_port *port; - u32 vlan_features = TEAM_VLAN_FEATURES; + u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL; unsigned short max_hard_header_len = ETH_HLEN; unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index dbde341..a58dfeb 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -49,7 +49,7 @@ struct lb_port_mapping { struct lb_priv_ex { struct team *team; struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE]; - struct sock_fprog *orig_fprog; + struct sock_fprog_kern *orig_fprog; struct { unsigned int refresh_interval; /* in tenths of second */ struct delayed_work refresh_dw; @@ -241,15 +241,15 @@ static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) return 0; } -static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, +static int __fprog_create(struct sock_fprog_kern **pfprog, u32 data_len, const void *data) { - struct sock_fprog *fprog; + struct sock_fprog_kern *fprog; struct sock_filter *filter = (struct sock_filter *) data; if (data_len % sizeof(struct sock_filter)) return -EINVAL; - fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL); + fprog = kmalloc(sizeof(*fprog), GFP_KERNEL); if (!fprog) return -ENOMEM; fprog->filter = kmemdup(filter, data_len, GFP_KERNEL); @@ -262,7 +262,7 @@ static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, return 0; } -static void __fprog_destroy(struct sock_fprog *fprog) +static void __fprog_destroy(struct sock_fprog_kern *fprog) { kfree(fprog->filter); kfree(fprog); @@ -273,7 +273,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) struct lb_priv *lb_priv = get_lb_priv(team); struct sk_filter *fp = NULL; struct sk_filter *orig_fp; - struct sock_fprog *fprog = NULL; + struct sock_fprog_kern *fprog = NULL; int err; if (ctx->data.bin_val.len) { diff --git a/drivers/net/tun.c b/drivers/net/tun.c index ee328ba..98bad1f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -498,12 +498,12 @@ static void tun_detach_all(struct net_device *dev) for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); BUG_ON(!tfile); - wake_up_all(&tfile->wq.wait); + tfile->socket.sk->sk_data_ready(tfile->socket.sk); RCU_INIT_POINTER(tfile->tun, NULL); --tun->numqueues; } list_for_each_entry(tfile, &tun->disabled, next) { - wake_up_all(&tfile->wq.wait); + tfile->socket.sk->sk_data_ready(tfile->socket.sk); RCU_INIT_POINTER(tfile->tun, NULL); } BUG_ON(tun->numqueues != 0); @@ -807,8 +807,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) /* Notify and wake up reader process */ if (tfile->flags & TUN_FASYNC) kill_fasync(&tfile->fasync, SIGIO, POLL_IN); - wake_up_interruptible_poll(&tfile->wq.wait, POLLIN | - POLLRDNORM | POLLRDBAND); + tfile->socket.sk->sk_data_ready(tfile->socket.sk); rcu_read_unlock(); return NETDEV_TX_OK; @@ -965,7 +964,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table *wait) tun_debug(KERN_INFO, tun, "tun_chr_poll\n"); - poll_wait(file, &tfile->wq.wait, wait); + poll_wait(file, sk_sleep(sk), wait); if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= POLLIN | POLLRDNORM; @@ -1330,47 +1329,26 @@ done: static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, const struct iovec *iv, ssize_t len, int noblock) { - DECLARE_WAITQUEUE(wait, current); struct sk_buff *skb; ssize_t ret = 0; + int peeked, err, off = 0; tun_debug(KERN_INFO, tun, "tun_do_read\n"); - if (unlikely(!noblock)) - add_wait_queue(&tfile->wq.wait, &wait); - while (len) { - if (unlikely(!noblock)) - current->state = TASK_INTERRUPTIBLE; + if (!len) + return ret; - /* Read frames from the queue */ - if (!(skb = skb_dequeue(&tfile->socket.sk->sk_receive_queue))) { - if (noblock) { - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - if (tun->dev->reg_state != NETREG_REGISTERED) { - ret = -EIO; - break; - } - - /* Nothing to read, let's sleep */ - schedule(); - continue; - } + if (tun->dev->reg_state != NETREG_REGISTERED) + return -EIO; + /* Read frames from queue */ + skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0, + &peeked, &off, &err); + if (skb) { ret = tun_put_user(tun, tfile, skb, iv, len); kfree_skb(skb); - break; - } - - if (unlikely(!noblock)) { - current->state = TASK_RUNNING; - remove_wait_queue(&tfile->wq.wait, &wait); - } + } else + ret = err; return ret; } @@ -2199,8 +2177,8 @@ static int tun_chr_open(struct inode *inode, struct file * file) tfile->flags = 0; tfile->ifindex = 0; - rcu_assign_pointer(tfile->socket.wq, &tfile->wq); init_waitqueue_head(&tfile->wq.wait); + RCU_INIT_POINTER(tfile->socket.wq, &tfile->wq); tfile->socket.file = file; tfile->socket.ops = &tun_socket_ops; diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index bc23273..5ee7a1d 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -420,6 +420,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) struct usb_cdc_ncm_dpe16 *dpe16; int ndpoffset; int loopcount = 50; /* arbitrary max preventing infinite loop */ + u32 payload = 0; u8 *c; u16 tci; @@ -482,6 +483,7 @@ next_ndp: if (!skb) goto error; usbnet_skb_return(dev, skb); + payload += len; /* count payload bytes in this NTB */ } } err_ndp: @@ -490,6 +492,10 @@ err_ndp: if (ndpoffset && loopcount--) goto next_ndp; + /* update stats */ + ctx->rx_overhead += skb_in->len - payload; + ctx->rx_ntbs++; + return 1; error: return 0; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index d23bca5..80a844e 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -65,19 +65,384 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx); static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); static struct usb_driver cdc_ncm_driver; -static int cdc_ncm_setup(struct usbnet *dev) +struct cdc_ncm_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; + int stat_offset; +}; + +#define CDC_NCM_STAT(str, m) { \ + .stat_string = str, \ + .sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \ + .stat_offset = offsetof(struct cdc_ncm_ctx, m) } +#define CDC_NCM_SIMPLE_STAT(m) CDC_NCM_STAT(__stringify(m), m) + +static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = { + CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full), + CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full), + CDC_NCM_SIMPLE_STAT(tx_reason_timeout), + CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram), + CDC_NCM_SIMPLE_STAT(tx_overhead), + CDC_NCM_SIMPLE_STAT(tx_ntbs), + CDC_NCM_SIMPLE_STAT(rx_overhead), + CDC_NCM_SIMPLE_STAT(rx_ntbs), +}; + +static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(cdc_ncm_gstrings_stats); + default: + return -EOPNOTSUPP; + } +} + +static void cdc_ncm_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats __always_unused *stats, + u64 *data) +{ + struct usbnet *dev = netdev_priv(netdev); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + int i; + char *p = NULL; + + for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { + p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset; + data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } +} + +static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { + memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + } +} + +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx); + +static const struct ethtool_ops cdc_ncm_ethtool_ops = { + .get_settings = usbnet_get_settings, + .set_settings = usbnet_set_settings, + .get_link = usbnet_get_link, + .nway_reset = usbnet_nway_reset, + .get_drvinfo = usbnet_get_drvinfo, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_ts_info = ethtool_op_get_ts_info, + .get_sset_count = cdc_ncm_get_sset_count, + .get_strings = cdc_ncm_get_strings, + .get_ethtool_stats = cdc_ncm_get_ethtool_stats, +}; + +static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u32 val, max, min; + + /* clamp new_rx to sane values */ + min = USB_CDC_NCM_NTB_MIN_IN_SIZE; + max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); + + /* dwNtbInMaxSize spec violation? Use MIN size for both limits */ + if (max < min) { + dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n", + le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min); + max = min; + } + + val = clamp_t(u32, new_rx, min, max); + if (val != new_rx) + dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max); + + return val; +} + +static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u32 val, max, min; + + /* clamp new_tx to sane values */ + min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); + max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); + + /* some devices set dwNtbOutMaxSize too low for the above default */ + min = min(min, max); + + val = clamp_t(u32, new_tx, min, max); + if (val != new_tx) + dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max); + + return val; +} + +static ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + return sprintf(buf, "%u\n", ctx->min_tx_pkt); +} + +static ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + return sprintf(buf, "%u\n", ctx->rx_max); +} + +static ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + return sprintf(buf, "%u\n", ctx->tx_max); +} + +static ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC); +} + +static ssize_t cdc_ncm_store_min_tx_pkt(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + unsigned long val; + + /* no need to restrict values - anything from 0 to infinity is OK */ + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + ctx->min_tx_pkt = val; + return len; +} + +static ssize_t cdc_ncm_store_rx_max(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + unsigned long val; + + if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val) + return -EINVAL; + + cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max); + return len; +} + +static ssize_t cdc_ncm_store_tx_max(struct device *d, struct device_attribute *attr, const char *buf, size_t len) { + struct usbnet *dev = netdev_priv(to_net_dev(d)); struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + unsigned long val; + + if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val) + return -EINVAL; + + cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val); + return len; +} + +static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + ssize_t ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; + if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX)) + return -EINVAL; + + spin_lock_bh(&ctx->mtx); + ctx->timer_interval = val * NSEC_PER_USEC; + if (!ctx->timer_interval) + ctx->tx_timer_pending = 0; + spin_unlock_bh(&ctx->mtx); + return len; +} + +static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt); +static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max); +static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max); +static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs); + +#define NCM_PARM_ATTR(name, format, tocpu) \ +static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \ +{ \ + struct usbnet *dev = netdev_priv(to_net_dev(d)); \ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \ + return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name)); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL) + +NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu); +NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu); +NCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu); +NCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu); +NCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu); +NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu); + +static struct attribute *cdc_ncm_sysfs_attrs[] = { + &dev_attr_min_tx_pkt.attr, + &dev_attr_rx_max.attr, + &dev_attr_tx_max.attr, + &dev_attr_tx_timer_usecs.attr, + &dev_attr_bmNtbFormatsSupported.attr, + &dev_attr_dwNtbInMaxSize.attr, + &dev_attr_wNdpInDivisor.attr, + &dev_attr_wNdpInPayloadRemainder.attr, + &dev_attr_wNdpInAlignment.attr, + &dev_attr_dwNtbOutMaxSize.attr, + &dev_attr_wNdpOutDivisor.attr, + &dev_attr_wNdpOutPayloadRemainder.attr, + &dev_attr_wNdpOutAlignment.attr, + &dev_attr_wNtbOutMaxDatagrams.attr, + NULL, +}; + +static struct attribute_group cdc_ncm_sysfs_attr_group = { + .name = "cdc_ncm", + .attrs = cdc_ncm_sysfs_attrs, +}; + +/* handle rx_max and tx_max changes */ +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; u32 val; - u8 flags; - u8 iface_no; - int err; - int eth_hlen; - u16 mbim_mtu; - u16 ntb_fmt_supported; - __le16 max_datagram_size; - iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; + val = cdc_ncm_check_rx_max(dev, new_rx); + + /* inform device about NTB input size changes */ + if (val != ctx->rx_max) { + __le32 dwNtbInMaxSize = cpu_to_le32(val); + + dev_info(&dev->intf->dev, "setting rx_max = %u\n", val); + + /* tell device to use new size */ + if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + 0, iface_no, &dwNtbInMaxSize, 4) < 0) + dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n"); + else + ctx->rx_max = val; + } + + /* usbnet use these values for sizing rx queues */ + if (dev->rx_urb_size != ctx->rx_max) { + dev->rx_urb_size = ctx->rx_max; + if (netif_running(dev->net)) + usbnet_unlink_rx_urbs(dev); + } + + val = cdc_ncm_check_tx_max(dev, new_tx); + if (val != ctx->tx_max) + dev_info(&dev->intf->dev, "setting tx_max = %u\n", val); + + /* Adding a pad byte here if necessary simplifies the handling + * in cdc_ncm_fill_tx_frame, making tx_max always represent + * the real skb max size. + * + * We cannot use dev->maxpacket here because this is called from + * .bind which is called before usbnet sets up dev->maxpacket + */ + if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) && + val % usb_maxpacket(dev->udev, dev->out, 1) == 0) + val++; + + /* we might need to flush any pending tx buffers if running */ + if (netif_running(dev->net) && val > ctx->tx_max) { + netif_tx_lock_bh(dev->net); + usbnet_start_xmit(NULL, dev->net); + /* make sure tx_curr_skb is reallocated if it was empty */ + if (ctx->tx_curr_skb) { + dev_kfree_skb_any(ctx->tx_curr_skb); + ctx->tx_curr_skb = NULL; + } + ctx->tx_max = val; + netif_tx_unlock_bh(dev->net); + } else { + ctx->tx_max = val; + } + + dev->hard_mtu = ctx->tx_max; + + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + + /* never pad more than 3 full USB packets per transfer */ + ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1), + CDC_NCM_MIN_TX_PKT, ctx->tx_max); +} + +/* helpers for NCM and MBIM differences */ +static u8 cdc_ncm_flags(struct usbnet *dev) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) + return ctx->mbim_desc->bmNetworkCapabilities; + if (ctx->func_desc) + return ctx->func_desc->bmNetworkCapabilities; + return 0; +} + +static int cdc_ncm_eth_hlen(struct usbnet *dev) +{ + if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting)) + return 0; + return ETH_HLEN; +} + +static u32 cdc_ncm_min_dgram_size(struct usbnet *dev) +{ + if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting)) + return CDC_MBIM_MIN_DATAGRAM_SIZE; + return CDC_NCM_MIN_DATAGRAM_SIZE; +} + +static u32 cdc_ncm_max_dgram_size(struct usbnet *dev) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) + return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); + if (ctx->ether_desc) + return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); + return CDC_NCM_MAX_DATAGRAM_SIZE; +} + +/* initial one-time device setup. MUST be called with the data interface + * in altsetting 0 + */ +static int cdc_ncm_init(struct usbnet *dev) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; + int err; err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS, USB_TYPE_CLASS | USB_DIR_IN @@ -89,7 +454,36 @@ static int cdc_ncm_setup(struct usbnet *dev) return err; /* GET_NTB_PARAMETERS is required */ } - /* read correct set of parameters according to device mode */ + /* set CRC Mode */ + if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) { + dev_dbg(&dev->intf->dev, "Setting CRC mode off\n"); + err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + USB_CDC_NCM_CRC_NOT_APPENDED, + iface_no, NULL, 0); + if (err < 0) + dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n"); + } + + /* set NTB format, if both formats are supported. + * + * "The host shall only send this command while the NCM Data + * Interface is in alternate setting 0." + */ + if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) & + USB_CDC_NCM_NTB32_SUPPORTED) { + dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); + err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + USB_CDC_NCM_NTB16_FORMAT, + iface_no, NULL, 0); + if (err < 0) + dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n"); + } + + /* set initial device values */ ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize); ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize); ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder); @@ -97,72 +491,79 @@ static int cdc_ncm_setup(struct usbnet *dev) ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment); /* devices prior to NCM Errata shall set this field to zero */ ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams); - ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported); - - /* there are some minor differences in NCM and MBIM defaults */ - if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) { - if (!ctx->mbim_desc) - return -EINVAL; - eth_hlen = 0; - flags = ctx->mbim_desc->bmNetworkCapabilities; - ctx->max_datagram_size = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); - if (ctx->max_datagram_size < CDC_MBIM_MIN_DATAGRAM_SIZE) - ctx->max_datagram_size = CDC_MBIM_MIN_DATAGRAM_SIZE; - } else { - if (!ctx->func_desc) - return -EINVAL; - eth_hlen = ETH_HLEN; - flags = ctx->func_desc->bmNetworkCapabilities; - ctx->max_datagram_size = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); - if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE) - ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE; - } - - /* common absolute max for NCM and MBIM */ - if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE) - ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; dev_dbg(&dev->intf->dev, "dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n", ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, - ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags); + ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev)); /* max count of tx datagrams */ if ((ctx->tx_max_datagrams == 0) || (ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX)) ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; - /* verify maximum size of received NTB in bytes */ - if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) { - dev_dbg(&dev->intf->dev, "Using min receive length=%d\n", - USB_CDC_NCM_NTB_MIN_IN_SIZE); - ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE; - } + /* set up maximum NDP size */ + ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); - if (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX) { - dev_dbg(&dev->intf->dev, "Using default maximum receive length=%d\n", - CDC_NCM_NTB_MAX_SIZE_RX); - ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX; - } + /* initial coalescing timer interval */ + ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; - /* inform device about NTB input size changes */ - if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { - __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); + return 0; +} - err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - 0, iface_no, &dwNtbInMaxSize, 4); - if (err < 0) - dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n"); +/* set a new max datagram size */ +static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; + __le16 max_datagram_size; + u16 mbim_mtu; + int err; + + /* set default based on descriptors */ + ctx->max_datagram_size = clamp_t(u32, new_size, + cdc_ncm_min_dgram_size(dev), + CDC_NCM_MAX_DATAGRAM_SIZE); + + /* inform the device about the selected Max Datagram Size? */ + if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE)) + goto out; + + /* read current mtu value from device */ + err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, iface_no, &max_datagram_size, 2); + if (err < 0) { + dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n"); + goto out; } - /* verify maximum size of transmitted NTB in bytes */ - if (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX) { - dev_dbg(&dev->intf->dev, "Using default maximum transmit length=%d\n", - CDC_NCM_NTB_MAX_SIZE_TX); - ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX; + if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size) + goto out; + + max_datagram_size = cpu_to_le16(ctx->max_datagram_size); + err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE, + USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, + 0, iface_no, &max_datagram_size, 2); + if (err < 0) + dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n"); + +out: + /* set MTU to max supported by the device if necessary */ + dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev)); + + /* do not exceed operater preferred MTU */ + if (ctx->mbim_extended_desc) { + mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU); + if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu) + dev->net->mtu = mbim_mtu; } +} + +static void cdc_ncm_fix_modulus(struct usbnet *dev) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u32 val; /* * verify that the structure alignment is: @@ -199,68 +600,31 @@ static int cdc_ncm_setup(struct usbnet *dev) } /* adjust TX-remainder according to NCM specification. */ - ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) & + ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) & (ctx->tx_modulus - 1)); +} - /* additional configuration */ - - /* set CRC Mode */ - if (flags & USB_CDC_NCM_NCAP_CRC_MODE) { - err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - USB_CDC_NCM_CRC_NOT_APPENDED, - iface_no, NULL, 0); - if (err < 0) - dev_dbg(&dev->intf->dev, "Setting CRC mode off failed\n"); - } - - /* set NTB format, if both formats are supported */ - if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) { - err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - USB_CDC_NCM_NTB16_FORMAT, - iface_no, NULL, 0); - if (err < 0) - dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit failed\n"); - } - - /* inform the device about the selected Max Datagram Size */ - if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE)) - goto out; - - /* read current mtu value from device */ - err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, - USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, iface_no, &max_datagram_size, 2); - if (err < 0) { - dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n"); - goto out; - } - - if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size) - goto out; +static int cdc_ncm_setup(struct usbnet *dev) +{ + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u32 def_rx, def_tx; - max_datagram_size = cpu_to_le16(ctx->max_datagram_size); - err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE, - USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, - 0, iface_no, &max_datagram_size, 2); - if (err < 0) - dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n"); + /* be conservative when selecting intial buffer size to + * increase the number of hosts this will work for + */ + def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX, + le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); + def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX, + le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); -out: - /* set MTU to max supported by the device if necessary */ - if (dev->net->mtu > ctx->max_datagram_size - eth_hlen) - dev->net->mtu = ctx->max_datagram_size - eth_hlen; + /* clamp rx_max and tx_max and inform device */ + cdc_ncm_update_rxtx_max(dev, def_rx, def_tx); - /* do not exceed operater preferred MTU */ - if (ctx->mbim_extended_desc) { - mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU); - if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu) - dev->net->mtu = mbim_mtu; - } + /* sanitize the modulus and remainder values */ + cdc_ncm_fix_modulus(dev); + /* set max datagram size */ + cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev)); return 0; } @@ -424,10 +788,21 @@ advance: } /* check if we got everything */ - if (!ctx->data || (!ctx->mbim_desc && !ctx->ether_desc)) { - dev_dbg(&intf->dev, "CDC descriptors missing\n"); + if (!ctx->data) { + dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n"); goto error; } + if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) { + if (!ctx->mbim_desc) { + dev_dbg(&intf->dev, "MBIM functional descriptor missing\n"); + goto error; + } + } else { + if (!ctx->ether_desc || !ctx->func_desc) { + dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n"); + goto error; + } + } /* claim data interface, if different from control */ if (ctx->data != ctx->control) { @@ -447,8 +822,8 @@ advance: goto error2; } - /* initialize data interface */ - if (cdc_ncm_setup(dev)) + /* initialize basic device settings */ + if (cdc_ncm_init(dev)) goto error2; /* configure data interface */ @@ -477,18 +852,14 @@ advance: dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr); } - /* usbnet use these values for sizing tx/rx queues */ - dev->hard_mtu = ctx->tx_max; - dev->rx_urb_size = ctx->rx_max; + /* finish setting up the device specific data */ + cdc_ncm_setup(dev); - /* cdc_ncm_setup will override dwNtbOutMaxSize if it is - * outside the sane range. Adding a pad byte here if necessary - * simplifies the handling in cdc_ncm_fill_tx_frame, making - * tx_max always represent the real skb max size. - */ - if (ctx->tx_max != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) && - ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0) - ctx->tx_max++; + /* override ethtool_ops */ + dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; + + /* add our sysfs attrs */ + dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group; return 0; @@ -627,7 +998,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max); /* verify that there is room for the NDP and the datagram (reserve) */ - if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE) + if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size) return NULL; /* link to it */ @@ -637,7 +1008,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ nth16->wNdpIndex = cpu_to_le16(skb->len); /* push a new empty NDP */ - ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE); + ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size); ndp16->dwSignature = sign; ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16)); return ndp16; @@ -682,6 +1053,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* count total number of frames in this NTB */ ctx->tx_curr_frame_num = 0; + + /* recent payload counter for this skb_out */ + ctx->tx_curr_frame_payload = 0; } for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { @@ -719,6 +1093,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ctx->tx_rem_sign = sign; skb = NULL; ready2send = 1; + ctx->tx_reason_ntb_full++; /* count reason for transmitting */ } break; } @@ -732,12 +1107,14 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); + ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */ dev_kfree_skb_any(skb); skb = NULL; /* send now if this NDP is full */ if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) { ready2send = 1; + ctx->tx_reason_ndp_full++; /* count reason for transmitting */ break; } } @@ -757,7 +1134,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ctx->tx_curr_skb = skb_out; goto exit_no_skb; - } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) { + } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) { /* wait for more frames */ /* push variables */ ctx->tx_curr_skb = skb_out; @@ -767,11 +1144,13 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) goto exit_no_skb; } else { + if (n == ctx->tx_max_datagrams) + ctx->tx_reason_max_datagram++; /* count reason for transmitting */ /* frame goes out */ /* variables will be reset at next call */ } - /* If collected data size is less or equal CDC_NCM_MIN_TX_PKT + /* If collected data size is less or equal ctx->min_tx_pkt * bytes, we send buffers as it is. If we get more data, it * would be more efficient for USB HS mobile device with DMA * engine to receive a full size NTB, than canceling DMA @@ -781,7 +1160,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) * a ZLP after full sized NTBs. */ if (!(dev->driver_info->flags & FLAG_SEND_ZLP) && - skb_out->len > CDC_NCM_MIN_TX_PKT) + skb_out->len > ctx->min_tx_pkt) memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len); else if (skb_out->len < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0) @@ -794,11 +1173,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* return skb */ ctx->tx_curr_skb = NULL; dev->net->stats.tx_packets += ctx->tx_curr_frame_num; + + /* keep private stats: framing overhead and number of NTBs */ + ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; + ctx->tx_ntbs++; + + /* usbnet has already counted all the framing overhead. + * Adjust the stats so that the tx_bytes counter show real + * payload data instead. + */ + dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; + return skb_out; exit_no_skb: - /* Start timer, if there is a remaining skb */ - if (ctx->tx_curr_skb != NULL) + /* Start timer, if there is a remaining non-empty skb */ + if (ctx->tx_curr_skb != NULL && n > 0) cdc_ncm_tx_timeout_start(ctx); return NULL; } @@ -809,7 +1199,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx) /* start timer, if not already started */ if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop))) hrtimer_start(&ctx->tx_timer, - ktime_set(0, CDC_NCM_TIMER_INTERVAL), + ktime_set(0, ctx->timer_interval), HRTIMER_MODE_REL); } @@ -834,6 +1224,7 @@ static void cdc_ncm_txpath_bh(unsigned long param) cdc_ncm_tx_timeout_start(ctx); spin_unlock_bh(&ctx->mtx); } else if (dev->net != NULL) { + ctx->tx_reason_timeout++; /* count reason for transmitting */ spin_unlock_bh(&ctx->mtx); netif_tx_lock_bh(dev->net); usbnet_start_xmit(NULL, dev->net); @@ -969,6 +1360,7 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) struct usb_cdc_ncm_dpe16 *dpe16; int ndpoffset; int loopcount = 50; /* arbitrary max preventing infinite loop */ + u32 payload = 0; ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); if (ndpoffset < 0) @@ -1014,13 +1406,13 @@ next_ndp: break; } else { - skb = skb_clone(skb_in, GFP_ATOMIC); + /* create a fresh copy to reduce truesize */ + skb = netdev_alloc_skb_ip_align(dev->net, len); if (!skb) goto error; - skb->len = len; - skb->data = ((u8 *)skb_in->data) + offset; - skb_set_tail_pointer(skb, len); + memcpy(skb_put(skb, len), skb_in->data + offset, len); usbnet_skb_return(dev, skb); + payload += len; /* count payload bytes in this NTB */ } } err_ndp: @@ -1029,6 +1421,10 @@ err_ndp: if (ndpoffset && loopcount--) goto next_ndp; + /* update stats */ + ctx->rx_overhead += skb_in->len - payload; + ctx->rx_ntbs++; + return 1; error: return 0; @@ -1048,14 +1444,14 @@ cdc_ncm_speed_change(struct usbnet *dev, */ if ((tx_speed > 1000000) && (rx_speed > 1000000)) { netif_info(dev, link, dev->net, - "%u mbit/s downlink %u mbit/s uplink\n", - (unsigned int)(rx_speed / 1000000U), - (unsigned int)(tx_speed / 1000000U)); + "%u mbit/s downlink %u mbit/s uplink\n", + (unsigned int)(rx_speed / 1000000U), + (unsigned int)(tx_speed / 1000000U)); } else { netif_info(dev, link, dev->net, - "%u kbit/s downlink %u kbit/s uplink\n", - (unsigned int)(rx_speed / 1000U), - (unsigned int)(tx_speed / 1000U)); + "%u kbit/s downlink %u kbit/s uplink\n", + (unsigned int)(rx_speed / 1000U), + (unsigned int)(tx_speed / 1000U)); } } @@ -1085,11 +1481,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE. */ - ctx->connected = le16_to_cpu(event->wValue); netif_info(dev, link, dev->net, "network connection: %sconnected\n", - ctx->connected ? "" : "dis"); - usbnet_link_change(dev, ctx->connected, 0); + !!event->wValue ? "" : "dis"); + usbnet_link_change(dev, !!event->wValue, 0); break; case USB_CDC_NOTIFY_SPEED_CHANGE: @@ -1109,23 +1504,11 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) } } -static int cdc_ncm_check_connect(struct usbnet *dev) -{ - struct cdc_ncm_ctx *ctx; - - ctx = (struct cdc_ncm_ctx *)dev->data[0]; - if (ctx == NULL) - return 1; /* disconnected */ - - return !ctx->connected; -} - static const struct driver_info cdc_ncm_info = { .description = "CDC NCM", .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, - .check_connect = cdc_ncm_check_connect, .manage_power = usbnet_manage_power, .status = cdc_ncm_status, .rx_fixup = cdc_ncm_rx_fixup, @@ -1139,7 +1522,6 @@ static const struct driver_info wwan_info = { | FLAG_WWAN, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, - .check_connect = cdc_ncm_check_connect, .manage_power = usbnet_manage_power, .status = cdc_ncm_status, .rx_fixup = cdc_ncm_rx_fixup, @@ -1153,7 +1535,6 @@ static const struct driver_info wwan_noarp_info = { | FLAG_WWAN | FLAG_NOARP, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, - .check_connect = cdc_ncm_check_connect, .manage_power = usbnet_manage_power, .status = cdc_ncm_status, .rx_fixup = cdc_ncm_rx_fixup, diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c index 312178d..f9822bc 100644 --- a/drivers/net/usb/huawei_cdc_ncm.c +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -172,24 +172,11 @@ err: return ret; } -static int huawei_cdc_ncm_check_connect(struct usbnet *usbnet_dev) -{ - struct cdc_ncm_ctx *ctx; - - ctx = (struct cdc_ncm_ctx *)usbnet_dev->data[0]; - - if (ctx == NULL) - return 1; /* disconnected */ - - return !ctx->connected; -} - static const struct driver_info huawei_cdc_ncm_info = { .description = "Huawei CDC NCM device", .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, .bind = huawei_cdc_ncm_bind, .unbind = huawei_cdc_ncm_unbind, - .check_connect = huawei_cdc_ncm_check_connect, .manage_power = huawei_cdc_ncm_manage_power, .rx_fixup = cdc_ncm_rx_fixup, .tx_fixup = cdc_ncm_tx_fixup, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 9f91c7a..2543196 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -630,12 +630,10 @@ int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) int ret; void *tmp; - tmp = kmalloc(size, GFP_KERNEL); + tmp = kmemdup(data, size, GFP_KERNEL); if (!tmp) return -ENOMEM; - memcpy(tmp, data, size); - ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, value, index, tmp, size, 500); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index bcfff0d..02f6d1c 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -26,6 +26,7 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/delay.h> #include <linux/if.h> #include <linux/hdlc.h> #include <asm/io.h> @@ -678,7 +679,6 @@ static inline void fst_cpureset(struct fst_card_info *card) { unsigned char interrupt_line_register; - unsigned long j = jiffies + 1; unsigned int regval; if (card->family == FST_FAMILY_TXU) { @@ -696,16 +696,12 @@ fst_cpureset(struct fst_card_info *card) /* * We are delaying here to allow the 9054 to reset itself */ - j = jiffies + 1; - while (jiffies < j) - /* Do nothing */ ; + usleep_range(10, 20); outw(0x240f, card->pci_conf + CNTRL_9054 + 2); /* * We are delaying here to allow the 9054 to reload its eeprom */ - j = jiffies + 1; - while (jiffies < j) - /* Do nothing */ ; + usleep_range(10, 20); outw(0x040f, card->pci_conf + CNTRL_9054 + 2); if (pci_write_config_byte diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index de3bbf4..cdd45fb 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -1322,10 +1322,6 @@ NOTE: This is rather a useless action right now, as the static int sdla_change_mtu(struct net_device *dev, int new_mtu) { - struct frad_local *flp; - - flp = netdev_priv(dev); - if (netif_running(dev)) return -EBUSY; diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 9c34d2f..9c78090 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -500,26 +500,23 @@ int i2400m_pm_notifier(struct notifier_block *notifier, */ int i2400m_pre_reset(struct i2400m *i2400m) { - int result; struct device *dev = i2400m_dev(i2400m); d_fnstart(3, dev, "(i2400m %p)\n", i2400m); d_printf(1, dev, "pre-reset shut down\n"); - result = 0; mutex_lock(&i2400m->init_mutex); if (i2400m->updown) { netif_tx_disable(i2400m->wimax_dev.net_dev); __i2400m_dev_stop(i2400m); - result = 0; /* down't set updown to zero -- this way * post_reset can restore properly */ } mutex_unlock(&i2400m->init_mutex); if (i2400m->bus_release) i2400m->bus_release(i2400m); - d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); - return result; + d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m); + return 0; } EXPORT_SYMBOL_GPL(i2400m_pre_reset); diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 99b3bfa..10fd12e 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s) static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, int manifest_sync_timeout) { - u8 *block; - struct dfu_status dfu_stat_buf; int ret = 0; int need_dfu_state = 1; int is_done = 0; - u8 dfu_state = 0; u32 dfu_timeout = 0; int bsize = 0; int blockno = 0; + struct dfu_status *dfu_stat_buf = NULL; + u8 *dfu_state = NULL; + u8 *block = NULL; at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, manifest_sync_timeout); @@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, return -EINVAL; } + dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); + if (!dfu_stat_buf) { + ret = -ENOMEM; + goto exit; + } + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); - if (!block) - return -ENOMEM; + if (!block) { + ret = -ENOMEM; + goto exit; + } + + dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); + if (!dfu_state) { + ret = -ENOMEM; + goto exit; + } + *dfu_state = 0; do { if (need_dfu_state) { - ret = at76_dfu_get_state(udev, &dfu_state); + ret = at76_dfu_get_state(udev, dfu_state); if (ret < 0) { dev_err(&udev->dev, "cannot get DFU state: %d\n", ret); @@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, need_dfu_state = 0; } - switch (dfu_state) { + switch (*dfu_state) { case STATE_DFU_DOWNLOAD_SYNC: at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); - ret = at76_dfu_get_status(udev, &dfu_stat_buf); + ret = at76_dfu_get_status(udev, dfu_stat_buf); if (ret >= 0) { - dfu_state = dfu_stat_buf.state; - dfu_timeout = at76_get_timeout(&dfu_stat_buf); + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); need_dfu_state = 0; } else dev_err(&udev->dev, @@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, case STATE_DFU_MANIFEST_SYNC: at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); - ret = at76_dfu_get_status(udev, &dfu_stat_buf); + ret = at76_dfu_get_status(udev, dfu_stat_buf); if (ret < 0) break; - dfu_state = dfu_stat_buf.state; - dfu_timeout = at76_get_timeout(&dfu_stat_buf); + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); need_dfu_state = 0; /* override the timeout from the status response, @@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, break; default: - at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state); + at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); ret = -EINVAL; break; } } while (!is_done && (ret >= 0)); exit: + kfree(dfu_state); kfree(block); + kfree(dfu_stat_buf); + if (ret >= 0) ret = 0; @@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) dev_err(&udev->dev, "loading %dth firmware block failed: %d\n", blockno, ret); + ret = -EIO; goto exit; } buf += bsize; @@ -2020,6 +2039,44 @@ static void at76_configure_filter(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &priv->work_set_promisc); } +static int at76_set_wep(struct at76_priv *priv) +{ + int ret = 0; + struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; + + priv->mib_buf.type = MIB_MAC_WEP; + priv->mib_buf.size = sizeof(struct mib_mac_wep); + priv->mib_buf.index = 0; + + memset(mib_data, 0, sizeof(*mib_data)); + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + mib_data->encryption_level = 2; + else + mib_data->encryption_level = 1; + + /* always exclude unencrypted if WEP is active */ + mib_data->exclude_unencrypted = 1; + } else { + mib_data->exclude_unencrypted = 0; + mib_data->encryption_level = 0; + } + + mib_data->privacy_invoked = priv->wep_enabled; + mib_data->wep_default_key_id = priv->wep_key_id; + memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, + sizeof(priv->wep_keys)); + + ret = at76_set_mib(priv, &priv->mib_buf); + + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (wep) failed: %d\n", ret); + + return ret; +} + static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) @@ -2062,7 +2119,7 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, priv->wep_enabled = 1; } - at76_startup_device(priv); + at76_set_wep(priv); mutex_unlock(&priv->mtx); @@ -2330,16 +2387,22 @@ static int at76_probe(struct usb_interface *interface, struct usb_device *udev; int op_mode; int need_ext_fw = 0; - struct mib_fw_version fwv; + struct mib_fw_version *fwv = NULL; int board_type = (int)id->driver_info; udev = usb_get_dev(interface_to_usbdev(interface)); + fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); + if (!fwv) { + ret = -ENOMEM; + goto exit; + } + /* Load firmware into kernel memory */ fwe = at76_load_firmware(udev, board_type); if (!fwe) { ret = -ENOENT; - goto error; + goto exit; } op_mode = at76_get_op_mode(udev); @@ -2353,7 +2416,7 @@ static int at76_probe(struct usb_interface *interface, dev_err(&interface->dev, "cannot handle a device in HW_CONFIG_MODE\n"); ret = -EBUSY; - goto error; + goto exit; } if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH @@ -2366,10 +2429,10 @@ static int at76_probe(struct usb_interface *interface, dev_err(&interface->dev, "error %d downloading internal firmware\n", ret); - goto error; + goto exit; } usb_put_dev(udev); - return ret; + goto exit; } /* Internal firmware already inside the device. Get firmware @@ -2382,8 +2445,8 @@ static int at76_probe(struct usb_interface *interface, * query the device for the fw version */ if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { - ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); - if (ret < 0 || (fwv.major | fwv.minor) == 0) + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); + if (ret < 0 || (fwv->major | fwv->minor) == 0) need_ext_fw = 1; } else /* No way to check firmware version, reload to be sure */ @@ -2394,37 +2457,37 @@ static int at76_probe(struct usb_interface *interface, "downloading external firmware\n"); ret = at76_load_external_fw(udev, fwe); - if (ret) - goto error; + if (ret < 0) + goto exit; /* Re-check firmware version */ - ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); if (ret < 0) { dev_err(&interface->dev, "error %d getting firmware version\n", ret); - goto error; + goto exit; } } priv = at76_alloc_new_device(udev); if (!priv) { ret = -ENOMEM; - goto error; + goto exit; } usb_set_intfdata(interface, priv); - memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); + memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); priv->board_type = board_type; ret = at76_init_new_device(priv, interface); if (ret < 0) at76_delete_device(priv); - return ret; - -error: - usb_put_dev(udev); +exit: + kfree(fwv); + if (ret < 0) + usb_put_dev(udev); return ret; } diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h index f14a654..4718aa5 100644 --- a/drivers/net/wireless/at76c50x-usb.h +++ b/drivers/net/wireless/at76c50x-usb.h @@ -219,18 +219,6 @@ struct at76_req_join { u8 reserved; } __packed; -struct set_mib_buffer { - u8 type; - u8 size; - u8 index; - u8 reserved; - union { - u8 byte; - __le16 word; - u8 addr[ETH_ALEN]; - } data; -} __packed; - struct mib_local { u16 reserved0; u8 beacon_enable; @@ -334,6 +322,19 @@ struct mib_mdomain { u8 channel_list[14]; /* 0 for invalid channels */ } __packed; +struct set_mib_buffer { + u8 type; + u8 size; + u8 index; + u8 reserved; + union { + u8 byte; + __le16 word; + u8 addr[ETH_ALEN]; + struct mib_mac_wep wep_mib; + } data; +} __packed; + struct at76_fw_header { __le32 crc; /* CRC32 of the whole image */ __le32 board_type; /* firmware compatibility code */ diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 3a9bdf51..111ab70 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -201,7 +201,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, \ addr = host_interest_item_address(HI_ITEM(item)); \ ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \ - *val = __le32_to_cpu(tmp); \ + if (!ret) \ + *val = __le32_to_cpu(tmp); \ ret; \ }) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 1e4cad8..d185dc0 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -329,6 +329,33 @@ exit: return ret; } +void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe) +{ + struct ath10k *ar = pipe->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_ring *src_ring = pipe->src_ring; + u32 ctrl_addr = pipe->ctrl_addr; + + lockdep_assert_held(&ar_pci->ce_lock); + + /* + * This function must be called only if there is an incomplete + * scatter-gather transfer (before index register is updated) + * that needs to be cleaned up. + */ + if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index)) + return; + + if (WARN_ON_ONCE(src_ring->write_index == + ath10k_ce_src_ring_write_index_get(ar, ctrl_addr))) + return; + + src_ring->write_index--; + src_ring->write_index &= src_ring->nentries_mask; + + src_ring->per_transfer_context[src_ring->write_index] = NULL; +} + int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, void *per_transfer_context, u32 buffer, diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index fd0bc35..7a5a36f 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -160,6 +160,8 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, unsigned int transfer_id, unsigned int flags); +void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe); + void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6abde37..82017f5 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -58,36 +58,6 @@ static void ath10k_send_suspend_complete(struct ath10k *ar) complete(&ar->target_suspend); } -static int ath10k_init_connect_htc(struct ath10k *ar) -{ - int status; - - status = ath10k_wmi_connect_htc_service(ar); - if (status) - goto conn_fail; - - /* Start HTC */ - status = ath10k_htc_start(&ar->htc); - if (status) - goto conn_fail; - - /* Wait for WMI event to be ready */ - status = ath10k_wmi_wait_for_service_ready(ar); - if (status <= 0) { - ath10k_warn("wmi service ready event not received"); - status = -ETIMEDOUT; - goto timeout; - } - - ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n"); - return 0; - -timeout: - ath10k_htc_stop(&ar->htc); -conn_fail: - return status; -} - static int ath10k_init_configure_target(struct ath10k *ar) { u32 param_host; @@ -680,8 +650,9 @@ static void ath10k_core_restart(struct work_struct *work) switch (ar->state) { case ATH10K_STATE_ON: - ath10k_halt(ar); ar->state = ATH10K_STATE_RESTARTING; + del_timer_sync(&ar->scan.timeout); + ath10k_reset_scan((unsigned long)ar); ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: @@ -690,6 +661,8 @@ static void ath10k_core_restart(struct work_struct *work) ath10k_warn("cannot restart a device that hasn't been started\n"); break; case ATH10K_STATE_RESTARTING: + /* hw restart might be requested from multiple places */ + break; case ATH10K_STATE_RESTARTED: ar->state = ATH10K_STATE_WEDGED; /* fall through */ @@ -701,70 +674,6 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); } -struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - const struct ath10k_hif_ops *hif_ops) -{ - struct ath10k *ar; - - ar = ath10k_mac_create(); - if (!ar) - return NULL; - - ar->ath_common.priv = ar; - ar->ath_common.hw = ar->hw; - - ar->p2p = !!ath10k_p2p; - ar->dev = dev; - - ar->hif.priv = hif_priv; - ar->hif.ops = hif_ops; - - init_completion(&ar->scan.started); - init_completion(&ar->scan.completed); - init_completion(&ar->scan.on_channel); - init_completion(&ar->target_suspend); - - init_completion(&ar->install_key_done); - init_completion(&ar->vdev_setup_done); - - setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); - - ar->workqueue = create_singlethread_workqueue("ath10k_wq"); - if (!ar->workqueue) - goto err_wq; - - mutex_init(&ar->conf_mutex); - spin_lock_init(&ar->data_lock); - - INIT_LIST_HEAD(&ar->peers); - init_waitqueue_head(&ar->peer_mapping_wq); - - init_completion(&ar->offchan_tx_completed); - INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); - skb_queue_head_init(&ar->offchan_tx_queue); - - INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); - skb_queue_head_init(&ar->wmi_mgmt_tx_queue); - - INIT_WORK(&ar->restart_work, ath10k_core_restart); - - return ar; - -err_wq: - ath10k_mac_destroy(ar); - return NULL; -} -EXPORT_SYMBOL(ath10k_core_create); - -void ath10k_core_destroy(struct ath10k *ar) -{ - flush_workqueue(ar->workqueue); - destroy_workqueue(ar->workqueue); - - ath10k_mac_destroy(ar); -} -EXPORT_SYMBOL(ath10k_core_destroy); - int ath10k_core_start(struct ath10k *ar) { int status; @@ -805,10 +714,28 @@ int ath10k_core_start(struct ath10k *ar) goto err; } + status = ath10k_htt_init(ar); + if (status) { + ath10k_err("failed to init htt: %d\n", status); + goto err_wmi_detach; + } + + status = ath10k_htt_tx_alloc(&ar->htt); + if (status) { + ath10k_err("failed to alloc htt tx: %d\n", status); + goto err_wmi_detach; + } + + status = ath10k_htt_rx_alloc(&ar->htt); + if (status) { + ath10k_err("failed to alloc htt rx: %d\n", status); + goto err_htt_tx_detach; + } + status = ath10k_hif_start(ar); if (status) { ath10k_err("could not start HIF: %d\n", status); - goto err_wmi_detach; + goto err_htt_rx_detach; } status = ath10k_htc_wait_target(&ar->htc); @@ -817,15 +744,30 @@ int ath10k_core_start(struct ath10k *ar) goto err_hif_stop; } - status = ath10k_htt_attach(ar); + status = ath10k_htt_connect(&ar->htt); if (status) { - ath10k_err("could not attach htt (%d)\n", status); + ath10k_err("failed to connect htt (%d)\n", status); goto err_hif_stop; } - status = ath10k_init_connect_htc(ar); - if (status) - goto err_htt_detach; + status = ath10k_wmi_connect(ar); + if (status) { + ath10k_err("could not connect wmi: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_htc_start(&ar->htc); + if (status) { + ath10k_err("failed to start htc: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_wmi_wait_for_service_ready(ar); + if (status <= 0) { + ath10k_warn("wmi service ready event not received"); + status = -ETIMEDOUT; + goto err_htc_stop; + } ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n", ar->hw->wiphy->fw_version); @@ -833,23 +775,25 @@ int ath10k_core_start(struct ath10k *ar) status = ath10k_wmi_cmd_init(ar); if (status) { ath10k_err("could not send WMI init command (%d)\n", status); - goto err_disconnect_htc; + goto err_htc_stop; } status = ath10k_wmi_wait_for_unified_ready(ar); if (status <= 0) { ath10k_err("wmi unified ready event not received\n"); status = -ETIMEDOUT; - goto err_disconnect_htc; + goto err_htc_stop; } - status = ath10k_htt_attach_target(&ar->htt); - if (status) - goto err_disconnect_htc; + status = ath10k_htt_setup(&ar->htt); + if (status) { + ath10k_err("failed to setup htt: %d\n", status); + goto err_htc_stop; + } status = ath10k_debug_start(ar); if (status) - goto err_disconnect_htc; + goto err_htc_stop; ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; INIT_LIST_HEAD(&ar->arvifs); @@ -868,12 +812,14 @@ int ath10k_core_start(struct ath10k *ar) return 0; -err_disconnect_htc: +err_htc_stop: ath10k_htc_stop(&ar->htc); -err_htt_detach: - ath10k_htt_detach(&ar->htt); err_hif_stop: ath10k_hif_stop(ar); +err_htt_rx_detach: + ath10k_htt_rx_free(&ar->htt); +err_htt_tx_detach: + ath10k_htt_tx_free(&ar->htt); err_wmi_detach: ath10k_wmi_detach(ar); err: @@ -908,10 +854,14 @@ void ath10k_core_stop(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); /* try to suspend target */ - ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR); + if (ar->state != ATH10K_STATE_RESTARTING) + ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR); + ath10k_debug_stop(ar); ath10k_htc_stop(&ar->htc); - ath10k_htt_detach(&ar->htt); + ath10k_hif_stop(ar); + ath10k_htt_tx_free(&ar->htt); + ath10k_htt_rx_free(&ar->htt); ath10k_wmi_detach(ar); } EXPORT_SYMBOL(ath10k_core_stop); @@ -1003,22 +953,15 @@ static int ath10k_core_check_chip_id(struct ath10k *ar) return 0; } -int ath10k_core_register(struct ath10k *ar, u32 chip_id) +static void ath10k_core_register_work(struct work_struct *work) { + struct ath10k *ar = container_of(work, struct ath10k, register_work); int status; - ar->chip_id = chip_id; - - status = ath10k_core_check_chip_id(ar); - if (status) { - ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); - return status; - } - status = ath10k_core_probe_fw(ar); if (status) { ath10k_err("could not probe fw (%d)\n", status); - return status; + goto err; } status = ath10k_mac_register(ar); @@ -1033,18 +976,43 @@ int ath10k_core_register(struct ath10k *ar, u32 chip_id) goto err_unregister_mac; } - return 0; + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); + return; err_unregister_mac: ath10k_mac_unregister(ar); err_release_fw: ath10k_core_free_firmware_files(ar); - return status; +err: + device_release_driver(ar->dev); + return; +} + +int ath10k_core_register(struct ath10k *ar, u32 chip_id) +{ + int status; + + ar->chip_id = chip_id; + + status = ath10k_core_check_chip_id(ar); + if (status) { + ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); + return status; + } + + queue_work(ar->workqueue, &ar->register_work); + + return 0; } EXPORT_SYMBOL(ath10k_core_register); void ath10k_core_unregister(struct ath10k *ar) { + cancel_work_sync(&ar->register_work); + + if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) + return; + /* We must unregister from mac80211 before we stop HTC and HIF. * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ @@ -1056,6 +1024,71 @@ void ath10k_core_unregister(struct ath10k *ar) } EXPORT_SYMBOL(ath10k_core_unregister); +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + const struct ath10k_hif_ops *hif_ops) +{ + struct ath10k *ar; + + ar = ath10k_mac_create(); + if (!ar) + return NULL; + + ar->ath_common.priv = ar; + ar->ath_common.hw = ar->hw; + + ar->p2p = !!ath10k_p2p; + ar->dev = dev; + + ar->hif.priv = hif_priv; + ar->hif.ops = hif_ops; + + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + init_completion(&ar->scan.on_channel); + init_completion(&ar->target_suspend); + + init_completion(&ar->install_key_done); + init_completion(&ar->vdev_setup_done); + + setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + + ar->workqueue = create_singlethread_workqueue("ath10k_wq"); + if (!ar->workqueue) + goto err_wq; + + mutex_init(&ar->conf_mutex); + spin_lock_init(&ar->data_lock); + + INIT_LIST_HEAD(&ar->peers); + init_waitqueue_head(&ar->peer_mapping_wq); + + init_completion(&ar->offchan_tx_completed); + INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); + skb_queue_head_init(&ar->offchan_tx_queue); + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + + INIT_WORK(&ar->register_work, ath10k_core_register_work); + INIT_WORK(&ar->restart_work, ath10k_core_restart); + + return ar; + +err_wq: + ath10k_mac_destroy(ar); + return NULL; +} +EXPORT_SYMBOL(ath10k_core_create); + +void ath10k_core_destroy(struct ath10k *ar) +{ + flush_workqueue(ar->workqueue); + destroy_workqueue(ar->workqueue); + + ath10k_mac_destroy(ar); +} +EXPORT_SYMBOL(ath10k_core_destroy); + MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 2c1dfd7..68ceef6 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -335,6 +335,7 @@ enum ath10k_dev_flags { /* Indicates that ath10k device is during CAC phase of DFS */ ATH10K_CAC_RUNNING, ATH10K_FLAG_FIRST_BOOT_DONE, + ATH10K_FLAG_CORE_REGISTERED, }; struct ath10k { @@ -440,6 +441,12 @@ struct ath10k { bool radar_enabled; int num_started_vdevs; + /* Protected by conf-mutex */ + u8 supp_tx_chainmask; + u8 supp_rx_chainmask; + u8 cfg_tx_chainmask; + u8 cfg_rx_chainmask; + struct wmi_pdev_set_wmm_params_arg wmm_params; struct completion install_key_done; @@ -470,6 +477,7 @@ struct ath10k { enum ath10k_state state; + struct work_struct register_work; struct work_struct restart_work; /* cycle count is reported twice for each visited channel during scan. diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 5b58dbb..e493db4 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -830,17 +830,11 @@ int ath10k_htc_start(struct ath10k_htc *htc) return 0; } -/* - * stop HTC communications, i.e. stop interrupt reception, and flush all - * queued buffers - */ void ath10k_htc_stop(struct ath10k_htc *htc) { spin_lock_bh(&htc->tx_lock); htc->stopped = true; spin_unlock_bh(&htc->tx_lock); - - ath10k_hif_stop(htc->ar); } /* registered target arrival callback from the HIF layer */ diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 69697af5..19c12cc 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -22,7 +22,7 @@ #include "core.h" #include "debug.h" -static int ath10k_htt_htc_attach(struct ath10k_htt *htt) +int ath10k_htt_connect(struct ath10k_htt *htt) { struct ath10k_htc_svc_conn_req conn_req; struct ath10k_htc_svc_conn_resp conn_resp; @@ -48,39 +48,14 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) return 0; } -int ath10k_htt_attach(struct ath10k *ar) +int ath10k_htt_init(struct ath10k *ar) { struct ath10k_htt *htt = &ar->htt; - int ret; htt->ar = ar; htt->max_throughput_mbps = 800; /* - * Connect to HTC service. - * This has to be done before calling ath10k_htt_rx_attach, - * since ath10k_htt_rx_attach involves sending a rx ring configure - * message to the target. - */ - ret = ath10k_htt_htc_attach(htt); - if (ret) { - ath10k_err("could not attach htt htc (%d)\n", ret); - goto err_htc_attach; - } - - ret = ath10k_htt_tx_attach(htt); - if (ret) { - ath10k_err("could not attach htt tx (%d)\n", ret); - goto err_htc_attach; - } - - ret = ath10k_htt_rx_attach(htt); - if (ret) { - ath10k_err("could not attach htt rx (%d)\n", ret); - goto err_rx_attach; - } - - /* * Prefetch enough data to satisfy target * classification engine. * This is for LL chips. HL chips will probably @@ -93,11 +68,6 @@ int ath10k_htt_attach(struct ath10k *ar) 2; /* ip4 dscp or ip6 priority */ return 0; - -err_rx_attach: - ath10k_htt_tx_detach(htt); -err_htc_attach: - return ret; } #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) @@ -117,7 +87,7 @@ static int ath10k_htt_verify_version(struct ath10k_htt *htt) return 0; } -int ath10k_htt_attach_target(struct ath10k_htt *htt) +int ath10k_htt_setup(struct ath10k_htt *htt) { int status; @@ -140,9 +110,3 @@ int ath10k_htt_attach_target(struct ath10k_htt *htt) return ath10k_htt_send_rx_ring_cfg_ll(htt); } - -void ath10k_htt_detach(struct ath10k_htt *htt) -{ - ath10k_htt_rx_detach(htt); - ath10k_htt_tx_detach(htt); -} diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 645a563..9a26346 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1328,14 +1328,16 @@ struct htt_rx_desc { #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) -int ath10k_htt_attach(struct ath10k *ar); -int ath10k_htt_attach_target(struct ath10k_htt *htt); -void ath10k_htt_detach(struct ath10k_htt *htt); - -int ath10k_htt_tx_attach(struct ath10k_htt *htt); -void ath10k_htt_tx_detach(struct ath10k_htt *htt); -int ath10k_htt_rx_attach(struct ath10k_htt *htt); -void ath10k_htt_rx_detach(struct ath10k_htt *htt); +int ath10k_htt_connect(struct ath10k_htt *htt); +int ath10k_htt_init(struct ath10k *ar); +int ath10k_htt_setup(struct ath10k_htt *htt); + +int ath10k_htt_tx_alloc(struct ath10k_htt *htt); +void ath10k_htt_tx_free(struct ath10k_htt *htt); + +int ath10k_htt_rx_alloc(struct ath10k_htt *htt); +void ath10k_htt_rx_free(struct ath10k_htt *htt); + void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index f85a3cf..6c102b1 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -225,10 +225,26 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg) ath10k_htt_rx_msdu_buff_replenish(htt); } -void ath10k_htt_rx_detach(struct ath10k_htt *htt) +static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt) { - int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; + struct sk_buff *skb; + int i; + + for (i = 0; i < htt->rx_ring.size; i++) { + skb = htt->rx_ring.netbufs_ring[i]; + if (!skb) + continue; + + dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + htt->rx_ring.netbufs_ring[i] = NULL; + } +} +void ath10k_htt_rx_free(struct ath10k_htt *htt) +{ del_timer_sync(&htt->rx_ring.refill_retry_timer); tasklet_kill(&htt->rx_replenish_task); tasklet_kill(&htt->txrx_compl_task); @@ -236,18 +252,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt) skb_queue_purge(&htt->tx_compl_q); skb_queue_purge(&htt->rx_compl_q); - while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { - struct sk_buff *skb = - htt->rx_ring.netbufs_ring[sw_rd_idx]; - struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); - - dma_unmap_single(htt->ar->dev, cb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]); - sw_rd_idx++; - sw_rd_idx &= htt->rx_ring.size_mask; - } + ath10k_htt_rx_ring_clean_up(htt); dma_free_coherent(htt->ar->dev, (htt->rx_ring.size * @@ -277,6 +282,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) idx = htt->rx_ring.sw_rd_idx.msdu_payld; msdu = htt->rx_ring.netbufs_ring[idx]; + htt->rx_ring.netbufs_ring[idx] = NULL; idx++; idx &= htt->rx_ring.size_mask; @@ -306,6 +312,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; struct htt_rx_desc *rx_desc; + bool corrupted = false; lockdep_assert_held(&htt->rx_ring.lock); @@ -399,7 +406,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0), RX_MSDU_START_INFO0_MSDU_LENGTH); msdu_chained = rx_desc->frag_info.ring2_more_count; - msdu_chaining = msdu_chained; if (msdu_len_invalid) msdu_len = 0; @@ -427,11 +433,15 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu->next = next; msdu = next; + msdu_chaining = 1; } last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & RX_MSDU_END_INFO0_LAST_MSDU; + if (msdu_chaining && !last_msdu) + corrupted = true; + if (last_msdu) { msdu->next = NULL; break; @@ -447,6 +457,20 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_chaining = -1; /* + * Apparently FW sometimes reports weird chained MSDU sequences with + * more than one rx descriptor. This seems like a bug but needs more + * analyzing. For the time being fix it by dropping such sequences to + * avoid blowing up the host system. + */ + if (corrupted) { + ath10k_warn("failed to pop chained msdus, dropping\n"); + ath10k_htt_rx_free_msdu_chain(*head_msdu); + *head_msdu = NULL; + *tail_msdu = NULL; + msdu_chaining = -EINVAL; + } + + /* * Don't refill the ring yet. * * First, the elements popped here are still in use - it is not @@ -468,7 +492,7 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr) ath10k_htt_rx_msdu_buff_replenish(htt); } -int ath10k_htt_rx_attach(struct ath10k_htt *htt) +int ath10k_htt_rx_alloc(struct ath10k_htt *htt) { dma_addr_t paddr; void *vaddr; @@ -494,7 +518,7 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt); htt->rx_ring.netbufs_ring = - kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *), + kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *), GFP_KERNEL); if (!htt->rx_ring.netbufs_ring) goto err_netbuf; @@ -754,17 +778,30 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt, struct ieee80211_rx_status *rx_status, struct sk_buff *skb, - enum htt_rx_mpdu_encrypt_type enctype) + enum htt_rx_mpdu_encrypt_type enctype, + enum rx_msdu_decap_format fmt, + bool dot11frag) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + rx_status->flag &= ~(RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); - if (enctype == HTT_RX_MPDU_ENCRYPT_NONE) { - rx_status->flag &= ~(RX_FLAG_DECRYPTED | - RX_FLAG_IV_STRIPPED | - RX_FLAG_MMIC_STRIPPED); + if (enctype == HTT_RX_MPDU_ENCRYPT_NONE) + return; + + /* + * There's no explicit rx descriptor flag to indicate whether a given + * frame has been decrypted or not. We're forced to use the decap + * format as an implicit indication. However fragmentation rx is always + * raw and it probably never reports undecrypted raws. + * + * This makes sure sniffed frames are reported as-is without stripping + * the protected flag. + */ + if (fmt == RX_MSDU_DECAP_RAW && !dot11frag) return; - } rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | @@ -918,7 +955,8 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, } skb_in = skb; - ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype); + ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt, + false); skb = skb->next; skb_in->next = NULL; @@ -1000,7 +1038,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, break; } - ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype); + ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false); ath10k_process_rx(htt->ar, rx_status, skb); } @@ -1288,6 +1326,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, } /* FIXME: implement signal strength */ + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; hdr = (struct ieee80211_hdr *)msdu_head->data; rxd = (void *)msdu_head->data - sizeof(*rxd); @@ -1306,7 +1345,8 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); - ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype); + ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt, + true); msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); if (tkip_mic_err) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 7a3e2e4..7064354 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -83,7 +83,7 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) __clear_bit(msdu_id, htt->used_msdu_ids); } -int ath10k_htt_tx_attach(struct ath10k_htt *htt) +int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { spin_lock_init(&htt->tx_lock); init_waitqueue_head(&htt->empty_tx_wq); @@ -120,7 +120,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) return 0; } -static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) +static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt) { struct htt_tx_done tx_done = {0}; int msdu_id; @@ -141,9 +141,9 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) spin_unlock_bh(&htt->tx_lock); } -void ath10k_htt_tx_detach(struct ath10k_htt *htt) +void ath10k_htt_tx_free(struct ath10k_htt *htt) { - ath10k_htt_tx_cleanup_pending(htt); + ath10k_htt_tx_free_pending(htt); kfree(htt->pending_tx); kfree(htt->used_msdu_ids); dma_pool_destroy(htt->tx_pool); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 0ac5437..a210800 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -54,7 +54,10 @@ static int ath10k_send_key(struct ath10k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: arg.key_cipher = WMI_CIPHER_AES_CCM; - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; + else + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; break; case WLAN_CIPHER_SUITE_TKIP: arg.key_cipher = WMI_CIPHER_TKIP; @@ -1888,8 +1891,13 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) wep_key_work); int ret, keyidx = arvif->def_wep_key_newidx; + mutex_lock(&arvif->ar->conf_mutex); + + if (arvif->ar->state != ATH10K_STATE_ON) + goto unlock; + if (arvif->def_wep_key_idx == keyidx) - return; + goto unlock; ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", arvif->vdev_id, keyidx); @@ -1902,10 +1910,13 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) ath10k_warn("failed to update wep key index for vdev %d: %d\n", arvif->vdev_id, ret); - return; + goto unlock; } arvif->def_wep_key_idx = keyidx; + +unlock: + mutex_unlock(&arvif->ar->conf_mutex); } static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) @@ -2286,11 +2297,23 @@ static void ath10k_tx(struct ieee80211_hw *hw, ath10k_tx_htt(ar, skb); } -/* - * Initialize various parameters with default vaules. - */ +/* Must not be called with conf_mutex held as workers can use that also. */ +static void ath10k_drain_tx(struct ath10k *ar) +{ + /* make sure rcu-protected mac80211 tx path itself is drained */ + synchronize_net(); + + ath10k_offchan_tx_purge(ar); + ath10k_mgmt_over_wmi_tx_purge(ar); + + cancel_work_sync(&ar->offchan_tx_work); + cancel_work_sync(&ar->wmi_mgmt_tx_work); +} + void ath10k_halt(struct ath10k *ar) { + struct ath10k_vif *arvif; + lockdep_assert_held(&ar->conf_mutex); if (ath10k_monitor_is_enabled(ar)) { @@ -2301,61 +2324,144 @@ void ath10k_halt(struct ath10k *ar) } del_timer_sync(&ar->scan.timeout); - ath10k_offchan_tx_purge(ar); - ath10k_mgmt_over_wmi_tx_purge(ar); + ath10k_reset_scan((unsigned long)ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - del_timer(&ar->scan.timeout); - ar->scan.in_progress = false; - ieee80211_scan_completed(ar->hw, true); + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->beacon) + continue; + + dma_unmap_single(arvif->ar->dev, + ATH10K_SKB_CB(arvif->beacon)->paddr, + arvif->beacon->len, DMA_TO_DEVICE); + dev_kfree_skb_any(arvif->beacon); + arvif->beacon = NULL; } spin_unlock_bh(&ar->data_lock); } +static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + if (ar->cfg_tx_chainmask) { + *tx_ant = ar->cfg_tx_chainmask; + *rx_ant = ar->cfg_rx_chainmask; + } else { + *tx_ant = ar->supp_tx_chainmask; + *rx_ant = ar->supp_rx_chainmask; + } + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ar->cfg_tx_chainmask = tx_ant; + ar->cfg_rx_chainmask = rx_ant; + + if ((ar->state != ATH10K_STATE_ON) && + (ar->state != ATH10K_STATE_RESTARTED)) + return 0; + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask, + tx_ant); + if (ret) { + ath10k_warn("failed to set tx-chainmask: %d, req 0x%x\n", + ret, tx_ant); + return ret; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask, + rx_ant); + if (ret) { + ath10k_warn("failed to set rx-chainmask: %d, req 0x%x\n", + ret, rx_ant); + return ret; + } + + return 0; +} + +static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = __ath10k_set_antenna(ar, tx_ant, rx_ant); + mutex_unlock(&ar->conf_mutex); + return ret; +} + static int ath10k_start(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; int ret = 0; + /* + * This makes sense only when restarting hw. It is harmless to call + * uncoditionally. This is necessary to make sure no HTT/WMI tx + * commands will be submitted while restarting. + */ + ath10k_drain_tx(ar); + mutex_lock(&ar->conf_mutex); - if (ar->state != ATH10K_STATE_OFF && - ar->state != ATH10K_STATE_RESTARTING) { + switch (ar->state) { + case ATH10K_STATE_OFF: + ar->state = ATH10K_STATE_ON; + break; + case ATH10K_STATE_RESTARTING: + ath10k_halt(ar); + ar->state = ATH10K_STATE_RESTARTED; + break; + case ATH10K_STATE_ON: + case ATH10K_STATE_RESTARTED: + case ATH10K_STATE_WEDGED: + WARN_ON(1); ret = -EINVAL; - goto exit; + goto err; } ret = ath10k_hif_power_up(ar); if (ret) { ath10k_err("Could not init hif: %d\n", ret); - ar->state = ATH10K_STATE_OFF; - goto exit; + goto err_off; } ret = ath10k_core_start(ar); if (ret) { ath10k_err("Could not init core: %d\n", ret); - ath10k_hif_power_down(ar); - ar->state = ATH10K_STATE_OFF; - goto exit; + goto err_power_down; } - if (ar->state == ATH10K_STATE_OFF) - ar->state = ATH10K_STATE_ON; - else if (ar->state == ATH10K_STATE_RESTARTING) - ar->state = ATH10K_STATE_RESTARTED; - ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); - if (ret) + if (ret) { ath10k_warn("failed to enable PMF QOS: %d\n", ret); + goto err_core_stop; + } ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1); - if (ret) + if (ret) { ath10k_warn("failed to enable dynamic BW: %d\n", ret); + goto err_core_stop; + } + + if (ar->cfg_tx_chainmask) + __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, + ar->cfg_rx_chainmask); /* * By default FW set ARP frames ac to voice (6). In that case ARP @@ -2371,14 +2477,25 @@ static int ath10k_start(struct ieee80211_hw *hw) if (ret) { ath10k_warn("failed to set arp ac override parameter: %d\n", ret); - goto exit; + goto err_core_stop; } ar->num_started_vdevs = 0; ath10k_regd_update(ar); - ret = 0; -exit: + mutex_unlock(&ar->conf_mutex); + return 0; + +err_core_stop: + ath10k_core_stop(ar); + +err_power_down: + ath10k_hif_power_down(ar); + +err_off: + ar->state = ATH10K_STATE_OFF; + +err: mutex_unlock(&ar->conf_mutex); return ret; } @@ -2387,19 +2504,15 @@ static void ath10k_stop(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; + ath10k_drain_tx(ar); + mutex_lock(&ar->conf_mutex); - if (ar->state == ATH10K_STATE_ON || - ar->state == ATH10K_STATE_RESTARTED || - ar->state == ATH10K_STATE_WEDGED) + if (ar->state != ATH10K_STATE_OFF) { ath10k_halt(ar); - - ar->state = ATH10K_STATE_OFF; + ar->state = ATH10K_STATE_OFF; + } mutex_unlock(&ar->conf_mutex); - ath10k_mgmt_over_wmi_tx_purge(ar); - - cancel_work_sync(&ar->offchan_tx_work); - cancel_work_sync(&ar->wmi_mgmt_tx_work); cancel_work_sync(&ar->restart_work); } @@ -2771,6 +2884,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); if (arvif->beacon) { + dma_unmap_single(arvif->ar->dev, + ATH10K_SKB_CB(arvif->beacon)->paddr, + arvif->beacon->len, DMA_TO_DEVICE); dev_kfree_skb_any(arvif->beacon); arvif->beacon = NULL; } @@ -2909,7 +3025,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, arvif->u.ap.hidden_ssid = info->hidden_ssid; } - if (changed & BSS_CHANGED_BSSID) { + /* + * Firmware manages AP self-peer internally so make sure to not create + * it in driver. Otherwise AP self-peer deletion may timeout later. + */ + if (changed & BSS_CHANGED_BSSID && + vif->type != NL80211_IFTYPE_AP) { if (!is_zero_ether_addr(info->bssid)) { ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d create peer %pM\n", @@ -4126,14 +4247,6 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, fixed_nss, force_sgi); } -static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_chan_def *chandef) -{ - /* there's no need to do anything here. vif->csa_active is enough */ - return; -} - static void ath10k_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -4237,10 +4350,11 @@ static const struct ieee80211_ops ath10k_ops = { .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, + .set_antenna = ath10k_set_antenna, + .get_antenna = ath10k_get_antenna, .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, .set_bitrate_mask = ath10k_set_bitrate_mask, - .channel_switch_beacon = ath10k_channel_switch_beacon, .sta_rc_update = ath10k_sta_rc_update, .get_tsf = ath10k_get_tsf, #ifdef CONFIG_PM @@ -4586,6 +4700,18 @@ int ath10k_mac_register(struct ath10k *ar) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + /* TODO: Have to deal with 2x2 chips if/when the come out. */ + ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK; + ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK; + } else { + ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK; + ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK; + } + + ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask; + ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask; + if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index bf1083d..d0004d5 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -59,6 +59,7 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)"); /* how long wait to wait for target to initialise, in ms */ #define ATH10K_PCI_TARGET_WAIT 3000 +#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3 #define QCA988X_2_0_DEVICE_ID (0x003c) @@ -761,17 +762,21 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id]; struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl; struct ath10k_ce_ring *src_ring = ce_pipe->src_ring; - unsigned int nentries_mask = src_ring->nentries_mask; - unsigned int sw_index = src_ring->sw_index; - unsigned int write_index = src_ring->write_index; - int err, i; + unsigned int nentries_mask; + unsigned int sw_index; + unsigned int write_index; + int err, i = 0; spin_lock_bh(&ar_pci->ce_lock); + nentries_mask = src_ring->nentries_mask; + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) < n_items)) { err = -ENOBUFS; - goto unlock; + goto err; } for (i = 0; i < n_items - 1; i++) { @@ -788,7 +793,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, items[i].transfer_id, CE_SEND_FLAG_GATHER); if (err) - goto unlock; + goto err; } /* `i` is equal to `n_items -1` after for() */ @@ -806,10 +811,15 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, items[i].transfer_id, 0); if (err) - goto unlock; + goto err; + + spin_unlock_bh(&ar_pci->ce_lock); + return 0; + +err: + for (; i > 0; i--) + __ath10k_ce_send_revert(ce_pipe); - err = 0; -unlock: spin_unlock_bh(&ar_pci->ce_lock); return err; } @@ -1271,6 +1281,9 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n"); + if (WARN_ON(!ar_pci->started)) + return; + ret = ath10k_ce_disable_interrupts(ar); if (ret) ath10k_warn("failed to disable CE interrupts: %d\n", ret); @@ -1802,6 +1815,26 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } +/* this function effectively clears target memory controller assert line */ +static void ath10k_pci_warm_reset_si0(struct ath10k *ar) +{ + u32 val; + + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_SI0_RST_MASK); + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + + msleep(10); + + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS, + val & ~SOC_RESET_CONTROL_SI0_RST_MASK); + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + + msleep(10); +} + static int ath10k_pci_warm_reset(struct ath10k *ar) { int ret = 0; @@ -1860,6 +1893,8 @@ static int ath10k_pci_warm_reset(struct ath10k *ar) SOC_RESET_CONTROL_ADDRESS); msleep(10); + ath10k_pci_warm_reset_si0(ar); + /* debug */ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CAUSE_ADDRESS); @@ -1988,6 +2023,28 @@ err: return ret; } +static int ath10k_pci_hif_power_up_warm(struct ath10k *ar) +{ + int i, ret; + + /* + * Sometime warm reset succeeds after retries. + * + * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work + * at first try. + */ + for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) { + ret = __ath10k_pci_hif_power_up(ar, false); + if (ret == 0) + break; + + ath10k_warn("failed to warm reset (attempt %d out of %d): %d\n", + i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret); + } + + return ret; +} + static int ath10k_pci_hif_power_up(struct ath10k *ar) { int ret; @@ -1999,10 +2056,10 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) * preferred (and safer) way to perform a device reset is through a * warm reset. * - * Warm reset doesn't always work though (notably after a firmware - * crash) so fall back to cold reset if necessary. + * Warm reset doesn't always work though so fall back to cold reset may + * be necessary. */ - ret = __ath10k_pci_hif_power_up(ar, false); + ret = ath10k_pci_hif_power_up_warm(ar); if (ret) { ath10k_warn("failed to power up target using warm reset: %d\n", ret); @@ -2196,10 +2253,7 @@ static void ath10k_pci_early_irq_tasklet(unsigned long data) if (fw_ind & FW_IND_EVENT_PENDING) { ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, fw_ind & ~FW_IND_EVENT_PENDING); - - /* Some structures are unavailable during early boot or at - * driver teardown so just print that the device has crashed. */ - ath10k_warn("device crashed - no diagnostics available\n"); + ath10k_pci_hif_dump_area(ar); } ath10k_pci_sleep(ar); @@ -2452,6 +2506,10 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar) if (val == 0xffffffff) continue; + /* the device has crashed so don't bother trying anymore */ + if (val & FW_IND_EVENT_PENDING) + break; + if (val & FW_IND_INITIALIZED) break; @@ -2464,7 +2522,22 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar) mdelay(10); } while (time_before(jiffies, timeout)); - if (val == 0xffffffff || !(val & FW_IND_INITIALIZED)) { + if (val == 0xffffffff) { + ath10k_err("failed to read device register, device is gone\n"); + ret = -EIO; + goto out; + } + + if (val & FW_IND_EVENT_PENDING) { + ath10k_warn("device has crashed during init\n"); + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, + val & ~FW_IND_EVENT_PENDING); + ath10k_pci_hif_dump_area(ar); + ret = -ECOMM; + goto out; + } + + if (!(val & FW_IND_INITIALIZED)) { ath10k_err("failed to receive initialized event from target: %08x\n", val); ret = -ETIMEDOUT; @@ -2586,18 +2659,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, ar); - /* - * Without any knowledge of the Host, the Target may have been reset or - * power cycled and its Config Space may no longer reflect the PCI - * address space that was assigned earlier by the PCI infrastructure. - * Refresh it now. - */ - ret = pci_assign_resource(pdev, BAR_NUM); - if (ret) { - ath10k_err("failed to assign PCI space: %d\n", ret); - goto err_ar; - } - ret = pci_enable_device(pdev); if (ret) { ath10k_err("failed to enable PCI device: %d\n", ret); @@ -2709,8 +2770,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) if (!ar_pci) return; - tasklet_kill(&ar_pci->msi_fw_err); - ath10k_core_unregister(ar); ath10k_pci_free_ce(ar); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index fe4d5f1..4b7782a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -639,6 +639,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) struct sk_buff *wmi_skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int len; + u32 buf_len = skb->len; u16 fc; hdr = (struct ieee80211_hdr *)skb->data; @@ -648,6 +649,15 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) return -EINVAL; len = sizeof(cmd->hdr) + skb->len; + + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) { + len += IEEE80211_CCMP_MIC_LEN; + buf_len += IEEE80211_CCMP_MIC_LEN; + } + len = round_up(len, 4); wmi_skb = ath10k_wmi_alloc_skb(len); @@ -659,7 +669,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); cmd->hdr.tx_rate = 0; cmd->hdr.tx_power = 0; - cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len)); + cmd->hdr.buf_len = __cpu_to_le32(buf_len); memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN); memcpy(cmd->buf, skb->data, skb->len); @@ -957,10 +967,16 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) * frames with Protected Bit set. */ if (ieee80211_has_protected(hdr->frame_control) && !ieee80211_is_auth(hdr->frame_control)) { - status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | - RX_FLAG_MMIC_STRIPPED; - hdr->frame_control = __cpu_to_le16(fc & + status->flag |= RX_FLAG_DECRYPTED; + + if (!ieee80211_is_action(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control)) { + status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & ~IEEE80211_FCTL_PROTECTED); + } } ath10k_dbg(ATH10K_DBG_MGMT, @@ -1431,6 +1447,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ATH10K_SKB_CB(arvif->beacon)->paddr, arvif->beacon->len, DMA_TO_DEVICE); dev_kfree_skb_any(arvif->beacon); + arvif->beacon = NULL; } ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev, @@ -1440,6 +1457,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ATH10K_SKB_CB(bcn)->paddr); if (ret) { ath10k_warn("failed to map beacon: %d\n", ret); + dev_kfree_skb_any(bcn); goto skip; } @@ -2357,7 +2375,7 @@ void ath10k_wmi_detach(struct ath10k *ar) ar->wmi.num_mem_chunks = 0; } -int ath10k_wmi_connect_htc_service(struct ath10k *ar) +int ath10k_wmi_connect(struct ath10k *ar) { int status; struct ath10k_htc_svc_conn_req conn_req; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index ae83822..89ef3b3 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2323,9 +2323,9 @@ struct wmi_pdev_param_map { #define WMI_PDEV_PARAM_UNSUPPORTED 0 enum wmi_pdev_param { - /* TX chian mask */ + /* TX chain mask */ WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, - /* RX chian mask */ + /* RX chain mask */ WMI_PDEV_PARAM_RX_CHAIN_MASK, /* TX power limit for 2G Radio */ WMI_PDEV_PARAM_TXPOWER_LIMIT2G, @@ -4259,7 +4259,7 @@ void ath10k_wmi_detach(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); -int ath10k_wmi_connect_htc_service(struct ath10k *ar); +int ath10k_wmi_connect(struct ath10k *ar); int ath10k_wmi_pdev_set_channel(struct ath10k *ar, const struct wmi_channel_arg *); int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0928508..0e26f4a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1759,7 +1759,7 @@ static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) } static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2974,7 +2974,7 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2985,7 +2985,8 @@ static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, } static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params) + const u8 *mac, + struct station_parameters *params) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 0c0e1e3..4d7f9e4 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2320,7 +2320,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, return ret; } -int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk) +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk) { struct sk_buff *skb; struct wmi_add_krk_cmd *cmd; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 1f05ecd..7809afb 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2616,7 +2616,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, u8 *key_material, u8 key_op_ctrl, u8 *mac_addr, enum wmi_sync_flag sync_flag); -int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk); +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk); int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index); int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, const u8 *pmkid, bool set); diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 8e1c7b0..8fcd586 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -53,7 +53,8 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o ath9k_common-y:= common.o \ common-init.o \ - common-beacon.o + common-beacon.o \ + common-debug.o ath9k_htc-y += htc_hst.o \ hif_usb.o \ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 33a2ae7..2ca8f7e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -23,8 +23,8 @@ #include <linux/leds.h> #include <linux/completion.h> -#include "debug.h" #include "common.h" +#include "debug.h" #include "mci.h" #include "dfs.h" #include "spectral.h" @@ -274,6 +274,7 @@ struct ath_node { #ifdef CONFIG_ATH9K_STATION_STATISTICS struct ath_rx_rate_stats rx_rate_stats; #endif + u8 key_idx[4]; }; struct ath_tx_control { @@ -769,6 +770,7 @@ struct ath_softc { struct ath_ant_comb ant_comb; u8 ant_tx, ant_rx; struct dfs_pattern_detector *dfs_detector; + u64 dfs_prev_pulse_ts; u32 wow_enabled; /* relay(fs) channel for spectral scan */ struct rchan *rfs_chan_spec_scan; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index bd9e634..e387f0b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -537,8 +537,6 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc, cur_conf->dtim_period = bss_conf->dtim_period; cur_conf->dtim_count = 1; cur_conf->ibss_creator = bss_conf->ibss_creator; - cur_conf->bmiss_timeout = - ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; /* * It looks like mac80211 may end up using beacon interval of zero in @@ -549,6 +547,9 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc, if (cur_conf->beacon_interval == 0) cur_conf->beacon_interval = 100; + cur_conf->bmiss_timeout = + ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; + /* * We don't parse dtim period from mac80211 during the driver * initialization as it breaks association with hidden-ssid diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c new file mode 100644 index 0000000..3b289f9 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-debug.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2008-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "common.h" + +static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_hw *ah = file->private_data; + u32 len = 0, size = 6000; + char *buf; + size_t retval; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_modal_eeprom = { + .read = read_file_modal_eeprom, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + +void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah) +{ + debugfs_create_file("modal_eeprom", S_IRUSR, debugfs_phy, ah, + &fops_modal_eeprom); +} +EXPORT_SYMBOL(ath9k_cmn_debug_modal_eeprom); + +static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_hw *ah = file->private_data; + u32 len = 0, size = 1500; + ssize_t retval = 0; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_base_eeprom = { + .read = read_file_base_eeprom, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah) +{ + debugfs_create_file("base_eeprom", S_IRUSR, debugfs_phy, ah, + &fops_base_eeprom); +} +EXPORT_SYMBOL(ath9k_cmn_debug_base_eeprom); + +void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats, + struct ath_rx_status *rs) +{ +#define RX_PHY_ERR_INC(c) rxstats->phy_err_stats[c]++ +#define RX_CMN_STAT_INC(c) (rxstats->c++) + + RX_CMN_STAT_INC(rx_pkts_all); + rxstats->rx_bytes_all += rs->rs_datalen; + + if (rs->rs_status & ATH9K_RXERR_CRC) + RX_CMN_STAT_INC(crc_err); + if (rs->rs_status & ATH9K_RXERR_DECRYPT) + RX_CMN_STAT_INC(decrypt_crc_err); + if (rs->rs_status & ATH9K_RXERR_MIC) + RX_CMN_STAT_INC(mic_err); + if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE) + RX_CMN_STAT_INC(pre_delim_crc_err); + if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST) + RX_CMN_STAT_INC(post_delim_crc_err); + if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY) + RX_CMN_STAT_INC(decrypt_busy_err); + + if (rs->rs_status & ATH9K_RXERR_PHY) { + RX_CMN_STAT_INC(phy_err); + if (rs->rs_phyerr < ATH9K_PHYERR_MAX) + RX_PHY_ERR_INC(rs->rs_phyerr); + } + +#undef RX_CMN_STAT_INC +#undef RX_PHY_ERR_INC +} +EXPORT_SYMBOL(ath9k_cmn_debug_stat_rx); + +static ssize_t read_file_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define RXS_ERR(s, e) \ + do { \ + len += scnprintf(buf + len, size - len, \ + "%18s : %10u\n", s, \ + rxstats->e); \ + } while (0) + + struct ath_rx_stats *rxstats = file->private_data; + char *buf; + unsigned int len = 0, size = 1600; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + RXS_ERR("PKTS-ALL", rx_pkts_all); + RXS_ERR("BYTES-ALL", rx_bytes_all); + RXS_ERR("BEACONS", rx_beacons); + RXS_ERR("FRAGS", rx_frags); + RXS_ERR("SPECTRAL", rx_spectral); + + RXS_ERR("CRC ERR", crc_err); + RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); + RXS_ERR("PHY ERR", phy_err); + RXS_ERR("MIC ERR", mic_err); + RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); + RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); + RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); + RXS_ERR("LENGTH-ERR", rx_len_err); + RXS_ERR("OOM-ERR", rx_oom_err); + RXS_ERR("RATE-ERR", rx_rate_err); + RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef RXS_ERR +} + +static const struct file_operations fops_recv = { + .read = read_file_recv, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_cmn_debug_recv(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats) +{ + debugfs_create_file("recv", S_IRUSR, debugfs_phy, rxstats, + &fops_recv); +} +EXPORT_SYMBOL(ath9k_cmn_debug_recv); + +static ssize_t read_file_phy_err(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PHY_ERR(s, p) \ + len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \ + rxstats->phy_err_stats[p]); + + struct ath_rx_stats *rxstats = file->private_data; + char *buf; + unsigned int len = 0, size = 1600; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); + PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); + PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); + PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); + PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); + PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); + PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); + PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); + PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); + PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); + PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); + PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); + PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); + PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); + PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); + PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); + PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); + PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); + PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); + PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); + PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef PHY_ERR +} + +static const struct file_operations fops_phy_err = { + .read = read_file_phy_err, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats) +{ + debugfs_create_file("phy_err", S_IRUSR, debugfs_phy, rxstats, + &fops_phy_err); +} +EXPORT_SYMBOL(ath9k_cmn_debug_phy_err); diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h new file mode 100644 index 0000000..7c97884 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-debug.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2008-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + + +/** + * struct ath_rx_stats - RX Statistics + * @rx_pkts_all: No. of total frames received, including ones that + may have had errors. + * @rx_bytes_all: No. of total bytes received, including ones that + may have had errors. + * @crc_err: No. of frames with incorrect CRC value + * @decrypt_crc_err: No. of frames whose CRC check failed after + decryption process completed + * @phy_err: No. of frames whose reception failed because the PHY + encountered an error + * @mic_err: No. of frames with incorrect TKIP MIC verification failure + * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections + * @post_delim_crc_err: Post-Frame delimiter CRC error detections + * @decrypt_busy_err: Decryption interruptions counter + * @phy_err_stats: Individual PHY error statistics + * @rx_len_err: No. of frames discarded due to bad length. + * @rx_oom_err: No. of frames dropped due to OOM issues. + * @rx_rate_err: No. of frames dropped due to rate errors. + * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. + * @rx_beacons: No. of beacons received. + * @rx_frags: No. of rx-fragements received. + * @rx_spectral: No of spectral packets received. + */ +struct ath_rx_stats { + u32 rx_pkts_all; + u32 rx_bytes_all; + u32 crc_err; + u32 decrypt_crc_err; + u32 phy_err; + u32 mic_err; + u32 pre_delim_crc_err; + u32 post_delim_crc_err; + u32 decrypt_busy_err; + u32 phy_err_stats[ATH9K_PHYERR_MAX]; + u32 rx_len_err; + u32 rx_oom_err; + u32 rx_rate_err; + u32 rx_too_many_frags_err; + u32 rx_beacons; + u32 rx_frags; + u32 rx_spectral; +}; + +void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah); +void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah); +void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats, + struct ath_rx_status *rs); +void ath9k_cmn_debug_recv(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats); +void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats); diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index ca38116..ffc454b 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -23,6 +23,7 @@ #include "common-init.h" #include "common-beacon.h" +#include "common-debug.h" /* Common header for Atheros 802.11n base driver cores */ diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 780ff1b..6cc42be 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -948,151 +948,11 @@ static const struct file_operations fops_reset = { .llseek = default_llseek, }; -static ssize_t read_file_recv(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define RXS_ERR(s, e) \ - do { \ - len += scnprintf(buf + len, size - len, \ - "%18s : %10u\n", s, \ - sc->debug.stats.rxstats.e);\ - } while (0) - - struct ath_softc *sc = file->private_data; - char *buf; - unsigned int len = 0, size = 1600; - ssize_t retval = 0; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - RXS_ERR("PKTS-ALL", rx_pkts_all); - RXS_ERR("BYTES-ALL", rx_bytes_all); - RXS_ERR("BEACONS", rx_beacons); - RXS_ERR("FRAGS", rx_frags); - RXS_ERR("SPECTRAL", rx_spectral); - - RXS_ERR("CRC ERR", crc_err); - RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); - RXS_ERR("PHY ERR", phy_err); - RXS_ERR("MIC ERR", mic_err); - RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); - RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); - RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); - RXS_ERR("LENGTH-ERR", rx_len_err); - RXS_ERR("OOM-ERR", rx_oom_err); - RXS_ERR("RATE-ERR", rx_rate_err); - RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef RXS_ERR -} - void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { -#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ - - RX_STAT_INC(rx_pkts_all); - sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; - - if (rs->rs_status & ATH9K_RXERR_CRC) - RX_STAT_INC(crc_err); - if (rs->rs_status & ATH9K_RXERR_DECRYPT) - RX_STAT_INC(decrypt_crc_err); - if (rs->rs_status & ATH9K_RXERR_MIC) - RX_STAT_INC(mic_err); - if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE) - RX_STAT_INC(pre_delim_crc_err); - if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST) - RX_STAT_INC(post_delim_crc_err); - if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY) - RX_STAT_INC(decrypt_busy_err); - - if (rs->rs_status & ATH9K_RXERR_PHY) { - RX_STAT_INC(phy_err); - if (rs->rs_phyerr < ATH9K_PHYERR_MAX) - RX_PHY_ERR_INC(rs->rs_phyerr); - } - -#undef RX_PHY_ERR_INC + ath9k_cmn_debug_stat_rx(&sc->debug.stats.rxstats, rs); } -static const struct file_operations fops_recv = { - .read = read_file_recv, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_phy_err(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PHY_ERR(s, p) \ - len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \ - sc->debug.stats.rxstats.phy_err_stats[p]); - - struct ath_softc *sc = file->private_data; - char *buf; - unsigned int len = 0, size = 1600; - ssize_t retval = 0; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); - PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); - PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); - PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); - PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); - PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); - PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); - PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); - PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); - PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); - PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); - PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); - PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); - PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); - PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); - PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); - PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); - PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); - PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); - PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); - PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); - PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); - PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PHY_ERR -} - -static const struct file_operations fops_phy_err = { - .read = read_file_phy_err, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - static ssize_t read_file_regidx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1268,62 +1128,6 @@ static const struct file_operations fops_dump_nfcal = { .llseek = default_llseek, }; -static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - u32 len = 0, size = 1500; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size); - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_base_eeprom = { - .read = read_file_base_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - u32 len = 0, size = 6000; - char *buf; - size_t retval; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size); - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_modal_eeprom = { - .read = read_file_modal_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1524,10 +1328,10 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_misc); debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_reset); - debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_recv); - debugfs_create_file("phy_err", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_phy_err); + + ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); + ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); + debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy, &ah->rxchainmask); debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy, @@ -1547,10 +1351,10 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_regdump); debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dump_nfcal); - debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_base_eeprom); - debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_modal_eeprom); + + ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah); + ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah); + debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 559a68c..53ae15b 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -221,50 +221,6 @@ struct ath_rx_rate_stats { } cck_stats[4]; }; -/** - * struct ath_rx_stats - RX Statistics - * @rx_pkts_all: No. of total frames received, including ones that - may have had errors. - * @rx_bytes_all: No. of total bytes received, including ones that - may have had errors. - * @crc_err: No. of frames with incorrect CRC value - * @decrypt_crc_err: No. of frames whose CRC check failed after - decryption process completed - * @phy_err: No. of frames whose reception failed because the PHY - encountered an error - * @mic_err: No. of frames with incorrect TKIP MIC verification failure - * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections - * @post_delim_crc_err: Post-Frame delimiter CRC error detections - * @decrypt_busy_err: Decryption interruptions counter - * @phy_err_stats: Individual PHY error statistics - * @rx_len_err: No. of frames discarded due to bad length. - * @rx_oom_err: No. of frames dropped due to OOM issues. - * @rx_rate_err: No. of frames dropped due to rate errors. - * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. - * @rx_beacons: No. of beacons received. - * @rx_frags: No. of rx-fragements received. - * @rx_spectral: No of spectral packets received. - */ -struct ath_rx_stats { - u32 rx_pkts_all; - u32 rx_bytes_all; - u32 crc_err; - u32 decrypt_crc_err; - u32 phy_err; - u32 mic_err; - u32 pre_delim_crc_err; - u32 post_delim_crc_err; - u32 decrypt_busy_err; - u32 phy_err_stats[ATH9K_PHYERR_MAX]; - u32 rx_len_err; - u32 rx_oom_err; - u32 rx_rate_err; - u32 rx_too_many_frags_err; - u32 rx_beacons; - u32 rx_frags; - u32 rx_spectral; -}; - #define ANT_MAIN 0 #define ANT_ALT 1 diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c index 857bb28..726271c 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.c +++ b/drivers/net/wireless/ath/ath9k/dfs.c @@ -178,12 +178,12 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, pe.ts = mactime; if (ath9k_postprocess_radar_event(sc, &ard, &pe)) { struct dfs_pattern_detector *pd = sc->dfs_detector; - static u64 last_ts; ath_dbg(common, DFS, "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " "width=%d, rssi=%d, delta_ts=%llu\n", - pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts); - last_ts = pe.ts; + pe.freq, pe.ts, pe.width, pe.rssi, + pe.ts - sc->dfs_prev_pulse_ts); + sc->dfs_prev_pulse_ts = pe.ts; DFS_STAT_INC(sc, pulses_processed); if (pd != NULL && pd->add_pulse(pd, &pe)) { DFS_STAT_INC(sc, radar_detected); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index dab1f0c..09a5d72 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -325,14 +325,14 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb) #define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) #define TX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a) -#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++) -#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c += a) +#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c++) +#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c += a) #define CAB_STAT_INC priv->debug.tx_stats.cab_queued++ #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++) void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, - struct ath_htc_rx_status *rxs); + struct ath_rx_status *rs); struct ath_tx_stats { u32 buf_queued; @@ -345,25 +345,18 @@ struct ath_tx_stats { u32 queue_stats[IEEE80211_NUM_ACS]; }; -struct ath_rx_stats { +struct ath_skbrx_stats { u32 skb_allocated; u32 skb_completed; u32 skb_completed_bytes; u32 skb_dropped; - u32 err_crc; - u32 err_decrypt_crc; - u32 err_mic; - u32 err_pre_delim; - u32 err_post_delim; - u32 err_decrypt_busy; - u32 err_phy; - u32 err_phy_stats[ATH9K_PHYERR_MAX]; }; struct ath9k_debug { struct dentry *debugfs_phy; struct ath_tx_stats tx_stats; struct ath_rx_stats rx_stats; + struct ath_skbrx_stats skbrx_stats; }; void ath9k_htc_get_et_strings(struct ieee80211_hw *hw, @@ -385,7 +378,7 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw, #define TX_QSTAT_INC(c) do { } while (0) static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, - struct ath_htc_rx_status *rxs) + struct ath_rx_status *rs) { } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index fb071ee..8b529e4 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -243,39 +243,14 @@ static const struct file_operations fops_xmit = { }; void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, - struct ath_htc_rx_status *rxs) + struct ath_rx_status *rs) { -#define RX_PHY_ERR_INC(c) priv->debug.rx_stats.err_phy_stats[c]++ - - if (rxs->rs_status & ATH9K_RXERR_CRC) - priv->debug.rx_stats.err_crc++; - if (rxs->rs_status & ATH9K_RXERR_DECRYPT) - priv->debug.rx_stats.err_decrypt_crc++; - if (rxs->rs_status & ATH9K_RXERR_MIC) - priv->debug.rx_stats.err_mic++; - if (rxs->rs_status & ATH9K_RX_DELIM_CRC_PRE) - priv->debug.rx_stats.err_pre_delim++; - if (rxs->rs_status & ATH9K_RX_DELIM_CRC_POST) - priv->debug.rx_stats.err_post_delim++; - if (rxs->rs_status & ATH9K_RX_DECRYPT_BUSY) - priv->debug.rx_stats.err_decrypt_busy++; - - if (rxs->rs_status & ATH9K_RXERR_PHY) { - priv->debug.rx_stats.err_phy++; - if (rxs->rs_phyerr < ATH9K_PHYERR_MAX) - RX_PHY_ERR_INC(rxs->rs_phyerr); - } - -#undef RX_PHY_ERR_INC + ath9k_cmn_debug_stat_rx(&priv->debug.rx_stats, rs); } -static ssize_t read_file_recv(struct file *file, char __user *user_buf, +static ssize_t read_file_skb_rx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { -#define PHY_ERR(s, p) \ - len += scnprintf(buf + len, size - len, "%20s : %10u\n", s, \ - priv->debug.rx_stats.err_phy_stats[p]); - struct ath9k_htc_priv *priv = file->private_data; char *buf; unsigned int len = 0, size = 1500; @@ -287,63 +262,13 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, len += scnprintf(buf + len, size - len, "%20s : %10u\n", "SKBs allocated", - priv->debug.rx_stats.skb_allocated); + priv->debug.skbrx_stats.skb_allocated); len += scnprintf(buf + len, size - len, "%20s : %10u\n", "SKBs completed", - priv->debug.rx_stats.skb_completed); + priv->debug.skbrx_stats.skb_completed); len += scnprintf(buf + len, size - len, "%20s : %10u\n", "SKBs Dropped", - priv->debug.rx_stats.skb_dropped); - - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "CRC ERR", - priv->debug.rx_stats.err_crc); - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "DECRYPT CRC ERR", - priv->debug.rx_stats.err_decrypt_crc); - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "MIC ERR", - priv->debug.rx_stats.err_mic); - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "PRE-DELIM CRC ERR", - priv->debug.rx_stats.err_pre_delim); - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "POST-DELIM CRC ERR", - priv->debug.rx_stats.err_post_delim); - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "DECRYPT BUSY ERR", - priv->debug.rx_stats.err_decrypt_busy); - len += scnprintf(buf + len, size - len, - "%20s : %10u\n", "TOTAL PHY ERR", - priv->debug.rx_stats.err_phy); - - - PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); - PHY_ERR("TIMING", ATH9K_PHYERR_TIMING); - PHY_ERR("PARITY", ATH9K_PHYERR_PARITY); - PHY_ERR("RATE", ATH9K_PHYERR_RATE); - PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH); - PHY_ERR("RADAR", ATH9K_PHYERR_RADAR); - PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE); - PHY_ERR("TOR", ATH9K_PHYERR_TOR); - PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING); - PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); - PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); - PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); - PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP); - PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE); - PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT); - PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING); - PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC); - PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART); - PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); - PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP); - PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR); - PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); - PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); + priv->debug.skbrx_stats.skb_dropped); if (len > size) len = size; @@ -352,12 +277,10 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, kfree(buf); return retval; - -#undef PHY_ERR } -static const struct file_operations fops_recv = { - .read = read_file_recv, +static const struct file_operations fops_skb_rx = { + .read = read_file_skb_rx, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -486,423 +409,6 @@ static const struct file_operations fops_debug = { .llseek = default_llseek, }; -static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath9k_htc_priv *priv = file->private_data; - struct ath_common *common = ath9k_hw_common(priv->ah); - struct base_eep_header *pBase = NULL; - unsigned int len = 0, size = 1500; - ssize_t retval = 0; - char *buf; - - pBase = ath9k_htc_get_eeprom_base(priv); - - if (pBase == NULL) { - ath_err(common, "Unknown EEPROM type\n"); - return 0; - } - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", "Major Version", - pBase->version >> 12); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", "Minor Version", - pBase->version & 0xFFF); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", "Checksum", - pBase->checksum); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", "Length", - pBase->length); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", "RegDomain1", - pBase->regDmn[0]); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", "RegDomain2", - pBase->regDmn[1]); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "TX Mask", pBase->txMask); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "RX Mask", pBase->rxMask); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Allow 5GHz", - !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Allow 2GHz", - !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 2GHz HT20", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 2GHz HT40", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 5Ghz HT20", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 5Ghz HT40", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Big Endian", - !!(pBase->eepMisc & 0x01)); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Major Ver", - (pBase->binBuildNumber >> 24) & 0xFF); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Minor Ver", - (pBase->binBuildNumber >> 16) & 0xFF); - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Build", - (pBase->binBuildNumber >> 8) & 0xFF); - - /* - * UB91 specific data. - */ - if (AR_SREV_9271(priv->ah)) { - struct base_eep_header_4k *pBase4k = - &priv->ah->eeprom.map4k.baseEepHeader; - - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "TX Gain type", - pBase4k->txGainType); - } - - /* - * UB95 specific data. - */ - if (priv->ah->hw_version.usbdev == AR9287_USB) { - struct base_eep_ar9287_header *pBase9287 = - &priv->ah->eeprom.map9287.baseEepHeader; - - len += scnprintf(buf + len, size - len, - "%20s : %10ddB\n", - "Power Table Offset", - pBase9287->pwrTableOffset); - - len += scnprintf(buf + len, size - len, - "%20s : %10d\n", - "OpenLoop Power Ctrl", - pBase9287->openLoopPwrCntl); - } - - len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_base_eeprom = { - .read = read_file_base_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_4k_modal_eeprom(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_EEP(_s, _val) \ - do { \ - len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ - _s, (_val)); \ - } while (0) - - struct ath9k_htc_priv *priv = file->private_data; - struct modal_eep_4k_header *pModal = &priv->ah->eeprom.map4k.modalHeader; - unsigned int len = 0, size = 2048; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); - PR_EEP("Ant. Common Control", pModal->antCtrlCommon); - PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); - PR_EEP("Switch Settle", pModal->switchSettling); - PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); - PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); - PR_EEP("ADC Desired size", pModal->adcDesiredSize); - PR_EEP("PGA Desired size", pModal->pgaDesiredSize); - PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]); - PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); - PR_EEP("txEndToRxOn", pModal->txEndToRxOn); - PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); - PR_EEP("CCA Threshold)", pModal->thresh62); - PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); - PR_EEP("xpdGain", pModal->xpdGain); - PR_EEP("External PD", pModal->xpd); - PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); - PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); - PR_EEP("pdGainOverlap", pModal->pdGainOverlap); - PR_EEP("O/D Bias Version", pModal->version); - PR_EEP("CCK OutputBias", pModal->ob_0); - PR_EEP("BPSK OutputBias", pModal->ob_1); - PR_EEP("QPSK OutputBias", pModal->ob_2); - PR_EEP("16QAM OutputBias", pModal->ob_3); - PR_EEP("64QAM OutputBias", pModal->ob_4); - PR_EEP("CCK Driver1_Bias", pModal->db1_0); - PR_EEP("BPSK Driver1_Bias", pModal->db1_1); - PR_EEP("QPSK Driver1_Bias", pModal->db1_2); - PR_EEP("16QAM Driver1_Bias", pModal->db1_3); - PR_EEP("64QAM Driver1_Bias", pModal->db1_4); - PR_EEP("CCK Driver2_Bias", pModal->db2_0); - PR_EEP("BPSK Driver2_Bias", pModal->db2_1); - PR_EEP("QPSK Driver2_Bias", pModal->db2_2); - PR_EEP("16QAM Driver2_Bias", pModal->db2_3); - PR_EEP("64QAM Driver2_Bias", pModal->db2_4); - PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); - PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); - PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); - PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); - PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); - PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); - PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); - PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]); - PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]); - PR_EEP("Ant. Diversity ctl1", pModal->antdiv_ctl1); - PR_EEP("Ant. Diversity ctl2", pModal->antdiv_ctl2); - PR_EEP("TX Diversity", pModal->tx_diversity); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PR_EEP -} - -static ssize_t read_def_modal_eeprom(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_EEP(_s, _val) \ - do { \ - if (pBase->opCapFlags & AR5416_OPFLAGS_11G) { \ - pModal = &priv->ah->eeprom.def.modalHeader[1]; \ - len += scnprintf(buf + len, size - len, "%20s : %8d%7s", \ - _s, (_val), "|"); \ - } \ - if (pBase->opCapFlags & AR5416_OPFLAGS_11A) { \ - pModal = &priv->ah->eeprom.def.modalHeader[0]; \ - len += scnprintf(buf + len, size - len, "%9d\n",\ - (_val)); \ - } \ - } while (0) - - struct ath9k_htc_priv *priv = file->private_data; - struct base_eep_header *pBase = &priv->ah->eeprom.def.baseEepHeader; - struct modal_eep_header *pModal = NULL; - unsigned int len = 0, size = 3500; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += scnprintf(buf + len, size - len, - "%31s %15s\n", "2G", "5G"); - len += scnprintf(buf + len, size - len, - "%32s %16s\n", "====", "====\n"); - - PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); - PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); - PR_EEP("Chain2 Ant. Control", pModal->antCtrlChain[2]); - PR_EEP("Ant. Common Control", pModal->antCtrlCommon); - PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); - PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]); - PR_EEP("Chain2 Ant. Gain", pModal->antennaGainCh[2]); - PR_EEP("Switch Settle", pModal->switchSettling); - PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); - PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]); - PR_EEP("Chain2 TxRxAtten", pModal->txRxAttenCh[2]); - PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); - PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]); - PR_EEP("Chain2 RxTxMargin", pModal->rxTxMarginCh[2]); - PR_EEP("ADC Desired size", pModal->adcDesiredSize); - PR_EEP("PGA Desired size", pModal->pgaDesiredSize); - PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]); - PR_EEP("Chain1 xlna Gain", pModal->xlnaGainCh[1]); - PR_EEP("Chain2 xlna Gain", pModal->xlnaGainCh[2]); - PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); - PR_EEP("txEndToRxOn", pModal->txEndToRxOn); - PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); - PR_EEP("CCA Threshold)", pModal->thresh62); - PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); - PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]); - PR_EEP("Chain2 NF Threshold", pModal->noiseFloorThreshCh[2]); - PR_EEP("xpdGain", pModal->xpdGain); - PR_EEP("External PD", pModal->xpd); - PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); - PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]); - PR_EEP("Chain2 I Coefficient", pModal->iqCalICh[2]); - PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); - PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]); - PR_EEP("Chain2 Q Coefficient", pModal->iqCalQCh[2]); - PR_EEP("pdGainOverlap", pModal->pdGainOverlap); - PR_EEP("Chain0 OutputBias", pModal->ob); - PR_EEP("Chain0 DriverBias", pModal->db); - PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); - PR_EEP("2chain pwr decrease", pModal->pwrDecreaseFor2Chain); - PR_EEP("3chain pwr decrease", pModal->pwrDecreaseFor3Chain); - PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); - PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); - PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); - PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); - PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]); - PR_EEP("Chain2 bswAtten", pModal->bswAtten[2]); - PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); - PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]); - PR_EEP("Chain2 bswMargin", pModal->bswMargin[2]); - PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); - PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]); - PR_EEP("Chain1 xatten2Db", pModal->xatten2Db[1]); - PR_EEP("Chain2 xatten2Db", pModal->xatten2Db[2]); - PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]); - PR_EEP("Chain1 xatten2Margin", pModal->xatten2Margin[1]); - PR_EEP("Chain2 xatten2Margin", pModal->xatten2Margin[2]); - PR_EEP("Chain1 OutputBias", pModal->ob_ch1); - PR_EEP("Chain1 DriverBias", pModal->db_ch1); - PR_EEP("LNA Control", pModal->lna_ctl); - PR_EEP("XPA Bias Freq0", pModal->xpaBiasLvlFreq[0]); - PR_EEP("XPA Bias Freq1", pModal->xpaBiasLvlFreq[1]); - PR_EEP("XPA Bias Freq2", pModal->xpaBiasLvlFreq[2]); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PR_EEP -} - -static ssize_t read_9287_modal_eeprom(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_EEP(_s, _val) \ - do { \ - len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ - _s, (_val)); \ - } while (0) - - struct ath9k_htc_priv *priv = file->private_data; - struct modal_eep_ar9287_header *pModal = &priv->ah->eeprom.map9287.modalHeader; - unsigned int len = 0, size = 3000; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); - PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); - PR_EEP("Ant. Common Control", pModal->antCtrlCommon); - PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); - PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]); - PR_EEP("Switch Settle", pModal->switchSettling); - PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); - PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]); - PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); - PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]); - PR_EEP("ADC Desired size", pModal->adcDesiredSize); - PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); - PR_EEP("txEndToRxOn", pModal->txEndToRxOn); - PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); - PR_EEP("CCA Threshold)", pModal->thresh62); - PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); - PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]); - PR_EEP("xpdGain", pModal->xpdGain); - PR_EEP("External PD", pModal->xpd); - PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); - PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]); - PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); - PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]); - PR_EEP("pdGainOverlap", pModal->pdGainOverlap); - PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); - PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); - PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); - PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); - PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); - PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]); - PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); - PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]); - PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); - PR_EEP("AR92x7 Version", pModal->version); - PR_EEP("DriverBias1", pModal->db1); - PR_EEP("DriverBias2", pModal->db1); - PR_EEP("CCK OutputBias", pModal->ob_cck); - PR_EEP("PSK OutputBias", pModal->ob_psk); - PR_EEP("QAM OutputBias", pModal->ob_qam); - PR_EEP("PAL_OFF OutputBias", pModal->ob_pal_off); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PR_EEP -} - -static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath9k_htc_priv *priv = file->private_data; - - if (AR_SREV_9271(priv->ah)) - return read_4k_modal_eeprom(file, user_buf, count, ppos); - else if (priv->ah->hw_version.usbdev == AR9280_USB) - return read_def_modal_eeprom(file, user_buf, count, ppos); - else if (priv->ah->hw_version.usbdev == AR9287_USB) - return read_9287_modal_eeprom(file, user_buf, count, ppos); - - return 0; -} - -static const struct file_operations fops_modal_eeprom = { - .read = read_file_modal_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - - /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" static const char ath9k_htc_gstrings_stats[][ETH_GSTRING_LEN] = { @@ -947,6 +453,8 @@ int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw, #define STXBASE priv->debug.tx_stats #define SRXBASE priv->debug.rx_stats +#define SKBTXBASE priv->debug.tx_stats +#define SKBRXBASE priv->debug.skbrx_stats #define ASTXQ(a) \ data[i++] = STXBASE.a[IEEE80211_AC_BE]; \ data[i++] = STXBASE.a[IEEE80211_AC_BK]; \ @@ -960,24 +468,24 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw, struct ath9k_htc_priv *priv = hw->priv; int i = 0; - data[i++] = STXBASE.skb_success; - data[i++] = STXBASE.skb_success_bytes; - data[i++] = SRXBASE.skb_completed; - data[i++] = SRXBASE.skb_completed_bytes; + data[i++] = SKBTXBASE.skb_success; + data[i++] = SKBTXBASE.skb_success_bytes; + data[i++] = SKBRXBASE.skb_completed; + data[i++] = SKBRXBASE.skb_completed_bytes; ASTXQ(queue_stats); - data[i++] = SRXBASE.err_crc; - data[i++] = SRXBASE.err_decrypt_crc; - data[i++] = SRXBASE.err_phy; - data[i++] = SRXBASE.err_mic; - data[i++] = SRXBASE.err_pre_delim; - data[i++] = SRXBASE.err_post_delim; - data[i++] = SRXBASE.err_decrypt_busy; + data[i++] = SRXBASE.crc_err; + data[i++] = SRXBASE.decrypt_crc_err; + data[i++] = SRXBASE.phy_err; + data[i++] = SRXBASE.mic_err; + data[i++] = SRXBASE.pre_delim_crc_err; + data[i++] = SRXBASE.post_delim_crc_err; + data[i++] = SRXBASE.decrypt_busy_err; - data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_RADAR]; - data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_OFDM_TIMING]; - data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_CCK_TIMING]; + data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_RADAR]; + data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_OFDM_TIMING]; + data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_CCK_TIMING]; WARN_ON(i != ATH9K_HTC_SSTATS_LEN); } @@ -1001,18 +509,21 @@ int ath9k_htc_init_debug(struct ath_hw *ah) priv, &fops_tgt_rx_stats); debugfs_create_file("xmit", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_xmit); - debugfs_create_file("recv", S_IRUSR, priv->debug.debugfs_phy, - priv, &fops_recv); + debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy, + priv, &fops_skb_rx); + + ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats); + ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats); + debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_slot); debugfs_create_file("queue", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_queue); debugfs_create_file("debug", S_IRUSR | S_IWUSR, priv->debug.debugfs_phy, priv, &fops_debug); - debugfs_create_file("base_eeprom", S_IRUSR, priv->debug.debugfs_phy, - priv, &fops_base_eeprom); - debugfs_create_file("modal_eeprom", S_IRUSR, priv->debug.debugfs_phy, - priv, &fops_modal_eeprom); + + ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah); + ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index f46cd02..5627917 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -95,8 +95,10 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) if ((vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_MESH_POINT) && - bss_conf->enable_beacon) + bss_conf->enable_beacon) { priv->reconfig_beacon = true; + priv->rearm_ani = true; + } if (bss_conf->assoc) { priv->rearm_ani = true; @@ -257,6 +259,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, ath9k_htc_ps_wakeup(priv); + ath9k_htc_stop_ani(priv); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 289f3d8..bb86eb2 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -996,8 +996,6 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, goto rx_next; } - ath9k_htc_err_stat_rx(priv, rxstatus); - /* Get the RX status information */ memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); @@ -1005,6 +1003,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, /* Copy everything from ath_htc_rx_status (HTC_RX_FRAME_HEADER). * After this, we can drop this part of skb. */ rx_status_htc_to_ath(&rx_stats, rxstatus); + ath9k_htc_err_stat_rx(priv, &rx_stats); rx_status->mactime = be64_to_cpu(rxstatus->rs_tstamp); skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index c8a9dfa..2a8ed83 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -26,7 +26,6 @@ #include "ar9003_mac.h" #include "ar9003_mci.h" #include "ar9003_phy.h" -#include "debug.h" #include "ath9k.h" static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); @@ -246,6 +245,8 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) return; case AR9300_DEVID_AR953X: ah->hw_version.macVersion = AR_SREV_VERSION_9531; + if (ah->get_mac_revision) + ah->hw_version.macRev = ah->get_mac_revision(); return; } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 4243509..0246b99 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -61,6 +61,10 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); +static int ath9k_use_chanctx; +module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); +MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); + bool is_ath9k_unloaded; #ifdef CONFIG_MAC80211_LEDS @@ -508,7 +512,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); - if (!pdata) { + if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; sc->sc_ah->led_pin = -1; } else { @@ -646,8 +650,7 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc) } static const struct ieee80211_iface_limit if_limits[] = { - { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_WDS) }, + { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) }, { .max = 8, .types = #ifdef CONFIG_MAC80211_MESH BIT(NL80211_IFTYPE_MESH_POINT) | @@ -657,6 +660,10 @@ static const struct ieee80211_iface_limit if_limits[] = { BIT(NL80211_IFTYPE_P2P_GO) }, }; +static const struct ieee80211_iface_limit wds_limits[] = { + { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, +}; + static const struct ieee80211_iface_limit if_dfs_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | #ifdef CONFIG_MAC80211_MESH @@ -673,6 +680,13 @@ static const struct ieee80211_iface_combination if_comb[] = { .num_different_channels = 1, .beacon_int_infra_match = true, }, + { + .limits = wds_limits, + .n_limits = ARRAY_SIZE(wds_limits), + .max_interfaces = 2048, + .num_different_channels = 1, + .beacon_int_infra_match = true, + }, #ifdef CONFIG_ATH9K_DFS_CERTIFIED { .limits = if_dfs_limits, @@ -714,19 +728,23 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + hw->wiphy->features |= (NL80211_FEATURE_ACTIVE_MONITOR | + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE); if (!config_enabled(CONFIG_ATH9K_TX99)) { hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_WDS) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + if (!ath9k_use_chanctx) { + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); + } else + hw->wiphy->n_iface_combinations = 1; } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 51ce36f..275205a 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -958,3 +958,25 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah) return; } EXPORT_SYMBOL(ath9k_hw_set_interrupts); + +#define ATH9K_HW_MAX_DCU 10 +#define ATH9K_HW_SLICE_PER_DCU 16 +#define ATH9K_HW_BIT_IN_SLICE 16 +void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set) +{ + int dcu_idx; + u32 filter; + + for (dcu_idx = 0; dcu_idx < 10; dcu_idx++) { + filter = SM(set, AR_D_TXBLK_WRITE_COMMAND); + filter |= SM(dcu_idx, AR_D_TXBLK_WRITE_DCU); + filter |= SM((destidx / ATH9K_HW_SLICE_PER_DCU), + AR_D_TXBLK_WRITE_SLICE); + filter |= BIT(destidx % ATH9K_HW_BIT_IN_SLICE); + ath_dbg(ath9k_hw_common(ah), PS, + "DCU%d staid %d set %d txfilter %08x\n", + dcu_idx, destidx, set, filter); + REG_WRITE(ah, AR_D_TXBLK_BASE, filter); + } +} +EXPORT_SYMBOL(ath9k_hw_set_tx_filter); diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 89df634..da76867 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -729,6 +729,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); void ath9k_hw_abortpcurecv(struct ath_hw *ah); bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset); int ath9k_hw_beaconq_setup(struct ath_hw *ah); +void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set); /* Interrupt Handling */ bool ath9k_hw_intrpend(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 8d7b9b6..62ac95d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -421,6 +421,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, an->sc = sc; an->sta = sta; an->vif = vif; + memset(&an->key_idx, 0, sizeof(an->key_idx)); ath_tx_node_init(sc, an); } @@ -1461,8 +1462,10 @@ static int ath9k_sta_add(struct ieee80211_hw *hw, return 0; key = ath_key_config(common, vif, sta, &ps_key); - if (key > 0) + if (key > 0) { an->ps_key = key; + an->key_idx[0] = key; + } return 0; } @@ -1480,6 +1483,7 @@ static void ath9k_del_ps_key(struct ath_softc *sc, ath_key_delete(common, &ps_key); an->ps_key = 0; + an->key_idx[0] = 0; } static int ath9k_sta_remove(struct ieee80211_hw *hw, @@ -1494,6 +1498,19 @@ static int ath9k_sta_remove(struct ieee80211_hw *hw, return 0; } +static void ath9k_sta_set_tx_filter(struct ath_hw *ah, + struct ath_node *an, + bool set) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { + if (!an->key_idx[i]) + continue; + ath9k_hw_set_tx_filter(ah, an->key_idx[i], set); + } +} + static void ath9k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, @@ -1506,8 +1523,10 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, case STA_NOTIFY_SLEEP: an->sleeping = true; ath_tx_aggr_sleep(sta, sc, an); + ath9k_sta_set_tx_filter(sc->sc_ah, an, true); break; case STA_NOTIFY_AWAKE: + ath9k_sta_set_tx_filter(sc->sc_ah, an, false); an->sleeping = false; ath_tx_aggr_wakeup(sc, an); break; @@ -1563,7 +1582,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - int ret = 0; + struct ath_node *an = NULL; + int ret = 0, i; if (ath9k_modparam_nohwcrypt) return -ENOSPC; @@ -1585,13 +1605,16 @@ static int ath9k_set_key(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); - ath_dbg(common, CONFIG, "Set HW Key\n"); + ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd); + if (sta) + an = (struct ath_node *)sta->drv_priv; switch (cmd) { case SET_KEY: if (sta) ath9k_del_ps_key(sc, vif, sta); + key->hw_key_idx = 0; ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; @@ -1604,9 +1627,27 @@ static int ath9k_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } + if (an && key->hw_key_idx) { + for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { + if (an->key_idx[i]) + continue; + an->key_idx[i] = key->hw_key_idx; + break; + } + WARN_ON(i == ARRAY_SIZE(an->key_idx)); + } break; case DISABLE_KEY: ath_key_delete(common, key); + if (an) { + for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { + if (an->key_idx[i] != key->hw_key_idx) + continue; + an->key_idx[i] = 0; + break; + } + } + key->hw_key_idx = 0; break; default: ret = -EINVAL; @@ -1716,7 +1757,6 @@ out: void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_vif *avp = (void *)vif->drv_priv; - unsigned long flags; u32 tsf; if (!sc->p2p_ps_timer) @@ -1726,14 +1766,9 @@ void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) return; sc->p2p_ps_vif = avp; - - spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (!(sc->ps_flags & PS_BEACON_SYNC)) { - tsf = ath9k_hw_gettsf32(sc->sc_ah); - ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); - ath9k_update_p2p_ps_timer(sc, avp); - } - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + tsf = ath9k_hw_gettsf32(sc->sc_ah); + ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); + ath9k_update_p2p_ps_timer(sc, avp); } static void ath9k_bss_info_changed(struct ieee80211_hw *hw, @@ -1750,6 +1785,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; + unsigned long flags; int slottime; ath9k_ps_wakeup(sc); @@ -1812,7 +1848,10 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_P2P_PS) { spin_lock_bh(&sc->sc_pcu_lock); - ath9k_update_p2p_ps(sc, vif); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (!(sc->ps_flags & PS_BEACON_SYNC)) + ath9k_update_p2p_ps(sc, vif); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); spin_unlock_bh(&sc->sc_pcu_lock); } @@ -2191,14 +2230,6 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(ATH_OP_SCANNING, &common->op_flags); } -static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_chan_def *chandef) -{ - /* depend on vif->csa_active only */ - return; -} - struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, @@ -2246,5 +2277,4 @@ struct ieee80211_ops ath9k_ops = { #endif .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, - .channel_switch_beacon = ath9k_channel_switch_beacon, }; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 914dbc6..4dec09e 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -686,7 +686,7 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) struct ath_softc *sc = (struct ath_softc *) common->priv; struct ath9k_platform_data *pdata = sc->dev->platform_data; - if (pdata) { + if (pdata && !pdata->use_eeprom) { if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { ath_err(common, "%s: eeprom read failed, offset %08x is out of range\n", @@ -914,6 +914,7 @@ static int ath_pci_suspend(struct device *device) */ ath9k_stop_btcoex(sc); ath9k_hw_disable(sc->sc_ah); + del_timer_sync(&sc->sleep_timer); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); return 0; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 441c714..9105a92 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -34,7 +34,8 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) * buffer (or rx fifo). This can incorrectly acknowledge packets * to a sender if last desc is self-linked. */ -static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf) +static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf, + bool flush) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -59,18 +60,19 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf) common->rx_bufsize, 0); - if (sc->rx.rxlink == NULL) - ath9k_hw_putrxbuf(ah, bf->bf_daddr); - else + if (sc->rx.rxlink) *sc->rx.rxlink = bf->bf_daddr; + else if (!flush) + ath9k_hw_putrxbuf(ah, bf->bf_daddr); sc->rx.rxlink = &ds->ds_link; } -static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf) +static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf, + bool flush) { if (sc->rx.buf_hold) - ath_rx_buf_link(sc, sc->rx.buf_hold); + ath_rx_buf_link(sc, sc->rx.buf_hold, flush); sc->rx.buf_hold = bf; } @@ -442,7 +444,7 @@ int ath_startrecv(struct ath_softc *sc) sc->rx.buf_hold = NULL; sc->rx.rxlink = NULL; list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { - ath_rx_buf_link(sc, bf); + ath_rx_buf_link(sc, bf, false); } /* We could have deleted elements so the list may be empty now */ @@ -538,8 +540,8 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) sc->ps_flags &= ~PS_BEACON_SYNC; ath_dbg(common, PS, "Reconfigure beacon timers based on synchronized timestamp\n"); - ath9k_set_beacon(sc); - + if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0))) + ath9k_set_beacon(sc); if (sc->p2p_ps_vif) ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); } @@ -1118,12 +1120,12 @@ requeue_drop_frag: requeue: list_add_tail(&bf->list, &sc->rx.rxbuf); - if (edma) { - ath_rx_edma_buf_link(sc, qtype); - } else { - ath_rx_buf_relink(sc, bf); + if (!edma) { + ath_rx_buf_relink(sc, bf, flush); if (!flush) ath9k_hw_rxena(ah); + } else if (!flush) { + ath_rx_edma_buf_link(sc, qtype); } if (!budget--) diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index b1fd3fa..f1bbce3 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -505,9 +505,6 @@ #define AR_D_QCUMASK 0x000003FF #define AR_D_QCUMASK_RESV0 0xFFFFFC00 -#define AR_D_TXBLK_CMD 0x1038 -#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i)) - #define AR_D0_LCL_IFS 0x1040 #define AR_D1_LCL_IFS 0x1044 #define AR_D2_LCL_IFS 0x1048 diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index ca115f3..f35c7f3 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -1076,8 +1076,14 @@ static int carl9170_usb_probe(struct usb_interface *intf, carl9170_set_state(ar, CARL9170_STOPPED); - return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, + err = request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2); + if (err) { + usb_put_dev(udev); + usb_put_dev(udev); + carl9170_free(ar); + } + return err; } static void carl9170_usb_disconnect(struct usb_interface *intf) diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index a1a69c5..650be79 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -73,9 +73,52 @@ static const struct radar_types etsi_radar_types_v15 = { .radar_types = etsi_radar_ref_types_v15, }; -/* for now, we support ETSI radar types, FCC and JP are TODO */ +#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ +{ \ + ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ + PMIN - PRI_TOLERANCE, \ + PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF, \ + PPB_THRESH(PPB), PRI_TOLERANCE, \ +} + +static const struct radar_detector_specs fcc_radar_ref_types[] = { + FCC_PATTERN(0, 0, 1, 1428, 1428, 1, 18), + FCC_PATTERN(1, 0, 5, 150, 230, 1, 23), + FCC_PATTERN(2, 6, 10, 200, 500, 1, 16), + FCC_PATTERN(3, 11, 20, 200, 500, 1, 12), + FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1), + FCC_PATTERN(5, 0, 1, 333, 333, 1, 9), +}; + +static const struct radar_types fcc_radar_types = { + .region = NL80211_DFS_FCC, + .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types), + .radar_types = fcc_radar_ref_types, +}; + +#define JP_PATTERN FCC_PATTERN +static const struct radar_detector_specs jp_radar_ref_types[] = { + JP_PATTERN(0, 0, 1, 1428, 1428, 1, 18), + JP_PATTERN(1, 2, 3, 3846, 3846, 1, 18), + JP_PATTERN(2, 0, 1, 1388, 1388, 1, 18), + JP_PATTERN(3, 1, 2, 4000, 4000, 1, 18), + JP_PATTERN(4, 0, 5, 150, 230, 1, 23), + JP_PATTERN(5, 6, 10, 200, 500, 1, 16), + JP_PATTERN(6, 11, 20, 200, 500, 1, 12), + JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1), + JP_PATTERN(5, 0, 1, 333, 333, 1, 9), +}; + +static const struct radar_types jp_radar_types = { + .region = NL80211_DFS_JP, + .num_radar_types = ARRAY_SIZE(jp_radar_ref_types), + .radar_types = jp_radar_ref_types, +}; + static const struct radar_types *dfs_domains[] = { &etsi_radar_types_v15, + &fcc_radar_types, + &jp_radar_types, }; /** diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 7bf0ef8..6398693 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2068,7 +2068,7 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) if (!msg_ind) goto nomem; msg_ind->msg_len = len; - msg_ind->msg = kmalloc(len, GFP_KERNEL); + msg_ind->msg = kmemdup(buf, len, GFP_KERNEL); if (!msg_ind->msg) { kfree(msg_ind); nomem: @@ -2080,7 +2080,6 @@ nomem: msg_header->msg_type); break; } - memcpy(msg_ind->msg, buf, len); mutex_lock(&wcn->hal_ind_mutex); list_add_tail(&msg_ind->list, &wcn->hal_ind_queue); queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4806a49..820d4eb 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -172,7 +172,7 @@ static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, static int wil_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; @@ -288,6 +288,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } wil->scan_request = request; + mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO); memset(&cmd, 0, sizeof(cmd)); cmd.cmd.num_channels = 0; @@ -671,7 +672,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, } static int wil_cfg80211_del_station(struct wiphy *wiphy, - struct net_device *dev, u8 *mac) + struct net_device *dev, const u8 *mac) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index ecdabe4..8d4bc4b 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -35,7 +35,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, void __iomem *x = wmi_addr(wil, vring->hwtail); seq_printf(s, "VRING %s = {\n", name); - seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " pa = %pad\n", &vring->pa); seq_printf(s, " va = 0x%p\n", vring->va); seq_printf(s, " size = %d\n", vring->size); seq_printf(s, " swtail = %d\n", vring->swtail); @@ -473,7 +473,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) u[0], u[1], u[2], u[3]); seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", u[4], u[5], u[6], u[7]); - seq_printf(s, " SKB = %p\n", skb); + seq_printf(s, " SKB = 0x%p\n", skb); if (skb) { skb_get(skb); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 1b265fd..11e6d9d 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -81,7 +81,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) memset(&sta->stats, 0, sizeof(sta->stats)); } -static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) { int cid = -ENOENT; struct net_device *ndev = wil_to_ndev(wil); @@ -150,6 +150,15 @@ static void wil_connect_timer_fn(ulong x) schedule_work(&wil->disconnect_worker); } +static void wil_scan_timer_fn(ulong x) +{ + struct wil6210_priv *wil = (void *)x; + + clear_bit(wil_status_fwready, &wil->status); + wil_err(wil, "Scan timeout detected, start fw error recovery\n"); + schedule_work(&wil->fw_error_worker); +} + static void wil_fw_error_worker(struct work_struct *work) { struct wil6210_priv *wil = container_of(work, @@ -161,12 +170,30 @@ static void wil_fw_error_worker(struct work_struct *work) if (no_fw_recovery) return; + /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO + * passed since last recovery attempt + */ + if (time_is_after_jiffies(wil->last_fw_recovery + + WIL6210_FW_RECOVERY_TO)) + wil->recovery_count++; + else + wil->recovery_count = 1; /* fw was alive for a long time */ + + if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { + wil_err(wil, "too many recovery attempts (%d), giving up\n", + wil->recovery_count); + return; + } + + wil->last_fw_recovery = jiffies; + mutex_lock(&wil->mutex); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: - wil_info(wil, "fw error recovery started...\n"); + wil_info(wil, "fw error recovery started (try %d)...\n", + wil->recovery_count); wil_reset(wil); /* need to re-allocate Rx ring after reset */ @@ -230,6 +257,7 @@ int wil_priv_init(struct wil6210_priv *wil) wil->pending_connect_cid = -1; setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); INIT_WORK(&wil->connect_worker, wil_connect_worker); INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); @@ -249,10 +277,12 @@ int wil_priv_init(struct wil6210_priv *wil) return -EAGAIN; } + wil->last_fw_recovery = jiffies; + return 0; } -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) { del_timer_sync(&wil->connect_timer); _wil6210_disconnect(wil, bssid); @@ -260,6 +290,7 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) void wil_priv_deinit(struct wil6210_priv *wil) { + del_timer_sync(&wil->scan_timer); cancel_work_sync(&wil->disconnect_worker); cancel_work_sync(&wil->fw_error_worker); mutex_lock(&wil->mutex); @@ -391,6 +422,7 @@ int wil_reset(struct wil6210_priv *wil) if (wil->scan_request) { wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request); + del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, true); wil->scan_request = NULL; } @@ -520,6 +552,7 @@ static int __wil_down(struct wil6210_priv *wil) napi_disable(&wil->napi_tx); if (wil->scan_request) { + del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, true); wil->scan_request = NULL; } diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index fdcaeb8..106b6dc 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -32,12 +32,26 @@ static int wil_stop(struct net_device *ndev) return wil_down(wil); } +static int wil_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG) + return -EINVAL; + + wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu); + ndev->mtu = new_mtu; + + return 0; +} + static const struct net_device_ops wil_netdev_ops = { .ndo_open = wil_open, .ndo_stop = wil_stop, .ndo_start_xmit = wil_start_xmit, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = wil_change_mtu, }; static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 0660884..1e2e07b 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -138,7 +138,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_release_reg; } /* rollback to err_iounmap */ - dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr); wil = wil_if_alloc(dev, csr); if (IS_ERR(wil)) { diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index ec29954..747ae12 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -49,10 +49,17 @@ static void wil_release_reorder_frames(struct wil6210_priv *wil, { int index; - while (seq_less(r->head_seq_num, hseq)) { + /* note: this function is never called with + * hseq preceding r->head_seq_num, i.e it is always true + * !seq_less(hseq, r->head_seq_num) + * and thus on loop exit it should be + * r->head_seq_num == hseq + */ + while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) { index = reorder_index(r, r->head_seq_num); wil_release_reorder_frame(wil, r, index); } + r->head_seq_num = hseq; } static void wil_reorder_release(struct wil6210_priv *wil, diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index c8c5474..0784ef3 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -64,6 +64,22 @@ static inline int wil_vring_avail_tx(struct vring *vring) return vring->size - used - 1; } +/** + * wil_vring_wmark_low - low watermark for available descriptor space + */ +static inline int wil_vring_wmark_low(struct vring *vring) +{ + return vring->size/8; +} + +/** + * wil_vring_wmark_high - high watermark for available descriptor space + */ +static inline int wil_vring_wmark_high(struct vring *vring) +{ + return vring->size/4; +} + static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) { struct device *dev = wil_to_dev(wil); @@ -98,8 +114,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) _d->dma.status = TX_DMA_STATUS_DU; } - wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, - vring->va, (unsigned long long)vring->pa, vring->ctx); + wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size, + vring->va, &vring->pa, vring->ctx); return 0; } @@ -880,8 +896,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), - skb->data, (unsigned long long)pa); + wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb), + skb->data, &pa); wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); @@ -1007,7 +1023,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) rc = wil_tx_vring(wil, vring, skb); /* do we still have enough room in the vring? */ - if (wil_vring_avail_tx(vring) < vring->size/8) + if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) netif_tx_stop_all_queues(wil_to_ndev(wil)); switch (rc) { @@ -1116,7 +1132,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) done++; } } - if (wil_vring_avail_tx(vring) > vring->size/4) + if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) netif_tx_wake_all_queues(wil_to_ndev(wil)); return done; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index d3b8659..e25edc5 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -40,6 +40,9 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MAX_CID (8) /* HW limit */ #define WIL6210_NAPI_BUDGET (16) /* arbitrary */ #define WIL6210_ITR_TRSH (10000) /* arbitrary - about 15 IRQs/msec */ +#define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */ +#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000) +#define WIL6210_SCAN_TO msecs_to_jiffies(10000) /* Hardware definitions begin */ @@ -361,6 +364,8 @@ struct wil6210_priv { u32 fw_version; u32 hw_version; u8 n_mids; /* number of additional MIDs as reported by FW */ + int recovery_count; /* num of FW recovery attempts in a short time */ + unsigned long last_fw_recovery; /* jiffies of last fw recovery */ /* profile */ u32 monitor_flags; u32 secure_pcp; /* create secure PCP? */ @@ -382,6 +387,7 @@ struct wil6210_priv { struct work_struct disconnect_worker; struct work_struct fw_error_worker; /* for FW error recovery */ struct timer_list connect_timer; + struct timer_list scan_timer; /* detect scan timeout */ int pending_connect_cid; struct list_head pending_wmi_ev; /* @@ -508,7 +514,7 @@ void wil_wdev_free(struct wil6210_priv *wil); int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan); int wmi_pcp_stop(struct wil6210_priv *wil); -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid); int wil_rx_init(struct wil6210_priv *wil); void wil_rx_fini(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index e9a11cb..6cc0e18 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -351,6 +351,7 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, bool aborted = (data->status != WMI_SCAN_SUCCESS); wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, aborted); wil->scan_request = NULL; } else { @@ -658,21 +659,27 @@ void wmi_recv_cmd(struct wil6210_priv *wil) u8 *cmd; void __iomem *src; ulong flags; + unsigned n; if (!test_bit(wil_status_reset_done, &wil->status)) { wil_err(wil, "Reset not completed\n"); return; } - for (;;) { + for (n = 0;; n++) { u16 len; r->head = ioread32(wil->csr + HOST_MBOX + offsetof(struct wil6210_mbox_ctl, rx.head)); - if (r->tail == r->head) + if (r->tail == r->head) { + if (n == 0) + wil_dbg_wmi(wil, "No events?\n"); return; + } - /* read cmd from tail */ + wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n", + r->head, r->tail); + /* read cmd descriptor from tail */ wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), sizeof(struct wil6210_mbox_ring_desc)); if (d_tail.sync == 0) { @@ -680,13 +687,18 @@ void wmi_recv_cmd(struct wil6210_priv *wil) return; } + /* read cmd header from descriptor */ if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { wil_err(wil, "Mbox evt at 0x%08x?\n", le32_to_cpu(d_tail.addr)); return; } - len = le16_to_cpu(hdr.len); + wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), + hdr.flags); + + /* read cmd buffer from descriptor */ src = wmi_buffer(wil, d_tail.addr) + sizeof(struct wil6210_mbox_hdr); evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, @@ -702,9 +714,6 @@ void wmi_recv_cmd(struct wil6210_priv *wil) iowrite32(0, wil->csr + HOSTADDR(r->tail) + offsetof(struct wil6210_mbox_ring_desc, sync)); /* indicate */ - wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", - le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), - hdr.flags); if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi; @@ -734,6 +743,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil) wil_dbg_wmi(wil, "queue_work -> %d\n", q); } } + if (n > 1) + wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n); } int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 088d544..e3f67b8 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -1,7 +1,8 @@ config B43 tristate "Broadcom 43xx wireless support (mac80211 stack)" - depends on SSB_POSSIBLE && MAC80211 && HAS_DMA - select SSB + depends on (BCMA_POSSIBLE || SSB_POSSIBLE) && MAC80211 && HAS_DMA + select BCMA if B43_BCMA + select SSB if B43_SSB select FW_LOADER ---help--- b43 is a driver for the Broadcom 43xx series wireless devices. @@ -27,14 +28,33 @@ config B43 If unsure, say M. config B43_BCMA - bool "Support for BCMA bus" - depends on B43 && (BCMA = y || BCMA = B43) - default y + bool config B43_SSB bool - depends on B43 && (SSB = y || SSB = B43) - default y + +choice + prompt "Supported bus types" + depends on B43 + default B43_BCMA_AND_SSB + +config B43_BUSES_BCMA_AND_SSB + bool "BCMA and SSB" + depends on BCMA_POSSIBLE && SSB_POSSIBLE + select B43_BCMA + select B43_SSB + +config B43_BUSES_BCMA + bool "BCMA only" + depends on BCMA_POSSIBLE + select B43_BCMA + +config B43_BUSES_SSB + bool "SSB only" + depends on SSB_POSSIBLE + select B43_SSB + +endchoice # Auto-select SSB PCI-HOST support, if possible config B43_PCI_AUTOSELECT @@ -53,7 +73,7 @@ config B43_PCICORE_AUTOSELECT config B43_PCMCIA bool "Broadcom 43xx PCMCIA device support" - depends on B43 && SSB_PCMCIAHOST_POSSIBLE + depends on B43 && B43_SSB && SSB_PCMCIAHOST_POSSIBLE select SSB_PCMCIAHOST ---help--- Broadcom 43xx PCMCIA device support. @@ -73,7 +93,7 @@ config B43_PCMCIA config B43_SDIO bool "Broadcom 43xx SDIO device support" - depends on B43 && SSB_SDIOHOST_POSSIBLE + depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE select SSB_SDIOHOST ---help--- Broadcom 43xx device support for Soft-MAC SDIO devices. @@ -98,7 +118,7 @@ config B43_BCMA_PIO config B43_PIO bool - depends on B43 + depends on B43 && B43_SSB select SSB_BLOCKIO default y @@ -116,7 +136,7 @@ config B43_PHY_N config B43_PHY_LP bool "Support for low-power (LP-PHY) devices" - depends on B43 + depends on B43 && B43_SSB default y ---help--- Support for the LP-PHY. diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/b43/bus.h index 184c956..f3205c6 100644 --- a/drivers/net/wireless/b43/bus.h +++ b/drivers/net/wireless/b43/bus.h @@ -5,7 +5,9 @@ enum b43_bus_type { #ifdef CONFIG_B43_BCMA B43_BUS_BCMA, #endif +#ifdef CONFIG_B43_SSB B43_BUS_SSB, +#endif }; struct b43_bus_dev { @@ -52,13 +54,21 @@ struct b43_bus_dev { static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev) { +#ifdef CONFIG_B43_SSB return (dev->bus_type == B43_BUS_SSB && dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA); +#else + return false; +#endif } static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev) { +#ifdef CONFIG_B43_SSB return (dev->bus_type == B43_BUS_SSB && dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO); +#else + return false; +#endif } struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core); diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 32e08d3..32538ac 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -182,7 +182,7 @@ static struct ieee80211_rate __b43_ratetable[] = { #define b43_g_ratetable (__b43_ratetable + 0) #define b43_g_ratetable_size 12 -#define CHAN4G(_channel, _freq, _flags) { \ +#define CHAN2G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ @@ -191,23 +191,31 @@ static struct ieee80211_rate __b43_ratetable[] = { .max_power = 30, \ } static struct ieee80211_channel b43_2ghz_chantable[] = { - CHAN4G(1, 2412, 0), - CHAN4G(2, 2417, 0), - CHAN4G(3, 2422, 0), - CHAN4G(4, 2427, 0), - CHAN4G(5, 2432, 0), - CHAN4G(6, 2437, 0), - CHAN4G(7, 2442, 0), - CHAN4G(8, 2447, 0), - CHAN4G(9, 2452, 0), - CHAN4G(10, 2457, 0), - CHAN4G(11, 2462, 0), - CHAN4G(12, 2467, 0), - CHAN4G(13, 2472, 0), - CHAN4G(14, 2484, 0), + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), }; -#undef CHAN4G +#undef CHAN2G +#define CHAN4G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 4000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} #define CHAN5G(_channel, _flags) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ @@ -217,6 +225,18 @@ static struct ieee80211_channel b43_2ghz_chantable[] = { .max_power = 30, \ } static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { + CHAN4G(184, 0), CHAN4G(186, 0), + CHAN4G(188, 0), CHAN4G(190, 0), + CHAN4G(192, 0), CHAN4G(194, 0), + CHAN4G(196, 0), CHAN4G(198, 0), + CHAN4G(200, 0), CHAN4G(202, 0), + CHAN4G(204, 0), CHAN4G(206, 0), + CHAN4G(208, 0), CHAN4G(210, 0), + CHAN4G(212, 0), CHAN4G(214, 0), + CHAN4G(216, 0), CHAN4G(218, 0), + CHAN4G(220, 0), CHAN4G(222, 0), + CHAN4G(224, 0), CHAN4G(226, 0), + CHAN4G(228, 0), CHAN5G(32, 0), CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), @@ -260,18 +280,7 @@ static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { CHAN5G(170, 0), CHAN5G(172, 0), CHAN5G(174, 0), CHAN5G(176, 0), CHAN5G(178, 0), CHAN5G(180, 0), - CHAN5G(182, 0), CHAN5G(184, 0), - CHAN5G(186, 0), CHAN5G(188, 0), - CHAN5G(190, 0), CHAN5G(192, 0), - CHAN5G(194, 0), CHAN5G(196, 0), - CHAN5G(198, 0), CHAN5G(200, 0), - CHAN5G(202, 0), CHAN5G(204, 0), - CHAN5G(206, 0), CHAN5G(208, 0), - CHAN5G(210, 0), CHAN5G(212, 0), - CHAN5G(214, 0), CHAN5G(216, 0), - CHAN5G(218, 0), CHAN5G(220, 0), - CHAN5G(222, 0), CHAN5G(224, 0), - CHAN5G(226, 0), CHAN5G(228, 0), + CHAN5G(182, 0), }; static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { @@ -295,6 +304,7 @@ static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; +#undef CHAN4G #undef CHAN5G static struct ieee80211_supported_band b43_band_5GHz_nphy = { @@ -1175,18 +1185,7 @@ static void b43_bcma_phy_reset(struct b43_wldev *dev) bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); udelay(2); - /* Take PHY out of reset */ - flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - flags &= ~B43_BCMA_IOCTL_PHY_RESET; - flags |= BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); - udelay(1); - - /* Do not force clock anymore */ - flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - flags &= ~BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); - udelay(1); + b43_phy_take_out_of_reset(dev); } static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) @@ -1208,10 +1207,9 @@ static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) } #endif +#ifdef CONFIG_B43_SSB static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) { - struct ssb_device *sdev = dev->dev->sdev; - u32 tmslow; u32 flags = 0; if (gmode) @@ -1223,18 +1221,9 @@ static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) b43_device_enable(dev, flags); msleep(2); /* Wait for the PLL to turn on. */ - /* Now take the PHY out of Reset again */ - tmslow = ssb_read32(sdev, SSB_TMSLOW); - tmslow |= SSB_TMSLOW_FGC; - tmslow &= ~B43_TMSLOW_PHYRESET; - ssb_write32(sdev, SSB_TMSLOW, tmslow); - ssb_read32(sdev, SSB_TMSLOW); /* flush */ - msleep(1); - tmslow &= ~SSB_TMSLOW_FGC; - ssb_write32(sdev, SSB_TMSLOW, tmslow); - ssb_read32(sdev, SSB_TMSLOW); /* flush */ - msleep(1); + b43_phy_take_out_of_reset(dev); } +#endif void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode) { @@ -2709,32 +2698,37 @@ static int b43_upload_initvals(struct b43_wldev *dev) struct b43_firmware *fw = &dev->fw; const struct b43_iv *ivals; size_t count; - int err; hdr = (const struct b43_fw_header *)(fw->initvals.data->data); ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len); count = be32_to_cpu(hdr->size); - err = b43_write_initvals(dev, ivals, count, + return b43_write_initvals(dev, ivals, count, fw->initvals.data->size - hdr_len); - if (err) - goto out; - if (fw->initvals_band.data) { - hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); - ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); - count = be32_to_cpu(hdr->size); - err = b43_write_initvals(dev, ivals, count, - fw->initvals_band.data->size - hdr_len); - if (err) - goto out; - } -out: +} - return err; +static int b43_upload_initvals_band(struct b43_wldev *dev) +{ + const size_t hdr_len = sizeof(struct b43_fw_header); + const struct b43_fw_header *hdr; + struct b43_firmware *fw = &dev->fw; + const struct b43_iv *ivals; + size_t count; + + if (!fw->initvals_band.data) + return 0; + + hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); + ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); + count = be32_to_cpu(hdr->size); + return b43_write_initvals(dev, ivals, count, + fw->initvals_band.data->size - hdr_len); } /* Initialize the GPIOs * http://bcm-specs.sipsolutions.net/GPIO */ + +#ifdef CONFIG_B43_SSB static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) { struct ssb_bus *bus = dev->dev->sdev->bus; @@ -2745,10 +2739,13 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) return bus->chipco.dev; #endif } +#endif static int b43_gpio_init(struct b43_wldev *dev) { +#ifdef CONFIG_B43_SSB struct ssb_device *gpiodev; +#endif u32 mask, set; b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); @@ -2807,7 +2804,9 @@ static int b43_gpio_init(struct b43_wldev *dev) /* Turn off all GPIO stuff. Call this on module unload, for example. */ static void b43_gpio_cleanup(struct b43_wldev *dev) { +#ifdef CONFIG_B43_SSB struct ssb_device *gpiodev; +#endif switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA @@ -3091,6 +3090,10 @@ static int b43_chip_init(struct b43_wldev *dev) if (err) goto err_gpio_clean; + err = b43_upload_initvals_band(dev); + if (err) + goto err_gpio_clean; + /* Turn the Analog on and initialize the PHY. */ phy->ops->switch_analog(dev, 1); err = b43_phy_init(dev); @@ -3690,37 +3693,6 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } -static void b43_put_phy_into_reset(struct b43_wldev *dev) -{ - u32 tmp; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - b43err(dev->wl, - "Putting PHY into reset not supported on BCMA\n"); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~B43_TMSLOW_GMODE; - tmp |= B43_TMSLOW_PHYRESET; - tmp |= SSB_TMSLOW_FGC; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - msleep(1); - - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~SSB_TMSLOW_FGC; - tmp |= B43_TMSLOW_PHYRESET; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - msleep(1); - - break; -#endif - } -} - static const char *band_to_string(enum ieee80211_band band) { switch (band) { @@ -3736,89 +3708,75 @@ static const char *band_to_string(enum ieee80211_band band) } /* Expects wl->mutex locked */ -static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan) +static int b43_switch_band(struct b43_wldev *dev, + struct ieee80211_channel *chan) { - struct b43_wldev *up_dev = NULL; - struct b43_wldev *down_dev; - int err; - bool uninitialized_var(gmode); - int prev_status; + struct b43_phy *phy = &dev->phy; + bool gmode; + u32 tmp; - /* Find a device and PHY which supports the band. */ switch (chan->band) { case IEEE80211_BAND_5GHZ: - if (wl->current_dev->phy.supports_5ghz) { - up_dev = wl->current_dev; - gmode = false; - } + gmode = false; break; case IEEE80211_BAND_2GHZ: - if (wl->current_dev->phy.supports_2ghz) { - up_dev = wl->current_dev; - gmode = true; - } + gmode = true; break; default: B43_WARN_ON(1); return -EINVAL; } - if (!up_dev) { - b43err(wl, "Could not find a device for %s-GHz band operation\n", + if (!((gmode && phy->supports_2ghz) || + (!gmode && phy->supports_5ghz))) { + b43err(dev->wl, "This device doesn't support %s-GHz band\n", band_to_string(chan->band)); return -ENODEV; } - if (!!wl->current_dev->phy.gmode == !!gmode) { + + if (!!phy->gmode == !!gmode) { /* This device is already running. */ return 0; } - b43dbg(wl, "Switching to %s-GHz band\n", + + b43dbg(dev->wl, "Switching to %s GHz band\n", band_to_string(chan->band)); - down_dev = wl->current_dev; - prev_status = b43_status(down_dev); - /* Shutdown the currently running core. */ - if (prev_status >= B43_STAT_STARTED) - down_dev = b43_wireless_core_stop(down_dev); - if (prev_status >= B43_STAT_INITIALIZED) - b43_wireless_core_exit(down_dev); + /* Some new devices don't need disabling radio for band switching */ + if (!(phy->type == B43_PHYTYPE_N && phy->rev >= 3)) + b43_software_rfkill(dev, true); - if (down_dev != up_dev) { - /* We switch to a different core, so we put PHY into - * RESET on the old core. */ - b43_put_phy_into_reset(down_dev); + phy->gmode = gmode; + b43_phy_put_into_reset(dev); + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + if (gmode) + tmp |= B43_BCMA_IOCTL_GMODE; + else + tmp &= ~B43_BCMA_IOCTL_GMODE; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + if (gmode) + tmp |= B43_TMSLOW_GMODE; + else + tmp &= ~B43_TMSLOW_GMODE; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + break; +#endif } + b43_phy_take_out_of_reset(dev); - /* Now start the new core. */ - up_dev->phy.gmode = gmode; - if (prev_status >= B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(up_dev); - if (err) { - b43err(wl, "Fatal: Could not initialize device for " - "selected %s-GHz band\n", - band_to_string(chan->band)); - goto init_failure; - } - } - if (prev_status >= B43_STAT_STARTED) { - err = b43_wireless_core_start(up_dev); - if (err) { - b43err(wl, "Fatal: Could not start device for " - "selected %s-GHz band\n", - band_to_string(chan->band)); - b43_wireless_core_exit(up_dev); - goto init_failure; - } - } - B43_WARN_ON(b43_status(up_dev) != prev_status); + b43_upload_initvals_band(dev); - wl->current_dev = up_dev; + b43_phy_init(dev); return 0; -init_failure: - /* Whoops, failed to init the new core. No core is operating now. */ - wl->current_dev = NULL; - return err; } /* Write the short and long frame retry limit values. */ @@ -3851,8 +3809,10 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) dev = wl->current_dev; + b43_mac_suspend(dev); + /* Switch the band (if necessary). This might change the active core. */ - err = b43_switch_band(wl, conf->chandef.chan); + err = b43_switch_band(dev, conf->chandef.chan); if (err) goto out_unlock_mutex; @@ -3871,8 +3831,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) else phy->is_40mhz = false; - b43_mac_suspend(dev); - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) b43_set_retry_limits(dev, conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); @@ -4582,8 +4540,12 @@ static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev) struct ssb_bus *bus; u32 tmp; +#ifdef CONFIG_B43_SSB if (dev->dev->bus_type != B43_BUS_SSB) return; +#else + return; +#endif bus = dev->dev->sdev->bus; @@ -4738,7 +4700,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) } if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ -#ifdef CONFIG_SSB_DRIVER_PCICORE +#if defined(CONFIG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE) if (dev->dev->bus_type == B43_BUS_SSB && dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && dev->dev->sdev->bus->pcicore.dev->id.revision <= 10) @@ -5129,10 +5091,82 @@ static void b43_wireless_core_detach(struct b43_wldev *dev) b43_phy_free(dev); } +static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy, + bool *have_5ghz_phy) +{ + u16 dev_id = 0; + +#ifdef CONFIG_B43_BCMA + if (dev->dev->bus_type == B43_BUS_BCMA && + dev->dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI) + dev_id = dev->dev->bdev->bus->host_pci->device; +#endif +#ifdef CONFIG_B43_SSB + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) + dev_id = dev->dev->sdev->bus->host_pci->device; +#endif + /* Override with SPROM value if available */ + if (dev->dev->bus_sprom->dev_id) + dev_id = dev->dev->bus_sprom->dev_id; + + /* Note: below IDs can be "virtual" (not maching e.g. real PCI ID) */ + switch (dev_id) { + case 0x4324: /* BCM4306 */ + case 0x4312: /* BCM4311 */ + case 0x4319: /* BCM4318 */ + case 0x4328: /* BCM4321 */ + case 0x432b: /* BCM4322 */ + case 0x4350: /* BCM43222 */ + case 0x4353: /* BCM43224 */ + case 0x0576: /* BCM43224 */ + case 0x435f: /* BCM6362 */ + case 0x4331: /* BCM4331 */ + case 0x4359: /* BCM43228 */ + case 0x43a0: /* BCM4360 */ + case 0x43b1: /* BCM4352 */ + /* Dual band devices */ + *have_2ghz_phy = true; + *have_5ghz_phy = true; + return; + case 0x4321: /* BCM4306 */ + case 0x4313: /* BCM4311 */ + case 0x431a: /* BCM4318 */ + case 0x432a: /* BCM4321 */ + case 0x432d: /* BCM4322 */ + case 0x4352: /* BCM43222 */ + case 0x4333: /* BCM4331 */ + case 0x43a2: /* BCM4360 */ + case 0x43b3: /* BCM4352 */ + /* 5 GHz only devices */ + *have_2ghz_phy = false; + *have_5ghz_phy = true; + return; + } + + /* As a fallback, try to guess using PHY type */ + switch (dev->phy.type) { + case B43_PHYTYPE_A: + *have_2ghz_phy = false; + *have_5ghz_phy = true; + return; + case B43_PHYTYPE_G: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + case B43_PHYTYPE_LCN: + *have_2ghz_phy = true; + *have_5ghz_phy = false; + return; + } + + B43_WARN_ON(1); +} + static int b43_wireless_core_attach(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; - struct pci_dev *pdev = NULL; + struct b43_phy *phy = &dev->phy; int err; u32 tmp; bool have_2ghz_phy = false, have_5ghz_phy = false; @@ -5144,19 +5178,15 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) * that in core_init(), too. */ -#ifdef CONFIG_B43_SSB - if (dev->dev->bus_type == B43_BUS_SSB && - dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) - pdev = dev->dev->sdev->bus->host_pci; -#endif - err = b43_bus_powerup(dev, 0); if (err) { b43err(wl, "Bus powerup failed\n"); goto out; } - /* Get the PHY type. */ + phy->do_full_init = true; + + /* Try to guess supported bands for the first init needs */ switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: @@ -5180,48 +5210,29 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) dev->phy.gmode = have_2ghz_phy; b43_wireless_core_reset(dev, dev->phy.gmode); + /* Get the PHY type. */ err = b43_phy_versioning(dev); if (err) goto err_powerdown; - /* Check if this device supports multiband. */ - if (!pdev || - (pdev->device != 0x4312 && - pdev->device != 0x4319 && pdev->device != 0x4324)) { - /* No multiband support. */ - have_2ghz_phy = false; + + /* Get real info about supported bands */ + b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy); + + /* We don't support 5 GHz on some PHYs yet */ + switch (dev->phy.type) { + case B43_PHYTYPE_A: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + b43warn(wl, "5 GHz band is unsupported on this PHY\n"); have_5ghz_phy = false; - switch (dev->phy.type) { - case B43_PHYTYPE_A: - have_5ghz_phy = true; - break; - case B43_PHYTYPE_LP: //FIXME not always! -#if 0 //FIXME enabling 5GHz causes a NULL pointer dereference - have_5ghz_phy = 1; -#endif - case B43_PHYTYPE_G: - case B43_PHYTYPE_N: - case B43_PHYTYPE_HT: - case B43_PHYTYPE_LCN: - have_2ghz_phy = true; - break; - default: - B43_WARN_ON(1); - } } - if (dev->phy.type == B43_PHYTYPE_A) { - /* FIXME */ - b43err(wl, "IEEE 802.11a devices are unsupported\n"); + + if (!have_2ghz_phy && !have_5ghz_phy) { + b43err(wl, "b43 can't support any band on this device\n"); err = -EOPNOTSUPP; goto err_powerdown; } - if (1 /* disable A-PHY */) { - /* FIXME: For now we disable the A-PHY on multi-PHY devices. */ - if (dev->phy.type != B43_PHYTYPE_N && - dev->phy.type != B43_PHYTYPE_LP) { - have_2ghz_phy = true; - have_5ghz_phy = false; - } - } err = b43_phy_allocate(dev); if (err) @@ -5310,6 +5321,7 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \ (pdev->subsystem_device == _subdevice) ) +#ifdef CONFIG_B43_SSB static void b43_sprom_fixup(struct ssb_bus *bus) { struct pci_dev *pdev; @@ -5341,6 +5353,7 @@ static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl) ssb_set_devtypedata(dev->sdev, NULL); ieee80211_free_hw(hw); } +#endif static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) { diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 3e45989..08244b3 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -96,12 +96,16 @@ int b43_phy_init(struct b43_wldev *dev) phy->channel = ops->get_default_chan(dev); + phy->ops->switch_analog(dev, true); b43_software_rfkill(dev, false); + err = ops->init(dev); if (err) { b43err(dev->wl, "PHY init failed\n"); goto err_block_rf; } + phy->do_full_init = false; + /* Make sure to switch hardware and firmware (SHM) to * the default channel. */ err = b43_switch_channel(dev, ops->get_default_chan(dev)); @@ -113,6 +117,7 @@ int b43_phy_init(struct b43_wldev *dev) return 0; err_phy_exit: + phy->do_full_init = true; if (ops->exit) ops->exit(dev); err_block_rf: @@ -126,6 +131,7 @@ void b43_phy_exit(struct b43_wldev *dev) const struct b43_phy_operations *ops = dev->phy.ops; b43_software_rfkill(dev, true); + dev->phy.do_full_init = true; if (ops->exit) ops->exit(dev); } @@ -312,6 +318,90 @@ void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) } } +void b43_phy_put_into_reset(struct b43_wldev *dev) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_GMODE; + tmp |= B43_BCMA_IOCTL_PHY_RESET; + tmp |= BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~B43_TMSLOW_GMODE; + tmp |= B43_TMSLOW_PHYRESET; + tmp |= SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + usleep_range(1000, 2000); + + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + usleep_range(1000, 2000); + + break; +#endif + } +} + +void b43_phy_take_out_of_reset(struct b43_wldev *dev) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + /* Unset reset bit (with forcing clock) */ + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_PHY_RESET; + tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; + tmp |= BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + + /* Do not force clock anymore */ + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~BCMA_IOCTL_FGC; + tmp |= B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + /* Unset reset bit (with forcing clock) */ + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~B43_TMSLOW_PHYRESET; + tmp &= ~B43_TMSLOW_PHYCLKEN; + tmp |= SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ + usleep_range(1000, 2000); + + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~SSB_TMSLOW_FGC; + tmp |= B43_TMSLOW_PHYCLKEN; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ + usleep_range(1000, 2000); + break; +#endif + } +} + int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct b43_phy *phy = &(dev->phy); diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index f1b9993..4ad6240 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -231,9 +231,12 @@ struct b43_phy { /* HT info */ bool is_40mhz; - /* GMODE bit enabled? */ + /* Is GMODE (2 GHz mode) bit enabled? */ bool gmode; + /* After power reset full init has to be performed */ + bool do_full_init; + /* Analog Type */ u8 analog; /* B43_PHYTYPE_ */ @@ -390,6 +393,9 @@ void b43_phy_lock(struct b43_wldev *dev); */ void b43_phy_unlock(struct b43_wldev *dev); +void b43_phy_put_into_reset(struct b43_wldev *dev); +void b43_phy_take_out_of_reset(struct b43_wldev *dev); + /** * b43_switch_channel - Switch to another channel */ diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 41dab89..86569f6 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -700,13 +700,11 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev) b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78); b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80); - if (dev->phy.n->init_por) { + if (dev->phy.do_full_init) { b43_radio_2057_rcal(dev); b43_radio_2057_rccal(dev); } b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8); - - dev->phy.n->init_por = false; } /* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */ @@ -1028,7 +1026,7 @@ static void b43_radio_init2056_post(struct b43_wldev *dev) b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2); b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC); b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1); - if (dev->phy.n->init_por) + if (dev->phy.do_full_init) b43_radio_2056_rcal(dev); } @@ -1041,8 +1039,6 @@ static void b43_radio_init2056(struct b43_wldev *dev) b43_radio_init2056_pre(dev); b2056_upload_inittabs(dev, 0, 0); b43_radio_init2056_post(dev); - - dev->phy.n->init_por = false; } /************************************************** @@ -5561,7 +5557,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4); nphy->spur_avoid = (phy->rev >= 3) ? B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE; - nphy->init_por = true; nphy->gain_boost = true; /* this way we follow wl, assume it is true */ nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */ nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */ @@ -5602,8 +5597,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2; nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2; } - - nphy->init_por = true; } static void b43_nphy_op_free(struct b43_wldev *dev) @@ -5714,10 +5707,12 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, } } else { if (dev->phy.rev >= 7) { - b43_radio_2057_init(dev); + if (!dev->phy.radio_on) + b43_radio_2057_init(dev); b43_switch_channel(dev, dev->phy.channel); } else if (dev->phy.rev >= 3) { - b43_radio_init2056(dev); + if (!dev->phy.radio_on) + b43_radio_init2056(dev); b43_switch_channel(dev, dev->phy.channel); } else { b43_radio_init2055(dev); diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 9a5b6bc..ecfbf66 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -931,7 +931,6 @@ struct b43_phy_n { u16 papd_epsilon_offset[2]; s32 preamble_override; u32 bb_mult_save; - bool init_por; bool gain_boost; bool elna_gain_config; diff --git a/drivers/net/wireless/b43/radio_2056.c b/drivers/net/wireless/b43/radio_2056.c index a07e4ca..2ce2560 100644 --- a/drivers/net/wireless/b43/radio_2056.c +++ b/drivers/net/wireless/b43/radio_2056.c @@ -9058,6 +9058,231 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev11[] = { { + .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { + .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { + .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { + .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { + .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { + .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { + .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { + .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { + .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { + .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { + .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { + .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { + .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { + .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { + .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { + .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { + .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { + .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { + .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { + .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { + .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { + .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { + .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { + .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { + .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, @@ -9067,6 +9292,15 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), }, { + .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, @@ -9076,6 +9310,15 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), }, { + .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, @@ -9085,6 +9328,492 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), }, { + .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { + .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { + .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { + .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { + .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { + .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { + .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { + .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { + .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { + .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { + .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { + .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { + .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { + .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { + .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { + .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { + .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { + .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { + .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { + .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { + .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { + .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { + .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { + .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { + .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { + .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { + .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { + .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { + .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { + .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { + .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { + .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { + .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { + .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { + .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { + .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { + .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { + .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { + .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { + .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { + .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { + .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { + .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { + .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { + .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { + .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { + .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { + .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { + .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { + .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { + .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { + .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { + .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { + .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, @@ -9094,6 +9823,33 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), }, { + .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { + .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { + .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, @@ -9103,6 +9859,33 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), }, { + .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { + .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { + .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, @@ -9112,6 +9895,33 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), }, { + .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { + .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, @@ -9121,6 +9931,33 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), }, { + .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { + .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { + .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, @@ -9130,6 +9967,87 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), }, { + .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { + .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { + .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { + .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { + .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { + .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { + .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { + .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { + .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, 0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 50d03ff..4047c05 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -3042,30 +3042,32 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) antswlut = sprom->fem.ghz2.antswlut; /* Static tables */ - ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); - ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); - ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); - ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); - ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); - ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3); - ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); - ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); - ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); - ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); - ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); - ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); - ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); - ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); - ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); - ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); - ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); - ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); - ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); - ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); - ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); - ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); + ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3); + ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); + ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); + ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); + ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); + ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); + ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); + ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); + ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); + } /* Volatile tables */ if (antswlut < ARRAY_SIZE(b43_ntab_antswctl_r3)) @@ -3078,20 +3080,22 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) static void b43_nphy_tables_init_rev0(struct b43_wldev *dev) { /* Static tables */ - ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); - ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); - ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); - ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); - ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); - ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); - ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); - ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); - ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); - ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); - ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); - ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); - ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); - ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); + ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); + ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); + ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); + ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); + ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); + ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); + ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); + ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); + ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); + ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); + ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); + ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); + ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + } /* Volatile tables */ ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 31adb8c..4f38f19 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -408,7 +408,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, mac_ctl |= B43_TXH_MAC_HWSEQ; if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) mac_ctl |= B43_TXH_MAC_STMSDU; - if (phy->type == B43_PHYTYPE_A) + if (!phy->gmode) mac_ctl |= B43_TXH_MAC_5GHZ; /* Overwrite rates[0].count to make the retry calculation diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 1d2ceac..98e67c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -33,7 +33,7 @@ brcmfmac-objs += \ bcdc.o \ dhd_common.o \ dhd_linux.o \ - nvram.o \ + firmware.o \ btcoex.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 939d6b1..16f9ab2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -186,7 +186,7 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); u32 brcmf_get_chip_info(struct brcmf_if *ifp); -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success); /* Sets dongle media info (drv_version, mac address). */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index c453561..7735328 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -63,7 +63,6 @@ struct brcmf_bus_dcmd { */ struct brcmf_bus_ops { int (*preinit)(struct device *dev); - int (*init)(struct device *dev); void (*stop)(struct device *dev); int (*txdata)(struct device *dev, struct sk_buff *skb); int (*txctl)(struct device *dev, unsigned char *msg, uint len); @@ -99,6 +98,7 @@ struct brcmf_bus { unsigned long tx_realloc; u32 chip; u32 chiprev; + bool always_use_fws_queue; struct brcmf_bus_ops *ops; }; @@ -113,11 +113,6 @@ static inline int brcmf_bus_preinit(struct brcmf_bus *bus) return bus->ops->preinit(bus->dev); } -static inline int brcmf_bus_init(struct brcmf_bus *bus) -{ - return bus->ops->init(bus->dev); -} - static inline void brcmf_bus_stop(struct brcmf_bus *bus) { bus->ops->stop(bus->dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 6a8983a..ed3e32c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -32,6 +32,9 @@ #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 #define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" +/* boost value for RSSI_DELTA in preferred join selection */ +#define BRCMF_JOIN_PREF_RSSI_BOOST 8 + bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec) @@ -246,6 +249,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; u8 buf[BRCMF_DCMD_SMLEN]; + struct brcmf_join_pref_params join_pref_params[2]; char *ptr; s32 err; @@ -298,6 +302,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ + join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; + join_pref_params[0].len = 2; + join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; + join_pref_params[0].band = WLC_BAND_5G; + join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; + join_pref_params[1].len = 2; + join_pref_params[1].rssi_gain = 0; + join_pref_params[1].band = 0; + err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, + sizeof(join_pref_params)); + if (err) + brcmf_err("Set join_pref error (%d)\n", err); + /* Setup event_msgs, enable E_IF */ err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 7d28cd3..09dd8c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -190,7 +190,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - struct ethhdr *eh; + struct ethhdr *eh = (struct ethhdr *)(skb->data); brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); @@ -236,6 +236,9 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } + if (eh->h_proto == htons(ETH_P_PAE)) + atomic_inc(&ifp->pend_8021x_cnt); + ret = brcmf_fws_process_skb(ifp, skb); done: @@ -538,31 +541,26 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) brcmf_netif_rx(ifp, skb); } -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success) { struct brcmf_if *ifp; struct ethhdr *eh; - u8 ifidx; u16 type; - int res; - - res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp); ifp = drvr->iflist[ifidx]; if (!ifp) goto done; - if (res == 0) { - eh = (struct ethhdr *)(txp->data); - type = ntohs(eh->h_proto); + eh = (struct ethhdr *)(txp->data); + type = ntohs(eh->h_proto); - if (type == ETH_P_PAE) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } + if (type == ETH_P_PAE) { + atomic_dec(&ifp->pend_8021x_cnt); + if (waitqueue_active(&ifp->pend_8021x_wait)) + wake_up(&ifp->pend_8021x_wait); } + if (!success) ifp->stats.tx_errors++; done: @@ -573,13 +571,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; + u8 ifidx; /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(drvr->fws)) { if (!success) brcmf_fws_bustxfail(drvr->fws, txp); } else { - brcmf_txfinalize(drvr, txp, success); + if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp)) + brcmu_pkt_buf_free_skb(txp); + else + brcmf_txfinalize(drvr, txp, ifidx, success); } } @@ -914,13 +916,6 @@ int brcmf_bus_start(struct device *dev) brcmf_dbg(TRACE, "\n"); - /* Bring up the bus */ - ret = brcmf_bus_init(bus_if); - if (ret != 0) { - brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret); - return ret; - } - /* add primary networking interface */ ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); if (IS_ERR(ifp)) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 13c89a0..8fa0dbb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -42,7 +42,7 @@ #include <soc.h> #include "sdio_host.h" #include "chip.h" -#include "nvram.h" +#include "firmware.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ @@ -632,43 +632,28 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; - -static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus, - enum brcmf_firmware_type type) +static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, + enum brcmf_firmware_type type) { - const struct firmware *fw; - const char *name; - int err, i; + int i; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { - if (brcmf_fwname_data[i].chipid == bus->ci->chip && - brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) { + if (brcmf_fwname_data[i].chipid == ci->chip && + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { switch (type) { case BRCMF_FIRMWARE_BIN: - name = brcmf_fwname_data[i].bin; - break; + return brcmf_fwname_data[i].bin; case BRCMF_FIRMWARE_NVRAM: - name = brcmf_fwname_data[i].nv; - break; + return brcmf_fwname_data[i].nv; default: brcmf_err("invalid firmware type (%d)\n", type); return NULL; } - goto found; } } brcmf_err("Unknown chipid %d [%d]\n", - bus->ci->chip, bus->ci->chiprev); + ci->chip, ci->chiprev); return NULL; - -found: - err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev); - if ((err) || (!fw)) { - brcmf_err("fail to request firmware %s (%d)\n", name, err); - return NULL; - } - - return fw; } static void pkt_align(struct sk_buff *p, int len, int align) @@ -3278,20 +3263,13 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, } static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, - const struct firmware *nv) + void *vars, u32 varsz) { - void *vars; - u32 varsz; int address; int err; brcmf_dbg(TRACE, "Enter\n"); - vars = brcmf_nvram_strip(nv, &varsz); - - if (vars == NULL) - return -EINVAL; - address = bus->ci->ramsize - varsz + bus->ci->rambase; err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); if (err) @@ -3300,15 +3278,14 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) err = -EIO; - brcmf_nvram_free(vars); - return err; } -static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, + const struct firmware *fw, + void *nvram, u32 nvlen) { int bcmerror = -EFAULT; - const struct firmware *fw; u32 rstvec; sdio_claim_host(bus->sdiodev->func[1]); @@ -3317,12 +3294,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) /* Keep arm in reset */ brcmf_chip_enter_download(bus->ci); - fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); - if (fw == NULL) { - bcmerror = -ENOENT; - goto err; - } - rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); @@ -3330,17 +3301,12 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) release_firmware(fw); if (bcmerror) { brcmf_err("dongle image file download failed\n"); + brcmf_fw_nvram_free(nvram); goto err; } - fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); - if (fw == NULL) { - bcmerror = -ENOENT; - goto err; - } - - bcmerror = brcmf_sdio_download_nvram(bus, fw); - release_firmware(fw); + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); + brcmf_fw_nvram_free(nvram); if (bcmerror) { brcmf_err("dongle nvram file download failed\n"); goto err; @@ -3490,97 +3456,6 @@ done: return err; } -static int brcmf_sdio_bus_init(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - int err, ret = 0; - u8 saveclk; - - brcmf_dbg(TRACE, "Enter\n"); - - /* try to download image and nvram to the dongle */ - if (bus_if->state == BRCMF_BUS_DOWN) { - bus->alp_only = true; - err = brcmf_sdio_download_firmware(bus); - if (err) - return err; - bus->alp_only = false; - } - - if (!bus->sdiodev->bus_if->drvr) - return 0; - - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - - sdio_claim_host(bus->sdiodev->func[1]); - - /* Make sure backplane clock is on, needed to generate F2 interrupt */ - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - if (bus->clkstate != CLK_AVAIL) - goto exit; - - /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (!err) { - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); - } - if (err) { - brcmf_err("Failed to force clock for F2: err %d\n", err); - goto exit; - } - - /* Enable function 2 (frame transfers) */ - w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, - offsetof(struct sdpcmd_regs, tosbmailboxdata)); - err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]); - - - brcmf_dbg(INFO, "enable F2: err=%d\n", err); - - /* If F2 successfully enabled, set core and enable interrupts */ - if (!err) { - /* Set up the interrupt mask and enable interrupts */ - bus->hostintmask = HOSTINTMASK; - w_sdreg32(bus, bus->hostintmask, - offsetof(struct sdpcmd_regs, hostintmask)); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); - } else { - /* Disable F2 again */ - sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); - ret = -ENODEV; - } - - if (brcmf_chip_sr_capable(bus->ci)) { - brcmf_sdio_sr_init(bus); - } else { - /* Restore previous clock setting */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - saveclk, &err); - } - - if (ret == 0) { - ret = brcmf_sdiod_intr_register(bus->sdiodev); - if (ret != 0) - brcmf_err("intr register failed:%d\n", ret); - } - - /* If we didn't come up, turn off backplane clock */ - if (ret != 0) - brcmf_sdio_clkctl(bus, CLK_NONE, false); - -exit: - sdio_release_host(bus->sdiodev->func[1]); - - return ret; -} - void brcmf_sdio_isr(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -4020,13 +3895,114 @@ brcmf_sdio_watchdog(unsigned long data) static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .stop = brcmf_sdio_bus_stop, .preinit = brcmf_sdio_bus_preinit, - .init = brcmf_sdio_bus_init, .txdata = brcmf_sdio_bus_txdata, .txctl = brcmf_sdio_bus_txctl, .rxctl = brcmf_sdio_bus_rxctl, .gettxq = brcmf_sdio_bus_gettxq, }; +static void brcmf_sdio_firmware_callback(struct device *dev, + const struct firmware *code, + void *nvram, u32 nvram_len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + int err = 0; + u8 saveclk; + + brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); + + /* try to download image and nvram to the dongle */ + if (bus_if->state == BRCMF_BUS_DOWN) { + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; + } + + if (!bus_if->drvr) + return; + + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + + sdio_claim_host(sdiodev->func[1]); + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + if (bus->clkstate != CLK_AVAIL) + goto release; + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + brcmf_err("Failed to force clock for F2: err %d\n", err); + goto release; + } + + /* Enable function 2 (frame transfers) */ + w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, + offsetof(struct sdpcmd_regs, tosbmailboxdata)); + err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); + + + brcmf_dbg(INFO, "enable F2: err=%d\n", err); + + /* If F2 successfully enabled, set core and enable interrupts */ + if (!err) { + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + w_sdreg32(bus, bus->hostintmask, + offsetof(struct sdpcmd_regs, hostintmask)); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); + } else { + /* Disable F2 again */ + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + goto release; + } + + if (brcmf_chip_sr_capable(bus->ci)) { + brcmf_sdio_sr_init(bus); + } else { + /* Restore previous clock setting */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + saveclk, &err); + } + + if (err == 0) { + err = brcmf_sdiod_intr_register(sdiodev); + if (err != 0) + brcmf_err("intr register failed:%d\n", err); + } + + /* If we didn't come up, turn off backplane clock */ + if (err != 0) + brcmf_sdio_clkctl(bus, CLK_NONE, false); + + sdio_release_host(sdiodev->func[1]); + + err = brcmf_bus_start(dev); + if (err != 0) { + brcmf_err("dongle is not responding\n"); + goto fail; + } + return; + +release: + sdio_release_host(sdiodev->func[1]); +fail: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); + device_release_driver(dev); +} + struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { int ret; @@ -4110,8 +4086,13 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) goto fail; } + /* Query the F2 block size, set roundup accordingly */ + bus->blocksize = bus->sdiodev->func[2]->cur_blksize; + bus->roundup = min(max_roundup, bus->blocksize); + /* Allocate buffers */ if (bus->sdiodev->bus_if->maxctl) { + bus->sdiodev->bus_if->maxctl += bus->roundup; bus->rxblen = roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), ALIGNMENT) + bus->head_align; @@ -4139,10 +4120,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->idletime = BRCMF_IDLE_INTERVAL; bus->idleclock = BRCMF_IDLE_ACTIVE; - /* Query the F2 block size, set roundup accordingly */ - bus->blocksize = bus->sdiodev->func[2]->cur_blksize; - bus->roundup = min(max_roundup, bus->blocksize); - /* SR state */ bus->sleeping = false; bus->sr_enabled = false; @@ -4150,10 +4127,14 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); - /* if firmware path present try to download and bring up bus */ - ret = brcmf_bus_start(bus->sdiodev->dev); + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, + brcmf_sdio_get_fwname(bus->ci, + BRCMF_FIRMWARE_BIN), + brcmf_sdio_get_fwname(bus->ci, + BRCMF_FIRMWARE_NVRAM), + brcmf_sdio_firmware_callback); if (ret != 0) { - brcmf_err("dongle is not responding\n"); + brcmf_err("async firmware request failed: %d\n", ret); goto fail; } @@ -4173,9 +4154,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) /* De-register interrupt handler */ brcmf_sdiod_intr_unregister(bus->sdiodev); - if (bus->sdiodev->bus_if->drvr) { - brcmf_detach(bus->sdiodev->dev); - } + brcmf_detach(bus->sdiodev->dev); cancel_work_sync(&bus->datawork); if (bus->brcmf_wq) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c new file mode 100644 index 0000000..7b7d237 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/firmware.h> + +#include "dhd_dbg.h" +#include "firmware.h" + +enum nvram_parser_state { + IDLE, + KEY, + VALUE, + COMMENT, + END +}; + +/** + * struct nvram_parser - internal info for parser. + * + * @state: current parser state. + * @fwnv: input buffer being parsed. + * @nvram: output buffer with parse result. + * @nvram_len: lenght of parse result. + * @line: current line. + * @column: current column in line. + * @pos: byte offset in input buffer. + * @entry: start position of key,value entry. + */ +struct nvram_parser { + enum nvram_parser_state state; + const struct firmware *fwnv; + u8 *nvram; + u32 nvram_len; + u32 line; + u32 column; + u32 pos; + u32 entry; +}; + +static bool is_nvram_char(char c) +{ + /* comment marker excluded */ + if (c == '#') + return false; + + /* key and value may have any other readable character */ + return (c > 0x20 && c < 0x7f); +} + +static bool is_whitespace(char c) +{ + return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); +} + +static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) +{ + char c; + + c = nvp->fwnv->data[nvp->pos]; + if (c == '\n') + return COMMENT; + if (is_whitespace(c)) + goto proceed; + if (c == '#') + return COMMENT; + if (is_nvram_char(c)) { + nvp->entry = nvp->pos; + return KEY; + } + brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n", + nvp->line, nvp->column); +proceed: + nvp->column++; + nvp->pos++; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) +{ + enum nvram_parser_state st = nvp->state; + char c; + + c = nvp->fwnv->data[nvp->pos]; + if (c == '=') { + st = VALUE; + } else if (!is_nvram_char(c)) { + brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", + nvp->line, nvp->column); + return COMMENT; + } + + nvp->column++; + nvp->pos++; + return st; +} + +static enum nvram_parser_state +brcmf_nvram_handle_value(struct nvram_parser *nvp) +{ + char c; + char *skv; + char *ekv; + u32 cplen; + + c = nvp->fwnv->data[nvp->pos]; + if (!is_nvram_char(c)) { + /* key,value pair complete */ + ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; + skv = (u8 *)&nvp->fwnv->data[nvp->entry]; + cplen = ekv - skv; + /* copy to output buffer */ + memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); + nvp->nvram_len += cplen; + nvp->nvram[nvp->nvram_len] = '\0'; + nvp->nvram_len++; + return IDLE; + } + nvp->pos++; + nvp->column++; + return VALUE; +} + +static enum nvram_parser_state +brcmf_nvram_handle_comment(struct nvram_parser *nvp) +{ + char *eol, *sol; + + sol = (char *)&nvp->fwnv->data[nvp->pos]; + eol = strchr(sol, '\n'); + if (eol == NULL) + return END; + + /* eat all moving to next line */ + nvp->line++; + nvp->column = 1; + nvp->pos += (eol - sol) + 1; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) +{ + /* final state */ + return END; +} + +static enum nvram_parser_state +(*nv_parser_states[])(struct nvram_parser *nvp) = { + brcmf_nvram_handle_idle, + brcmf_nvram_handle_key, + brcmf_nvram_handle_value, + brcmf_nvram_handle_comment, + brcmf_nvram_handle_end +}; + +static int brcmf_init_nvram_parser(struct nvram_parser *nvp, + const struct firmware *nv) +{ + memset(nvp, 0, sizeof(*nvp)); + nvp->fwnv = nv; + /* Alloc for extra 0 byte + roundup by 4 + length field */ + nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvp->nvram) + return -ENOMEM; + + nvp->line = 1; + nvp->column = 1; + return 0; +} + +/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil + * and ending in a NUL. Removes carriage returns, empty lines, comment lines, + * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. + * End of buffer is completed with token identifying length of buffer. + */ +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) +{ + struct nvram_parser nvp; + u32 pad; + u32 token; + __le32 token_le; + + if (brcmf_init_nvram_parser(&nvp, nv) < 0) + return NULL; + + while (nvp.pos < nv->size) { + nvp.state = nv_parser_states[nvp.state](&nvp); + if (nvp.state == END) + break; + } + pad = nvp.nvram_len; + *new_length = roundup(nvp.nvram_len + 1, 4); + while (pad != *new_length) { + nvp.nvram[pad] = 0; + pad++; + } + + token = *new_length / 4; + token = (~token << 16) | (token & 0x0000FFFF); + token_le = cpu_to_le32(token); + + memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); + *new_length += sizeof(token_le); + + return nvp.nvram; +} + +void brcmf_fw_nvram_free(void *nvram) +{ + kfree(nvram); +} + +struct brcmf_fw { + struct device *dev; + u16 flags; + const struct firmware *code; + const char *nvram_name; + void (*done)(struct device *dev, const struct firmware *fw, + void *nvram_image, u32 nvram_len); +}; + +static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + u32 nvram_length = 0; + void *nvram = NULL; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + + if (fw) { + nvram = brcmf_fw_nvram_strip(fw, &nvram_length); + release_firmware(fw); + if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + } + + fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); + kfree(fwctx); + return; + +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + if (fwctx->code) + release_firmware(fwctx->code); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + int ret; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw) + goto fail; + + /* only requested code so done here */ + if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + fwctx->code = fw; + ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, + fwctx->dev, GFP_KERNEL, fwctx, + brcmf_fw_request_nvram_done); + + if (!ret) + return; + + /* when nvram is optional call .done() callback here */ + if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + + /* failed nvram request */ + release_firmware(fw); +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + struct brcmf_fw *fwctx; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); + if (!fw_cb || !code) + return -EINVAL; + + if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) + return -EINVAL; + + fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); + if (!fwctx) + return -ENOMEM; + + fwctx->dev = dev; + fwctx->flags = flags; + fwctx->done = fw_cb; + if (flags & BRCMF_FW_REQUEST_NVRAM) + fwctx->nvram_name = nvram; + + return request_firmware_nowait(THIS_MODULE, true, code, dev, + GFP_KERNEL, fwctx, + brcmf_fw_request_code_done); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index d4545809..6431bfd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/nvram.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -13,12 +13,24 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef BRCMFMAC_NVRAM_H -#define BRCMFMAC_NVRAM_H +#ifndef BRCMFMAC_FIRMWARE_H +#define BRCMFMAC_FIRMWARE_H +#define BRCMF_FW_REQUEST 0x000F +#define BRCMF_FW_REQUEST_NVRAM 0x0001 +#define BRCMF_FW_REQ_FLAGS 0x00F0 +#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 -void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length); -void brcmf_nvram_free(void *nvram); - +void brcmf_fw_nvram_free(void *nvram); +/* + * Request firmware(s) asynchronously. When the asynchronous request + * fails it will not use the callback, but call device_release_driver() + * instead which will call the driver .remove() callback. + */ +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)); -#endif /* BRCMFMAC_NVRAM_H */ +#endif /* BRCMFMAC_FIRMWARE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 614e488..2bc68a2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -53,6 +53,14 @@ #define BRCMF_OBSS_COEX_OFF 0 #define BRCMF_OBSS_COEX_ON 1 +/* join preference types for join_pref iovar */ +enum brcmf_join_pref_types { + BRCMF_JOIN_PREF_RSSI = 1, + BRCMF_JOIN_PREF_WPA, + BRCMF_JOIN_PREF_BAND, + BRCMF_JOIN_PREF_RSSI_DELTA, +}; + enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_CLIENT, BRCMF_FIL_P2P_IF_GO, @@ -282,6 +290,22 @@ struct brcmf_assoc_params_le { __le16 chanspec_list[1]; }; +/** + * struct join_pref params - parameters for preferred join selection. + * + * @type: preference type (see enum brcmf_join_pref_types). + * @len: length of bytes following (currently always 2). + * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA). + * @band: band to which selection preference applies. + * This is used if @type is BAND or RSSI_DELTA. + */ +struct brcmf_join_pref_params { + u8 type; + u8 len; + u8 rssi_gain; + u8 band; +}; + /* used for join with or without a specific bssid and channel list */ struct brcmf_join_params { struct brcmf_ssid_le ssid_le; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index c3e7d76..699908d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -476,6 +476,7 @@ struct brcmf_fws_info { bool bus_flow_blocked; bool creditmap_received; u8 mode; + bool avoid_queueing; }; /* @@ -1369,13 +1370,12 @@ done: } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u32 genbit, - u16 seq) + struct sk_buff *skb, u8 ifidx, + u32 genbit, u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u32 hslot; int ret; - u8 ifidx; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); @@ -1389,29 +1389,21 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->generation = genbit; - ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (ret == 0) { - brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); - brcmf_skbcb(skb)->htod_seq = seq; - if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { - brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); - brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); - } else { - brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); - } - ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, - skb); + brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); + brcmf_skbcb(skb)->htod_seq = seq; + if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); + brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); + } else { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); } + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); if (ret != 0) { - /* suppress q is full or hdrpull failed, drop this packet */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, - true); + /* suppress q is full drop this packet */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); } else { - /* - * Mark suppressed to avoid a double free during - * wlfc cleanup - */ + /* Mark suppressed to avoid a double free during wlfc cleanup */ brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); } @@ -1428,6 +1420,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, struct sk_buff *skb; struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; + u8 ifidx; brcmf_dbg(DATA, "flags %d\n", flags); @@ -1476,12 +1469,15 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } brcmf_fws_macdesc_return_req_credit(skb); + if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) { + brcmu_pkt_buf_free_skb(skb); + return -EINVAL; + } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit, - seq); - + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx, + genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, true); + brcmf_txfinalize(fws->drvr, skb, ifidx, true); return 0; } @@ -1868,7 +1864,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) struct ethhdr *eh = (struct ethhdr *)(skb->data); int fifo = BRCMF_FWS_FIFO_BCMC; bool multicast = is_multicast_ether_addr(eh->h_dest); - bool pae = eh->h_proto == htons(ETH_P_PAE); + int rc = 0; brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); /* determine the priority */ @@ -1876,8 +1872,13 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) skb->priority = cfg80211_classify8021d(skb, NULL); drvr->tx_multicast += !!multicast; - if (pae) - atomic_inc(&ifp->pend_8021x_cnt); + + if (fws->avoid_queueing) { + rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); + if (rc < 0) + brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + return rc; + } /* set control buffer information */ skcb->if_flags = 0; @@ -1899,15 +1900,12 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_schedule_deq(fws); } else { brcmf_err("drop skb: no hanger slot\n"); - if (pae) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } - brcmu_pkt_buf_free_skb(skb); + brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + rc = -ENOMEM; } brcmf_fws_unlock(fws); - return 0; + + return rc; } void brcmf_fws_reset_interface(struct brcmf_if *ifp) @@ -1982,7 +1980,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) - brcmf_txfinalize(drvr, skb, false); + brcmf_txfinalize(drvr, skb, ifidx, + false); if (fws->bus_flow_blocked) break; } @@ -2039,6 +2038,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr) fws->drvr = drvr; fws->fcmode = fcmode; + if ((drvr->bus_if->always_use_fws_queue == false) && + (fcmode == BRCMF_FWS_FCMODE_NONE)) { + fws->avoid_queueing = true; + brcmf_dbg(INFO, "FWS queueing will be avoided\n"); + return 0; + } + fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); if (fws->fws_wq == NULL) { brcmf_err("workqueue creation failed\n"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c deleted file mode 100644 index d5ef86d..0000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2013 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/firmware.h> - -#include "nvram.h" - -/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a file - * and ending in a NUL. Removes carriage returns, empty lines, comment lines, - * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. - * End of buffer is completed with token identifying length of buffer. - */ -void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length) -{ - u8 *nvram; - u32 i; - u32 len; - u32 column; - u8 val; - bool comment; - u32 token; - __le32 token_le; - - /* Alloc for extra 0 byte + roundup by 4 + length field */ - nvram = kmalloc(nv->size + 1 + 3 + sizeof(token_le), GFP_KERNEL); - if (!nvram) - return NULL; - - len = 0; - column = 0; - comment = false; - for (i = 0; i < nv->size; i++) { - val = nv->data[i]; - if (val == 0) - break; - if (val == '\r') - continue; - if (comment && (val != '\n')) - continue; - comment = false; - if (val == '#') { - comment = true; - continue; - } - if (val == '\n') { - if (column == 0) - continue; - nvram[len] = 0; - len++; - column = 0; - continue; - } - nvram[len] = val; - len++; - column++; - } - column = len; - *new_length = roundup(len + 1, 4); - while (column != *new_length) { - nvram[column] = 0; - column++; - } - - token = *new_length / 4; - token = (~token << 16) | (token & 0x0000FFFF); - token_le = cpu_to_le32(token); - - memcpy(&nvram[*new_length], &token_le, sizeof(token_le)); - *new_length += sizeof(token_le); - - return nvram; -} - -void brcmf_nvram_free(void *nvram) -{ - kfree(nvram); -} - - diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 24f65cd..6db51a6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -25,6 +25,7 @@ #include <dhd_bus.h> #include <dhd_dbg.h> +#include "firmware.h" #include "usb_rdl.h" #include "usb.h" @@ -61,12 +62,6 @@ struct brcmf_usb_image { u8 *image; int image_len; }; -static struct list_head fw_image_list; - -struct intr_transfer_buf { - u32 notification; - u32 reserved; -}; struct brcmf_usbdev_info { struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ @@ -75,7 +70,7 @@ struct brcmf_usbdev_info { struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; - uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; + uint rx_pipe, tx_pipe, rx_pipe2; int rx_low_watermark; int tx_low_watermark; @@ -87,7 +82,7 @@ struct brcmf_usbdev_info { struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; - u8 *image; /* buffer for combine fw and nvram */ + const u8 *image; /* buffer for combine fw and nvram */ int image_len; struct usb_device *usbdev; @@ -104,10 +99,6 @@ struct brcmf_usbdev_info { ulong ctl_op; struct urb *bulk_urb; /* used for FW download */ - struct urb *intr_urb; /* URB for interrupt endpoint */ - int intr_size; /* Size of interrupt message */ - int interval; /* Interrupt polling interval */ - struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */ }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, @@ -531,39 +522,6 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) } } -static void -brcmf_usb_intr_complete(struct urb *urb) -{ - struct brcmf_usbdev_info *devinfo = - (struct brcmf_usbdev_info *)urb->context; - int err; - - brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); - - if (devinfo == NULL) - return; - - if (unlikely(urb->status)) { - if (urb->status == -ENOENT || - urb->status == -ESHUTDOWN || - urb->status == -ENODEV) { - brcmf_usb_state_change(devinfo, - BRCMFMAC_USB_STATE_DOWN); - } - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) { - brcmf_err("intr cb when DBUS down, ignoring\n"); - return; - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { - err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); - if (err) - brcmf_err("usb_submit_urb, err=%d\n", err); - } -} - static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); @@ -619,7 +577,6 @@ static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); u16 ifnum; - int ret; brcmf_dbg(USB, "Enter\n"); if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) @@ -628,23 +585,6 @@ static int brcmf_usb_up(struct device *dev) /* Success, indicate devinfo is fully up */ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP); - if (devinfo->intr_urb) { - usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev, - devinfo->intr_pipe, - &devinfo->intr, - devinfo->intr_size, - (usb_complete_t)brcmf_usb_intr_complete, - devinfo, - devinfo->interval); - - ret = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); - if (ret) { - brcmf_err("USB_SUBMIT_URB failed with status %d\n", - ret); - return -EINVAL; - } - } - if (devinfo->ctl_urb) { devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); @@ -681,8 +621,6 @@ static void brcmf_usb_down(struct device *dev) return; brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); - if (devinfo->intr_urb) - usb_kill_urb(devinfo->intr_urb); if (devinfo->ctl_urb) usb_kill_urb(devinfo->ctl_urb); @@ -1021,7 +959,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) } err = brcmf_usb_dlstart(devinfo, - devinfo->image, devinfo->image_len); + (u8 *)devinfo->image, devinfo->image_len); if (err == 0) err = brcmf_usb_dlrun(devinfo); return err; @@ -1036,7 +974,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) brcmf_usb_free_q(&devinfo->rx_freeq, false); brcmf_usb_free_q(&devinfo->tx_freeq, false); - usb_free_urb(devinfo->intr_urb); usb_free_urb(devinfo->ctl_urb); usb_free_urb(devinfo->bulk_urb); @@ -1080,68 +1017,20 @@ static int check_file(const u8 *headers) return -1; } -static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) +static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) { - s8 *fwname; - const struct firmware *fw; - struct brcmf_usb_image *fw_image; - int err; - - brcmf_dbg(USB, "Enter\n"); switch (devinfo->bus_pub.devid) { case 43143: - fwname = BRCMF_USB_43143_FW_NAME; - break; + return BRCMF_USB_43143_FW_NAME; case 43235: case 43236: case 43238: - fwname = BRCMF_USB_43236_FW_NAME; - break; + return BRCMF_USB_43236_FW_NAME; case 43242: - fwname = BRCMF_USB_43242_FW_NAME; - break; + return BRCMF_USB_43242_FW_NAME; default: - return -EINVAL; - break; - } - brcmf_dbg(USB, "Loading FW %s\n", fwname); - list_for_each_entry(fw_image, &fw_image_list, list) { - if (fw_image->fwname == fwname) { - devinfo->image = fw_image->image; - devinfo->image_len = fw_image->image_len; - return 0; - } - } - /* fw image not yet loaded. Load it now and add to list */ - err = request_firmware(&fw, fwname, devinfo->dev); - if (!fw) { - brcmf_err("fail to request firmware %s\n", fwname); - return err; - } - if (check_file(fw->data) < 0) { - brcmf_err("invalid firmware %s\n", fwname); - return -EINVAL; + return NULL; } - - fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC); - if (!fw_image) - return -ENOMEM; - INIT_LIST_HEAD(&fw_image->list); - list_add_tail(&fw_image->list, &fw_image_list); - fw_image->fwname = fwname; - fw_image->image = vmalloc(fw->size); - if (!fw_image->image) - return -ENOMEM; - - memcpy(fw_image->image, fw->data, fw->size); - fw_image->image_len = fw->size; - - release_firmware(fw); - - devinfo->image = fw_image->image; - devinfo->image_len = fw_image->image_len; - - return 0; } @@ -1186,11 +1075,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, goto error; devinfo->tx_freecount = ntxq; - devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!devinfo->intr_urb) { - brcmf_err("usb_alloc_urb (intr) failed\n"); - goto error; - } devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->ctl_urb) { brcmf_err("usb_alloc_urb (ctl) failed\n"); @@ -1202,16 +1086,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, goto error; } - if (!brcmf_usb_dlneeded(devinfo)) - return &devinfo->bus_pub; - - brcmf_dbg(USB, "Start fw downloading\n"); - if (brcmf_usb_get_fw(devinfo)) - goto error; - - if (brcmf_usb_fw_download(devinfo)) - goto error; - return &devinfo->bus_pub; error: @@ -1222,18 +1096,77 @@ error: static struct brcmf_bus_ops brcmf_usb_bus_ops = { .txdata = brcmf_usb_tx, - .init = brcmf_usb_up, .stop = brcmf_usb_down, .txctl = brcmf_usb_tx_ctlpkt, .rxctl = brcmf_usb_rx_ctlpkt, }; +static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(devinfo->dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + return ret; + } + + ret = brcmf_usb_up(devinfo->dev); + if (ret) + goto fail; + + ret = brcmf_bus_start(devinfo->dev); + if (ret) + goto fail; + + return 0; +fail: + brcmf_detach(devinfo->dev); + return ret; +} + +static void brcmf_usb_probe_phase2(struct device *dev, + const struct firmware *fw, + void *nvram, u32 nvlen) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_usbdev_info *devinfo; + int ret; + + brcmf_dbg(USB, "Start fw downloading\n"); + ret = check_file(fw->data); + if (ret < 0) { + brcmf_err("invalid firmware\n"); + release_firmware(fw); + goto error; + } + + devinfo = bus->bus_priv.usb->devinfo; + devinfo->image = fw->data; + devinfo->image_len = fw->size; + + ret = brcmf_usb_fw_download(devinfo); + release_firmware(fw); + if (ret) + goto error; + + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto error; + + return; +error: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); + device_release_driver(dev); +} + static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) { struct brcmf_bus *bus = NULL; struct brcmf_usbdev *bus_pub = NULL; - int ret; struct device *dev = devinfo->dev; + int ret; brcmf_dbg(USB, "Enter\n"); bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); @@ -1254,22 +1187,18 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; bus->proto_type = BRCMF_PROTO_BCDC; + bus->always_use_fws_queue = true; - /* Attach to the common driver interface */ - ret = brcmf_attach(dev); - if (ret) { - brcmf_err("brcmf_attach failed\n"); - goto fail; - } - - ret = brcmf_bus_start(dev); - if (ret) { - brcmf_err("dongle is not responding\n"); - brcmf_detach(dev); - goto fail; + if (!brcmf_usb_dlneeded(devinfo)) { + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto fail; } - + /* request firmware here */ + brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); return 0; + fail: /* Release resources in reverse order */ kfree(bus); @@ -1357,9 +1286,6 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto fail; } - endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num); - devinfo->rx_pipe = 0; devinfo->rx_pipe2 = 0; devinfo->tx_pipe = 0; @@ -1391,16 +1317,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) } } - /* Allocate interrupt URB and data buffer */ - /* RNDIS says 8-byte intr, our old drivers used 4-byte */ - if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16)) - devinfo->intr_size = 8; - else - devinfo->intr_size = 4; - - devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; - - if (usb->speed == USB_SPEED_HIGH) + if (usb->speed == USB_SPEED_SUPER) + brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); + else if (usb->speed == USB_SPEED_HIGH) brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); else brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); @@ -1455,23 +1374,18 @@ static int brcmf_usb_resume(struct usb_interface *intf) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(USB, "Enter\n"); - if (!brcmf_attach(devinfo->dev)) - return brcmf_bus_start(&usb->dev); - - return 0; + return brcmf_usb_bus_setup(devinfo); } static int brcmf_usb_reset_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - brcmf_dbg(USB, "Enter\n"); - if (!brcmf_usb_fw_download(devinfo)) - return brcmf_usb_resume(intf); - - return -EIO; + return brcmf_fw_get_firmwares(&usb->dev, 0, + brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); } #define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c @@ -1506,16 +1420,6 @@ static struct usb_driver brcmf_usbdrvr = { .disable_hub_initiated_lpm = 1, }; -static void brcmf_release_fw(struct list_head *q) -{ - struct brcmf_usb_image *fw_image, *next; - - list_for_each_entry_safe(fw_image, next, q, list) { - vfree(fw_image->image); - list_del_init(&fw_image->list); - } -} - static int brcmf_usb_reset_device(struct device *dev, void *notused) { /* device past is the usb interface so we @@ -1534,12 +1438,10 @@ void brcmf_usb_exit(void) ret = driver_for_each_device(drv, NULL, NULL, brcmf_usb_reset_device); usb_deregister(&brcmf_usbdrvr); - brcmf_release_fw(&fw_image_list); } void brcmf_usb_register(void) { brcmf_dbg(USB, "Enter\n"); - INIT_LIST_HEAD(&fw_image_list); usb_register(&brcmf_usbdrvr); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index afb3d15..d8fa276 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -221,9 +221,9 @@ static const struct ieee80211_regdomain brcmf_regdom = { */ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), /* IEEE 802.11a, channel 36..64 */ - REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } + REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } }; static const u32 __wl_cipher_suites[] = { @@ -341,6 +341,60 @@ static u8 brcmf_mw_to_qdbm(u16 mw) return qdbm; } +static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, + struct cfg80211_chan_def *ch) +{ + struct brcmu_chan ch_inf; + s32 primary_offset; + + brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", + ch->chan->center_freq, ch->center_freq1, ch->width); + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); + primary_offset = ch->center_freq1 - ch->chan->center_freq; + switch (ch->width) { + case NL80211_CHAN_WIDTH_20: + ch_inf.bw = BRCMU_CHAN_BW_20; + WARN_ON(primary_offset != 0); + break; + case NL80211_CHAN_WIDTH_40: + ch_inf.bw = BRCMU_CHAN_BW_40; + if (primary_offset < 0) + ch_inf.sb = BRCMU_CHAN_SB_U; + else + ch_inf.sb = BRCMU_CHAN_SB_L; + break; + case NL80211_CHAN_WIDTH_80: + ch_inf.bw = BRCMU_CHAN_BW_80; + if (primary_offset < 0) { + if (primary_offset < -CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_UU; + else + ch_inf.sb = BRCMU_CHAN_SB_UL; + } else { + if (primary_offset > CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_LL; + else + ch_inf.sb = BRCMU_CHAN_SB_LU; + } + break; + default: + WARN_ON_ONCE(1); + } + switch (ch->chan->band) { + case IEEE80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case IEEE80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; + default: + WARN_ON_ONCE(1); + } + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch) { @@ -586,6 +640,9 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, if (err) brcmf_err("Scan abort failed\n"); } + + brcmf_set_mpc(ifp, 1); + /* * e-scan can be initiated by scheduled scan * which takes precedence. @@ -595,12 +652,10 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, cfg->sched_escan = false; if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); - brcmf_set_mpc(ifp, 1); } else if (scan_request) { brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, aborted); - brcmf_set_mpc(ifp, 1); } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); @@ -1236,8 +1291,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, params->chandef.chan->center_freq); if (params->channel_fixed) { /* adding chanspec */ - chanspec = channel_to_chanspec(&cfg->d11inf, - params->chandef.chan); + chanspec = chandef_to_chanspec(&cfg->d11inf, + ¶ms->chandef); join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); join_params.params_le.chanspec_num = cpu_to_le32(1); @@ -2182,7 +2237,7 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; @@ -3124,7 +3179,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } if (!request->n_ssids || !request->n_match_sets) { - brcmf_err("Invalid sched scan req!! n_ssids:%d\n", + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", request->n_ssids); return -EINVAL; } @@ -3734,23 +3789,6 @@ brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, } static s32 -brcmf_cfg80211_set_channel(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, - struct ieee80211_channel *channel) -{ - u16 chanspec; - s32 err; - - brcmf_dbg(TRACE, "band=%d, center_freq=%d\n", channel->band, - channel->center_freq); - - chanspec = channel_to_chanspec(&cfg->d11inf, channel); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - - return err; -} - -static s32 brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_ap_settings *settings) { @@ -3765,11 +3803,12 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_join_params join_params; enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; + u16 chanspec; - brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n", - cfg80211_get_chandef_type(&settings->chandef), - settings->beacon_interval, - settings->dtim_period); + brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", + settings->chandef.chan->hw_value, + settings->chandef.center_freq1, settings->chandef.width, + settings->beacon_interval, settings->dtim_period); brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); @@ -3826,9 +3865,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - err = brcmf_cfg80211_set_channel(cfg, ifp, settings->chandef.chan); + chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); if (err < 0) { - brcmf_err("Set Channel failed, %d\n", err); + brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); goto exit; } @@ -3975,7 +4015,7 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, static int brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac) + const u8 *mac) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_scb_val_le scbval; @@ -4203,7 +4243,7 @@ static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) } static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, - struct net_device *ndev, u8 *peer, + struct net_device *ndev, const u8 *peer, enum nl80211_tdls_operation oper) { struct brcmf_if *ifp; @@ -4364,6 +4404,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); @@ -4685,7 +4727,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct ieee80211_channel *chan; s32 err = 0; - u16 reason; if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); @@ -4706,16 +4747,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); - if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) { - reason = 0; - if (((e->event_code == BRCMF_E_DEAUTH_IND) || - (e->event_code == BRCMF_E_DISASSOC_IND)) && - (e->reason != WLAN_REASON_UNSPECIFIED)) - reason = e->reason; - cfg80211_disconnected(ndev, reason, NULL, 0, - GFP_KERNEL); - } } brcmf_link_down(ifp->vif); brcmf_init_prof(ndev_to_prof(ndev)); @@ -4948,7 +4979,7 @@ static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) if (!err) { /* only set 2G bandwidth using bw_cap command */ band_bwcap.band = cpu_to_le32(WLC_BAND_2G); - band_bwcap.bw_cap = cpu_to_le32(WLC_BW_40MHZ_BIT); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, sizeof(band_bwcap)); } else { @@ -5215,6 +5246,9 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; + if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_80) + continue; update = false; for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { if (band_chan_arr[j].hw_value == ch.chnum) { @@ -5231,10 +5265,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, ieee80211_channel_to_frequency(ch.chnum, band); band_chan_arr[index].hw_value = ch.chnum; - if (ch.bw == BRCMU_CHAN_BW_40) { - /* assuming the order is HT20, HT40 Upper, - * HT40 lower from chanspecs - */ + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_80MHZ; + } else if (ch.bw == BRCMU_CHAN_BW_40) { ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; if (ch.sb == BRCMU_CHAN_SB_U) { @@ -5255,8 +5292,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, IEEE80211_CHAN_NO_HT40MINUS; } } else { + /* disable other bandwidths for now as mentioned + * order assure they are enabled for subsequent + * chanspecs. + */ band_chan_arr[index].flags = - IEEE80211_CHAN_NO_HT40; + IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; ch.bw = BRCMU_CHAN_BW_20; cfg->d11inf.encchspec(&ch); channel = ch.chspec; @@ -5323,13 +5365,63 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) } } +static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + band->ht_cap.ht_supported = true; + if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) +{ + u16 mcs_map; + int i; + + for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) + mcs_map = (mcs_map << 2) | supp; + + return cpu_to_le16(mcs_map); +} + +static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + __le16 mcs_map; + + /* not allowed in 2.4G band */ + if (band->band == IEEE80211_BAND_2GHZ) + return; + + band->vht_cap.vht_supported = true; + /* 80MHz is mandatory */ + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { + band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; + } + /* all support 256-QAM */ + mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); + band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; + band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; +} + static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct wiphy *wiphy; s32 phy_list; u32 band_list[3]; - u32 nmode; + u32 nmode = 0; + u32 vhtmode = 0; u32 bw_cap[2] = { 0, 0 }; u32 rxchain; u32 nchain; @@ -5360,14 +5452,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", band_list[0], band_list[1], band_list[2]); + (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); if (err) { brcmf_err("nmode error (%d)\n", err); } else { brcmf_get_bwcap(ifp, bw_cap); } - brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode, - bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]); + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], + bw_cap[IEEE80211_BAND_5GHZ]); err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); if (err) { @@ -5398,17 +5492,10 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) else continue; - if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - band->ht_cap.ht_supported = true; - band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); - band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (nmode) + brcmf_update_ht_cap(band, bw_cap, nchain); + if (vhtmode) + brcmf_update_vht_cap(band, bw_cap, nchain); bands[band->band] = band; } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 9417cb5..af8ba64 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4870,14 +4870,11 @@ static void brcms_c_detach_module(struct brcms_c_info *wlc) /* * low level detach */ -static int brcms_b_detach(struct brcms_c_info *wlc) +static void brcms_b_detach(struct brcms_c_info *wlc) { uint i; struct brcms_hw_band *band; struct brcms_hardware *wlc_hw = wlc->hw; - int callbacks; - - callbacks = 0; brcms_b_detach_dmapio(wlc_hw); @@ -4900,9 +4897,6 @@ static int brcms_b_detach(struct brcms_c_info *wlc) ai_detach(wlc_hw->sih); wlc_hw->sih = NULL; } - - return callbacks; - } /* @@ -4917,14 +4911,15 @@ static int brcms_b_detach(struct brcms_c_info *wlc) */ uint brcms_c_detach(struct brcms_c_info *wlc) { - uint callbacks = 0; + uint callbacks; if (wlc == NULL) return 0; - callbacks += brcms_b_detach(wlc); + brcms_b_detach(wlc); /* delete software timers */ + callbacks = 0; if (!brcms_c_radio_monitor_stop(wlc)) callbacks++; diff --git a/drivers/net/wireless/brcm80211/brcmutil/d11.c b/drivers/net/wireless/brcm80211/brcmutil/d11.c index 30e54e2..2b2522b 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/d11.c +++ b/drivers/net/wireless/brcm80211/brcmutil/d11.c @@ -21,19 +21,46 @@ #include <brcmu_wifi.h> #include <brcmu_d11.h> -static void brcmu_d11n_encchspec(struct brcmu_chan *ch) +static u16 d11n_sb(enum brcmu_chan_sb sb) { - ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK; + switch (sb) { + case BRCMU_CHAN_SB_NONE: + return BRCMU_CHSPEC_D11N_SB_N; + case BRCMU_CHAN_SB_L: + return BRCMU_CHSPEC_D11N_SB_L; + case BRCMU_CHAN_SB_U: + return BRCMU_CHSPEC_D11N_SB_U; + default: + WARN_ON(1); + } + return 0; +} - switch (ch->bw) { +static u16 d11n_bw(enum brcmu_chan_bw bw) +{ + switch (bw) { case BRCMU_CHAN_BW_20: - ch->chspec |= BRCMU_CHSPEC_D11N_BW_20 | BRCMU_CHSPEC_D11N_SB_N; - break; + return BRCMU_CHSPEC_D11N_BW_20; case BRCMU_CHAN_BW_40: + return BRCMU_CHSPEC_D11N_BW_40; default: - WARN_ON_ONCE(1); - break; + WARN_ON(1); } + return 0; +} + +static void brcmu_d11n_encchspec(struct brcmu_chan *ch) +{ + if (ch->bw == BRCMU_CHAN_BW_20) + ch->sb = BRCMU_CHAN_SB_NONE; + + ch->chspec = 0; + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, + BRCMU_CHSPEC_CH_SHIFT, ch->chnum); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK, + 0, d11n_sb(ch->sb)); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK, + 0, d11n_bw(ch->bw)); if (ch->chnum <= CH_MAX_2G_CHANNEL) ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G; @@ -41,23 +68,34 @@ static void brcmu_d11n_encchspec(struct brcmu_chan *ch) ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G; } -static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) +static u16 d11ac_bw(enum brcmu_chan_bw bw) { - ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK; - - switch (ch->bw) { + switch (bw) { case BRCMU_CHAN_BW_20: - ch->chspec |= BRCMU_CHSPEC_D11AC_BW_20; - break; + return BRCMU_CHSPEC_D11AC_BW_20; case BRCMU_CHAN_BW_40: + return BRCMU_CHSPEC_D11AC_BW_40; case BRCMU_CHAN_BW_80: - case BRCMU_CHAN_BW_80P80: - case BRCMU_CHAN_BW_160: + return BRCMU_CHSPEC_D11AC_BW_80; default: - WARN_ON_ONCE(1); - break; + WARN_ON(1); } + return 0; +} +static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) +{ + if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE) + ch->sb = BRCMU_CHAN_SB_L; + + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, + BRCMU_CHSPEC_CH_SHIFT, ch->chnum); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK, + 0, d11ac_bw(ch->bw)); + + ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK; if (ch->chnum <= CH_MAX_2G_CHANNEL) ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; else @@ -73,6 +111,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch) switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) { case BRCMU_CHSPEC_D11N_BW_20: ch->bw = BRCMU_CHAN_BW_20; + ch->sb = BRCMU_CHAN_SB_NONE; break; case BRCMU_CHSPEC_D11N_BW_40: ch->bw = BRCMU_CHAN_BW_40; @@ -112,6 +151,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) { case BRCMU_CHSPEC_D11AC_BW_20: ch->bw = BRCMU_CHAN_BW_20; + ch->sb = BRCMU_CHAN_SB_NONE; break; case BRCMU_CHSPEC_D11AC_BW_40: ch->bw = BRCMU_CHAN_BW_40; @@ -128,6 +168,25 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) break; case BRCMU_CHSPEC_D11AC_BW_80: ch->bw = BRCMU_CHAN_BW_80; + ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT); + switch (ch->sb) { + case BRCMU_CHAN_SB_LL: + ch->chnum -= CH_30MHZ_APART; + break; + case BRCMU_CHAN_SB_LU: + ch->chnum -= CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_UL: + ch->chnum += CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_UU: + ch->chnum += CH_30MHZ_APART; + break; + default: + WARN_ON_ONCE(1); + break; + } break; case BRCMU_CHSPEC_D11AC_BW_8080: case BRCMU_CHSPEC_D11AC_BW_160: diff --git a/drivers/net/wireless/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/brcm80211/include/brcmu_d11.h index 8660a2c..f9745ea 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_d11.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_d11.h @@ -108,13 +108,7 @@ enum brcmu_chan_bw { }; enum brcmu_chan_sb { - BRCMU_CHAN_SB_NONE = 0, - BRCMU_CHAN_SB_L, - BRCMU_CHAN_SB_U, - BRCMU_CHAN_SB_LL, - BRCMU_CHAN_SB_LU, - BRCMU_CHAN_SB_UL, - BRCMU_CHAN_SB_UU, + BRCMU_CHAN_SB_NONE = -1, BRCMU_CHAN_SB_LLL, BRCMU_CHAN_SB_LLU, BRCMU_CHAN_SB_LUL, @@ -123,6 +117,12 @@ enum brcmu_chan_sb { BRCMU_CHAN_SB_ULU, BRCMU_CHAN_SB_UUL, BRCMU_CHAN_SB_UUU, + BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL, + BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU, }; struct brcmu_chan { diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h index 74419d4..76b5d3a 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h @@ -29,6 +29,7 @@ #define CH_UPPER_SB 0x01 #define CH_LOWER_SB 0x02 #define CH_EWA_VALID 0x04 +#define CH_30MHZ_APART 6 #define CH_20MHZ_APART 4 #define CH_10MHZ_APART 2 #define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 74b3b4d..7fd5042 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -2,10 +2,6 @@ config IWLWIFI tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " depends on PCI && MAC80211 && HAS_IOMEM select FW_LOADER - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select MAC80211_LEDS ---help--- Select to build the driver supporting the: @@ -43,6 +39,14 @@ config IWLWIFI say M here and read <file:Documentation/kbuild/modules.txt>. The module will be called iwlwifi. +config IWLWIFI_LEDS + bool + depends on IWLWIFI + depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI + select LEDS_TRIGGERS + select MAC80211_LEDS + default y + config IWLDVM tristate "Intel Wireless WiFi DVM Firmware support" depends on IWLWIFI @@ -124,7 +128,6 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE Enable use of experimental ucode for testing and debugging. config IWLWIFI_DEVICE_TRACING - bool "iwlwifi device access tracing" depends on IWLWIFI depends on EVENT_TRACING diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/iwlwifi/dvm/Makefile index dce7ab2..4d19685 100644 --- a/drivers/net/wireless/iwlwifi/dvm/Makefile +++ b/drivers/net/wireless/iwlwifi/dvm/Makefile @@ -4,9 +4,10 @@ iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o iwldvm-objs += lib.o calib.o tt.o sta.o rx.o iwldvm-objs += power.o -iwldvm-objs += scan.o led.o +iwldvm-objs += scan.o iwldvm-objs += rxon.o devices.o +iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index be1086c..20e6aa9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -94,7 +94,6 @@ int iwl_send_calib_results(struct iwl_priv *priv) { struct iwl_host_cmd hcmd = { .id = REPLY_PHY_CALIBRATION_CMD, - .flags = CMD_SYNC, }; struct iwl_calib_result *res; diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index d2fe259..0ffb6ff 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -1481,7 +1481,7 @@ static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); + ret = iwl_send_statistics_request(priv, 0, false); mutex_unlock(&priv->mutex); if (ret) @@ -1868,7 +1868,7 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - iwl_send_statistics_request(priv, CMD_SYNC, true); + iwl_send_statistics_request(priv, 0, true); mutex_unlock(&priv->mutex); return count; @@ -2188,7 +2188,6 @@ static int iwl_cmd_echo_test(struct iwl_priv *priv) struct iwl_host_cmd cmd = { .id = REPLY_ECHO, .len = { 0 }, - .flags = CMD_SYNC, }; ret = iwl_dvm_send_cmd(priv, &cmd); @@ -2320,7 +2319,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, mutex_lock(&priv->mutex); /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, CMD_SYNC, 0, NULL); + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL); mutex_unlock(&priv->mutex); diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 3441f70..a6f22c3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -888,9 +888,11 @@ struct iwl_priv { struct iwl_event_log event_log; +#ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; unsigned long blink_on, blink_off; bool led_registered; +#endif /* WoWLAN GTK rekey data */ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 758c54e..34b41e5 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -417,7 +417,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, struct iwl_host_cmd hcmd = { .id = REPLY_CHANNEL_SWITCH, .len = { sizeof(cmd), }, - .flags = CMD_SYNC, .data = { &cmd, }, }; @@ -579,7 +578,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, struct iwl_host_cmd hcmd = { .id = REPLY_CHANNEL_SWITCH, .len = { sizeof(*cmd), }, - .flags = CMD_SYNC, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; int err; diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/iwlwifi/dvm/led.h index 6a0817d..1c6b225 100644 --- a/drivers/net/wireless/iwlwifi/dvm/led.h +++ b/drivers/net/wireless/iwlwifi/dvm/led.h @@ -36,8 +36,20 @@ struct iwl_priv; #define IWL_LED_ACTIVITY (0<<1) #define IWL_LED_LINK (1<<1) +#ifdef CONFIG_IWLWIFI_LEDS void iwlagn_led_enable(struct iwl_priv *priv); void iwl_leds_init(struct iwl_priv *priv); void iwl_leds_exit(struct iwl_priv *priv); +#else +static inline void iwlagn_led_enable(struct iwl_priv *priv) +{ +} +static inline void iwl_leds_init(struct iwl_priv *priv) +{ +} +static inline void iwl_leds_exit(struct iwl_priv *priv) +{ +} +#endif #endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index d169228..2191621 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -81,7 +81,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv) else tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; - return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0, sizeof(tx_power_cmd), &tx_power_cmd); } @@ -141,7 +141,6 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk) struct iwl_host_cmd cmd = { .id = REPLY_TXFIFO_FLUSH, .len = { sizeof(struct iwl_txfifo_flush_cmd), }, - .flags = CMD_SYNC, .data = { &flush_cmd, }, }; @@ -333,12 +332,12 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) memcpy(&bt_cmd_v2.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2); + 0, sizeof(bt_cmd_v2), &bt_cmd_v2); } else { memcpy(&bt_cmd_v1.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1); + 0, sizeof(bt_cmd_v1), &bt_cmd_v1); } if (ret) IWL_ERR(priv, "failed to send BT Coex Config\n"); @@ -1044,7 +1043,6 @@ int iwlagn_send_patterns(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_WOWLAN_PATTERNS, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .flags = CMD_SYNC, }; int i, err; @@ -1201,7 +1199,6 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) if (key_data.use_rsc_tsc) { struct iwl_host_cmd rsc_tsc_cmd = { .id = REPLY_WOWLAN_TSC_RSC_PARAMS, - .flags = CMD_SYNC, .data[0] = key_data.rsc_tsc, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .len[0] = sizeof(*key_data.rsc_tsc), @@ -1215,7 +1212,7 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) if (key_data.use_tkip) { ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_TKIP_PARAMS, - CMD_SYNC, sizeof(tkip_cmd), + 0, sizeof(tkip_cmd), &tkip_cmd); if (ret) goto out; @@ -1231,20 +1228,20 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_KEK_KCK_MATERIAL, - CMD_SYNC, sizeof(kek_kck_cmd), + 0, sizeof(kek_kck_cmd), &kek_kck_cmd); if (ret) goto out; } } - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0, sizeof(d3_cfg_cmd), &d3_cfg_cmd); if (ret) goto out; ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, - CMD_SYNC, sizeof(wakeup_filter_cmd), + 0, sizeof(wakeup_filter_cmd), &wakeup_filter_cmd); if (ret) goto out; diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 6a00353..0b7f46f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -128,7 +128,6 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv) struct iwl_tx_beacon_cmd *tx_beacon_cmd; struct iwl_host_cmd cmd = { .id = REPLY_TX_BEACON, - .flags = CMD_SYNC, }; struct ieee80211_tx_info *info; u32 frame_size; @@ -311,8 +310,7 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) sizeof(struct iwl_statistics_cmd), &statistics_cmd); else - return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, - CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0, sizeof(struct iwl_statistics_cmd), &statistics_cmd); } @@ -622,7 +620,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) ret = iwl_dvm_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, - CMD_SYNC, sizeof(adv_cmd), &adv_cmd); + 0, sizeof(adv_cmd), &adv_cmd); if (ret) IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); else @@ -637,7 +635,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) ret = iwl_dvm_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, - CMD_SYNC, sizeof(cmd), &cmd); + 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); else @@ -673,9 +671,7 @@ static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_dvm_send_cmd_pdu(priv, - TX_ANT_CONFIGURATION_CMD, - CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0, sizeof(struct iwl_tx_ant_config_cmd), &tx_ant_cmd); } else { @@ -703,7 +699,7 @@ static void iwl_send_bt_config(struct iwl_priv *priv) (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd)) + 0, sizeof(struct iwl_bt_cmd), &bt_cmd)) IWL_ERR(priv, "failed to send BT Coex Config\n"); } @@ -987,7 +983,7 @@ static void iwl_bg_restart(struct work_struct *data) ieee80211_restart_hw(priv->hw); else IWL_ERR(priv, - "Cannot request restart before registrating with mac80211"); + "Cannot request restart before registrating with mac80211\n"); } else { WARN_ON(1); } @@ -1127,7 +1123,6 @@ static void iwl_option_config(struct iwl_priv *priv) static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) { struct iwl_nvm_data *data = priv->nvm_data; - char *debug_msg; if (data->sku_cap_11n_enable && !priv->cfg->ht_params) { @@ -1141,8 +1136,8 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) return -EINVAL; } - debug_msg = "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n"; - IWL_DEBUG_INFO(priv, debug_msg, + IWL_DEBUG_INFO(priv, + "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n", data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled", data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled", data->sku_cap_11n_enable ? "" : "NOT", "enabled"); @@ -1350,7 +1345,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, iwl_set_hw_params(priv); if (!(priv->nvm_data->sku_cap_ipan_enable)) { - IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN"); + IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n"); ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; /* * if not PAN, then don't support P2P -- might be a uCode @@ -2019,10 +2014,10 @@ void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { if (!test_bit(mq, &priv->transport_queue_stop)) { - IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq); + IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq); ieee80211_wake_queue(priv->hw, mq); } else { - IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq); + IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq); } } diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index b4e6141..f2c1439 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -278,7 +278,7 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); - return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0, sizeof(struct iwl_powertable_cmd), cmd); } @@ -361,7 +361,7 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); } else - IWL_ERR(priv, "set power fail, ret = %d", ret); + IWL_ERR(priv, "set power fail, ret = %d\n", ret); return ret; } diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index aa773a2..32b78a6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -1453,7 +1453,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv, tbl->action = IWL_LEGACY_SWITCH_SISO; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -1628,7 +1628,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, tbl->action = IWL_SISO_SWITCH_ANTENNA1; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -1799,7 +1799,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, tbl->action = IWL_MIMO2_SWITCH_SISO_A; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -1969,7 +1969,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, tbl->action = IWL_MIMO3_SWITCH_SISO_A; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -2709,7 +2709,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_link_cmd(NULL, lq_sta, rate); priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true); } static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index cd83773..debec96 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -786,7 +786,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - ieee80211_rx_ni(priv->hw, skb); + ieee80211_rx(priv->hw, skb); } static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c index 503a81e..ed50de6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -104,7 +104,7 @@ static int iwlagn_disable_bss(struct iwl_priv *priv, send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - CMD_SYNC, sizeof(*send), send); + 0, sizeof(*send), send); send->filter_flags = old_filter; @@ -134,7 +134,7 @@ static int iwlagn_disable_pan(struct iwl_priv *priv, send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; send->dev_type = RXON_DEV_TYPE_P2P; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - CMD_SYNC, sizeof(*send), send); + 0, sizeof(*send), send); send->filter_flags = old_filter; send->dev_type = old_dev_type; @@ -160,7 +160,7 @@ static int iwlagn_disconn_pan(struct iwl_priv *priv, int ret; send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, sizeof(*send), send); send->filter_flags = old_filter; @@ -189,7 +189,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv, ctx->qos_data.qos_active, ctx->qos_data.def_qos_parm.qos_flags); - ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0, sizeof(struct iwl_qosparam_cmd), &ctx->qos_data.def_qos_parm); if (ret) @@ -353,7 +353,7 @@ static int iwl_send_rxon_timing(struct iwl_priv *priv, le16_to_cpu(ctx->timing.atim_window)); return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, - CMD_SYNC, sizeof(ctx->timing), &ctx->timing); + 0, sizeof(ctx->timing), &ctx->timing); } static int iwlagn_rxon_disconn(struct iwl_priv *priv, @@ -495,7 +495,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv, * Associated RXON doesn't clear the station table in uCode, * so we don't need to restore stations etc. after this. */ - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, sizeof(struct iwl_rxon_cmd), &ctx->staging); if (ret) { IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); @@ -610,7 +610,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv) cmd.slots[0].width = cpu_to_le16(slot0); cmd.slots[1].width = cpu_to_le16(slot1); - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); @@ -823,7 +823,7 @@ static int iwl_check_rxon_cmd(struct iwl_priv *priv, if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { - IWL_WARN(priv, "CCK and auto detect"); + IWL_WARN(priv, "CCK and auto detect\n"); errors |= BIT(8); } @@ -1395,7 +1395,7 @@ static void iwlagn_chain_noise_reset(struct iwl_priv *priv) priv->phy_calib_chain_noise_reset_cmd); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, - CMD_SYNC, sizeof(cmd), &cmd); + 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "Could not send REPLY_PHY_CALIBRATION_CMD\n"); diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index be98b91..43bef90 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -59,7 +59,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv) int ret; struct iwl_host_cmd cmd = { .id = REPLY_SCAN_ABORT_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; __le32 *status; @@ -639,7 +639,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = { sizeof(struct iwl_scan_cmd), }, - .flags = CMD_SYNC, }; struct iwl_scan_cmd *scan; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index 9cdd91c..6ec86ad 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -39,7 +39,7 @@ static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) lockdep_assert_held(&priv->sta_lock); if (sta_id >= IWLAGN_STATION_COUNT) { - IWL_ERR(priv, "invalid sta_id %u", sta_id); + IWL_ERR(priv, "invalid sta_id %u\n", sta_id); return -EINVAL; } if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) @@ -165,7 +165,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, iwl_free_resp(&cmd); if (cmd.handler_status) - IWL_ERR(priv, "%s - error in the CMD response %d", __func__, + IWL_ERR(priv, "%s - error in the CMD response %d\n", __func__, cmd.handler_status); return cmd.handler_status; @@ -261,7 +261,7 @@ int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, cmd.station_flags = flags; cmd.sta.sta_id = sta_id; - return iwl_send_add_sta(priv, &cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &cmd, 0); } static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, @@ -413,7 +413,7 @@ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, spin_unlock_bh(&priv->sta_lock); /* Add station to device's station table */ - ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + ret = iwl_send_add_sta(priv, &sta_cmd, 0); if (ret) { spin_lock_bh(&priv->sta_lock); IWL_ERR(priv, "Adding station %pM failed.\n", @@ -456,7 +456,6 @@ static int iwl_send_remove_station(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = { sizeof(struct iwl_rem_sta_cmd), }, - .flags = CMD_SYNC, .data = { &rm_sta_cmd, }, }; @@ -740,7 +739,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) send_lq = true; } spin_unlock_bh(&priv->sta_lock); - ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + ret = iwl_send_add_sta(priv, &sta_cmd, 0); if (ret) { spin_lock_bh(&priv->sta_lock); IWL_ERR(priv, "Adding station %pM failed.\n", @@ -756,8 +755,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) * current LQ command */ if (send_lq) - iwl_send_lq_cmd(priv, ctx, &lq, - CMD_SYNC, true); + iwl_send_lq_cmd(priv, ctx, &lq, 0, true); spin_lock_bh(&priv->sta_lock); priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; } @@ -968,7 +966,7 @@ int iwlagn_add_bssid_station(struct iwl_priv *priv, return -ENOMEM; } - ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true); + ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); if (ret) IWL_ERR(priv, "Link quality command failed (%d)\n", ret); @@ -999,7 +997,6 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = ctx->wep_key_cmd, .data = { wep_cmd, }, - .flags = CMD_SYNC, }; might_sleep(); @@ -1248,7 +1245,7 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv, sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; sta_cmd.mode = STA_CONTROL_MODIFY_MSK; - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } int iwl_set_dynamic_key(struct iwl_priv *priv, @@ -1284,13 +1281,13 @@ int iwl_set_dynamic_key(struct iwl_priv *priv, ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - seq.tkip.iv32, p1k, CMD_SYNC); + seq.tkip.iv32, p1k, 0); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - 0, NULL, CMD_SYNC); + 0, NULL, 0); break; default: IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); @@ -1409,7 +1406,7 @@ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, @@ -1433,7 +1430,7 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, @@ -1458,7 +1455,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index 058c589..acb981a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -236,7 +236,7 @@ static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) { IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); /* make request to retrieve statistics information */ - iwl_send_statistics_request(priv, CMD_SYNC, false); + iwl_send_statistics_request(priv, 0, false); /* Reschedule the ct_kill wait timer */ mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 398dd09..3255a17 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -402,10 +402,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv, /* aggregation is on for this <sta,tid> */ if (info->flags & IEEE80211_TX_CTL_AMPDU && tid_data->agg.state != IWL_AGG_ON) { - IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:" - " Tx flags = 0x%08x, agg.state = %d", + IWL_ERR(priv, + "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n", info->flags, tid_data->agg.state); - IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", + IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n", sta_id, tid, IEEE80211_SEQ_TO_SN(tid_data->seq_number)); goto drop_unlock_sta; @@ -416,7 +416,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, */ if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && tid_data->agg.state != IWL_AGG_OFF, - "Tx while agg.state = %d", tid_data->agg.state)) + "Tx while agg.state = %d\n", tid_data->agg.state)) goto drop_unlock_sta; seq_number = tid_data->seq_number; @@ -778,8 +778,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, - "Can continue DELBA flow ssn = next_recl =" - " %d", tid_data->next_reclaimed); + "Can continue DELBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); iwl_trans_txq_disable(priv->trans, tid_data->agg.txq_id); iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); @@ -791,8 +791,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, - "Can continue ADDBA flow ssn = next_recl =" - " %d", tid_data->next_reclaimed); + "Can continue ADDBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); tid_data->agg.state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); } @@ -1216,8 +1216,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, ctx->vif->type == NL80211_IFTYPE_STATION) { /* block and stop all queues */ priv->passive_no_rx = true; - IWL_DEBUG_TX_QUEUES(priv, "stop all queues: " - "passive channel"); + IWL_DEBUG_TX_QUEUES(priv, + "stop all queues: passive channel\n"); ieee80211_stop_queues(priv->hw); IWL_DEBUG_TX_REPLY(priv, @@ -1271,7 +1271,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); - ieee80211_tx_status_ni(priv->hw, skb); + ieee80211_tx_status(priv->hw, skb); } return 0; @@ -1411,7 +1411,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status_ni(priv->hw, skb); + ieee80211_tx_status(priv->hw, skb); } return 0; diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index cf03ef5..d5cee15 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -172,7 +172,7 @@ static int iwl_send_wimax_coex(struct iwl_priv *priv) memset(&coex_cmd, 0, sizeof(coex_cmd)); return iwl_dvm_send_cmd_pdu(priv, - COEX_PRIORITY_TABLE_CMD, CMD_SYNC, + COEX_PRIORITY_TABLE_CMD, 0, sizeof(coex_cmd), &coex_cmd); } @@ -205,7 +205,7 @@ void iwl_send_prio_tbl(struct iwl_priv *priv) memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, sizeof(iwl_bt_prio_tbl)); if (iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC, + REPLY_BT_COEX_PRIO_TABLE, 0, sizeof(prio_tbl_cmd), &prio_tbl_cmd)) IWL_ERR(priv, "failed to send BT prio tbl command\n"); } @@ -218,7 +218,7 @@ int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) env_cmd.action = action; env_cmd.type = type; ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PROT_ENV, CMD_SYNC, + REPLY_BT_COEX_PROT_ENV, 0, sizeof(env_cmd), &env_cmd); if (ret) IWL_ERR(priv, "failed to send BT env command\n"); diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index f73de23..4873006 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -98,7 +98,7 @@ #define NVM_HW_SECTION_NUM_FAMILY_7000 0 static const struct iwl_base_params iwl7000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .shadow_ram_support = true, diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index f5bd82b..51c4153 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -83,9 +83,10 @@ #define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_8000 10 +#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000.bin" static const struct iwl_base_params iwl8000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .shadow_ram_support = true, @@ -118,6 +119,7 @@ const struct iwl_cfg iwl8260_2ac_cfg = { .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, }; const struct iwl_cfg iwl8260_n_cfg = { @@ -127,6 +129,7 @@ const struct iwl_cfg iwl8260_n_cfg = { .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, }; MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h index 7f37fb8..04a483d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h @@ -102,9 +102,7 @@ /* EEPROM */ #define IWLAGN_EEPROM_IMG_SIZE 2048 -/* OTP */ -/* lower blocks contain EEPROM image and calibration data */ -#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ + /* high blocks contain PAPD data */ #define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ #define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 7ce82d9..b7047905 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -193,6 +193,11 @@ struct iwl_ht_params { #define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 #define EEPROM_REGULATORY_BAND_NO_HT40 0 +/* lower blocks contain EEPROM image and calibration data */ +#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */ + struct iwl_eeprom_params { const u8 regulatory_bands[7]; bool enhanced_txpower; @@ -269,6 +274,8 @@ struct iwl_cfg { u8 nvm_hw_section_num; bool lp_xtal_workaround; const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; + bool no_power_up_nic_in_init; + const char *default_nvm_file; }; /* diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c index 8a44f59..09feff4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -61,8 +61,6 @@ * *****************************************************************************/ -#define DEBUG - #include <linux/device.h> #include <linux/interrupt.h> #include <linux/export.h> @@ -128,8 +126,8 @@ void __iwl_dbg(struct device *dev, #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(level) && (!limit || net_ratelimit())) - dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', - function, &vaf); + dev_printk(KERN_DEBUG, dev, "%c %s %pV", + in_interrupt() ? 'I' : 'U', function, &vaf); #endif trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); va_end(args); diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index c8cbdbe..2950835 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -47,12 +47,32 @@ void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3); void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3); void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); +/* not all compilers can evaluate strlen() at compile time, so use sizeof() */ +#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n') + /* No matter what is m (priv, bus, trans), this will work */ -#define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a) -#define IWL_ERR_DEV(d, f, a...) __iwl_err((d), false, false, f, ## a) -#define IWL_WARN(m, f, a...) __iwl_warn((m)->dev, f, ## a) -#define IWL_INFO(m, f, a...) __iwl_info((m)->dev, f, ## a) -#define IWL_CRIT(m, f, a...) __iwl_crit((m)->dev, f, ## a) +#define IWL_ERR_DEV(d, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_err((d), false, false, f, ## a); \ + } while (0) +#define IWL_ERR(m, f, a...) \ + IWL_ERR_DEV((m)->dev, f, ## a) +#define IWL_WARN(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_warn((m)->dev, f, ## a); \ + } while (0) +#define IWL_INFO(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_info((m)->dev, f, ## a); \ + } while (0) +#define IWL_CRIT(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_crit((m)->dev, f, ## a); \ + } while (0) #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, @@ -72,12 +92,17 @@ do { \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) +#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...) \ + do { \ + CHECK_FOR_NEWLINE(fmt); \ + __iwl_dbg(dev, level, limit, __func__, fmt, ##args); \ + } while (0) #define IWL_DEBUG(m, level, fmt, args...) \ - __iwl_dbg((m)->dev, level, false, __func__, fmt, ##args) + __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args) #define IWL_DEBUG_DEV(dev, level, fmt, args...) \ - __iwl_dbg((dev), level, false, __func__, fmt, ##args) + __IWL_DEBUG_DEV(dev, level, false, fmt, ##args) #define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ - __iwl_dbg((m)->dev, level, true, __func__, fmt, ##args) + __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args) #ifdef CONFIG_IWLWIFI_DEBUG #define iwl_print_hex_dump(m, level, p, len) \ diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 0a3e841..f2a5c122 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1243,6 +1243,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .wd_disable = true, + .uapsd_disable = false, /* the rest are 0 by default */ }; IWL_EXPORT_SYMBOL(iwlwifi_mod_params); @@ -1356,6 +1357,10 @@ MODULE_PARM_DESC(wd_disable, module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); MODULE_PARM_DESC(nvm_file, "NVM file name"); +module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, + bool, S_IRUGO); +MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)"); + /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index f381908..2953ffc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -72,11 +72,14 @@ * @IWL_FW_ERROR_DUMP_SRAM: * @IWL_FW_ERROR_DUMP_REG: * @IWL_FW_ERROR_DUMP_RXF: + * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as + * &struct iwl_fw_error_dump_txcmd packets */ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_SRAM = 0, IWL_FW_ERROR_DUMP_REG = 1, IWL_FW_ERROR_DUMP_RXF = 2, + IWL_FW_ERROR_DUMP_TXCMD = 3, IWL_FW_ERROR_DUMP_MAX, }; @@ -105,4 +108,27 @@ struct iwl_fw_error_dump_file { u8 data[0]; } __packed; +/** + * struct iwl_fw_error_dump_txcmd - TX command data + * @cmdlen: original length of command + * @caplen: captured length of command (may be less) + * @data: captured command data, @caplen bytes + */ +struct iwl_fw_error_dump_txcmd { + __le32 cmdlen; + __le32 caplen; + u8 data[]; +} __packed; + +/** + * iwl_mvm_fw_error_next_data - advance fw error dump data pointer + * @data: previous data block + * Returns: next data block + */ +static inline struct iwl_fw_error_dump_data * +iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data) +{ + return (void *)(data->data + le32_to_cpu(data->len)); +} + #endif /* __fw_error_dump_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index f5927d0c..0aa7c00 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -74,7 +74,7 @@ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS - * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD + * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan * offload profile config command. * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six @@ -107,6 +107,7 @@ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21), IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22), IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23), + IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25), IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29), @@ -116,9 +117,11 @@ enum iwl_ucode_tlv_flag { /** * enum iwl_ucode_tlv_api - ucode api * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. + * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), + IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), }; /** @@ -194,6 +197,11 @@ struct fw_img { bool is_dual_cpus; }; +struct iwl_sf_region { + u32 addr; + u32 size; +}; + /* uCode version contains 4 values: Major/Minor/API/Serial */ #define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) #define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 44cc3cf..5eef4ae 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -33,6 +33,7 @@ #include "iwl-io.h" #include "iwl-csr.h" #include "iwl-debug.h" +#include "iwl-prph.h" #include "iwl-fh.h" #define IWL_POLL_INTERVAL 10 /* microseconds */ @@ -183,6 +184,23 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) } IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); +void iwl_force_nmi(struct iwl_trans *trans) +{ + /* + * In HW previous to the 8000 HW family, and in the 8000 HW family + * itself when the revision step==0, the DEVICE_SET_NMI_REG is used + * to force an NMI. Otherwise, a different register - + * DEVICE_SET_NMI_8000B_REG - is used. + */ + if ((trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) || + ((trans->hw_rev & 0xc) == 0x0)) + iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL); + else + iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG, + DEVICE_SET_NMI_8000B_VAL); +} +IWL_EXPORT_SYMBOL(iwl_force_nmi); + static const char *get_fh_string(int cmd) { #define IWL_CMD(x) case x: return #x diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 665ddd9..705d12c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -80,6 +80,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, u32 bits, u32 mask); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); +void iwl_force_nmi(struct iwl_trans *trans); /* Error handling */ int iwl_dump_fh(struct iwl_trans *trans, char **buf); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d994317..d051857 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -119,6 +119,7 @@ struct iwl_mod_params { #endif int ant_coupling; char *nvm_file; + bool uapsd_disable; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 4049c0d..85eee79 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -62,6 +62,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/etherdevice.h> #include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-nvm-parse.h" @@ -127,7 +128,7 @@ static const u8 iwl_nvm_channels[] = { static const u8 iwl_nvm_channels_family_8000[] = { /* 2.4 GHz */ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 5 GHz */ 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, @@ -137,7 +138,7 @@ static const u8 iwl_nvm_channels_family_8000[] = { #define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) #define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000) #define NUM_2GHZ_CHANNELS 14 -#define NUM_2GHZ_CHANNELS_FAMILY_8000 13 +#define NUM_2GHZ_CHANNELS_FAMILY_8000 14 #define FIRST_2GHZ_HT_MINUS 5 #define LAST_2GHZ_HT_PLUS 9 #define LAST_5GHZ_HT 161 @@ -450,13 +451,7 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *nvm_sec) { - u8 hw_addr[ETH_ALEN]; - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN); - else - memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000, - ETH_ALEN); + const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR); /* The byte order is little endian 16 bit, meaning 214365 */ data->hw_addr[0] = hw_addr[1]; @@ -467,6 +462,41 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg, data->hw_addr[5] = hw_addr[4]; } +static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *mac_override, + const __le16 *nvm_hw) +{ + const u8 *hw_addr; + + if (mac_override) { + hw_addr = (const u8 *)(mac_override + + MAC_ADDRESS_OVERRIDE_FAMILY_8000); + + /* The byte order is little endian 16 bit, meaning 214365 */ + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; + + if (is_valid_ether_addr(hw_addr)) + return; + } + + /* take the MAC address from the OTP */ + hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; +} + struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, @@ -526,7 +556,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, rx_chains); } else { /* MAC address in family 8000 */ - iwl_set_hw_address(cfg, data, mac_override); + iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw); iwl_init_sbands(dev, cfg, data, regulatory, sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c index b761ac4..d4fb5ca 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c @@ -345,7 +345,6 @@ static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type, struct iwl_phy_db_cmd phy_db_cmd; struct iwl_host_cmd cmd = { .id = PHY_DB_CMD, - .flags = CMD_SYNC, }; IWL_DEBUG_INFO(phy_db->trans, @@ -393,13 +392,13 @@ static int iwl_phy_db_send_all_channel_groups( entry->data); if (err) { IWL_ERR(phy_db->trans, - "Can't SEND phy_db section %d (%d), err %d", + "Can't SEND phy_db section %d (%d), err %d\n", type, i, err); return err; } IWL_DEBUG_INFO(phy_db->trans, - "Sent PHY_DB HCMD, type = %d num = %d", + "Sent PHY_DB HCMD, type = %d num = %d\n", type, i); } @@ -451,7 +450,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) IWL_NUM_PAPD_CH_GROUPS); if (err) { IWL_ERR(phy_db->trans, - "Cannot send channel specific PAPD groups"); + "Cannot send channel specific PAPD groups\n"); return err; } @@ -461,7 +460,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) IWL_NUM_TXP_CH_GROUPS); if (err) { IWL_ERR(phy_db->trans, - "Cannot send channel specific TX power groups"); + "Cannot send channel specific TX power groups\n"); return err; } diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 7793110..4997e27 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -105,6 +105,9 @@ /* Device NMI register */ #define DEVICE_SET_NMI_REG 0x00a01c30 +#define DEVICE_SET_NMI_VAL 0x1 +#define DEVICE_SET_NMI_8000B_REG 0x00a01c24 +#define DEVICE_SET_NMI_8000B_VAL 0x1000000 /* Shared registers (0x0..0x3ff, via target indirect or periphery */ #define SHR_BASE 0x00a10000 diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 22fd94e..34d49e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -189,10 +189,9 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) /** * enum CMD_MODE - how to send the host commands ? * - * @CMD_SYNC: The caller will be stalled until the fw responds to the command * @CMD_ASYNC: Return right away and don't wait for the response - * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the - * response. The caller needs to call iwl_free_resp when done. + * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of + * the response. The caller needs to call iwl_free_resp when done. * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the * command queue, but after other high priority commands. valid only * with CMD_ASYNC. @@ -202,7 +201,6 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) * (i.e. mark it as non-idle). */ enum CMD_MODE { - CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), CMD_SEND_IN_RFKILL = BIT(2), @@ -427,7 +425,7 @@ struct iwl_trans; * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. * If RFkill is asserted in the middle of a SYNC host command, it must * return -ERFKILL straight away. - * May sleep only if CMD_SYNC is set + * May sleep only if CMD_ASYNC is not set * @tx: send an skb * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. @@ -463,6 +461,11 @@ struct iwl_trans; * @unref: release a reference previously taken with @ref. Note that * initially the reference count is 1, making an initial @unref * necessary to allow low power states. + * @dump_data: fill a data dump with debug data, maybe containing last + * TX'ed commands and similar. When called with a NULL buffer and + * zero buffer length, provide only the (estimated) required buffer + * length. Return the used buffer length. + * Note that the transport must fill in the proper file headers. */ struct iwl_trans_ops { @@ -470,6 +473,8 @@ struct iwl_trans_ops { void (*op_mode_leave)(struct iwl_trans *iwl_trans); int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill); + int (*update_sf)(struct iwl_trans *trans, + struct iwl_sf_region *st_fwrd_space); void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans); @@ -511,6 +516,10 @@ struct iwl_trans_ops { u32 value); void (*ref)(struct iwl_trans *trans); void (*unref)(struct iwl_trans *trans); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen); +#endif }; /** @@ -629,6 +638,17 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans, return trans->ops->start_fw(trans, fw, run_in_rfkill); } +static inline int iwl_trans_update_sf(struct iwl_trans *trans, + struct iwl_sf_region *st_fwrd_space) +{ + might_sleep(); + + if (trans->ops->update_sf) + return trans->ops->update_sf(trans, st_fwrd_space); + + return 0; +} + static inline void iwl_trans_stop_device(struct iwl_trans *trans) { might_sleep(); @@ -664,6 +684,16 @@ static inline void iwl_trans_unref(struct iwl_trans *trans) trans->ops->unref(trans); } +#ifdef CONFIG_IWLWIFI_DEBUGFS +static inline u32 iwl_trans_dump_data(struct iwl_trans *trans, + void *buf, u32 buflen) +{ + if (!trans->ops->dump_data) + return 0; + return trans->ops->dump_data(trans, buf, buflen); +} +#endif + static inline int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { @@ -677,7 +707,7 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans, return -EIO; if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { - IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return -EIO; } @@ -719,7 +749,7 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, return -EIO; if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return trans->ops->tx(trans, skb, dev_cmd, queue); } @@ -728,7 +758,7 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs) { if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); trans->ops->reclaim(trans, queue, ssn, skbs); } @@ -745,7 +775,7 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, might_sleep(); if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) - IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); trans->ops->txq_enable(trans, queue, fifo, sta_id, tid, frame_limit, ssn); @@ -762,7 +792,7 @@ static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, u32 txq_bm) { if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return trans->ops->wait_tx_queue_empty(trans, txq_bm); } diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index ccdd3b7..c30d7f6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -3,8 +3,9 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o coex.o -iwlmvm-y += led.o tt.o offloading.o +iwlmvm-y += tt.o offloading.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o +iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 8f4b03d..c8c3b38 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -106,7 +106,7 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) { - return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, sizeof(struct iwl_bt_coex_prio_tbl_cmd), &iwl_bt_prio_tbl); } @@ -124,10 +124,10 @@ const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = { }; static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { - cpu_to_le32(0xf0f0f0f0), - cpu_to_le32(0xc0c0c0c0), - cpu_to_le32(0xfcfcfcfc), - cpu_to_le32(0xff00ff00), + cpu_to_le32(0xf0f0f0f0), /* 50% */ + cpu_to_le32(0xc0c0c0c0), /* 25% */ + cpu_to_le32(0xfcfcfcfc), /* 75% */ + cpu_to_le32(0xfefefefe), /* 87.5% */ }; static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { @@ -300,8 +300,8 @@ static const __le64 iwl_ci_mask[][3] = { }; static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = { - cpu_to_le32(0x22002200), - cpu_to_le32(0x33113311), + cpu_to_le32(0x28412201), + cpu_to_le32(0x11118451), }; struct corunning_block_luts { @@ -565,7 +565,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) .id = BT_CONFIG, .len = { sizeof(*bt_cmd), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_SYNC, }; int ret; u32 flags; @@ -611,14 +610,14 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); if (IWL_MVM_BT_COEX_CORUNNING) { - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); } if (IWL_MVM_BT_COEX_MPLUT) { bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); } if (mvm->cfg->bt_shared_single_ant) @@ -663,7 +662,6 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, .data[0] = &bt_cmd, .len = { sizeof(*bt_cmd), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_SYNC, }; int ret = 0; @@ -717,7 +715,8 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, return ret; } -int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) +static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, + bool enable) { struct iwl_bt_coex_cmd *bt_cmd; /* Send ASYNC since this can be sent from an atomic context */ @@ -735,8 +734,7 @@ int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) return 0; /* nothing to do */ - if (mvmsta->bt_reduced_txpower_dbg || - mvmsta->bt_reduced_txpower == enable) + if (mvmsta->bt_reduced_txpower == enable) return 0; bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); @@ -803,23 +801,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, switch (vif->type) { case NL80211_IFTYPE_STATION: + /* Count BSSes vifs */ + data->num_bss_ifaces++; /* default smps_mode for BSS / P2P client is AUTOMATIC */ smps_mode = IEEE80211_SMPS_AUTOMATIC; - data->num_bss_ifaces++; - - /* - * Count unassoc BSSes, relax SMSP constraints - * and disable reduced Tx Power - */ - if (!vif->bss_conf.assoc) { - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - if (iwl_mvm_bt_coex_reduced_txp(mvm, - mvmvif->ap_sta_id, - false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - return; - } break; case NL80211_IFTYPE_AP: /* default smps_mode for AP / GO is OFF */ @@ -845,8 +830,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, /* ... relax constraints and disable rssi events */ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); - if (vif->type == NL80211_IFTYPE_STATION) + data->reduced_tx_power = false; + if (vif->type == NL80211_IFTYPE_STATION) { + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + } return; } @@ -857,6 +846,11 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = vif->type == NL80211_IFTYPE_AP ? IEEE80211_SMPS_OFF : IEEE80211_SMPS_DYNAMIC; + + /* relax SMPS contraints for next association */ + if (!vif->bss_conf.assoc) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + IWL_DEBUG_COEX(data->mvm, "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", mvmvif->id, data->notif->bt_status, bt_activity_grading, @@ -903,22 +897,18 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, /* if secondary is not NULL, it might be a GO */ data->secondary = chanctx_conf; - /* don't reduce the Tx power if in loose scheme */ + /* + * don't reduce the Tx power if one of these is true: + * we are in LOOSE + * single share antenna product + * BT is active + * we are associated + */ if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || - mvm->cfg->bt_shared_single_ant) { - data->reduced_tx_power = false; - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - return; - } - - /* reduced Txpower only if BT is on, so ...*/ - if (!data->notif->bt_status) { - /* ... cancel reduced Tx power ... */ - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || + !data->notif->bt_status) { data->reduced_tx_power = false; - - /* ... and there is no need to get reports on RSSI any more. */ + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); return; } @@ -1022,9 +1012,9 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) /* Don't spam the fw with the same command over and over */ if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, CMD_SYNC, + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, sizeof(cmd), &cmd)) - IWL_ERR(mvm, "Failed to send BT_CI cmd"); + IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); } @@ -1039,7 +1029,6 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); } -/* upon association, the fw will send in BT Coex notification */ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *dev_cmd) @@ -1278,7 +1267,6 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, .id = BT_CONFIG, .len = { sizeof(*bt_cmd), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_SYNC, }; if (!IWL_MVM_BT_COEX_CORUNNING) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 7694472..645b3cf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -193,8 +193,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, wkc.wep_key.key_offset = data->wep_key_idx; } - ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC, - sizeof(wkc), &wkc); + ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc); data->error = ret != 0; mvm->ptk_ivlen = key->iv_len; @@ -341,7 +340,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, struct iwl_host_cmd cmd = { .id = WOWLAN_PATTERNS, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .flags = CMD_SYNC, }; int i, err; @@ -518,7 +516,6 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, .id = REMOTE_WAKE_CONFIG_CMD, .len = { sizeof(*cfg), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_SYNC, }; int ret; @@ -666,10 +663,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (WARN_ON(!vif->bss_conf.assoc)) return -EINVAL; - /* hack */ - vif->bss_conf.assoc = false; + ret = iwl_mvm_mac_ctxt_add(mvm, vif); - vif->bss_conf.assoc = true; if (ret) return ret; @@ -705,7 +700,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return ret; rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); if (ret) return ret; @@ -719,7 +714,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, for (i = 1; i < MAX_BINDINGS; i++) quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(quota_cmd), "a_cmd); if (ret) IWL_ERR(mvm, "Failed to send quota: %d\n", ret); @@ -739,7 +734,7 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, }; struct iwl_host_cmd cmd = { .id = NON_QOS_TX_COUNTER_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; int err; u32 size; @@ -781,7 +776,7 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->seqno_valid = false; - if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC, + if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0, sizeof(query_cmd), &query_cmd)) IWL_ERR(mvm, "failed to set non-QoS seqno\n"); } @@ -796,7 +791,7 @@ iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm, if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID) cmd_len = sizeof(*cmd); - return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, cmd_len, cmd); } @@ -825,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, }; struct iwl_host_cmd d3_cfg_cmd = { .id = D3_CONFIG_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, .data[0] = &d3_cfg_cmd_data, .len[0] = sizeof(d3_cfg_cmd_data), }; @@ -975,7 +970,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (key_data.use_rsc_tsc) { struct iwl_host_cmd rsc_tsc_cmd = { .id = WOWLAN_TSC_RSC_PARAM, - .flags = CMD_SYNC, .data[0] = key_data.rsc_tsc, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .len[0] = sizeof(*key_data.rsc_tsc), @@ -989,7 +983,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (key_data.use_tkip) { ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_TKIP_PARAM, - CMD_SYNC, sizeof(tkip_cmd), + 0, sizeof(tkip_cmd), &tkip_cmd); if (ret) goto out; @@ -1006,8 +1000,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_KEK_KCK_MATERIAL, - CMD_SYNC, + WOWLAN_KEK_KCK_MATERIAL, 0, sizeof(kek_kck_cmd), &kek_kck_cmd); if (ret) @@ -1023,7 +1016,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; - ret = iwl_mvm_send_proto_offload(mvm, vif, false, CMD_SYNC); + ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); if (ret) goto out; @@ -1035,7 +1028,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; - ret = iwl_mvm_power_update_mac(mvm, vif); + ret = iwl_mvm_power_update_mac(mvm); if (ret) goto out; @@ -1466,7 +1459,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, } err_info; struct iwl_host_cmd cmd = { .id = WOWLAN_GET_STATUSES, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; struct iwl_wowlan_status_data status; struct iwl_wowlan_status *fw_status; @@ -1492,7 +1485,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, } /* only for tracing for now */ - ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL); + ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL); if (ret) IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 6047cfd..2e90ff7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -175,7 +175,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, mutex_lock(&mvm->mutex); iwl_dbgfs_update_pm(mvm, vif, param, val); - ret = iwl_mvm_power_update_mac(mvm, vif); + ret = iwl_mvm_power_update_mac(mvm); mutex_unlock(&mvm->mutex); return ret ?: count; @@ -262,10 +262,9 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; pos += scnprintf(buf+pos, bufsz-pos, - "ap_sta_id %d - reduced Tx power %d force %d\n", + "ap_sta_id %d - reduced Tx power %d\n", ap_sta_id, - mvm_sta->bt_reduced_txpower, - mvm_sta->bt_reduced_txpower_dbg); + mvm_sta->bt_reduced_txpower); } } @@ -283,41 +282,6 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - struct iwl_mvm_sta *mvmsta; - bool reduced_tx_power; - int ret; - - if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) - return -ENOTCONN; - - if (strtobool(buf, &reduced_tx_power) != 0) - return -EINVAL; - - mutex_lock(&mvm->mutex); - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id); - if (IS_ERR_OR_NULL(mvmsta)) { - mutex_unlock(&mvm->mutex); - return -ENOTCONN; - } - - mvmsta->bt_reduced_txpower_dbg = false; - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - reduced_tx_power); - if (!ret) - mvmsta->bt_reduced_txpower_dbg = true; - - mutex_unlock(&mvm->mutex); - - return ret ? : count; -} - static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, enum iwl_dbgfs_bf_mask param, int value) { @@ -452,9 +416,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, mutex_lock(&mvm->mutex); iwl_dbgfs_update_bf(vif, param, value); if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) - ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC); + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); else - ret = iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC); + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); mutex_unlock(&mvm->mutex); return ret ?: count; @@ -558,7 +522,6 @@ MVM_DEBUGFS_READ_FILE_OPS(mac_params); MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(reduced_txp, 10); void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -590,7 +553,6 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) S_IRUSR); MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE_VIF(reduced_txp, mvmvif->dbgfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, S_IRUSR | S_IWUSR); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index f462c9b..29ca726 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -65,9 +65,8 @@ #include "mvm.h" #include "sta.h" #include "iwl-io.h" -#include "iwl-prph.h" #include "debugfs.h" -#include "fw-error-dump.h" +#include "iwl-fw-error-dump.h" static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) @@ -681,7 +680,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, mvm->restart_fw++; /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL); + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL); mutex_unlock(&mvm->mutex); @@ -691,7 +690,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - iwl_write_prph(mvm->trans, DEVICE_SET_NMI_REG, 1); + iwl_force_nmi(mvm->trans); return count; } @@ -838,7 +837,7 @@ static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf, /* send updated bcast filtering configuration */ if (mvm->dbgfs_bcast_filtering.override && iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC, + err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, sizeof(cmd), &cmd); mutex_unlock(&mvm->mutex); @@ -910,7 +909,7 @@ static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm, /* send updated bcast filtering configuration */ if (mvm->dbgfs_bcast_filtering.override && iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC, + err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, sizeof(cmd), &cmd); mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 6174c02..6959fda 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -187,9 +187,9 @@ enum iwl_scan_type { * this number of packets were received (typically 1) * @passive2active: is auto switching from passive to active during scan allowed * @rxchain_sel_flags: RXON_RX_CHAIN_* - * @max_out_time: in usecs, max out of serving channel time + * @max_out_time: in TUs, max out of serving channel time * @suspend_time: how long to pause scan when returning to service channel: - * bits 0-19: beacon interal in usecs (suspend before executing) + * bits 0-19: beacon interal in TUs (suspend before executing) * bits 20-23: reserved * bits 24-31: number of beacons (suspend between channels) * @rxon_flags: RXON_FLG_* @@ -387,8 +387,8 @@ enum scan_framework_client { * @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 uSec to be out of assoceated channel - * @suspend_time: pause scan this long when returning to service channel + * @max_out_time: max TUs to be out of assoceated channel + * @suspend_time: pause scan this TUs when returning to service channel * @flags: RXON flags * @filter_flags: RXONfilter * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz. diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 3d99cf5..883e702 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -99,7 +99,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) }; IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0, sizeof(tx_ant_cmd), &tx_ant_cmd); } @@ -137,6 +137,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr); mvm->umac_error_event_table = le32_to_cpu(palive2->error_info_addr); + mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr); + mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size); alive_data->valid = le16_to_cpu(palive2->status) == IWL_ALIVE_STATUS_OK; @@ -180,6 +182,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, int ret, i; enum iwl_ucode_type old_type = mvm->cur_ucode; static const u8 alive_cmd[] = { MVM_ALIVE }; + struct iwl_sf_region st_fwrd_space; fw = iwl_get_ucode_image(mvm, ucode_type); if (WARN_ON(!fw)) @@ -215,6 +218,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return -EIO; } + /* + * update the sdio allocation according to the pointer we get in the + * alive notification. + */ + st_fwrd_space.addr = mvm->sf_space.addr; + st_fwrd_space.size = mvm->sf_space.size; + ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space); + iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); /* @@ -256,7 +267,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); - return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, sizeof(phy_cfg_cmd), &phy_cfg_cmd); } @@ -295,7 +306,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) /* Read the NVM only at driver load time, no need to do this twice */ if (read_nvm) { /* Read nvm */ - ret = iwl_nvm_init(mvm); + ret = iwl_nvm_init(mvm, true); if (ret) { IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); goto error; @@ -303,7 +314,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) } /* In case we read the NVM from external file, load it to the NIC */ - if (iwlwifi_mod_params.nvm_file) + if (mvm->nvm_file_name) iwl_mvm_load_nvm_to_nic(mvm); ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 7110ec2..8b53027 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -685,7 +685,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, struct iwl_mac_ctx_cmd *cmd) { - int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC, + int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, sizeof(*cmd), cmd); if (ret) IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", @@ -693,19 +693,39 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, return ret; } -/* - * Fill the specific data for mac context of type station or p2p client - */ -static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_data_sta *ctxt_sta, - bool force_assoc_off) +static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action, bool force_assoc_off) { + struct iwl_mac_ctx_cmd cmd = {}; + struct iwl_mac_data_sta *ctxt_sta; + + WARN_ON(vif->type != NL80211_IFTYPE_STATION); + + /* Fill the common data for all mac context types */ + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + if (vif->p2p) { + struct ieee80211_p2p_noa_attr *noa = + &vif->bss_conf.p2p_noa_attr; + + cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & + IEEE80211_P2P_OPPPS_CTWINDOW_MASK); + ctxt_sta = &cmd.p2p_sta.sta; + } else { + ctxt_sta = &cmd.sta; + } + /* We need the dtim_period to set the MAC as associated */ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && !force_assoc_off) { u32 dtim_offs; + /* Allow beacons to pass through as long as we are not + * associated, or we do not have dtim period information. + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + /* * The DTIM count counts down, so when it is N that means N * more beacon intervals happen until the DTIM TBTT. Therefore @@ -752,51 +772,6 @@ static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm, ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval); ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid); -} - -static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_STATION || vif->p2p); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); - - /* Allow beacons to pass through as long as we are not associated,or we - * do not have dtim period information */ - if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period) - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - else - cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON); - - /* Fill the data specific for station mode */ - iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta, - action == FW_CTXT_ACTION_ADD); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr; - - WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); - - /* Fill the data specific for station mode */ - iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta, - action == FW_CTXT_ACTION_ADD); - - cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -1134,16 +1109,12 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, } static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 action) + u32 action, bool force_assoc_off) { switch (vif->type) { case NL80211_IFTYPE_STATION: - if (!vif->p2p) - return iwl_mvm_mac_ctxt_cmd_station(mvm, vif, - action); - else - return iwl_mvm_mac_ctxt_cmd_p2p_client(mvm, vif, - action); + return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action, + force_assoc_off); break; case NL80211_IFTYPE_AP: if (!vif->p2p) @@ -1173,7 +1144,8 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) vif->addr, ieee80211_vif_type_p2p(vif))) return -EIO; - ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD); + ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, + true); if (ret) return ret; @@ -1184,7 +1156,8 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return 0; } -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -1192,7 +1165,8 @@ int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) vif->addr, ieee80211_vif_type_p2p(vif))) return -EIO; - return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY); + return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, + force_assoc_off); } int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -1211,7 +1185,7 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->color)); cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, sizeof(cmd), &cmd); if (ret) { IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); @@ -1237,11 +1211,23 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, u32 rate __maybe_unused = le32_to_cpu(beacon->beacon_notify_hdr.initial_rate); + lockdep_assert_held(&mvm->mutex); + IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n", status & TX_STATUS_MSK, beacon->beacon_notify_hdr.failure_frame, le64_to_cpu(beacon->tsf), rate); + + if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) { + if (!ieee80211_csa_is_complete(mvm->csa_vif)) { + iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif); + } else { + ieee80211_csa_finish(mvm->csa_vif); + mvm->csa_vif = NULL; + } + } + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 97c3dea..7215f59 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -276,7 +276,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TIMING_BEACON_ONLY | IEEE80211_HW_CONNECTION_MONITOR | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; @@ -286,8 +285,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_RADIOTAP_MCS_HAVE_STBC; hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC; hw->rate_control_algorithm = "iwl-mvm-rs"; - hw->uapsd_queues = IWL_UAPSD_AC_INFO; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; /* * Enable 11w if advertised by firmware and software crypto @@ -298,9 +295,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) !iwlwifi_mod_params.sw_crypto) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - /* Disable uAPSD due to firmware issues */ - if (true) - hw->flags &= ~IEEE80211_HW_SUPPORTS_UAPSD; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && + IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 && + !iwlwifi_mod_params.uapsd_disable) { + hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; + hw->uapsd_queues = IWL_UAPSD_AC_INFO; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; + } hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); @@ -320,6 +321,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW) + hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; hw->wiphy->n_iface_combinations = ARRAY_SIZE(iwl_mvm_iface_combinations); @@ -539,13 +543,22 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, return -EACCES; /* return from D0i3 before starting a new Tx aggregation */ - if (action == IEEE80211_AMPDU_TX_START) { + switch (action) { + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_OPERATIONAL: iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG); tx_agg_ref = true; /* - * wait synchronously until D0i3 exit to get the correct - * sequence number for the tid + * for tx start, wait synchronously until D0i3 exit to + * get the correct sequence number for the tid. + * additionally, some other ampdu actions use direct + * target access, which is not handled automatically + * by the trans layer (unlike commands), so wait for + * d0i3 exit in these cases as well. */ if (!wait_event_timeout(mvm->d0i3_exit_waitq, !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) { @@ -553,6 +566,9 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); return -EIO; } + break; + default: + break; } mutex_lock(&mvm->mutex); @@ -757,7 +773,7 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, .pwr_restriction = cpu_to_le16(tx_power), }; - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, sizeof(reduce_txpwr_cmd), &reduce_txpwr_cmd); } @@ -816,12 +832,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_release; - ret = iwl_mvm_power_update_mac(mvm, vif); + ret = iwl_mvm_power_update_mac(mvm); if (ret) goto out_release; /* beacon filtering */ - ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC); + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); if (ret) goto out_remove_mac; @@ -967,7 +983,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; - iwl_mvm_power_update_mac(mvm, vif); + iwl_mvm_power_update_mac(mvm); iwl_mvm_mac_ctxt_remove(mvm, vif); out_release: @@ -1005,7 +1021,7 @@ static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); - ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, len, cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd); if (ret) IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); } @@ -1021,7 +1037,7 @@ static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) return; - ieee80211_iterate_active_interfaces( + ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_mc_iface_iterator, &iter_data); } @@ -1228,7 +1244,7 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) return 0; - return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, sizeof(cmd), &cmd); } #else @@ -1255,7 +1271,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); @@ -1335,10 +1351,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); iwl_mvm_sf_update(mvm, vif, false); - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC)); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) { - ret = iwl_mvm_power_update_mac(mvm, vif); + ret = iwl_mvm_power_update_mac(mvm); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } @@ -1349,16 +1365,19 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, } if (changes & BSS_CHANGED_CQM) { - IWL_DEBUG_MAC80211(mvm, "cqm info_changed"); + IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); /* reset cqm events tracking */ mvmvif->bf_data.last_cqm_event = 0; - ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC); - if (ret) - IWL_ERR(mvm, "failed to update CQM thresholds\n"); + if (mvmvif->bf_data.bf_enabled) { + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + if (ret) + IWL_ERR(mvm, + "failed to update CQM thresholds\n"); + } } if (changes & BSS_CHANGED_ARP_FILTER) { - IWL_DEBUG_MAC80211(mvm, "arp filter changed"); + IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); iwl_mvm_configure_bcast_filter(mvm, vif); } } @@ -1404,7 +1423,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, mvmvif->ap_ibss_active = true; /* power updated needs to be done before quotas */ - iwl_mvm_power_update_mac(mvm, vif); + iwl_mvm_power_update_mac(mvm); ret = iwl_mvm_update_quotas(mvm, vif); if (ret) @@ -1412,7 +1431,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); @@ -1422,7 +1441,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, return 0; out_quota_failed: - iwl_mvm_power_update_mac(mvm, vif); + iwl_mvm_power_update_mac(mvm); mvmvif->ap_ibss_active = false; iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: @@ -1452,13 +1471,13 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_power_update_mac(mvm, vif); + iwl_mvm_power_update_mac(mvm); iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -1479,7 +1498,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | BSS_CHANGED_BANDWIDTH) && - iwl_mvm_mac_ctxt_changed(mvm, vif)) + iwl_mvm_mac_ctxt_changed(mvm, vif, false)) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); /* Need to send a new beacon template to the FW */ @@ -1497,6 +1516,9 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) + iwl_mvm_sched_scan_stop(mvm, true); + switch (vif->type) { case NL80211_IFTYPE_STATION: iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); @@ -1527,7 +1549,7 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, switch (mvm->scan_status) { case IWL_MVM_SCAN_SCHED: - ret = iwl_mvm_sched_scan_stop(mvm); + ret = iwl_mvm_sched_scan_stop(mvm, true); if (ret) { ret = -EBUSY; goto out; @@ -1715,14 +1737,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { /* enable beacon filtering */ - if (vif->bss_conf.dtim_period) - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, - CMD_SYNC)); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { /* disable beacon filtering */ - WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC)); + WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0)); ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { @@ -1779,7 +1799,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); mutex_unlock(&mvm->mutex); return ret; } @@ -1814,6 +1834,11 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (!iwl_mvm_is_idle(mvm)) { + ret = -EBUSY; + goto out; + } + switch (mvm->scan_status) { case IWL_MVM_SCAN_OS: IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n"); @@ -1867,7 +1892,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); - ret = iwl_mvm_sched_scan_stop(mvm); + ret = iwl_mvm_sched_scan_stop(mvm, false); mutex_unlock(&mvm->mutex); iwl_mvm_wait_for_async_handlers(mvm); @@ -2163,10 +2188,10 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, return; mutex_lock(&mvm->mutex); + iwl_mvm_bt_coex_vif_change(mvm); iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, ctx->rx_chains_static, ctx->rx_chains_dynamic); - iwl_mvm_bt_coex_vif_change(mvm); mutex_unlock(&mvm->mutex); } @@ -2186,6 +2211,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_AP: + /* Unless it's a CSA flow we have nothing to do here */ + if (vif->csa_active) { + mvmvif->ap_ibss_active = true; + break; + } case NL80211_IFTYPE_ADHOC: /* * The AP binding flow is handled as part of the start_ap flow @@ -2209,7 +2239,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, * Power state must be updated before quotas, * otherwise fw will complain. */ - iwl_mvm_power_update_mac(mvm, vif); + iwl_mvm_power_update_mac(mvm); /* Setting the quota at this stage is only required for monitor * interfaces. For the other types, the bss_info changed flow @@ -2222,11 +2252,17 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, goto out_remove_binding; } + /* Handle binding during CSA */ + if (vif->type == NL80211_IFTYPE_AP) { + iwl_mvm_update_quotas(mvm, vif); + iwl_mvm_mac_ctxt_changed(mvm, vif, false); + } + goto out_unlock; out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_power_update_mac(mvm, vif); + iwl_mvm_power_update_mac(mvm); out_unlock: mutex_unlock(&mvm->mutex); if (ret) @@ -2246,22 +2282,29 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); switch (vif->type) { - case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: goto out_unlock; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; iwl_mvm_update_quotas(mvm, NULL); break; + case NL80211_IFTYPE_AP: + /* This part is triggered only during CSA */ + if (!vif->csa_active || !mvmvif->ap_ibss_active) + goto out_unlock; + + mvmvif->ap_ibss_active = false; + iwl_mvm_update_quotas(mvm, NULL); + /*TODO: bt_coex notification here? */ default: break; } iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_power_update_mac(mvm, vif); out_unlock: mvmvif->phy_ctxt = NULL; + iwl_mvm_power_update_mac(mvm); mutex_unlock(&mvm->mutex); } @@ -2325,9 +2368,8 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, return -EINVAL; if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) - return iwl_mvm_enable_beacon_filter(mvm, vif, - CMD_SYNC); - return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC); + return iwl_mvm_enable_beacon_filter(mvm, vif, 0); + return iwl_mvm_disable_beacon_filter(mvm, vif, 0); } return -EOPNOTSUPP; @@ -2348,6 +2390,53 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, } #endif +static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active, + "Another CSA is already in progress")) + goto out_unlock; + + IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n", + chandef->center_freq1); + mvm->csa_vif = vif; + +out_unlock: + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 queues, bool drop) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif; + struct iwl_mvm_sta *mvmsta; + + if (!vif || vif->type != NL80211_IFTYPE_STATION) + return; + + mutex_lock(&mvm->mutex); + mvmvif = iwl_mvm_vif_from_mac80211(vif); + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id); + + if (WARN_ON_ONCE(!mvmsta)) + goto done; + + if (drop) { + if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true)) + IWL_ERR(mvm, "flush request fail\n"); + } else { + iwl_trans_wait_tx_queue_empty(mvm->trans, + mvmsta->tfd_queue_msk); + } +done: + mutex_unlock(&mvm->mutex); +} + const struct ieee80211_ops iwl_mvm_hw_ops = { .tx = iwl_mvm_mac_tx, .ampdu_action = iwl_mvm_mac_ampdu_action, @@ -2371,6 +2460,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .sta_rc_update = iwl_mvm_sta_rc_update, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, + .flush = iwl_mvm_mac_flush, .sched_scan_start = iwl_mvm_mac_sched_scan_start, .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, .set_key = iwl_mvm_mac_set_key, @@ -2390,6 +2480,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .set_tim = iwl_mvm_set_tim, + .channel_switch_beacon = iwl_mvm_channel_switch_beacon, + CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 17c42da..fcc6c29 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -490,6 +490,7 @@ struct iwl_mvm { u32 log_event_table; u32 umac_error_event_table; bool support_umac_log; + struct iwl_sf_region sf_space; u32 ampdu_ref; @@ -501,6 +502,7 @@ struct iwl_mvm { u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; + const char *nvm_file_name; struct iwl_nvm_data *nvm_data; /* NVM sections */ struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS]; @@ -589,7 +591,9 @@ struct iwl_mvm { u32 *fw_error_rxf; u32 fw_error_rxf_len; +#ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; +#endif struct ieee80211_vif *p2p_device_vif; @@ -642,6 +646,8 @@ struct iwl_mvm { /* Indicate if device power save is allowed */ bool ps_disabled; + + struct ieee80211_vif *csa_vif; }; /* Extract MVM priv from op_mode and _hw */ @@ -757,7 +763,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd); /* NVM */ -int iwl_nvm_init(struct iwl_mvm *mvm); +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); int iwl_mvm_up(struct iwl_mvm *mvm); @@ -808,7 +814,8 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off); int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -852,7 +859,7 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); -int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm); +int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify); int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); @@ -887,7 +894,7 @@ int rs_pretty_print_rate(char *buf, const u32 rate); /* power management */ int iwl_mvm_power_update_device(struct iwl_mvm *mvm); -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm); int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, char *buf, int bufsz); @@ -896,8 +903,18 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +#ifdef CONFIG_IWLWIFI_LEDS int iwl_mvm_leds_init(struct iwl_mvm *mvm); void iwl_mvm_leds_exit(struct iwl_mvm *mvm); +#else +static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) +{ + return 0; +} +static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) +{ +} +#endif /* D3 (WoWLAN, NetDetect) */ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); @@ -950,7 +967,6 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum ieee80211_band band); u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, u8 ac); -int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable); enum iwl_bt_kill_msk { BT_KILL_MSK_DEFAULT, @@ -981,17 +997,11 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 flags); -int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, bool enable); -int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool force, - u32 flags); - /* SMPS */ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum iwl_mvm_smps_type_request req_type, enum ieee80211_smps_mode smps_request); +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm); /* Low latency */ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -1015,6 +1025,9 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) return mvmvif->low_latency; } +/* Assoc status */ +bool iwl_mvm_is_idle(struct iwl_mvm *mvm); + /* Thermal management and CT-kill */ void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); void iwl_mvm_tt_handler(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index cf2d09f..808f78f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -74,6 +74,12 @@ #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 +/* load nvm chunk response */ +enum { + READ_NVM_CHUNK_SUCCEED = 0, + READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 +}; + /* * prepare the NVM host command w/ the pointers to the nvm buffer * and send it to fw @@ -90,7 +96,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, .len = { sizeof(struct iwl_nvm_access_cmd), length }, - .flags = CMD_SYNC | CMD_SEND_IN_RFKILL, + .flags = CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, data }, /* data may come from vmalloc, so use _DUP */ .dataflags = { 0, IWL_HCMD_DFL_DUP }, @@ -112,7 +118,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; int ret, bytes_read, offset_read; @@ -139,10 +145,26 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, offset_read = le16_to_cpu(nvm_resp->offset); resp_data = nvm_resp->data; if (ret) { - IWL_ERR(mvm, - "NVM access command failed with status %d (device: %s)\n", - ret, mvm->cfg->name); - ret = -EINVAL; + if ((offset != 0) && + (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { + /* + * meaning of NOT_VALID_ADDRESS: + * driver try to read chunk from address that is + * multiple of 2K and got an error since addr is empty. + * meaning of (offset != 0): driver already + * read valid data from another chunk so this case + * is not an error. + */ + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", + offset); + ret = 0; + } else { + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM access command failed with status %d (device: %s)\n", + ret, mvm->cfg->name); + ret = -EIO; + } goto exit; } @@ -211,9 +233,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, while (ret == length) { ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); if (ret < 0) { - IWL_ERR(mvm, - "Cannot read NVM from section %d offset %d, length %d\n", - section, offset, length); + IWL_DEBUG_EEPROM(mvm->trans->dev, + "Cannot read NVM from section %d offset %d, length %d\n", + section, offset, length); return ret; } offset += ret; @@ -238,13 +260,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return NULL; } } else { + /* SW and REGULATORY sections are mandatory */ if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data || !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { IWL_ERR(mvm, "Can't parse empty family 8000 NVM sections\n"); return NULL; } + /* MAC_OVERRIDE or at least HW section must exist */ + if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && + !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { + IWL_ERR(mvm, + "Can't parse mac_address, empty sections\n"); + return NULL; + } } if (WARN_ON(!mvm->cfg)) @@ -311,16 +340,16 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) * get here after that we assume the NVM request can be satisfied * synchronously. */ - ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, + ret = request_firmware(&fw_entry, mvm->nvm_file_name, mvm->trans->dev); if (ret) { IWL_ERR(mvm, "ERROR: %s isn't available %d\n", - iwlwifi_mod_params.nvm_file, ret); + mvm->nvm_file_name, ret); return ret; } IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", - iwlwifi_mod_params.nvm_file, fw_entry->size); + mvm->nvm_file_name, fw_entry->size); if (fw_entry->size < sizeof(*file_sec)) { IWL_ERR(mvm, "NVM file too small\n"); @@ -427,53 +456,28 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) return ret; } -int iwl_nvm_init(struct iwl_mvm *mvm) +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) { - int ret, i, section; + int ret, section; u8 *nvm_buffer, *temp; - int nvm_to_read[NVM_MAX_NUM_SECTIONS]; - int num_of_sections_to_read; if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) return -EINVAL; - /* load external NVM if configured */ - if (iwlwifi_mod_params.nvm_file) { - /* move to External NVM flow */ - ret = iwl_mvm_read_external_nvm(mvm); - if (ret) - return ret; - } else { - /* list of NVM sections we are allowed/need to read */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { - nvm_to_read[0] = mvm->cfg->nvm_hw_section_num; - nvm_to_read[1] = NVM_SECTION_TYPE_SW; - nvm_to_read[2] = NVM_SECTION_TYPE_CALIBRATION; - nvm_to_read[3] = NVM_SECTION_TYPE_PRODUCTION; - num_of_sections_to_read = 4; - } else { - nvm_to_read[0] = NVM_SECTION_TYPE_SW; - nvm_to_read[1] = NVM_SECTION_TYPE_CALIBRATION; - nvm_to_read[2] = NVM_SECTION_TYPE_PRODUCTION; - nvm_to_read[3] = NVM_SECTION_TYPE_REGULATORY; - nvm_to_read[4] = NVM_SECTION_TYPE_MAC_OVERRIDE; - num_of_sections_to_read = 5; - } - + /* load NVM values from nic */ + if (read_nvm_from_nic) { /* Read From FW NVM */ IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - /* TODO: find correct NVM max size for a section */ nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, GFP_KERNEL); if (!nvm_buffer) return -ENOMEM; - for (i = 0; i < num_of_sections_to_read; i++) { - section = nvm_to_read[i]; + for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { /* we override the constness for initial read */ ret = iwl_nvm_read_section(mvm, section, nvm_buffer); if (ret < 0) - break; + continue; temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); if (!temp) { ret = -ENOMEM; @@ -502,15 +506,21 @@ int iwl_nvm_init(struct iwl_mvm *mvm) mvm->nvm_hw_blob.size = ret; break; } - WARN(1, "section: %d", section); } #endif } kfree(nvm_buffer); - if (ret < 0) + } + + /* load external NVM if configured */ + if (mvm->nvm_file_name) { + /* move to External NVM flow */ + ret = iwl_mvm_read_external_nvm(mvm); + if (ret) return ret; } + /* parse the relevant nvm sections */ mvm->nvm_data = iwl_parse_nvm_sections(mvm); if (!mvm->nvm_data) return -ENODATA; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 7a5a8ba..cc2f7de 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -79,8 +79,8 @@ #include "iwl-prph.h" #include "rs.h" #include "fw-api-scan.h" -#include "fw-error-dump.h" #include "time-event.h" +#include "iwl-fw-error-dump.h" /* * module name, copyright, version, etc. @@ -220,7 +220,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), - RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), + RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true), RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, iwl_mvm_rx_ant_coupling_notif, true), @@ -466,13 +466,24 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, min_backoff = calc_min_backoff(trans, cfg); iwl_mvm_tt_initialize(mvm, min_backoff); + /* set the nvm_file_name according to priority */ + if (iwlwifi_mod_params.nvm_file) + mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; + else + mvm->nvm_file_name = mvm->cfg->default_nvm_file; + + if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, + "not allowing power-up and not having nvm_file\n")) + goto out_free; /* - * If the NVM exists in an external file, - * there is no need to unnecessarily power up the NIC at driver load + * Even if nvm exists in the nvm_file driver should read agin the nvm + * from the nic because there might be entries that exist in the OTP + * and not in the file. + * for nics with no_power_up_nic_in_init: rely completley on nvm_file */ - if (iwlwifi_mod_params.nvm_file) { - err = iwl_nvm_init(mvm); + if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { + err = iwl_nvm_init(mvm, false); if (err) goto out_free; } else { @@ -519,7 +530,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); - if (!iwlwifi_mod_params.nvm_file) + if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) iwl_trans_op_mode_leave(trans); ieee80211_free_hw(mvm->hw); return NULL; @@ -816,6 +827,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; u32 file_len; + u32 trans_len; lockdep_assert_held(&mvm->mutex); @@ -827,6 +839,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(*dump_file) + sizeof(*dump_data) * 2; + trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); + if (trans_len) + file_len += trans_len; + dump_file = vmalloc(file_len); if (!dump_file) return; @@ -840,7 +856,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); - dump_data = (void *)((u8 *)dump_data->data + mvm->fw_error_rxf_len); + dump_data = iwl_mvm_fw_error_next_data(dump_data); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); @@ -858,6 +874,15 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) kfree(mvm->fw_error_sram); mvm->fw_error_sram = NULL; mvm->fw_error_sram_len = 0; + + if (trans_len) { + void *buf = iwl_mvm_fw_error_next_data(dump_data); + u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, + trans_len); + dump_data = (void *)((u8 *)buf + real_trans_len); + dump_file->file_len = + cpu_to_le32(file_len - trans_len + real_trans_len); + } } #endif @@ -1143,7 +1168,7 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); struct iwl_host_cmd get_status_cmd = { .id = WOWLAN_GET_STATUSES, - .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB, + .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, }; struct iwl_wowlan_status *status; int ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 237efe0..539f3a9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -156,6 +156,18 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, idle_cnt = chains_static; active_cnt = chains_dynamic; + /* In scenarios where we only ever use a single-stream rates, + * i.e. legacy 11b/g/a associations, single-stream APs or even + * static SMPS, enable both chains to get diversity, improving + * the case where we're far enough from the AP that attenuation + * between the two antennas is sufficiently different to impact + * performance. + */ + if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) { + idle_cnt = 2; + active_cnt = 2; + } + cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant << PHY_RX_CHAIN_VALID_POS); cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); @@ -187,7 +199,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef, chains_static, chains_dynamic); - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, sizeof(struct iwl_phy_context_cmd), &cmd); if (ret) @@ -202,18 +214,15 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { - int ret; - WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && ctxt->ref); lockdep_assert_held(&mvm->mutex); ctxt->channel = chandef->chan; - ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); - return ret; + return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD, 0); } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 78309f7..c182a8b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -123,28 +123,6 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); } -int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, bool enable) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - .ba_enable_beacon_abort = cpu_to_le32(enable), - }; - - if (!mvmvif->bf_data.bf_enabled) - return 0; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN) - cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); - - mvmvif->bf_data.ba_enabled = enable; - iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC); -} - static void iwl_mvm_power_log(struct iwl_mvm *mvm, struct iwl_mac_power_cmd *cmd) { @@ -268,10 +246,30 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; } +static void iwl_mvm_binding_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned long *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!mvmvif->phy_ctxt) + return; + + if (vif->type == NL80211_IFTYPE_STATION || + vif->type == NL80211_IFTYPE_AP) + __set_bit(mvmvif->phy_ctxt->id, data); +} + static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + unsigned long phy_ctxt_counter = 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_binding_iterator, + &phy_ctxt_counter); if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, ETH_ALEN)) @@ -289,6 +287,13 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, IEEE80211_P2P_OPPPS_ENABLE_BIT)) return false; + /* + * Avoid using uAPSD if client is in DCM - + * low latency issue in Miracast + */ + if (hweight8(phy_ctxt_counter) >= 2) + return false; + return true; } @@ -429,7 +434,7 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); #endif - return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0, sizeof(cmd), &cmd); } @@ -455,7 +460,7 @@ int iwl_mvm_power_update_device(struct iwl_mvm *mvm) "Sending device power command with flags = 0x%X\n", cmd.flags); - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, sizeof(cmd), + return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd), &cmd); } @@ -613,11 +618,15 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm, ap_same_channel = (bss_mvmvif->phy_ctxt->id == ap_mvmvif->phy_ctxt->id); - /* bss is not stand alone: enable PM if alone on its channel */ - if (vifs->bss_active && !(client_same_channel || ap_same_channel) && + /* clients are not stand alone: enable PM if DCM */ + if (!(client_same_channel || ap_same_channel) && (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) { + if (vifs->bss_active) bss_mvmvif->pm_enabled = true; - return; + if (vifs->p2p_active && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)) + p2p_mvmvif->pm_enabled = true; + return; } /* @@ -633,55 +642,6 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm, } } -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif; - struct iwl_power_vifs vifs = {}; - bool ba_enable; - int ret; - - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_power_set_pm(mvm, &vifs); - - /* disable PS if CAM */ - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) { - mvm->ps_disabled = true; - } else { - /* don't update device power state unless we add / remove monitor */ - if (vifs.monitor_vif) { - if (vifs.monitor_active) - mvm->ps_disabled = true; - ret = iwl_mvm_power_update_device(mvm); - if (ret) - return ret; - } - } - - if (vifs.bss_vif) { - ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); - if (ret) - return ret; - } - - if (vifs.p2p_vif) { - ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); - if (ret) - return ret; - } - - if (!vifs.bf_vif) - return 0; - - vif = vifs.bf_vif; - mvmvif = iwl_mvm_vif_from_mac80211(vif); - - ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || - !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif)); - - return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable); -} - #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, char *buf, @@ -801,7 +761,7 @@ static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - if (mvmvif != mvm->bf_allowed_vif || + if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period || vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; @@ -829,6 +789,26 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false); } +static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + + if (!mvmvif->bf_data.bf_enabled) + return 0; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); + + mvmvif->bf_data.ba_enabled = enable; + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); +} + int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 flags) @@ -848,6 +828,55 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, return ret; } +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) +{ + struct iwl_mvm_vif *mvmvif; + struct iwl_power_vifs vifs = {}; + bool ba_enable; + int ret; + + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_power_set_pm(mvm, &vifs); + + /* disable PS if CAM */ + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) { + mvm->ps_disabled = true; + } else { + /* don't update device power state unless we add / remove monitor */ + if (vifs.monitor_vif) { + if (vifs.monitor_active) + mvm->ps_disabled = true; + ret = iwl_mvm_power_update_device(mvm); + if (ret) + return ret; + } + } + + if (vifs.bss_vif) { + ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); + if (ret) + return ret; + } + + if (vifs.p2p_vif) { + ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); + if (ret) + return ret; + } + + if (!vifs.bf_vif) + return 0; + + mvmvif = iwl_mvm_vif_from_mac80211(vifs.bf_vif); + + ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || + !vifs.bf_vif->bss_conf.ps || + iwl_mvm_vif_low_latency(mvmvif)); + + return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable); +} + int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool enable, u32 flags) @@ -871,9 +900,10 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, if (WARN_ON(!dtimper_msec)) return 0; - cmd.flags |= - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); cmd.skip_dtim_periods = 300 / dtimper_msec; + if (cmd.skip_dtim_periods) + cmd.flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); } iwl_mvm_power_log(mvm, &cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -904,23 +934,3 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, return ret; } - -int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool force, - u32 flags) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (mvmvif != mvm->bf_allowed_vif) - return 0; - - if (!mvmvif->bf_data.bf_enabled) { - /* disable beacon filtering explicitly if force is true */ - if (force) - return iwl_mvm_disable_beacon_filter(mvm, vif, flags); - return 0; - } - - return iwl_mvm_enable_beacon_filter(mvm, vif, flags); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 35e86e0..ba68d7b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -285,7 +285,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) iwl_mvm_adjust_quota_for_noa(mvm, &cmd); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send quota: %d\n", ret); diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index d44b2b3..306a6ca 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -211,7 +211,7 @@ static const struct rs_tx_column rs_tx_columns[] = { .next_columns = { RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_SISO_ANT_A, - RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, RS_COLUMN_INVALID, RS_COLUMN_INVALID, RS_COLUMN_INVALID, @@ -223,8 +223,8 @@ static const struct rs_tx_column rs_tx_columns[] = { .ant = ANT_B, .next_columns = { RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_SISO_ANT_A, RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, RS_COLUMN_INVALID, RS_COLUMN_INVALID, RS_COLUMN_INVALID, @@ -238,10 +238,10 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2, RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_INVALID, + RS_COLUMN_INVALID, }, .checks = { rs_siso_allow, @@ -254,10 +254,10 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_A, RS_COLUMN_MIMO2, RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_SISO_ANT_A_SGI, RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_INVALID, + RS_COLUMN_INVALID, }, .checks = { rs_siso_allow, @@ -271,10 +271,10 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_MIMO2_SGI, RS_COLUMN_SISO_ANT_A, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_MIMO2, RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, }, .checks = { rs_siso_allow, @@ -289,10 +289,10 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_A_SGI, RS_COLUMN_MIMO2_SGI, RS_COLUMN_SISO_ANT_B, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2, RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, }, .checks = { rs_siso_allow, @@ -304,12 +304,12 @@ static const struct rs_tx_column rs_tx_columns[] = { .ant = ANT_AB, .next_columns = { RS_COLUMN_SISO_ANT_A, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_MIMO2_SGI, RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, }, .checks = { rs_mimo_allow, @@ -321,12 +321,12 @@ static const struct rs_tx_column rs_tx_columns[] = { .sgi = true, .next_columns = { RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2, RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, }, .checks = { rs_mimo_allow, @@ -1031,7 +1031,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, return; } -#ifdef CPTCFG_MAC80211_DEBUGFS +#ifdef CONFIG_MAC80211_DEBUGFS /* Disable last tx check if we are debugging with fixed rate */ if (lq_sta->dbg_fixed_rate) { IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n"); @@ -1335,105 +1335,50 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); } -/* - * Find starting rate for new "search" high-throughput mode of modulation. - * Goal is to find lowest expected rate (under perfect conditions) that is - * above the current measured throughput of "active" mode, to give new mode - * a fair chance to prove itself without too many challenges. - * - * This gets called when transitioning to more aggressive modulation - * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive - * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need - * to decrease to match "active" throughput. When moving from MIMO to SISO, - * bit rate will typically need to increase, but not if performance was bad. - */ static s32 rs_get_best_rate(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, /* "search" */ - u16 rate_mask, s8 index) + unsigned long rate_mask, s8 index) { - /* "active" values */ struct iwl_scale_tbl_info *active_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 active_sr = active_tbl->win[index].success_ratio; - s32 active_tpt = active_tbl->expected_tpt[index]; - /* expected "search" throughput */ + s32 success_ratio = active_tbl->win[index].success_ratio; + u16 expected_current_tpt = active_tbl->expected_tpt[index]; const u16 *tpt_tbl = tbl->expected_tpt; - - s32 new_rate, high, low, start_hi; u16 high_low; - s8 rate = index; - - new_rate = high = low = start_hi = IWL_RATE_INVALID; - - while (1) { - high_low = rs_get_adjacent_rate(mvm, rate, rate_mask, - tbl->rate.type); - - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; + u32 target_tpt; + int rate_idx; - /* - * Lower the "search" bit rate, to give new "search" mode - * approximately the same throughput as "active" if: - * - * 1) "Active" mode has been working modestly well (but not - * great), and expected "search" throughput (under perfect - * conditions) at candidate rate is above the actual - * measured "active" throughput (but less than expected - * "active" throughput under perfect conditions). - * OR - * 2) "Active" mode has been working perfectly or very well - * and expected "search" throughput (under perfect - * conditions) at candidate rate is above expected - * "active" throughput (under perfect conditions). - */ - if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && - ((active_sr > RS_SR_FORCE_DECREASE) && - (active_sr <= IWL_RATE_HIGH_TH) && - (tpt_tbl[rate] <= active_tpt))) || - ((active_sr >= IWL_RATE_SCALE_SWITCH) && - (tpt_tbl[rate] > active_tpt))) { - /* (2nd or later pass) - * If we've already tried to raise the rate, and are - * now trying to lower it, use the higher rate. */ - if (start_hi != IWL_RATE_INVALID) { - new_rate = start_hi; - break; - } - - new_rate = rate; - - /* Loop again with lower rate */ - if (low != IWL_RATE_INVALID) - rate = low; + if (success_ratio > RS_SR_NO_DECREASE) { + target_tpt = 100 * expected_current_tpt; + IWL_DEBUG_RATE(mvm, + "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", + success_ratio, target_tpt); + } else { + target_tpt = lq_sta->last_tpt; + IWL_DEBUG_RATE(mvm, + "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n", + success_ratio, target_tpt); + } - /* Lower rate not available, use the original */ - else - break; + rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG); - /* Else try to raise the "search" rate to match "active" */ - } else { - /* (2nd or later pass) - * If we've already tried to lower the rate, and are - * now trying to raise it, use the lower rate. */ - if (new_rate != IWL_RATE_INVALID) - break; + while (rate_idx != IWL_RATE_INVALID) { + if (target_tpt < (100 * tpt_tbl[rate_idx])) + break; - /* Loop again with higher rate */ - else if (high != IWL_RATE_INVALID) { - start_hi = high; - rate = high; + high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask, + tbl->rate.type); - /* Higher rate not available, use the original */ - } else { - new_rate = rate; - break; - } - } + rate_idx = (high_low >> 8) & 0xff; } - return new_rate; + IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n", + rate_idx, target_tpt, + rate_idx != IWL_RATE_INVALID ? + 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE); + + return rate_idx; } static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) @@ -1608,7 +1553,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, tpt = lq_sta->last_tpt / 100; expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, - tbl->rate.bw); + rs_bw_from_sta_bw(sta)); if (WARN_ON_ONCE(!expected_tpt_tbl)) continue; @@ -1649,7 +1594,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u16 rate_mask = 0; + unsigned long rate_mask = 0; u32 rate_idx = 0; memcpy(search_tbl, tbl, sz); @@ -1691,7 +1636,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, !(BIT(rate_idx) & rate_mask)) { IWL_DEBUG_RATE(mvm, "can not switch with index %d" - " rate mask %x\n", + " rate mask %lx\n", rate_idx, rate_mask); goto err; @@ -1805,16 +1750,21 @@ static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, *stronger = TPC_INVALID; } -static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct rs_rate *rate, - enum ieee80211_band band) +static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct rs_rate *rate, enum ieee80211_band band) { int index = rate->index; + bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); + bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && + !vif->bss_conf.ps); + IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n", + cam, sta_ps_disabled); /* * allow tpc only if power management is enabled, or bt coex * activity grade allows it and we are on 2.4Ghz. */ - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM && + if ((cam || sta_ps_disabled) && !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) return false; @@ -1916,7 +1866,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, #ifdef CONFIG_MAC80211_DEBUGFS if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { - IWL_DEBUG_RATE(mvm, "fixed tpc: %d", + IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", lq_sta->dbg_fixed_txp_reduction); lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction; return cur != lq_sta->dbg_fixed_txp_reduction; @@ -1931,9 +1881,9 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, band = chanctx_conf->def.chan->band; rcu_read_unlock(); - if (!rs_tpc_allowed(mvm, rate, band)) { + if (!rs_tpc_allowed(mvm, vif, rate, band)) { IWL_DEBUG_RATE(mvm, - "tpc is not allowed. remove txp restrictions"); + "tpc is not allowed. remove txp restrictions\n"); lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; return cur != TPC_NO_REDUCTION; } @@ -1959,12 +1909,12 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, /* override actions if we are on the edge */ if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) { - IWL_DEBUG_RATE(mvm, "already in lowest txp, stay"); + IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n"); action = TPC_ACTION_STAY; } else if (strong == TPC_INVALID && (action == TPC_ACTION_INCREASE || action == TPC_ACTION_NO_RESTIRCTION)) { - IWL_DEBUG_RATE(mvm, "already in highest txp, stay"); + IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n"); action = TPC_ACTION_STAY; } @@ -2235,7 +2185,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, break; case RS_ACTION_STAY: /* No change */ - update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) + update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); break; default: break; @@ -2489,10 +2440,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, if (i == IWL_RATE_9M_INDEX) continue; - /* Disable MCS9 as a workaround */ - if (i == IWL_RATE_MCS_9_INDEX) - continue; - /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ if (i == IWL_RATE_MCS_9_INDEX && sta->bandwidth == IEEE80211_STA_RX_BW_20) @@ -2511,10 +2458,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, if (i == IWL_RATE_9M_INDEX) continue; - /* Disable MCS9 as a workaround */ - if (i == IWL_RATE_MCS_9_INDEX) - continue; - /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ if (i == IWL_RATE_MCS_9_INDEX && sta->bandwidth == IEEE80211_STA_RX_BW_20) diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 63e7b16..4b6c7d4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -277,51 +277,22 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_scan_condition_iterator, &global_bound); - /* - * Under low latency traffic passive scan is fragmented meaning - * that dwell on a particular channel will be fragmented. Each fragment - * dwell time is 20ms and fragments period is 105ms. Skipping to next - * channel will be delayed by the same period - 105ms. So suspend_time - * parameter describing both fragments and channels skipping periods is - * set to 105ms. This value is chosen so that overall passive scan - * duration will not be too long. Max_out_time in this case is set to - * 70ms, so for active scanning operating channel will be left for 70ms - * while for passive still for 20ms (fragment dwell). - */ - if (global_bound) { - if (!iwl_mvm_low_latency(mvm)) { - params->suspend_time = ieee80211_tu_to_usec(100); - params->max_out_time = ieee80211_tu_to_usec(600); - } else { - params->suspend_time = ieee80211_tu_to_usec(105); - /* P2P doesn't support fragmented passive scan, so - * configure max_out_time to be at least longest dwell - * time for passive scan. - */ - if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { - params->max_out_time = ieee80211_tu_to_usec(70); - params->passive_fragmented = true; - } else { - u32 passive_dwell; - /* - * Use band G so that passive channel dwell time - * will be assigned with maximum value. - */ - band = IEEE80211_BAND_2GHZ; - passive_dwell = iwl_mvm_get_passive_dwell(band); - params->max_out_time = - ieee80211_tu_to_usec(passive_dwell); - } - } + if (!global_bound) + goto not_bound; + + params->suspend_time = 100; + params->max_out_time = 600; + + if (iwl_mvm_low_latency(mvm)) { + params->suspend_time = 250; + params->max_out_time = 250; } +not_bound: + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { - if (params->passive_fragmented) - params->dwell[band].passive = 20; - else - params->dwell[band].passive = - iwl_mvm_get_passive_dwell(band); + params->dwell[band].passive = iwl_mvm_get_passive_dwell(band); params->dwell[band].active = iwl_mvm_get_active_dwell(band, n_ssids); } @@ -335,7 +306,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, .id = SCAN_REQUEST_CMD, .len = { 0, }, .data = { mvm->scan_cmd, }, - .flags = CMD_SYNC, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, }; struct iwl_scan_cmd *cmd = mvm->scan_cmd; @@ -546,7 +516,7 @@ int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) ARRAY_SIZE(scan_abort_notif), iwl_mvm_scan_abort_notif, NULL); - ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL); + ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, 0, 0, NULL); if (ret) { IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret); /* mac80211's state will be cleaned in the nic_restart flow */ @@ -770,7 +740,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; int head = 0; - int tail = band_2ghz + band_5ghz; + int tail = band_2ghz + band_5ghz - 1; u32 ssid_bitmap; int cmd_len; int ret; @@ -778,7 +748,6 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct iwl_scan_offload_cfg *scan_cfg; struct iwl_host_cmd cmd = { .id = SCAN_OFFLOAD_CONFIG_CMD, - .flags = CMD_SYNC, }; struct iwl_mvm_scan_params params = {}; @@ -836,7 +805,6 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, struct iwl_scan_offload_blacklist *blacklist; struct iwl_host_cmd cmd = { .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, - .flags = CMD_SYNC, .len[1] = sizeof(*profile_cfg), .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .dataflags[1] = IWL_HCMD_DFL_NOCOPY, @@ -927,7 +895,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE); - return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, 0, sizeof(scan_req), &scan_req); } @@ -936,7 +904,6 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) int ret; struct iwl_host_cmd cmd = { .id = SCAN_OFFLOAD_ABORT_CMD, - .flags = CMD_SYNC, }; u32 status; @@ -965,7 +932,7 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) return ret; } -int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm) +int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify) { int ret; struct iwl_notification_wait wait_scan_done; @@ -1003,5 +970,8 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm) */ mvm->scan_status = IWL_MVM_SCAN_NONE; + if (notify) + ieee80211_sched_scan_stopped(mvm->hw); + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 3e11b9d..1fb01ea 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -327,7 +327,7 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) return -EINVAL; } - ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0, sizeof(rm_sta_cmd), &rm_sta_cmd); if (ret) { IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); @@ -1053,12 +1053,12 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, cmd.sta_id = sta_id; status = ADD_STA_SUCCESS; - if (cmd_flags == CMD_SYNC) - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); - else + if (cmd_flags & CMD_ASYNC) ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, sizeof(cmd), &cmd); + else + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), + &cmd, &status); switch (status) { case ADD_STA_SUCCESS: @@ -1111,7 +1111,7 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, remove_key ? "removing" : "installing", igtk_cmd.sta_id); - return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, sizeof(igtk_cmd), &igtk_cmd); } @@ -1198,15 +1198,15 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id, - seq.tkip.iv32, p1k, CMD_SYNC); + seq.tkip.iv32, p1k, 0); break; case WLAN_CIPHER_SUITE_CCMP: ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id, - 0, NULL, CMD_SYNC); + 0, NULL, 0); break; default: ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, - sta_id, 0, NULL, CMD_SYNC); + sta_id, 0, NULL, 0); } if (ret) diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index e5e3071..d98e8a2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -287,8 +287,6 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for * tid. * @max_agg_bufsize: the maximal size of the AGG buffer for this station - * @bt_reduced_txpower_dbg: debug mode in which %bt_reduced_txpower is forced - * by debugfs. * @bt_reduced_txpower: is reduced tx power enabled for this station * @next_status_eosp: the next reclaimed packet is a PS-Poll response and * we need to signal the EOSP @@ -309,7 +307,6 @@ struct iwl_mvm_sta { u32 mac_id_n_color; u16 tid_disable_agg; u8 max_agg_bufsize; - bool bt_reduced_txpower_dbg; bool bt_reduced_txpower; bool next_status_eosp; spinlock_t lock; diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index a940293..80100f6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -312,7 +312,7 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, ARRAY_SIZE(time_event_response), iwl_mvm_time_event_response, te_data); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, sizeof(*te_cmd), te_cmd); if (ret) { IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); @@ -434,7 +434,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, sizeof(time_cmd), &time_cmd); if (WARN_ON(ret)) return; diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 39a3e03..8685615 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -409,7 +409,6 @@ void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) .id = REPLY_THERMAL_MNG_BACKOFF, .len = { sizeof(u32), }, .data = { &backoff, }, - .flags = CMD_SYNC, }; backoff = max(backoff, mvm->thermal_throttle.min_backoff); diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index ff1b630..3846a6c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -958,7 +958,7 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), }; - u32 flags = sync ? CMD_SYNC : CMD_ASYNC; + u32 flags = sync ? 0 : CMD_ASYNC; ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, sizeof(flush_cmd), &flush_cmd); diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index c5f4532..aa9fc77 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -144,7 +144,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, "cmd flags %x", cmd->flags)) return -EINVAL; - cmd->flags |= CMD_SYNC | CMD_WANT_SKB; + cmd->flags |= CMD_WANT_SKB; ret = iwl_trans_send_cmd(mvm->trans, cmd); if (ret == -ERFKILL) { @@ -519,6 +519,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) iwl_mvm_dump_umac_error_log(mvm); } +#ifdef CONFIG_IWLWIFI_DEBUGFS void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm) { const struct fw_img *img; @@ -581,6 +582,7 @@ void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm) } iwl_trans_release_nic_access(mvm->trans, &flags); } +#endif /** * iwl_mvm_send_lq_cmd() - Send link quality command @@ -597,7 +599,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) struct iwl_host_cmd cmd = { .id = LQ_CMD, .len = { sizeof(struct iwl_lq_cmd), }, - .flags = init ? CMD_SYNC : CMD_ASYNC, + .flags = init ? 0 : CMD_ASYNC, .data = { lq, }, }; @@ -648,6 +650,39 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ieee80211_request_smps(vif, smps_mode); } +static void iwl_mvm_diversity_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *result = _data; + int i; + + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || + mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + *result = false; + } +} + +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) +{ + bool result = true; + + lockdep_assert_held(&mvm->mutex); + + if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + return false; + + if (!mvm->cfg->rx_with_siso_diversity) + return false; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_diversity_iter, &result); + + return result; +} + int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool value) { @@ -667,7 +702,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_bt_coex_vif_change(mvm); - return iwl_mvm_power_update_mac(mvm, vif); + return iwl_mvm_power_update_mac(mvm); } static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) @@ -688,3 +723,22 @@ bool iwl_mvm_low_latency(struct iwl_mvm *mvm) return result; } + +static void iwl_mvm_idle_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *idle = _data; + + if (!vif->bss_conf.idle) + *idle = false; +} + +bool iwl_mvm_is_idle(struct iwl_mvm *mvm) +{ + bool idle = true; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_idle_iter, &idle); + + return idle; +} diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 3d1d57f..7091a18 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -417,7 +417,7 @@ static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx) splx->package.count != 2 || splx->package.elements[0].type != ACPI_TYPE_INTEGER || splx->package.elements[0].integer.value != 0) { - IWL_ERR(trans, "Unsupported splx structure"); + IWL_ERR(trans, "Unsupported splx structure\n"); return 0; } @@ -426,14 +426,14 @@ static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx) limits->package.count < 2 || limits->package.elements[0].type != ACPI_TYPE_INTEGER || limits->package.elements[1].type != ACPI_TYPE_INTEGER) { - IWL_ERR(trans, "Invalid limits element"); + IWL_ERR(trans, "Invalid limits element\n"); return 0; } domain_type = &limits->package.elements[0]; power_limit = &limits->package.elements[1]; if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) { - IWL_DEBUG_INFO(trans, "WiFi power is not limited"); + IWL_DEBUG_INFO(trans, "WiFi power is not limited\n"); return 0; } @@ -450,26 +450,26 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) pxsx_handle = ACPI_HANDLE(&pdev->dev); if (!pxsx_handle) { IWL_DEBUG_INFO(trans, - "Could not retrieve root port ACPI handle"); + "Could not retrieve root port ACPI handle\n"); return; } /* Get the method's handle */ status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle); if (ACPI_FAILURE(status)) { - IWL_DEBUG_INFO(trans, "SPL method not found"); + IWL_DEBUG_INFO(trans, "SPL method not found\n"); return; } /* Call SPLC with no arguments */ status = acpi_evaluate_object(handle, NULL, NULL, &splx); if (ACPI_FAILURE(status)) { - IWL_ERR(trans, "SPLC invocation failed (0x%x)", status); + IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status); return; } trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer); - IWL_DEBUG_INFO(trans, "Default power limit set to %lld", + IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n", trans->dflt_pwr_limit); kfree(splx.pointer); } diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 1b95d85..6c22b23 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -117,21 +117,19 @@ struct iwl_dma_ptr { /** * iwl_queue_inc_wrap - increment queue index, wrap back to beginning * @index -- current index - * @n_bd -- total number of entries in queue (must be power of 2) */ -static inline int iwl_queue_inc_wrap(int index, int n_bd) +static inline int iwl_queue_inc_wrap(int index) { - return ++index & (n_bd - 1); + return ++index & (TFD_QUEUE_SIZE_MAX - 1); } /** * iwl_queue_dec_wrap - decrement queue index, wrap back to end * @index -- current index - * @n_bd -- total number of entries in queue (must be power of 2) */ -static inline int iwl_queue_dec_wrap(int index, int n_bd) +static inline int iwl_queue_dec_wrap(int index) { - return --index & (n_bd - 1); + return --index & (TFD_QUEUE_SIZE_MAX - 1); } struct iwl_cmd_meta { @@ -145,13 +143,13 @@ struct iwl_cmd_meta { * * Contains common data for Rx and Tx queues. * - * Note the difference between n_bd and n_window: the hardware - * always assumes 256 descriptors, so n_bd is always 256 (unless + * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware + * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless * there might be HW changes in the future). For the normal TX * queues, n_window, which is the size of the software queue data * is also 256; however, for the command queue, n_window is only * 32 since we don't need so many commands pending. Since the HW - * still uses 256 BDs for DMA though, n_bd stays 256. As a result, + * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result, * the software buffers (in the variables @meta, @txb in struct * iwl_txq) only have 32 entries, while the HW buffers (@tfds in * the same struct) have 256. @@ -162,7 +160,6 @@ struct iwl_cmd_meta { * data is a window overlayed over the HW queue. */ struct iwl_queue { - int n_bd; /* number of BDs in this queue */ int write_ptr; /* 1-st empty entry (index) host_w*/ int read_ptr; /* last used entry (index) host_r*/ /* use for monitoring and recovering the stuck queue */ @@ -373,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); +static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + return le16_to_cpu(tb->hi_n_len) >> 4; +} + /***************************************************** * Error handling ******************************************************/ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 4a26a08..a2698e5 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -850,7 +850,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) trans_pcie->ict_index, read); trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; trans_pcie->ict_index = - iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); + ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1)); read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index f98ef1e..788085b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -73,6 +73,7 @@ #include "iwl-csr.h" #include "iwl-prph.h" #include "iwl-agn-hw.h" +#include "iwl-fw-error-dump.h" #include "internal.h" static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) @@ -453,6 +454,7 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) { int ret; int t = 0; + int iter; IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); @@ -461,18 +463,23 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) if (ret >= 0) return 0; - /* If HW is not ready, prepare the conditions to check again */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); + for (iter = 0; iter < 10; iter++) { + /* If HW is not ready, prepare the conditions to check again */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE); + + do { + ret = iwl_pcie_set_hw_ready(trans); + if (ret >= 0) + return 0; - do { - ret = iwl_pcie_set_hw_ready(trans); - if (ret >= 0) - return 0; + usleep_range(200, 1000); + t += 200; + } while (t < 150000); + msleep(25); + } - usleep_range(200, 1000); - t += 200; - } while (t < 150000); + IWL_DEBUG_INFO(trans, "got NIC after %d iterations\n", iter); return ret; } @@ -1337,8 +1344,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) IWL_ERR(trans, "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", cnt, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, - SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & + (TFD_QUEUE_SIZE_MAX - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); } @@ -1669,6 +1676,61 @@ err: IWL_ERR(trans, "failed to create the trans debugfs entry\n"); return -ENOMEM; } + +static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) +{ + u32 cmdlen = 0; + int i; + + for (i = 0; i < IWL_NUM_OF_TBS; i++) + cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i); + + return cmdlen; +} + +static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, + void *buf, u32 buflen) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_fw_error_dump_data *data; + struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_fw_error_dump_txcmd *txcmd; + u32 len; + int i, ptr; + + if (!buf) + return sizeof(*data) + + cmdq->q.n_window * (sizeof(*txcmd) + + TFD_MAX_PAYLOAD_SIZE); + + len = 0; + data = buf; + data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); + txcmd = (void *)data->data; + spin_lock_bh(&cmdq->lock); + ptr = cmdq->q.write_ptr; + for (i = 0; i < cmdq->q.n_window; i++) { + u8 idx = get_cmd_index(&cmdq->q, ptr); + u32 caplen, cmdlen; + + cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]); + caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen); + + if (cmdlen) { + len += sizeof(*txcmd) + caplen; + txcmd->cmdlen = cpu_to_le32(cmdlen); + txcmd->caplen = cpu_to_le32(caplen); + memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen); + txcmd = (void *)((u8 *)txcmd->data + caplen); + } + + ptr = iwl_queue_dec_wrap(ptr); + } + spin_unlock_bh(&cmdq->lock); + + data->len = cpu_to_le32(len); + return sizeof(*data) + len; +} #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, struct dentry *dir) @@ -1711,6 +1773,10 @@ static const struct iwl_trans_ops trans_ops_pcie = { .grab_nic_access = iwl_trans_pcie_grab_nic_access, .release_nic_access = iwl_trans_pcie_release_nic_access, .set_bits_mask = iwl_trans_pcie_set_bits_mask, + +#ifdef CONFIG_IWLWIFI_DEBUGFS + .dump_data = iwl_trans_pcie_dump_data, +#endif }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, @@ -1788,6 +1854,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + trans->dev = &pdev->dev; + trans_pcie->pci_dev = pdev; + iwl_disable_interrupts(trans); + err = pci_enable_msi(pdev); if (err) { dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", err); @@ -1799,8 +1869,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } } - trans->dev = &pdev->dev; - trans_pcie->pci_dev = pdev; trans->hw_rev = iwl_read32(trans, CSR_HW_REV); trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), @@ -1826,8 +1894,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_pci_disable_msi; } - trans_pcie->inta_mask = CSR_INI_SET_MASK; - if (iwl_pcie_alloc_ict(trans)) goto out_free_cmd_pool; @@ -1839,6 +1905,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_free_ict; } + trans_pcie->inta_mask = CSR_INI_SET_MASK; + return trans; out_free_ict: diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index dde6031..038940a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q) /* * To avoid ambiguity between empty and completely full queues, there - * should always be less than q->n_bd elements in the queue. - * If q->n_window is smaller than q->n_bd, there is no need to reserve - * any queue entries for this purpose. + * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue. + * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need + * to reserve any queue entries for this purpose. */ - if (q->n_window < q->n_bd) + if (q->n_window < TFD_QUEUE_SIZE_MAX) max = q->n_window; else - max = q->n_bd - 1; + max = TFD_QUEUE_SIZE_MAX - 1; /* - * q->n_bd is a power of 2, so the following is equivalent to modulo by - * q->n_bd and is well defined for negative dividends. + * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to + * modulo by TFD_QUEUE_SIZE_MAX and is well defined. */ - used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1); + used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1); if (WARN_ON(used > max)) return 0; @@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q) /* * iwl_queue_init - Initialize queue's high/low-water and read/write indexes */ -static int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) +static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id) { - q->n_bd = count; q->n_window = slots_num; q->id = id; - /* count must be power-of-two size, otherwise iwl_queue_inc_wrap - * and iwl_queue_dec_wrap are broken. */ - if (WARN_ON(!is_power_of_2(count))) - return -EINVAL; - /* slots_num must be power-of-two size, otherwise * get_cmd_index is broken. */ if (WARN_ON(!is_power_of_2(slots_num))) @@ -197,17 +191,17 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) IWL_ERR(trans, "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", i, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, - SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) & + (TFD_QUEUE_SIZE_MAX - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); } for (i = q->read_ptr; i != q->write_ptr; - i = iwl_queue_inc_wrap(i, q->n_bd)) + i = iwl_queue_inc_wrap(i)) IWL_ERR(trans, "scratch %d = 0x%08x\n", i, le32_to_cpu(txq->scratchbufs[i].scratch)); - iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1); + iwl_force_nmi(trans); } /* @@ -338,12 +332,12 @@ void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { struct iwl_txq *txq = &trans_pcie->txq[i]; - spin_lock(&txq->lock); + spin_lock_bh(&txq->lock); if (trans_pcie->txq[i].need_update) { iwl_pcie_txq_inc_wr_ptr(trans, txq); trans_pcie->txq[i].need_update = false; } - spin_unlock(&txq->lock); + spin_unlock_bh(&txq->lock); } } @@ -359,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) return addr; } -static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - - return le16_to_cpu(tb->hi_n_len) >> 4; -} - static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) { @@ -425,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) { struct iwl_tfd *tfd_tmp = txq->tfds; - /* rd_ptr is bounded by n_bd and idx is bounded by n_window */ + /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and + * idx is bounded by n_window + */ int rd_ptr = txq->q.read_ptr; int idx = get_cmd_index(&txq->q, rd_ptr); lockdep_assert_held(&txq->lock); - /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ + /* We have only q->n_window txq->entries, but we use + * TFD_QUEUE_SIZE_MAX tfds + */ iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]); /* free SKB */ @@ -452,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) } static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, - dma_addr_t addr, u16 len, u8 reset) + dma_addr_t addr, u16 len, bool reset) { struct iwl_queue *q; struct iwl_tfd *tfd, *tfd_tmp; @@ -565,8 +556,7 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); /* Initialize queue's high/low-water marks, and head/tail indexes */ - ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, - txq_id); + ret = iwl_queue_init(&txq->q, slots_num, txq_id); if (ret) return ret; @@ -591,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) struct iwl_txq *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; - if (!q->n_bd) - return; - spin_lock_bh(&txq->lock); while (q->write_ptr != q->read_ptr) { IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, q->read_ptr); iwl_pcie_txq_free_tfd(trans, txq); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); } txq->active = false; spin_unlock_bh(&txq->lock); @@ -636,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) } /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) { - dma_free_coherent(dev, sizeof(struct iwl_tfd) * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); + if (txq->tfds) { + dma_free_coherent(dev, + sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX, + txq->tfds, txq->q.dma_addr); txq->q.dma_addr = 0; + txq->tfds = NULL; dma_free_coherent(dev, sizeof(*txq->scratchbufs) * txq->q.n_window, @@ -948,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - /* n_bd is usually 256 => n_bd - 1 = 0xff */ - int tfd_num = ssn & (txq->q.n_bd - 1); + int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1); struct iwl_queue *q = &txq->q; int last_to_free; @@ -973,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ - last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd); + last_to_free = iwl_queue_dec_wrap(tfd_num); if (!iwl_queue_used(q, last_to_free)) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, last_to_free, q->n_bd, + __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX, q->write_ptr, q->read_ptr); goto out; } @@ -988,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, for (; q->read_ptr != tfd_num; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) continue; @@ -1027,21 +1015,21 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) lockdep_assert_held(&txq->lock); - if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) { + if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, idx, q->n_bd, + __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX, q->write_ptr, q->read_ptr); return; } - for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { if (nfreed++ > 0) { IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx, q->write_ptr, q->read_ptr); - iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1); + iwl_force_nmi(trans); } } @@ -1327,28 +1315,39 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, cmd_pos = offsetof(struct iwl_device_cmd, payload); copy_size = sizeof(out_cmd->hdr); for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - int copy = 0; + int copy; if (!cmd->len[i]) continue; - /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ - if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { - copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; - - if (copy > cmd->len[i]) - copy = cmd->len[i]; - } - /* copy everything if not nocopy/dup */ if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | - IWL_HCMD_DFL_DUP))) + IWL_HCMD_DFL_DUP))) { copy = cmd->len[i]; - if (copy) { memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); cmd_pos += copy; copy_size += copy; + continue; + } + + /* + * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied + * in total (for the scratchbuf handling), but copy up to what + * we can fit into the payload for debug dump purposes. + */ + copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); + + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + + /* However, treat copy_size the proper way, we need it below */ + if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { + copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + + if (copy > cmd->len[i]) + copy = cmd->len[i]; + copy_size += copy; } } @@ -1363,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); iwl_pcie_txq_build_tfd(trans, txq, iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), - scratch_size, 1); + scratch_size, true); /* map first command fragment, if any remains */ if (copy_size > scratch_size) { @@ -1379,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } iwl_pcie_txq_build_tfd(trans, txq, phys_addr, - copy_size - scratch_size, 0); + copy_size - scratch_size, false); } /* map the remaining (adjusted) nocopy/dup fragments */ @@ -1402,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, goto out; } - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0); + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false); } out_meta->flags = cmd->flags; @@ -1445,7 +1444,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } /* Increment and update queue's write index */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); iwl_pcie_txq_inc_wr_ptr(trans, txq); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); @@ -1601,7 +1600,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, get_cmd_string(trans_pcie, cmd->id)); ret = -ETIMEDOUT; - iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1); + iwl_force_nmi(trans); iwl_trans_fw_error(trans); goto cancel; @@ -1740,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE); iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, - IWL_HCMD_SCRATCHBUF_SIZE, 1); + IWL_HCMD_SCRATCHBUF_SIZE, true); /* there must be data left over for TB1 or this code must be changed */ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); @@ -1750,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) goto out_err; - iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0); + iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); /* * Set up TFD's third entry to point directly to remainder @@ -1766,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, &txq->tfds[q->write_ptr]); goto out_err; } - iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0); + iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); } /* Set up entry for this TFD in Tx byte-count array */ @@ -1788,7 +1787,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); /* Tell device the write index *just past* this latest filled TFD */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); if (!wait_write_ptr) iwl_pcie_txq_inc_wr_ptr(trans, txq); diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 54e344a..47a998d 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1006,9 +1006,8 @@ struct cmd_key_material { } __packed; static int lbs_set_key_material(struct lbs_private *priv, - int key_type, - int key_info, - u8 *key, u16 key_len) + int key_type, int key_info, + const u8 *key, u16 key_len) { struct cmd_key_material cmd; int ret; @@ -1610,7 +1609,7 @@ static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, */ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct lbs_private *priv = wiphy_priv(wiphy); s8 signal, noise; diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index ab966f0..407784a 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -90,7 +90,8 @@ do { if ((lbs_debug & (grp)) == (grp)) \ #define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) #ifdef DEBUG -static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) +static inline void lbs_deb_hex(unsigned int grp, const char *prompt, + const u8 *buf, int len) { int i = 0; diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index c7366b0..e446fed 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -71,8 +71,10 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) skb->ip_summed = CHECKSUM_NONE; - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - return process_rxed_802_11_packet(priv, skb); + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = process_rxed_802_11_packet(priv, skb); + goto done; + } p_rx_pd = (struct rxpd *) skb->data; p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + @@ -86,7 +88,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { lbs_deb_rx("rx err: frame received with bad length\n"); dev->stats.rx_length_errors++; - ret = 0; + ret = -EINVAL; dev_kfree_skb(skb); goto done; } diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 2bd07d6..e1c2f67 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -749,3 +749,45 @@ void mwifiex_set_ba_params(struct mwifiex_private *priv) return; } + +u8 mwifiex_get_sec_chan_offset(int chan) +{ + u8 sec_offset; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case 165: + default: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + + return sec_offset; +} diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 40b007a..0b73fa0 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -63,6 +63,7 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, int cmd_action, struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); +u8 mwifiex_get_sec_chan_offset(int chan); static inline u8 mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, @@ -199,7 +200,7 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, } static inline u8 -mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra) +mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) { struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); if (node) diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 860dfe7..5b32106 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -100,6 +100,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, struct sk_buff *skb) { struct txpd *local_tx_pd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); skb_push(skb, sizeof(*local_tx_pd)); @@ -118,6 +119,9 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - sizeof(*local_tx_pd)); + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + if (local_tx_pd->tx_control == 0) /* TxCtrl set by user or default */ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); @@ -183,6 +187,9 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, tx_info_aggr->bss_type = tx_info_src->bss_type; tx_info_aggr->bss_num = tx_info_src->bss_num; + + if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; skb_aggr->priority = skb_src->priority; do_gettimeofday(&tv); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 21ee27a..e95dec9 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -994,7 +994,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, */ static int mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -1270,7 +1270,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, */ static int mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct mwifiex_sta_node *sta_node; @@ -2629,7 +2629,7 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, */ static int mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *extra_ies, size_t extra_ies_len) { @@ -2701,7 +2701,7 @@ mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, static int mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation action) + const u8 *peer, enum nl80211_tdls_operation action) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -2748,9 +2748,8 @@ mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, } static int -mwifiex_cfg80211_add_station(struct wiphy *wiphy, - struct net_device *dev, - u8 *mac, struct station_parameters *params) +mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -2765,9 +2764,9 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, } static int -mwifiex_cfg80211_change_station(struct wiphy *wiphy, - struct net_device *dev, - u8 *mac, struct station_parameters *params) +mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) { int ret; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index b485dc1..3175dd0 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -169,6 +169,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) #define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) +#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) #define TLV_TYPE_FW_API_REV (PROPRIETARY_TLV_BASE_ID + 199) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 @@ -229,6 +230,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) +#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) /* httxcfg bitmap * 0 reserved @@ -403,7 +405,7 @@ enum P2P_MODES { #define HS_CFG_CANCEL 0xffffffff #define HS_CFG_COND_DEF 0x00000000 #define HS_CFG_GPIO_DEF 0xff -#define HS_CFG_GAP_DEF 0 +#define HS_CFG_GAP_DEF 0xff #define HS_CFG_COND_BROADCAST_DATA 0x00000001 #define HS_CFG_COND_UNICAST_DATA 0x00000002 #define HS_CFG_COND_MAC_EVENT 0x00000004 @@ -487,6 +489,7 @@ enum P2P_MODES { #define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c #define EVENT_HOSTWAKE_STAIE 0x0000004d #define EVENT_CHANNEL_SWITCH_ANN 0x00000050 +#define EVENT_TDLS_GENERIC_EVENT 0x00000052 #define EVENT_EXT_SCAN_REPORT 0x00000058 #define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f @@ -519,6 +522,7 @@ enum P2P_MODES { #define ACT_TDLS_DELETE 0x00 #define ACT_TDLS_CREATE 0x01 #define ACT_TDLS_CONFIG 0x02 +#define TDLS_EVENT_LINK_TEAR_DOWN 3 #define MWIFIEX_FW_V15 15 @@ -535,6 +539,7 @@ struct mwifiex_ie_types_data { #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 #define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 +#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 struct txpd { u8 bss_type; @@ -577,7 +582,7 @@ struct rxpd { * [Bit 7] Reserved */ u8 ht_info; - u8 reserved; + u8 flags; } __packed; struct uap_txpd { @@ -708,6 +713,13 @@ struct mwifiex_ie_types_vendor_param_set { u8 ie[MWIFIEX_MAX_VSIE_LEN]; }; +#define MWIFIEX_TDLS_IDLE_TIMEOUT 60 + +struct mwifiex_ie_types_tdls_idle_timeout { + struct mwifiex_ie_types_header header; + __le16 value; +} __packed; + struct mwifiex_ie_types_rsn_param_set { struct mwifiex_ie_types_header header; u8 rsn_ie[1]; @@ -1745,6 +1757,15 @@ struct host_cmd_ds_802_11_subsc_evt { __le16 events; } __packed; +struct mwifiex_tdls_generic_event { + __le16 type; + u8 peer_mac[ETH_ALEN]; + union { + __le16 reason_code; + __le16 reserved; + } u; +} __packed; + struct mwifiex_ie { __le16 ie_index; __le16 mgmt_subtype_mask; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 3418119..1398afa 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -910,8 +910,6 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_process_sta_event(struct mwifiex_private *); int mwifiex_process_uap_event(struct mwifiex_private *); -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); void mwifiex_delete_all_station_list(struct mwifiex_private *priv); void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); @@ -1101,7 +1099,7 @@ mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) return 0; /* Clear csa channel, if DFS channel move time has passed */ - if (jiffies > priv->csa_expire_time) { + if (time_after(jiffies, priv->csa_expire_time)) { priv->csa_chan = 0; priv->csa_expire_time = 0; } @@ -1220,26 +1218,26 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); extern const struct ethtool_ops mwifiex_ethtool_ops; void mwifiex_del_all_sta_list(struct mwifiex_private *priv); -void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac); +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); void mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, int ies_len, struct mwifiex_sta_node *node); struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer, +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *extra_ies, size_t extra_ies_len); -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len); +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); -int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action); -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac); +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index c2cfeec..574d4b5 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1071,6 +1071,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. * Download ready interrupt to FW is deffered if Tx ring is not full and * additional payload can be accomodated. + * Caller must ensure tx_param parameter to this function is not NULL. */ static int mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index d75f4eb..45c5b34 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1738,6 +1738,19 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, return 0; } +static void mwifiex_complete_scan(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = 0; + if (!priv->scan_request) { + dev_dbg(adapter->dev, "complete internal scan\n"); + mwifiex_complete_cmd(adapter, adapter->curr_cmd); + } + } +} + static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; @@ -1751,16 +1764,9 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - adapter->cmd_wait_q.status = 0; - if (!priv->scan_request) { - dev_dbg(adapter->dev, - "complete internal scan\n"); - mwifiex_complete_cmd(adapter, - adapter->curr_cmd); - } - } + if (!adapter->ext_scan) + mwifiex_complete_scan(priv); + if (priv->report_scan_result) priv->report_scan_result = false; @@ -1965,6 +1971,9 @@ int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv) { dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n"); + + mwifiex_complete_scan(priv); + return 0; } diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index a1773d3..4ce3d7b 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1678,8 +1678,12 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, if (ret) { if (type == MWIFIEX_TYPE_CMD) adapter->cmd_sent = false; - if (type == MWIFIEX_TYPE_DATA) + if (type == MWIFIEX_TYPE_DATA) { adapter->data_sent = false; + /* restore curr_wr_port in error cases */ + card->curr_wr_port = port; + card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); + } } else { if (type == MWIFIEX_TYPE_DATA) { if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index e3cac14..88202ce 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1546,6 +1546,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, struct mwifiex_ie_types_extcap *extcap; struct mwifiex_ie_types_vhtcap *vht_capab; struct mwifiex_ie_types_aid *aid; + struct mwifiex_ie_types_tdls_idle_timeout *timeout; u8 *pos, qos_info; u16 config_len = 0; struct station_parameters *params = priv->sta_params; @@ -1643,6 +1644,12 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, config_len += sizeof(struct mwifiex_ie_types_aid); } + timeout = (void *)(pos + config_len); + timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); + timeout->header.len = cpu_to_le16(sizeof(timeout->value)); + timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT); + config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); + break; default: dev_err(priv->adapter->dev, "Unknown TDLS operation\n"); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index bfebb01..577f297 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -865,14 +865,20 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, switch (action) { case ACT_TDLS_DELETE: - if (reason) - dev_err(priv->adapter->dev, - "TDLS link delete for %pM failed: reason %d\n", - cmd_tdls_oper->peer_mac, reason); - else + if (reason) { + if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) + dev_dbg(priv->adapter->dev, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + else + dev_err(priv->adapter->dev, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + } else { dev_dbg(priv->adapter->dev, - "TDLS link config for %pM successful\n", + "TDLS link delete for %pM successful\n", cmd_tdls_oper->peer_mac); + } break; case ACT_TDLS_CREATE: if (reason) { diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 368450c..f6395ef 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -134,6 +134,46 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) netif_carrier_off(priv->netdev); } +static int mwifiex_parse_tdls_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_tdls_generic_event *tdls_evt = + (void *)event_skb->data + sizeof(adapter->event_cause); + + /* reserved 2 bytes are not mandatory in tdls event */ + if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) - + sizeof(u16) - sizeof(adapter->event_cause))) { + dev_err(adapter->dev, "Invalid event length!\n"); + return -1; + } + + sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac); + if (!sta_ptr) { + dev_err(adapter->dev, "cannot get sta entry!\n"); + return -1; + } + + switch (le16_to_cpu(tdls_evt->type)) { + case TDLS_EVENT_LINK_TEAR_DOWN: + cfg80211_tdls_oper_request(priv->netdev, + tdls_evt->peer_mac, + NL80211_TDLS_TEARDOWN, + le16_to_cpu(tdls_evt->u.reason_code), + GFP_KERNEL); + ret = mwifiex_tdls_oper(priv, tdls_evt->peer_mac, + MWIFIEX_TDLS_DISABLE_LINK); + queue_work(adapter->workqueue, &adapter->main_work); + break; + default: + break; + } + + return ret; +} + /* * This function handles events generated by firmware. * @@ -459,6 +499,10 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) false); break; + case EVENT_TDLS_GENERIC_EVENT: + ret = mwifiex_parse_tdls_event(priv, adapter->event_skb); + break; + default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index ed26387..8b639d7 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -183,6 +183,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, struct rx_packet_hdr *rx_pkt_hdr; u8 ta[ETH_ALEN]; u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; + struct mwifiex_sta_node *sta_ptr; local_rx_pd = (struct rxpd *) (skb->data); rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); @@ -213,14 +214,25 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, * If the packet is not an unicast packet then send the packet * directly to os. Don't pass thru rx reordering */ - if (!IS_11N_ENABLED(priv) || + if ((!IS_11N_ENABLED(priv) && + !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { mwifiex_process_rx_packet(priv, skb); return ret; } - if (mwifiex_queuing_ra_based(priv)) { + if (mwifiex_queuing_ra_based(priv) || + (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); + if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && + local_rx_pd->priority < MAX_NUM_TID) { + sta_ptr = mwifiex_get_sta_entry(priv, ta); + if (sta_ptr) + sta_ptr->rx_seq[local_rx_pd->priority] = + le16_to_cpu(local_rx_pd->seq_num); + } } else { if (rx_pkt_type != PKT_TYPE_BAR) priv->rx_seq[local_rx_pd->priority] = seq_num; diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 1236a5d..5fce7e7 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -128,6 +128,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) { struct mwifiex_adapter *adapter = priv->adapter; struct txpd *local_tx_pd; + struct mwifiex_tx_param tx_param; /* sizeof(struct txpd) + Interface specific header */ #define NULL_PACKET_HDR 64 u32 data_len = NULL_PACKET_HDR; @@ -168,8 +169,9 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) skb, NULL); } else { skb_push(skb, INTF_HEADER_LEN); + tx_param.next_pkt_len = 0; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb, NULL); + skb, &tx_param); } switch (ret) { case -EBUSY: diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 97662a1..e73034f 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -25,8 +25,8 @@ #define TDLS_RESP_FIX_LEN 8 #define TDLS_CONFIRM_FIX_LEN 6 -static void -mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) +static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, + const u8 *mac, u8 status) { struct mwifiex_ra_list_tbl *ra_list; struct list_head *tid_list; @@ -84,7 +84,8 @@ mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) return; } -static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac) +static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, + const u8 *mac) { struct mwifiex_ra_list_tbl *ra_list; struct list_head *ra_list_head; @@ -185,8 +186,50 @@ static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, return 0; } +static int +mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, + u8 vht_enabled, struct sk_buff *skb) +{ + struct ieee80211_ht_operation *ht_oper; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_bssdescriptor *bss_desc = + &priv->curr_bss_params.bss_descriptor; + u8 *pos; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + dev_warn(priv->adapter->dev, + "TDLS peer station not found in list\n"); + return -1; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(struct ieee80211_ht_operation); + ht_oper = (void *)pos; + + ht_oper->primary_chan = bss_desc->channel; + + /* follow AP's channel bandwidth */ + if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && + bss_desc->bcn_ht_cap && + ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) + ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; + + if (vht_enabled) { + ht_oper->ht_param = + mwifiex_get_sec_chan_offset(bss_desc->channel); + ht_oper->ht_param |= BIT(2); + } + + memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, + sizeof(struct ieee80211_ht_operation)); + + return 0; +} + static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, - u8 *mac, struct sk_buff *skb) + const u8 *mac, struct sk_buff *skb) { struct mwifiex_bssdescriptor *bss_desc; struct ieee80211_vht_operation *vht_oper; @@ -325,8 +368,9 @@ static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) } static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, struct sk_buff *skb) + const u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, struct sk_buff *skb) { struct ieee80211_tdls_data *tf; int ret; @@ -428,6 +472,17 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, dev_kfree_skb_any(skb); return ret; } + ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } else { + ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } } break; @@ -453,7 +508,8 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, } static void -mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) +mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) { struct ieee80211_tdls_lnkie *lnkid; @@ -467,8 +523,8 @@ mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) memcpy(lnkid->resp_sta, peer, ETH_ALEN); } -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, u16 status_code, const u8 *extra_ies, size_t extra_ies_len) { @@ -560,7 +616,8 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, } static int -mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, +mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { @@ -638,10 +695,10 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, return 0; } -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; @@ -848,7 +905,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, } static int -mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; @@ -869,7 +926,7 @@ mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer) } static int -mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; @@ -896,7 +953,7 @@ mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) } static int -mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; @@ -925,7 +982,7 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) } static int -mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct ieee80211_mcs_info mcs; @@ -982,7 +1039,7 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) return 0; } -int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) { switch (action) { case MWIFIEX_TDLS_ENABLE_LINK: @@ -997,7 +1054,7 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) return 0; } -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac) +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *sta_ptr; diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index c3824e3..6da5abf 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -259,7 +259,7 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, * NULL is returned if station entry is not found in associated STA list. */ struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; @@ -280,7 +280,7 @@ mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) * If received mac address is NULL, NULL is returned. */ struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac) +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; unsigned long flags; @@ -332,7 +332,7 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, } /* This function will delete a station entry from station list */ -void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; unsigned long flags; diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 94b6c74..d3671d0 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -92,7 +92,7 @@ mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) * The function also initializes the list with the provided RA. */ static struct mwifiex_ra_list_tbl * -mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) +mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) { struct mwifiex_ra_list_tbl *ra_list; @@ -139,8 +139,7 @@ static u8 mwifiex_get_random_ba_threshold(void) * This function allocates and adds a RA list for all TIDs * with the given RA. */ -void -mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) { int i; struct mwifiex_ra_list_tbl *ra_list; @@ -164,6 +163,7 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) if (!mwifiex_queuing_ra_based(priv)) { if (mwifiex_get_tdls_link_status(priv, ra) == TDLS_SETUP_COMPLETE) { + ra_list->tdls_link = true; ra_list->is_11n_enabled = mwifiex_tdls_peer_11n_enabled(priv, ra); } else { @@ -566,7 +566,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, - u8 *ra_addr) + const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; @@ -587,7 +587,8 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, * retrieved. */ struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; @@ -648,7 +649,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) dev_dbg(adapter->dev, "TDLS setup packet for %pM. Don't block\n", ra); - else + else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) tdls_status = mwifiex_get_tdls_link_status(priv, ra); } diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index 83e4208..eca56e3 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -99,7 +99,7 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, struct sk_buff *skb); -void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); void mwifiex_rotate_priolists(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra, int tid); @@ -123,7 +123,8 @@ void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, const struct host_cmd_ds_command *resp); struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr); +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr); u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); #endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index 49300d0..e27e328 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -988,8 +988,8 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) * tsc must be NULL or up to 8 bytes */ int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, u8 *key, u8 *rsc, size_t rsc_len, - u8 *tsc, size_t tsc_len) + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len) { struct { __le16 idx; diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index 8f6831f..466d1ed 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -38,8 +38,8 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv); int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); int __orinoco_hw_setup_enc(struct orinoco_private *priv); int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, u8 *key, u8 *rsc, size_t rsc_len, - u8 *tsc, size_t tsc_len); + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len); int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct net_device *dev, diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c index f9805c9..1cbb783 100644 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -1687,7 +1687,7 @@ static int ezusb_probe(struct usb_interface *interface, firmware.code = fw_entry->data; } if (firmware.size && firmware.code) { - if (ezusb_firmware_download(upriv, &firmware)) + if (ezusb_firmware_download(upriv, &firmware) < 0) goto error; } else { err("No firmware to download"); diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index b7a867b..6abdaf0 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -52,9 +52,9 @@ static int orinoco_set_key(struct orinoco_private *priv, int index, priv->keys[index].seq_len = seq_len; if (key_len) - memcpy(priv->keys[index].key, key, key_len); + memcpy((void *)priv->keys[index].key, key, key_len); if (seq_len) - memcpy(priv->keys[index].seq, seq, seq_len); + memcpy((void *)priv->keys[index].seq, seq, seq_len); switch (alg) { case ORINOCO_ALG_TKIP: diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 39d22a1..d2a9a08 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -517,7 +517,7 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast); static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo); + const u8 *mac, struct station_info *sinfo); static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo); @@ -2490,7 +2490,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, } static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 8416474..54aaeb0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -656,6 +656,7 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_START: common->vif_info[ii].seq_start = seq_no; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + status = 0; break; case IEEE80211_AMPDU_TX_STOP_CONT: diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index f2f7078..d3fbe33 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -63,7 +63,7 @@ static inline int rsi_create_kthread(struct rsi_common *common, u8 *name) { init_completion(&thread->completion); - thread->task = kthread_run(func_ptr, common, name); + thread->task = kthread_run(func_ptr, common, "%s", name); if (IS_ERR(thread->task)) return (int)PTR_ERR(thread->task); diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 1057245..86c43d1 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -68,6 +68,12 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, } } + /* If the port is powered down, we get a -EPROTO error, and this + * leads to a endless loop. So just say that the device is gone. + */ + if (status == -EPROTO) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + rt2x00_err(rt2x00dev, "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n", request, offset, status); diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 50d69b1..2c1c02b 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -284,6 +284,8 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.band = dev->conf.chandef.chan->band; rx_status.mactime = tsft; rx_status.flag |= RX_FLAG_MACTIME_START; + if (flags & RTL818X_RX_DESC_FLAG_SPLCP) + rx_status.flag |= RX_FLAG_SHORTPRE; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; @@ -461,18 +463,23 @@ static void rtl8180_tx(struct ieee80211_hw *dev, RTL818X_TX_DESC_FLAG_NO_ENC; rc_flags = info->control.rates[0].flags; + + /* HW will perform RTS-CTS when only RTS flags is set. + * HW will perform CTS-to-self when both RTS and CTS flags are set. + * RTS rate and RTS duration will be used also for CTS-to-self. + */ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { tx_flags |= RTL818X_TX_DESC_FLAG_RTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_duration = ieee80211_rts_duration(dev, priv->vif, + skb->len, info); } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - tx_flags |= RTL818X_TX_DESC_FLAG_CTS; + tx_flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_duration = ieee80211_ctstoself_duration(dev, priv->vif, + skb->len, info); } - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) - rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, - info); - if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) { unsigned int remainder; diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 0ca17cd..629ad8c 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -253,14 +253,21 @@ static void rtl8187_tx(struct ieee80211_hw *dev, flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; if (ieee80211_has_morefrags(tx_hdr->frame_control)) flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; + + /* HW will perform RTS-CTS when only RTS flags is set. + * HW will perform CTS-to-self when both RTS and CTS flags are set. + * RTS rate and RTS duration will be used also for CTS-to-self. + */ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { flags |= RTL818X_TX_DESC_FLAG_RTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; rts_dur = ieee80211_rts_duration(dev, priv->vif, skb->len, info); } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - flags |= RTL818X_TX_DESC_FLAG_CTS; + flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_dur = ieee80211_ctstoself_duration(dev, priv->vif, + skb->len, info); } if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { @@ -381,6 +388,8 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.freq = dev->conf.chandef.chan->center_freq; rx_status.band = dev->conf.chandef.chan->band; rx_status.flag |= RX_FLAG_MACTIME_START; + if (flags & RTL818X_RX_DESC_FLAG_SPLCP) + rx_status.flag |= RX_FLAG_SHORTPRE; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c index 94cd9df..b14cf5a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c @@ -2515,23 +2515,3 @@ void rtl88ee_suspend(struct ieee80211_hw *hw) void rtl88ee_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config = 0x%08X, write_into_reg =%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h index b4460a4..1850fde 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h @@ -61,8 +61,6 @@ void rtl8188ee_bt_reg_init(struct ieee80211_hw *hw); void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw); void rtl88ee_suspend(struct ieee80211_hw *hw); void rtl88ee_resume(struct ieee80211_hw *hw); -void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); void rtl88ee_fw_clk_off_timer_callback(unsigned long data); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c index 347af1e..842d693 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c @@ -93,6 +93,7 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw) u8 tid; rtl8188ee_bt_reg_init(hw); + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; rtlpriv->dm.dm_initialgain_enable = 1; rtlpriv->dm.dm_flag = 0; @@ -254,7 +255,6 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = { .enable_hw_sec = rtl88ee_enable_hw_security_config, .set_key = rtl88ee_set_key, .init_sw_leds = rtl88ee_init_sw_leds, - .allow_all_destaddr = rtl88ee_allow_all_destaddr, .get_bbreg = rtl88e_phy_query_bb_reg, .set_bbreg = rtl88e_phy_set_bb_reg, .get_rfreg = rtl88e_phy_query_rf_reg, @@ -266,6 +266,7 @@ static struct rtl_mod_params rtl88ee_mod_params = { .inactiveps = true, .swctrl_lps = false, .fwctrl_lps = true, + .msi_support = false, .debug = DBG_EMERG, }; @@ -382,10 +383,12 @@ module_param_named(debug, rtl88ee_mod_params.debug, int, 0444); module_param_named(ips, rtl88ee_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl88ee_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl88ee_mod_params.fwctrl_lps, bool, 0444); +module_param_named(msi, rtl88ee_mod_params.msi_support, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); +MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index 55adf04..cdecb0f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -2423,24 +2423,3 @@ void rtl92ce_suspend(struct ieee80211_hw *hw) void rtl92ce_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) {/* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - } else {/* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - } - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config=0x%08X, write_into_reg=%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h index 2d063b0..5533070 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h @@ -76,7 +76,5 @@ void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw); void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw); void rtl92ce_suspend(struct ieee80211_hw *hw); void rtl92ce_resume(struct ieee80211_hw *hw); -void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index b790320..12f21f4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -229,7 +229,6 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = { .enable_hw_sec = rtl92ce_enable_hw_security_config, .set_key = rtl92ce_set_key, .init_sw_leds = rtl92ce_init_sw_leds, - .allow_all_destaddr = rtl92ce_allow_all_destaddr, .get_bbreg = rtl92c_phy_query_bb_reg, .set_bbreg = rtl92c_phy_set_bb_reg, .set_rfreg = rtl92ce_phy_set_rf_reg, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index 07cb06d..a903c26 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -511,7 +511,7 @@ static int _rtl92cu_init_power_on(struct ieee80211_hw *hw) pr_info("MAC auto ON okay!\n"); break; } - if (pollingCount++ > 100) { + if (pollingCount++ > 1000) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Failed to polling REG_APS_FSMCO[APFM_ONMAC] done!\n"); return -ENODEV; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index c613110..361435f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -395,9 +395,6 @@ static struct usb_driver rtl8192cu_driver = { /* .resume = rtl_usb_resume, */ /* .reset_resume = rtl8192c_resume, */ #endif /* CONFIG_PM */ -#ifdef CONFIG_AUTOSUSPEND - .supports_autosuspend = 1, -#endif .disable_hub_initiated_lpm = 1, }; diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index 9098558..1c7101b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -2544,23 +2544,3 @@ void rtl92se_resume(struct ieee80211_hw *hw) pci_write_config_dword(rtlpci->pdev, 0x40, val & 0xffff00ff); } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config=0x%08X, write_into_reg=%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.h b/drivers/net/wireless/rtlwifi/rtl8192se/hw.h index da48aa8..4cacee1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.h @@ -74,7 +74,5 @@ void rtl92se_set_key(struct ieee80211_hw *hw, u8 enc_algo, bool is_wepkey, bool clear_all); void rtl92se_suspend(struct ieee80211_hw *hw); void rtl92se_resume(struct ieee80211_hw *hw); -void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index 2e8e6f8..1bff2a0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -290,7 +290,6 @@ static struct rtl_hal_ops rtl8192se_hal_ops = { .enable_hw_sec = rtl92se_enable_hw_security_config, .set_key = rtl92se_set_key, .init_sw_leds = rtl92se_init_sw_leds, - .allow_all_destaddr = rtl92se_allow_all_destaddr, .get_bbreg = rtl92s_phy_query_bb_reg, .set_bbreg = rtl92s_phy_set_bb_reg, .get_rfreg = rtl92s_phy_query_rf_reg, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c index 48fee1b..5b4a714 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c @@ -32,7 +32,6 @@ #include "dm.h" #include "fw.h" #include "../rtl8723com/fw_common.h" -#include "../rtl8723com/fw_common.h" #include "phy.h" #include "reg.h" #include "hal_btc.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c index 65c9e80..87f6916 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c @@ -2383,24 +2383,3 @@ void rtl8723ae_suspend(struct ieee80211_hw *hw) void rtl8723ae_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config=0x%08X, write_into_reg=%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h index 6fa24f79..d3bc39f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h @@ -67,7 +67,5 @@ void rtl8723ae_bt_reg_init(struct ieee80211_hw *hw); void rtl8723ae_bt_hw_init(struct ieee80211_hw *hw); void rtl8723ae_suspend(struct ieee80211_hw *hw); void rtl8723ae_resume(struct ieee80211_hw *hw); -void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c index 1087a3b..73cba1e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c @@ -238,7 +238,6 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = { .enable_hw_sec = rtl8723ae_enable_hw_security_config, .set_key = rtl8723ae_set_key, .init_sw_leds = rtl8723ae_init_sw_leds, - .allow_all_destaddr = rtl8723ae_allow_all_destaddr, .get_bbreg = rtl8723_phy_query_bb_reg, .set_bbreg = rtl8723_phy_set_bb_reg, .get_rfreg = rtl8723ae_phy_query_rf_reg, diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c index 0fdf090..3d55549 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c @@ -2501,23 +2501,3 @@ void rtl8723be_suspend(struct ieee80211_hw *hw) void rtl8723be_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da, - bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config = 0x%08X, write_into_reg =%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.h b/drivers/net/wireless/rtlwifi/rtl8723be/hw.h index b7449a9..64c7551 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.h @@ -59,6 +59,4 @@ void rtl8723be_bt_reg_init(struct ieee80211_hw *hw); void rtl8723be_bt_hw_init(struct ieee80211_hw *hw); void rtl8723be_suspend(struct ieee80211_hw *hw); void rtl8723be_resume(struct ieee80211_hw *hw); -void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da, - bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c index a072136..ff12bf4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c @@ -92,7 +92,7 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); rtl8723be_bt_reg_init(hw); - rtlpci->msi_support = false; + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); rtlpriv->dm.dm_initialgain_enable = 1; @@ -253,6 +253,7 @@ static struct rtl_mod_params rtl8723be_mod_params = { .inactiveps = true, .swctrl_lps = false, .fwctrl_lps = true, + .msi_support = false, .debug = DBG_EMERG, }; @@ -365,9 +366,11 @@ module_param_named(debug, rtl8723be_mod_params.debug, int, 0444); module_param_named(ips, rtl8723be_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); +module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444); MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\n"); +MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c index e0a0d8c..969eaea 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c @@ -33,7 +33,6 @@ #include "trx.h" #include "led.h" #include "dm.h" -#include "phy.h" static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) { diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 6965afd..407a793 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -1960,8 +1960,6 @@ struct rtl_hal_ops { u32 regaddr, u32 bitmask); void (*set_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); - void (*allow_all_destaddr)(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); void (*linked_set_reg) (struct ieee80211_hw *hw); void (*chk_switch_dmdp) (struct ieee80211_hw *hw); void (*dualmac_easy_concurrent) (struct ieee80211_hw *hw); @@ -2030,6 +2028,10 @@ struct rtl_mod_params { /* default: 1 = using linked fw power save */ bool fwctrl_lps; + + /* default: 0 = not using MSI interrupts mode */ + /* submodules should set their own defalut value */ + bool msi_support; }; struct rtl_hal_usbint_cfg { diff --git a/drivers/net/wireless/ti/wl1251/acx.c b/drivers/net/wireless/ti/wl1251/acx.c index 5a4ec56..5695628 100644 --- a/drivers/net/wireless/ti/wl1251/acx.c +++ b/drivers/net/wireless/ti/wl1251/acx.c @@ -2,7 +2,6 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/crc7.h> #include "wl1251.h" #include "reg.h" diff --git a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c index bf1fa18..ede31f0 100644 --- a/drivers/net/wireless/ti/wl1251/cmd.c +++ b/drivers/net/wireless/ti/wl1251/cmd.c @@ -2,7 +2,6 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/crc7.h> #include <linux/etherdevice.h> #include "wl1251.h" diff --git a/drivers/net/wireless/ti/wl1251/event.c b/drivers/net/wireless/ti/wl1251/event.c index db01053..c986303 100644 --- a/drivers/net/wireless/ti/wl1251/event.c +++ b/drivers/net/wireless/ti/wl1251/event.c @@ -124,11 +124,12 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) return ret; } - if (wl->vif && vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) { + if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); /* indicate to the stack, that beacons have been lost */ - ieee80211_beacon_loss(wl->vif); + if (wl->vif && wl->vif->type == NL80211_IFTYPE_STATION) + ieee80211_beacon_loss(wl->vif); } if (vector & REGAINED_BSS_EVENT_ID) { diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 757e257..4e782f1 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -550,6 +550,34 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } +static int wl1251_build_null_data(struct wl1251 *wl) +{ + struct sk_buff *skb = NULL; + int size; + void *ptr; + int ret = -ENOMEM; + + if (wl->bss_type == BSS_TYPE_IBSS) { + size = sizeof(struct wl12xx_null_data_template); + ptr = NULL; + } else { + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; + size = skb->len; + ptr = skb->data; + } + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size); + +out: + dev_kfree_skb(skb); + if (ret) + wl1251_warning("cmd buld null data failed: %d", ret); + + return ret; +} + static int wl1251_build_qos_null_data(struct wl1251 *wl) { struct ieee80211_qos_hdr template; @@ -687,16 +715,6 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) wl->power_level = conf->power_level; } - /* - * Tell stack that connection is lost because hw encryption isn't - * supported in monitor mode. - * This requires temporary enabling of the hw connection monitor flag - */ - if ((changed & IEEE80211_CONF_CHANGE_MONITOR) && wl->vif) { - wl->hw->flags |= IEEE80211_HW_CONNECTION_MONITOR; - ieee80211_connection_loss(wl->vif); - } - out_sleep: wl1251_ps_elp_sleep(wl); @@ -1103,24 +1121,19 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & BSS_CHANGED_BSSID) { + if ((changed & BSS_CHANGED_BSSID) && + memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - skb = ieee80211_nullfunc_get(wl->hw, wl->vif); - if (!skb) - goto out_sleep; - - ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, - skb->data, skb->len); - dev_kfree_skb(skb); - if (ret < 0) - goto out_sleep; + if (!is_zero_ether_addr(wl->bssid)) { + ret = wl1251_build_null_data(wl); + if (ret < 0) + goto out_sleep; - ret = wl1251_build_qos_null_data(wl); - if (ret < 0) - goto out; + ret = wl1251_build_qos_null_data(wl); + if (ret < 0) + goto out_sleep; - if (wl->bss_type != BSS_TYPE_IBSS) { ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, wl->dtim_period); if (ret < 0) @@ -1129,9 +1142,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_ASSOC) { - /* Disable temporary enabled hw connection monitor flag */ - wl->hw->flags &= ~IEEE80211_HW_CONNECTION_MONITOR; - if (bss_conf->assoc) { wl->beacon_int = bss_conf->beacon_int; @@ -1216,8 +1226,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, - wl->channel, wl->dtim_period); + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index b06d36d..a0aa8fa 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -23,6 +23,7 @@ #include <linux/irq.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/swab.h> #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/wl12xx.h> @@ -83,47 +84,44 @@ static void wl1251_spi_reset(struct wl1251 *wl) static void wl1251_spi_wake(struct wl1251 *wl) { - u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; struct spi_message m; + u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); - cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { wl1251_error("could not allocate cmd for spi init"); return; } - memset(crc, 0, sizeof(crc)); memset(&t, 0, sizeof(t)); spi_message_init(&m); /* Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ - cmd[2] = 0xff; - cmd[3] = 0xff; - cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; - cmd[0] = 0; - cmd[7] = 0; - cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; - cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + cmd[0] = 0xff; + cmd[1] = 0xff; + cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; + cmd[3] = 0; + cmd[4] = 0; + cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3; + cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + + cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS + | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) - cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; + cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; else - cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - - cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS - | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; - - crc[0] = cmd[1]; - crc[1] = cmd[0]; - crc[2] = cmd[7]; - crc[3] = cmd[6]; - crc[4] = cmd[5]; + cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; - cmd[4] |= WSPI_INIT_CMD_END; + cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; + /* + * The above is the logical order; it must actually be stored + * in the buffer byte-swapped. + */ + __swab32s((u32 *)cmd); + __swab32s((u32 *)cmd+1); t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; diff --git a/drivers/net/wireless/ti/wlcore/debugfs.h b/drivers/net/wireless/ti/wlcore/debugfs.h index f7381dd..0f2cfb0 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.h +++ b/drivers/net/wireless/ti/wlcore/debugfs.h @@ -57,7 +57,7 @@ static const struct file_operations name## _ops = { \ wl, &name## _ops); \ if (!entry || IS_ERR(entry)) \ goto err; \ - } while (0); + } while (0) #define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ @@ -66,7 +66,7 @@ static const struct file_operations name## _ops = { \ wl, &prefix## _## name## _ops); \ if (!entry || IS_ERR(entry)) \ goto err; \ - } while (0); + } while (0) #define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \ static ssize_t sub## _ ##name## _read(struct file *file, \ diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 077eb5b..02c91d6 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1416,7 +1416,7 @@ void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter) int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, u16 offset, u8 flags, - u8 *pattern, u8 len) + const u8 *pattern, u8 len) { struct wl12xx_rx_filter_field *field; diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 5f3a389..392c882 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -24,11 +24,12 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> +#include <linux/slab.h> +#include <linux/swab.h> #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/wl12xx.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include "wlcore.h" #include "wl12xx_80211.h" @@ -110,18 +111,16 @@ static void wl12xx_spi_reset(struct device *child) static void wl12xx_spi_init(struct device *child) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); - u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; struct spi_message m; + u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); - cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { dev_err(child->parent, "could not allocate cmd for spi init\n"); return; } - memset(crc, 0, sizeof(crc)); memset(&t, 0, sizeof(t)); spi_message_init(&m); @@ -129,30 +128,29 @@ static void wl12xx_spi_init(struct device *child) * Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ - cmd[2] = 0xff; - cmd[3] = 0xff; - cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; - cmd[0] = 0; - cmd[7] = 0; - cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; - cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + cmd[0] = 0xff; + cmd[1] = 0xff; + cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; + cmd[3] = 0; + cmd[4] = 0; + cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3; + cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + + cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS + | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) - cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; + cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; else - cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; + cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS - | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; - - crc[0] = cmd[1]; - crc[1] = cmd[0]; - crc[2] = cmd[7]; - crc[3] = cmd[6]; - crc[4] = cmd[5]; - - cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; - cmd[4] |= WSPI_INIT_CMD_END; + cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; + /* + * The above is the logical order; it must actually be stored + * in the buffer byte-swapped. + */ + __swab32s((u32 *)cmd); + __swab32s((u32 *)cmd+1); t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 756e890..c2c34a8 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -512,8 +512,8 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_queue_recovery_work(struct wl1271 *wl); size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, - u16 offset, u8 flags, - u8 *pattern, u8 len); + u16 offset, u8 flags, + const u8 *pattern, u8 len); void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 630a3fc..0d4a285 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -226,7 +226,7 @@ int xenvif_map_frontend_rings(struct xenvif *vif, grant_ref_t rx_ring_ref); /* Check for SKBs from frontend and schedule backend processing */ -void xenvif_check_rx_xenvif(struct xenvif *vif); +void xenvif_napi_schedule_or_enable_events(struct xenvif *vif); /* Prevent the device from generating any further traffic. */ void xenvif_carrier_off(struct xenvif *vif); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index a755733..53cdcdf 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -75,32 +75,8 @@ static int xenvif_poll(struct napi_struct *napi, int budget) work_done = xenvif_tx_action(vif, budget); if (work_done < budget) { - int more_to_do = 0; - unsigned long flags; - - /* It is necessary to disable IRQ before calling - * RING_HAS_UNCONSUMED_REQUESTS. Otherwise we might - * lose event from the frontend. - * - * Consider: - * RING_HAS_UNCONSUMED_REQUESTS - * <frontend generates event to trigger napi_schedule> - * __napi_complete - * - * This handler is still in scheduled state so the - * event has no effect at all. After __napi_complete - * this handler is descheduled and cannot get - * scheduled again. We lose event in this case and the ring - * will be completely stalled. - */ - - local_irq_save(flags); - - RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do); - if (!more_to_do) - __napi_complete(napi); - - local_irq_restore(flags); + napi_complete(napi); + xenvif_napi_schedule_or_enable_events(vif); } return work_done; @@ -194,7 +170,7 @@ static void xenvif_up(struct xenvif *vif) enable_irq(vif->tx_irq); if (vif->tx_irq != vif->rx_irq) enable_irq(vif->rx_irq); - xenvif_check_rx_xenvif(vif); + xenvif_napi_schedule_or_enable_events(vif); } static void xenvif_down(struct xenvif *vif) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 7666540..7367208 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -104,7 +104,7 @@ static inline unsigned long idx_to_kaddr(struct xenvif *vif, /* Find the containing VIF's structure from a pointer in pending_tx_info array */ -static inline struct xenvif* ubuf_to_vif(struct ubuf_info *ubuf) +static inline struct xenvif *ubuf_to_vif(const struct ubuf_info *ubuf) { u16 pending_idx = ubuf->desc; struct pending_tx_info *temp = @@ -323,6 +323,35 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, } /* + * Find the grant ref for a given frag in a chain of struct ubuf_info's + * skb: the skb itself + * i: the frag's number + * ubuf: a pointer to an element in the chain. It should not be NULL + * + * Returns a pointer to the element in the chain where the page were found. If + * not found, returns NULL. + * See the definition of callback_struct in common.h for more details about + * the chain. + */ +static const struct ubuf_info *xenvif_find_gref(const struct sk_buff *const skb, + const int i, + const struct ubuf_info *ubuf) +{ + struct xenvif *foreign_vif = ubuf_to_vif(ubuf); + + do { + u16 pending_idx = ubuf->desc; + + if (skb_shinfo(skb)->frags[i].page.p == + foreign_vif->mmap_pages[pending_idx]) + break; + ubuf = (struct ubuf_info *) ubuf->ctx; + } while (ubuf); + + return ubuf; +} + +/* * Prepare an SKB to be transmitted to the frontend. * * This function is responsible for allocating grant operations, meta @@ -346,9 +375,8 @@ static int xenvif_gop_skb(struct sk_buff *skb, int head = 1; int old_meta_prod; int gso_type; - struct ubuf_info *ubuf = skb_shinfo(skb)->destructor_arg; - grant_ref_t foreign_grefs[MAX_SKB_FRAGS]; - struct xenvif *foreign_vif = NULL; + const struct ubuf_info *ubuf = skb_shinfo(skb)->destructor_arg; + const struct ubuf_info *const head_ubuf = ubuf; old_meta_prod = npo->meta_prod; @@ -386,19 +414,6 @@ static int xenvif_gop_skb(struct sk_buff *skb, npo->copy_off = 0; npo->copy_gref = req->gref; - if ((skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) && - (ubuf->callback == &xenvif_zerocopy_callback)) { - int i = 0; - foreign_vif = ubuf_to_vif(ubuf); - - do { - u16 pending_idx = ubuf->desc; - foreign_grefs[i++] = - foreign_vif->pending_tx_info[pending_idx].req.gref; - ubuf = (struct ubuf_info *) ubuf->ctx; - } while (ubuf); - } - data = skb->data; while (data < skb_tail_pointer(skb)) { unsigned int offset = offset_in_page(data); @@ -415,13 +430,60 @@ static int xenvif_gop_skb(struct sk_buff *skb, } for (i = 0; i < nr_frags; i++) { + /* This variable also signals whether foreign_gref has a real + * value or not. + */ + struct xenvif *foreign_vif = NULL; + grant_ref_t foreign_gref; + + if ((skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) && + (ubuf->callback == &xenvif_zerocopy_callback)) { + const struct ubuf_info *const startpoint = ubuf; + + /* Ideally ubuf points to the chain element which + * belongs to this frag. Or if frags were removed from + * the beginning, then shortly before it. + */ + ubuf = xenvif_find_gref(skb, i, ubuf); + + /* Try again from the beginning of the list, if we + * haven't tried from there. This only makes sense in + * the unlikely event of reordering the original frags. + * For injected local pages it's an unnecessary second + * run. + */ + if (unlikely(!ubuf) && startpoint != head_ubuf) + ubuf = xenvif_find_gref(skb, i, head_ubuf); + + if (likely(ubuf)) { + u16 pending_idx = ubuf->desc; + + foreign_vif = ubuf_to_vif(ubuf); + foreign_gref = foreign_vif->pending_tx_info[pending_idx].req.gref; + /* Just a safety measure. If this was the last + * element on the list, the for loop will + * iterate again if a local page were added to + * the end. Using head_ubuf here prevents the + * second search on the chain. Or the original + * frags changed order, but that's less likely. + * In any way, ubuf shouldn't be NULL. + */ + ubuf = ubuf->ctx ? + (struct ubuf_info *) ubuf->ctx : + head_ubuf; + } else + /* This frag was a local page, added to the + * array after the skb left netback. + */ + ubuf = head_ubuf; + } xenvif_gop_frag_copy(vif, skb, npo, skb_frag_page(&skb_shinfo(skb)->frags[i]), skb_frag_size(&skb_shinfo(skb)->frags[i]), skb_shinfo(skb)->frags[i].page_offset, &head, foreign_vif, - foreign_grefs[i]); + foreign_vif ? foreign_gref : UINT_MAX); } return npo->meta_prod - old_meta_prod; @@ -654,7 +716,7 @@ done: notify_remote_via_irq(vif->rx_irq); } -void xenvif_check_rx_xenvif(struct xenvif *vif) +void xenvif_napi_schedule_or_enable_events(struct xenvif *vif) { int more_to_do; @@ -688,7 +750,7 @@ static void tx_credit_callback(unsigned long data) { struct xenvif *vif = (struct xenvif *)data; tx_add_credit(vif); - xenvif_check_rx_xenvif(vif); + xenvif_napi_schedule_or_enable_events(vif); } static void xenvif_tx_err(struct xenvif *vif, diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 65d4ca1..26c66a1 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -71,5 +71,6 @@ config NFC_PORT100 source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" source "drivers/nfc/nfcmrvl/Kconfig" +source "drivers/nfc/st21nfca/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index ae42a3f..23225b0 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index f2acd85..440291a 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -22,6 +22,8 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/delay.h> @@ -857,6 +859,92 @@ exit_state_wait_secure_write_answer: } } +#ifdef CONFIG_OF + +static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int ret; + + pp = client->dev.of_node; + if (!pp) { + ret = -ENODEV; + goto err_dt; + } + + /* Obtention of EN GPIO from device tree */ + ret = of_get_named_gpio(pp, "enable-gpios", 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + nfc_err(&client->dev, + "Failed to get EN gpio, error: %d\n", ret); + goto err_dt; + } + phy->gpio_en = ret; + + /* Configuration of EN GPIO */ + ret = gpio_request(phy->gpio_en, "pn544_en"); + if (ret) { + nfc_err(&client->dev, "Fail EN pin\n"); + goto err_dt; + } + ret = gpio_direction_output(phy->gpio_en, 0); + if (ret) { + nfc_err(&client->dev, "Fail EN pin direction\n"); + goto err_gpio_en; + } + + /* Obtention of FW GPIO from device tree */ + ret = of_get_named_gpio(pp, "firmware-gpios", 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + nfc_err(&client->dev, + "Failed to get FW gpio, error: %d\n", ret); + goto err_gpio_en; + } + phy->gpio_fw = ret; + + /* Configuration of FW GPIO */ + ret = gpio_request(phy->gpio_fw, "pn544_fw"); + if (ret) { + nfc_err(&client->dev, "Fail FW pin\n"); + goto err_gpio_en; + } + ret = gpio_direction_output(phy->gpio_fw, 0); + if (ret) { + nfc_err(&client->dev, "Fail FW pin direction\n"); + goto err_gpio_fw; + } + + /* IRQ */ + ret = irq_of_parse_and_map(pp, 0); + if (ret < 0) { + nfc_err(&client->dev, + "Unable to get irq, error: %d\n", ret); + goto err_gpio_fw; + } + client->irq = ret; + + return 0; + +err_gpio_fw: + gpio_free(phy->gpio_fw); +err_gpio_en: + gpio_free(phy->gpio_en); +err_dt: + return ret; +} + +#else + +static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} + +#endif + static int pn544_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -887,25 +975,36 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, phy); pdata = client->dev.platform_data; - if (pdata == NULL) { - nfc_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - if (pdata->request_resources == NULL) { - nfc_err(&client->dev, "request_resources() missing\n"); - return -EINVAL; - } + /* No platform data, using device tree. */ + if (!pdata && client->dev.of_node) { + r = pn544_hci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No DT data\n"); + return r; + } + /* Using platform data. */ + } else if (pdata) { - r = pdata->request_resources(client); - if (r) { - nfc_err(&client->dev, "Cannot get platform resources\n"); - return r; - } + if (pdata->request_resources == NULL) { + nfc_err(&client->dev, "request_resources() missing\n"); + return -EINVAL; + } - phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); - phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); - phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + r = pdata->request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get platform resources\n"); + return r; + } + + phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); + phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); + phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + } else { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } pn544_hci_i2c_platform_init(phy); @@ -930,8 +1029,12 @@ err_hci: free_irq(client->irq, phy); err_rti: - if (pdata->free_resources != NULL) + if (!pdata) { + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); + } else if (pdata->free_resources) { pdata->free_resources(); + } return r; } @@ -953,15 +1056,30 @@ static int pn544_hci_i2c_remove(struct i2c_client *client) pn544_hci_i2c_disable(phy); free_irq(client->irq, phy); - if (pdata->free_resources) + + /* No platform data, GPIOs have been requested by this driver */ + if (!pdata) { + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); + /* Using platform data */ + } else if (pdata->free_resources) { pdata->free_resources(); + } return 0; } +static const struct of_device_id of_pn544_i2c_match[] = { + { .compatible = "nxp,pn544-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pn544_i2c_match); + static struct i2c_driver pn544_hci_i2c_driver = { .driver = { .name = PN544_HCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_pn544_i2c_match), }, .probe = pn544_hci_i2c_probe, .id_table = pn544_hci_i2c_id_table, diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index b7a372a..4ac4d31 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -28,7 +28,8 @@ NFC_PROTO_MIFARE_MASK | \ NFC_PROTO_FELICA_MASK | \ NFC_PROTO_NFC_DEP_MASK | \ - NFC_PROTO_ISO14443_MASK) + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK) #define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ NFC_DIGITAL_DRV_CAPS_TG_CRC) @@ -120,6 +121,7 @@ struct port100_in_rf_setting { #define PORT100_COMM_TYPE_IN_212F 0x01 #define PORT100_COMM_TYPE_IN_424F 0x02 #define PORT100_COMM_TYPE_IN_106A 0x03 +#define PORT100_COMM_TYPE_IN_106B 0x07 static const struct port100_in_rf_setting in_rf_settings[] = { [NFC_DIGITAL_RF_TECH_212F] = { @@ -140,6 +142,12 @@ static const struct port100_in_rf_setting in_rf_settings[] = { .in_recv_set_number = 15, .in_recv_comm_type = PORT100_COMM_TYPE_IN_106A, }, + [NFC_DIGITAL_RF_TECH_106B] = { + .in_send_set_number = 3, + .in_send_comm_type = PORT100_COMM_TYPE_IN_106B, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_106B, + }, /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */ [NFC_DIGITAL_RF_TECH_LAST] = { 0 }, }; @@ -340,6 +348,32 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { { PORT100_IN_PROT_END, 0 }, }, + [NFC_DIGITAL_FRAMING_NFCB] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 20 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 0 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 1 }, + { PORT100_IN_PROT_CHECK_SOF, 1 }, + { PORT100_IN_PROT_ADD_EOF, 1 }, + { PORT100_IN_PROT_CHECK_EOF, 1 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCB_T4T] = { + /* nfc_digital_framing_nfcb */ + { PORT100_IN_PROT_END, 0 }, + }, /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */ [NFC_DIGITAL_FRAMING_LAST] = { { PORT100_IN_PROT_END, 0 }, diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig new file mode 100644 index 0000000..ee459f0 --- /dev/null +++ b/drivers/nfc/st21nfca/Kconfig @@ -0,0 +1,23 @@ +config NFC_ST21NFCA + tristate "STMicroelectronics ST21NFCA NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + STMicroelectronics ST21NFCA core driver. It implements the chipset + HCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will + be called st21nfca. + Say N if unsure. + +config NFC_ST21NFCA_I2C + tristate "NFC ST21NFCA i2c support" + depends on NFC_ST21NFCA && I2C && NFC_SHDLC + ---help--- + This module adds support for the STMicroelectronics st21nfca i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called st21nfca_i2c. + Say N if unsure. diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile new file mode 100644 index 0000000..038ed09 --- /dev/null +++ b/drivers/nfc/st21nfca/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for ST21NFCA HCI based NFC driver +# + +st21nfca_i2c-objs = i2c.o + +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca.o +obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c new file mode 100644 index 0000000..3f954ed --- /dev/null +++ b/drivers/nfc/st21nfca/i2c.c @@ -0,0 +1,724 @@ +/* + * I2C Link Layer for ST21NFCA HCI based Driver + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/crc-ccitt.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/nfc.h> +#include <linux/firmware.h> +#include <linux/unaligned/access_ok.h> +#include <linux/platform_data/st21nfca.h> + +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> +#include <net/nfc/nfc.h> + +#include "st21nfca.h" + +/* + * Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF. + * Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism + * called byte stuffing has been introduced. + * + * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING + * - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte) + * - xor byte with ST21NFCA_BYTE_STUFFING_MASK + */ +#define ST21NFCA_SOF_EOF 0x7e +#define ST21NFCA_BYTE_STUFFING_MASK 0x20 +#define ST21NFCA_ESCAPE_BYTE_STUFFING 0x7d + +/* SOF + 00 */ +#define ST21NFCA_FRAME_HEADROOM 2 + +/* 2 bytes crc + EOF */ +#define ST21NFCA_FRAME_TAILROOM 3 +#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \ + buf[1] == 0) + +#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" + +static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { + {ST21NFCA_HCI_DRIVER_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); + +struct st21nfca_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + unsigned int gpio_ena; + unsigned int gpio_irq; + unsigned int irq_polarity; + + struct sk_buff *pending_skb; + int current_read_len; + /* + * crc might have fail because i2c macro + * is disable due to other interface activity + */ + int crc_trials; + + int powered; + int run_mode; + + /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ + int hard_fault; + struct mutex phy_lock; +}; +static u8 len_seq[] = { 13, 24, 15, 29 }; +static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40}; + +#define I2C_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, 0); \ +} while (0) + +/* + * In order to get the CLF in a known state we generate an internal reboot + * using a proprietary command. + * Once the reboot is completed, we expect to receive a ST21NFCA_SOF_EOF + * fill buffer. + */ +static int st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy) +{ + u16 wait_reboot[] = { 50, 300, 1000 }; + char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E }; + u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE]; + int i, r = -1; + + for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) { + r = i2c_master_send(phy->i2c_dev, reboot_cmd, + sizeof(reboot_cmd)); + if (r < 0) + msleep(wait_reboot[i]); + } + if (r < 0) + return r; + + /* CLF is spending about 20ms to do an internal reboot */ + msleep(20); + r = -1; + for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) { + r = i2c_master_recv(phy->i2c_dev, tmp, + ST21NFCA_HCI_LLC_MAX_SIZE); + if (r < 0) + msleep(wait_reboot[i]); + } + if (r < 0) + return r; + + for (i = 0; i < ST21NFCA_HCI_LLC_MAX_SIZE && + tmp[i] == ST21NFCA_SOF_EOF; i++) + ; + + if (r != ST21NFCA_HCI_LLC_MAX_SIZE) + return -ENODEV; + + usleep_range(1000, 1500); + return 0; +} + +static int st21nfca_hci_i2c_enable(void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + + gpio_set_value(phy->gpio_ena, 1); + phy->powered = 1; + phy->run_mode = ST21NFCA_HCI_MODE; + + usleep_range(10000, 15000); + + return 0; +} + +static void st21nfca_hci_i2c_disable(void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + + pr_info("\n"); + gpio_set_value(phy->gpio_ena, 0); + + phy->powered = 0; +} + +static void st21nfca_hci_add_len_crc(struct sk_buff *skb) +{ + u16 crc; + u8 tmp; + + *skb_push(skb, 1) = 0; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + + tmp = crc & 0x00ff; + *skb_put(skb, 1) = tmp; + + tmp = (crc >> 8) & 0x00ff; + *skb_put(skb, 1) = tmp; +} + +static void st21nfca_hci_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, ST21NFCA_FRAME_HEADROOM); + skb_trim(skb, skb->len - ST21NFCA_FRAME_TAILROOM); +} + +/* + * Writing a frame must not return the number of written bytes. + * It must return either zero for success, or <0 for error. + * In addition, it must not alter the skb + */ +static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r = -1, i, j; + struct st21nfca_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2]; + + I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb); + + + if (phy->hard_fault != 0) + return phy->hard_fault; + + /* + * Compute CRC before byte stuffing computation on frame + * Note st21nfca_hci_add_len_crc is doing a byte stuffing + * on its own value + */ + st21nfca_hci_add_len_crc(skb); + + /* add ST21NFCA_SOF_EOF on tail */ + *skb_put(skb, 1) = ST21NFCA_SOF_EOF; + /* add ST21NFCA_SOF_EOF on head */ + *skb_push(skb, 1) = ST21NFCA_SOF_EOF; + + /* + * Compute byte stuffing + * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING + * insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte) + * xor byte with ST21NFCA_BYTE_STUFFING_MASK + */ + tmp[0] = skb->data[0]; + for (i = 1, j = 1; i < skb->len - 1; i++, j++) { + if (skb->data[i] == ST21NFCA_SOF_EOF + || skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) { + tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING; + j++; + tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK; + } else { + tmp[j] = skb->data[i]; + } + } + tmp[j] = skb->data[i]; + j++; + + /* + * Manage sleep mode + * Try 3 times to send data with delay between each + */ + mutex_lock(&phy->phy_lock); + for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) { + r = i2c_master_send(client, tmp, j); + if (r < 0) + msleep(wait_tab[i]); + } + mutex_unlock(&phy->phy_lock); + + if (r >= 0) { + if (r != j) + r = -EREMOTEIO; + else + r = 0; + } + + st21nfca_hci_remove_len_crc(skb); + + return r; +} + +static int get_frame_size(u8 *buf, int buflen) +{ + int len = 0; + if (buf[len + 1] == ST21NFCA_SOF_EOF) + return 0; + + for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++) + ; + + return len; +} + +static int check_crc(u8 *buf, int buflen) +{ + u16 crc; + + crc = crc_ccitt(0xffff, buf, buflen - 2); + crc = ~crc; + + if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) { + pr_err(ST21NFCA_HCI_DRIVER_NAME + ": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1], + buf[buflen - 2]); + + pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); + print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, + 16, 2, buf, buflen, false); + return -EPERM; + } + return 0; +} + +/* + * Prepare received data for upper layer. + * Received data include byte stuffing, crc and sof/eof + * which is not usable by hci part. + * returns: + * frame size without sof/eof, header and byte stuffing + * -EBADMSG : frame was incorrect and discarded + */ +static int st21nfca_hci_i2c_repack(struct sk_buff *skb) +{ + int i, j, r, size; + if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0)) + return -EBADMSG; + + size = get_frame_size(skb->data, skb->len); + if (size > 0) { + skb_trim(skb, size); + /* remove ST21NFCA byte stuffing for upper layer */ + for (i = 1, j = 0; i < skb->len; i++) { + if (skb->data[i + j] == + (u8) ST21NFCA_ESCAPE_BYTE_STUFFING) { + skb->data[i] = skb->data[i + j + 1] + | ST21NFCA_BYTE_STUFFING_MASK; + i++; + j++; + } + skb->data[i] = skb->data[i + j]; + } + /* remove byte stuffing useless byte */ + skb_trim(skb, i - j); + /* remove ST21NFCA_SOF_EOF from head */ + skb_pull(skb, 1); + + r = check_crc(skb->data, skb->len); + if (r != 0) { + i = 0; + return -EBADMSG; + } + + /* remove headbyte */ + skb_pull(skb, 1); + /* remove crc. Byte Stuffing is already removed here */ + skb_trim(skb, skb->len - 2); + return skb->len; + } + return 0; +} + +/* + * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees + * that i2c bus will be flushed and that next read will start on a new frame. + * returned skb contains only LLC header and payload. + * returns: + * frame size : if received frame is complete (find ST21NFCA_SOF_EOF at + * end of read) + * -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF + * at end of read) + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * (value returned from st21nfca_hci_i2c_repack) + * -EIO : if no ST21NFCA_SOF_EOF is found after reaching + * the read length end sequence + */ +static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy, + struct sk_buff *skb) +{ + int r, i; + u8 len; + u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD]; + struct i2c_client *client = phy->i2c_dev; + + if (phy->current_read_len < ARRAY_SIZE(len_seq)) { + len = len_seq[phy->current_read_len]; + + /* + * Add retry mecanism + * Operation on I2C interface may fail in case of operation on + * RF or SWP interface + */ + r = 0; + mutex_lock(&phy->phy_lock); + for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) { + r = i2c_master_recv(client, buf, len); + if (r < 0) + msleep(wait_tab[i]); + } + mutex_unlock(&phy->phy_lock); + + if (r != len) { + phy->current_read_len = 0; + return -EREMOTEIO; + } + + /* + * The first read sequence does not start with SOF. + * Data is corrupeted so we drop it. + */ + if (!phy->current_read_len && buf[0] != ST21NFCA_SOF_EOF) { + skb_trim(skb, 0); + phy->current_read_len = 0; + return -EIO; + } else if (phy->current_read_len && + IS_START_OF_FRAME(buf)) { + /* + * Previous frame transmission was interrupted and + * the frame got repeated. + * Received frame start with ST21NFCA_SOF_EOF + 00. + */ + skb_trim(skb, 0); + phy->current_read_len = 0; + } + + memcpy(skb_put(skb, len), buf, len); + + if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) { + phy->current_read_len = 0; + return st21nfca_hci_i2c_repack(skb); + } + phy->current_read_len++; + return -EAGAIN; + } + return -EIO; +} + +/* + * Reads an shdlc frame from the chip. This is not as straightforward as it + * seems. The frame format is data-crc, and corruption can occur anywhere + * while transiting on i2c bus, such that we could read an invalid data. + * The tricky case is when we read a corrupted data or crc. We must detect + * this here in order to determine that data can be transmitted to the hci + * core. This is the reason why we check the crc here. + * The CLF will repeat a frame until we send a RR on that frame. + * + * On ST21NFCA, IRQ goes in idle when read starts. As no size information are + * available in the incoming data, other IRQ might come. Every IRQ will trigger + * a read sequence with different length and will fill the current frame. + * The reception is complete once we reach a ST21NFCA_SOF_EOF. + */ +static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + struct i2c_client *client; + + int r; + + if (!phy || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + dev_dbg(&client->dev, "IRQ\n"); + + if (phy->hard_fault != 0) + return IRQ_HANDLED; + + r = st21nfca_hci_i2c_read(phy, phy->pending_skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + + nfc_hci_recv_frame(phy->hdev, NULL); + + return IRQ_HANDLED; + } else if (r == -EAGAIN || r == -EIO) { + return IRQ_HANDLED; + } else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) { + /* + * With ST21NFCA, only one interface (I2C, RF or SWP) + * may be active at a time. + * Having incorrect crc is usually due to i2c macrocell + * deactivation in the middle of a transmission. + * It may generate corrupted data on i2c. + * We give sometime to get i2c back. + * The complete frame will be repeated. + */ + msleep(wait_tab[phy->crc_trials]); + phy->crc_trials++; + phy->current_read_len = 0; + kfree_skb(phy->pending_skb); + } else if (r > 0) { + /* + * We succeeded to read data from the CLF and + * data is valid. + * Reset counter. + */ + nfc_hci_recv_frame(phy->hdev, phy->pending_skb); + phy->crc_trials = 0; + } + + phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); + if (phy->pending_skb == NULL) { + phy->hard_fault = -ENOMEM; + nfc_hci_recv_frame(phy->hdev, NULL); + } + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = st21nfca_hci_i2c_write, + .enable = st21nfca_hci_i2c_enable, + .disable = st21nfca_hci_i2c_disable, +}; + +#ifdef CONFIG_OF +static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int gpio; + int r; + + pp = client->dev.of_node; + if (!pp) + return -ENODEV; + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "enable-gpios", 0); + if (gpio < 0) { + nfc_err(&client->dev, "Failed to retrieve enable-gpios from device tree\n"); + return gpio; + } + + /* GPIO request and configuration */ + r = devm_gpio_request(&client->dev, gpio, "clf_enable"); + if (r) { + nfc_err(&client->dev, "Failed to request enable pin\n"); + return -ENODEV; + } + + r = gpio_direction_output(gpio, 1); + if (r) { + nfc_err(&client->dev, "Failed to set enable pin direction as output\n"); + return -ENODEV; + } + phy->gpio_ena = gpio; + + /* IRQ */ + r = irq_of_parse_and_map(pp, 0); + if (r < 0) { + nfc_err(&client->dev, + "Unable to get irq, error: %d\n", r); + return r; + } + + phy->irq_polarity = irq_get_trigger_type(r); + client->irq = r; + + return 0; +} +#else +static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} +#endif + +static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) +{ + struct st21nfca_nfc_platform_data *pdata; + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + int r; + int irq; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + /* store for later use */ + phy->gpio_irq = pdata->gpio_irq; + phy->gpio_ena = pdata->gpio_ena; + phy->irq_polarity = pdata->irq_polarity; + + r = devm_gpio_request(&client->dev, phy->gpio_irq, "wake_up"); + if (r) { + pr_err("%s : gpio_request failed\n", __FILE__); + return -ENODEV; + } + + r = gpio_direction_input(phy->gpio_irq); + if (r) { + pr_err("%s : gpio_direction_input failed\n", __FILE__); + return -ENODEV; + } + + if (phy->gpio_ena > 0) { + r = devm_gpio_request(&client->dev, + phy->gpio_ena, "clf_enable"); + if (r) { + pr_err("%s : ena gpio_request failed\n", __FILE__); + return -ENODEV; + } + r = gpio_direction_output(phy->gpio_ena, 1); + + if (r) { + pr_err("%s : ena gpio_direction_output failed\n", + __FILE__); + return -ENODEV; + } + } + + /* IRQ */ + irq = gpio_to_irq(phy->gpio_irq); + if (irq < 0) { + nfc_err(&client->dev, + "Unable to get irq number for GPIO %d error %d\n", + phy->gpio_irq, r); + return -ENODEV; + } + client->irq = irq; + + return 0; +} + +static int st21nfca_hci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st21nfca_i2c_phy *phy; + struct st21nfca_nfc_platform_data *pdata; + int r; + + dev_dbg(&client->dev, "%s\n", __func__); + dev_dbg(&client->dev, "IRQ: %d\n", client->irq); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy), + GFP_KERNEL); + if (!phy) { + nfc_err(&client->dev, + "Cannot allocate memory for st21nfca i2c phy.\n"); + return -ENOMEM; + } + + phy->i2c_dev = client; + phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); + if (phy->pending_skb == NULL) + return -ENOMEM; + + phy->current_read_len = 0; + phy->crc_trials = 0; + mutex_init(&phy->phy_lock); + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) { + r = st21nfca_hci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No platform data\n"); + return r; + } + } else if (pdata) { + r = st21nfca_hci_i2c_request_resources(client); + if (r) { + nfc_err(&client->dev, "Cannot get platform resources\n"); + return r; + } + } else { + nfc_err(&client->dev, "st21nfca platform resources not available\n"); + return -ENODEV; + } + + r = st21nfca_hci_platform_init(phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to reboot st21nfca\n"); + return -ENODEV; + } + + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + st21nfca_hci_irq_thread_fn, + phy->irq_polarity | IRQF_ONESHOT, + ST21NFCA_HCI_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + return r; + } + + return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, + ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM, + ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev); +} + +static int st21nfca_hci_i2c_remove(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __func__); + + st21nfca_hci_remove(phy->hdev); + + if (phy->powered) + st21nfca_hci_i2c_disable(phy); + + return 0; +} + +static const struct of_device_id of_st21nfca_i2c_match[] = { + { .compatible = "st,st21nfca_i2c", }, + {} +}; + +static struct i2c_driver st21nfca_hci_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = ST21NFCA_HCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_st21nfca_i2c_match), + }, + .probe = st21nfca_hci_i2c_probe, + .id_table = st21nfca_hci_i2c_id_table, + .remove = st21nfca_hci_i2c_remove, +}; + +module_i2c_driver(st21nfca_hci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c new file mode 100644 index 0000000..51e0f00b --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.c @@ -0,0 +1,698 @@ +/* + * HCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "st21nfca.h" + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define FULL_VERSION_LEN 3 + +/* Proprietary gates, events, commands and registers */ + +/* Commands that apply to all RF readers */ +#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK 0x30 + +#define ST21NFCA_RF_READER_ISO15693_GATE 0x12 +#define ST21NFCA_RF_READER_ISO15693_INVENTORY 0x01 + +/* + * Reader gate for communication with contact-less cards using Type A + * protocol ISO14443-3 but not compliant with ISO14443-4 + */ +#define ST21NFCA_RF_READER_14443_3_A_GATE 0x15 +#define ST21NFCA_RF_READER_14443_3_A_UID 0x02 +#define ST21NFCA_RF_READER_14443_3_A_ATQA 0x03 +#define ST21NFCA_RF_READER_14443_3_A_SAK 0x04 + +#define ST21NFCA_DEVICE_MGNT_GATE 0x01 +#define ST21NFCA_DEVICE_MGNT_PIPE 0x02 + +#define ST21NFCA_DM_GETINFO 0x13 +#define ST21NFCA_DM_GETINFO_PIPE_LIST 0x02 +#define ST21NFCA_DM_GETINFO_PIPE_INFO 0x01 +#define ST21NFCA_DM_PIPE_CREATED 0x02 +#define ST21NFCA_DM_PIPE_OPEN 0x04 +#define ST21NFCA_DM_RF_ACTIVE 0x80 + +#define ST21NFCA_DM_IS_PIPE_OPEN(p) \ + ((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN)) + +#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/ + +static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES); + +static struct nfc_hci_gate st21nfca_gates[] = { + {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE}, + {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE}, + {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE}, + {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, +}; + +struct st21nfca_pipe_info { + u8 pipe_state; + u8 src_host_id; + u8 src_gate_id; + u8 dst_host_id; + u8 dst_gate_id; +} __packed; + +/* Largest headroom needed for outgoing custom commands */ +#define ST21NFCA_CMDS_HEADROOM 7 + +static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) +{ + int i, j, r; + struct sk_buff *skb_pipe_list, *skb_pipe_info; + struct st21nfca_pipe_info *info; + + u8 pipe_list[] = { ST21NFCA_DM_GETINFO_PIPE_LIST, + NFC_HCI_TERMINAL_HOST_ID + }; + u8 pipe_info[] = { ST21NFCA_DM_GETINFO_PIPE_INFO, + NFC_HCI_TERMINAL_HOST_ID, 0 + }; + + skb_pipe_list = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL); + if (!skb_pipe_list) { + r = -ENOMEM; + goto free_list; + } + + skb_pipe_info = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL); + if (!skb_pipe_info) { + r = -ENOMEM; + goto free_info; + } + + /* On ST21NFCA device pipes number are dynamics + * A maximum of 16 pipes can be created at the same time + * If pipes are already created, hci_dev_up will fail. + * Doing a clear all pipe is a bad idea because: + * - It does useless EEPROM cycling + * - It might cause issue for secure elements support + * (such as removing connectivity or APDU reader pipe) + * A better approach on ST21NFCA is to: + * - get a pipe list for each host. + * (eg: NFC_HCI_HOST_CONTROLLER_ID for now). + * (TODO Later on UICC HOST and eSE HOST) + * - get pipe information + * - match retrieved pipe list in st21nfca_gates + * ST21NFCA_DEVICE_MGNT_GATE is a proprietary gate + * with ST21NFCA_DEVICE_MGNT_PIPE. + * Pipe can be closed and need to be open. + */ + r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, + ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE); + if (r < 0) + goto free_info; + + /* Get pipe list */ + r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list), + &skb_pipe_list); + if (r < 0) + goto free_info; + + /* Complete the existing gate_pipe table */ + for (i = 0; i < skb_pipe_list->len; i++) { + pipe_info[2] = skb_pipe_list->data[i]; + r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_GETINFO, pipe_info, + sizeof(pipe_info), &skb_pipe_info); + + if (r) + continue; + + /* + * Match pipe ID and gate ID + * Output format from ST21NFC_DM_GETINFO is: + * - pipe state (1byte) + * - source hid (1byte) + * - source gid (1byte) + * - destination hid (1byte) + * - destination gid (1byte) + */ + info = (struct st21nfca_pipe_info *) skb_pipe_info->data; + for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) && + (st21nfca_gates[j].gate != info->dst_gate_id); + j++) + ; + + if (j < ARRAY_SIZE(st21nfca_gates) && + st21nfca_gates[j].gate == info->dst_gate_id && + ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) { + st21nfca_gates[j].pipe = pipe_info[2]; + hdev->gate2pipe[st21nfca_gates[j].gate] = + st21nfca_gates[j].pipe; + } + } + + /* + * 3 gates have a well known pipe ID. + * They will never appear in the pipe list + */ + if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) { + for (i = skb_pipe_list->len + 3; + i < ARRAY_SIZE(st21nfca_gates); i++) { + r = nfc_hci_connect_gate(hdev, + NFC_HCI_HOST_CONTROLLER_ID, + st21nfca_gates[i].gate, + st21nfca_gates[i].pipe); + if (r < 0) + goto free_info; + } + } + + memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); +free_info: + kfree_skb(skb_pipe_info); +free_list: + kfree_skb(skb_pipe_list); + return r; +} + +static int st21nfca_hci_open(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int r; + + mutex_lock(&info->info_lock); + + if (info->state != ST21NFCA_ST_COLD) { + r = -EBUSY; + goto out; + } + + r = info->phy_ops->enable(info->phy_id); + + if (r == 0) + info->state = ST21NFCA_ST_READY; + +out: + mutex_unlock(&info->info_lock); + return r; +} + +static void st21nfca_hci_close(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + mutex_lock(&info->info_lock); + + if (info->state == ST21NFCA_ST_COLD) + goto out; + + info->phy_ops->disable(info->phy_id); + info->state = ST21NFCA_ST_COLD; + +out: + mutex_unlock(&info->info_lock); +} + +static int st21nfca_hci_ready(struct nfc_hci_dev *hdev) +{ + struct sk_buff *skb; + + u8 param; + int r; + + param = NFC_HCI_UICC_HOST_ID; + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_WHITELIST, ¶m, 1); + if (r < 0) + return r; + + /* Set NFC_MODE in device management gate to enable */ + r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_NFC_MODE, &skb); + if (r < 0) + return r; + + if (skb->data[0] == 0) { + kfree_skb(skb); + param = 1; + + r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_NFC_MODE, ¶m, 1); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, + NFC_HCI_ID_MGMT_VERSION_SW, &skb); + if (r < 0) + return r; + + if (skb->len != FULL_VERSION_LEN) { + kfree_skb(skb); + return -EINVAL; + } + + print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ", + DUMP_PREFIX_NONE, 16, 1, + skb->data, FULL_VERSION_LEN, false); + + kfree_skb(skb); + + return 0; +} + +static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->write(info->phy_id, skb); +} + +static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) +{ + int r; + + pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", + __func__, im_protocols, tm_protocols); + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + if (im_protocols) { + /* + * enable polling according to im_protocols & tm_protocols + * - CLOSE pipe according to im_protocols & tm_protocols + */ + if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + NFC_HCI_RF_READER_B_GATE); + if (r < 0) + return r; + } + + if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + NFC_HCI_RF_READER_A_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_F_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_14443_3_A_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_ISO15693_GATE); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + } + return r; +} + +static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa) +{ + int r; + struct sk_buff *atqa_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb); + if (r < 0) + goto exit; + + if (atqa_skb->len != 2) { + r = -EPROTO; + goto exit; + } + + *atqa = be16_to_cpu(*(__be16 *) atqa_skb->data); + +exit: + kfree_skb(atqa_skb); + return r; +} + +static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak) +{ + int r; + struct sk_buff *sak_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb); + if (r < 0) + goto exit; + + if (sak_skb->len != 1) { + r = -EPROTO; + goto exit; + } + + *sak = sak_skb->data[0]; + +exit: + kfree_skb(sak_skb); + return r; +} + +static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate, + int *len) +{ + int r; + struct sk_buff *uid_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb); + if (r < 0) + goto exit; + + if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + gate = uid_skb->data; + *len = uid_skb->len; +exit: + kfree_skb(uid_skb); + return r; +} + +static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + int r; + struct sk_buff *inventory_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_ISO15693_GATE, + ST21NFCA_RF_READER_ISO15693_INVENTORY, + &inventory_skb); + if (r < 0) + goto exit; + + skb_pull(inventory_skb, 2); + + if (inventory_skb->len == 0 || + inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + memcpy(target->iso15693_uid, inventory_skb->data, inventory_skb->len); + target->iso15693_dsfid = inventory_skb->data[1]; + target->is_iso15693 = 1; +exit: + kfree_skb(inventory_skb); + return r; +} + +static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target) +{ + int r, len; + u16 atqa; + u8 sak; + u8 uid[NFC_NFCID1_MAXSIZE]; + + switch (gate) { + case ST21NFCA_RF_READER_F_GATE: + target->supported_protocols = NFC_PROTO_FELICA_MASK; + break; + case ST21NFCA_RF_READER_14443_3_A_GATE: + /* ISO14443-3 type 1 or 2 tags */ + r = st21nfca_get_iso14443_3_atqa(hdev, &atqa); + if (r < 0) + return r; + if (atqa == 0x000c) { + target->supported_protocols = NFC_PROTO_JEWEL_MASK; + target->sens_res = 0x0c00; + } else { + r = st21nfca_get_iso14443_3_sak(hdev, &sak); + if (r < 0) + return r; + + r = st21nfca_get_iso14443_3_uid(hdev, uid, &len); + if (r < 0) + return r; + + target->supported_protocols = + nfc_hci_sak_to_protocol(sak); + if (target->supported_protocols == 0xffffffff) + return -EPROTO; + + target->sens_res = atqa; + target->sel_res = sak; + memcpy(target->nfcid1, uid, len); + target->nfcid1_len = len; + } + + break; + case ST21NFCA_RF_READER_ISO15693_GATE: + target->supported_protocols = NFC_PROTO_ISO15693_MASK; + r = st21nfca_get_iso15693_inventory(hdev, target); + if (r < 0) + return r; + break; + default: + return -EPROTO; + } + + return 0; +} + +#define ST21NFCA_CB_TYPE_READER_ISO15693 1 +static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb, + int err) +{ + struct st21nfca_hci_info *info = context; + + switch (info->async_cb_type) { + case ST21NFCA_CB_TYPE_READER_ISO15693: + if (err == 0) + skb_trim(skb, skb->len - 1); + info->async_cb(info->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + +/* + * Returns: + * <= 0: driver handled the data exchange + * 1: driver doesn't especially handle, please do standard processing + */ +static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev, + struct nfc_target *target, + struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__, + target->hci_reader_gate, skb->len); + + switch (target->hci_reader_gate) { + case ST21NFCA_RF_READER_F_GATE: + *skb_push(skb, 1) = 0x1a; + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, cb, cb_context); + case ST21NFCA_RF_READER_14443_3_A_GATE: + *skb_push(skb, 1) = 0x1a; /* CTR, see spec:10.2.2.1 */ + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, cb, cb_context); + case ST21NFCA_RF_READER_ISO15693_GATE: + info->async_cb_type = ST21NFCA_CB_TYPE_READER_ISO15693; + info->async_cb = cb; + info->async_cb_context = cb_context; + + *skb_push(skb, 1) = 0x17; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, + st21nfca_hci_data_exchange_cb, + info); + break; + default: + return 1; + } +} + +static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + u8 fwi = 0x11; + switch (target->hci_reader_gate) { + case NFC_HCI_RF_READER_A_GATE: + case NFC_HCI_RF_READER_B_GATE: + /* + * PRESENCE_CHECK on those gates is available + * However, the answer to this command is taking 3 * fwi + * if the card is no present. + * Instead, we send an empty I-Frame with a very short + * configurable fwi ~604µs. + */ + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL); + case ST21NFCA_RF_READER_14443_3_A_GATE: + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + ST21NFCA_RF_READER_CMD_PRESENCE_CHECK, + NULL, 0, NULL); + default: + return -EOPNOTSUPP; + } +} + +static struct nfc_hci_ops st21nfca_hci_ops = { + .open = st21nfca_hci_open, + .close = st21nfca_hci_close, + .load_session = st21nfca_hci_load_session, + .hci_ready = st21nfca_hci_ready, + .xmit = st21nfca_hci_xmit, + .start_poll = st21nfca_hci_start_poll, + .target_from_gate = st21nfca_hci_target_from_gate, + .im_transceive = st21nfca_hci_im_transceive, + .check_presence = st21nfca_hci_check_presence, +}; + +int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, + char *llc_name, int phy_headroom, int phy_tailroom, + int phy_payload, struct nfc_hci_dev **hdev) +{ + struct st21nfca_hci_info *info; + int r = 0; + int dev_num; + u32 protocols; + struct nfc_hci_init_data init_data; + unsigned long quirks = 0; + + info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto err_alloc_hdev; + } + + info->phy_ops = phy_ops; + info->phy_id = phy_id; + info->state = ST21NFCA_ST_COLD; + mutex_init(&info->info_lock); + + init_data.gate_count = ARRAY_SIZE(st21nfca_gates); + + memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); + + /* + * Session id must include the driver name + i2c bus addr + * persistent info to discriminate 2 identical chips + */ + dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES); + if (dev_num >= ST21NFCA_NUM_DEVICES) + goto err_alloc_hdev; + + scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x", + "ST21AH", dev_num); + + protocols = NFC_PROTO_JEWEL_MASK | + NFC_PROTO_MIFARE_MASK | + NFC_PROTO_FELICA_MASK | + NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK | + NFC_PROTO_ISO15693_MASK; + + set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks); + + info->hdev = + nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks, + protocols, llc_name, + phy_headroom + ST21NFCA_CMDS_HEADROOM, + phy_tailroom, phy_payload); + + if (!info->hdev) { + pr_err("Cannot allocate nfc hdev.\n"); + r = -ENOMEM; + goto err_alloc_hdev; + } + + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; + + *hdev = info->hdev; + + return 0; + +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: + kfree(info); + + return r; +} +EXPORT_SYMBOL(st21nfca_hci_probe); + +void st21nfca_hci_remove(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + nfc_hci_unregister_device(hdev); + nfc_hci_free_device(hdev); + kfree(info); +} +EXPORT_SYMBOL(st21nfca_hci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h new file mode 100644 index 0000000..334cd90 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LOCAL_ST21NFCA_H_ +#define __LOCAL_ST21NFCA_H_ + +#include <net/nfc/hci.h> + +#define HCI_MODE 0 + +/* framing in HCI mode */ +#define ST21NFCA_SOF_EOF_LEN 2 + +/* Almost every time value is 0 */ +#define ST21NFCA_HCI_LLC_LEN 1 + +/* Size in worst case : + * In normal case CRC len = 2 but byte stuffing + * may appear in case one CRC byte = ST21NFCA_SOF_EOF + */ +#define ST21NFCA_HCI_LLC_CRC 4 + +#define ST21NFCA_HCI_LLC_LEN_CRC (ST21NFCA_SOF_EOF_LEN + \ + ST21NFCA_HCI_LLC_LEN + \ + ST21NFCA_HCI_LLC_CRC) +#define ST21NFCA_HCI_LLC_MIN_SIZE (1 + ST21NFCA_HCI_LLC_LEN_CRC) + +/* Worst case when adding byte stuffing between each byte */ +#define ST21NFCA_HCI_LLC_MAX_PAYLOAD 29 +#define ST21NFCA_HCI_LLC_MAX_SIZE (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \ + ST21NFCA_HCI_LLC_MAX_PAYLOAD) + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define ST21NFCA_HCI_MODE 0 + +#define ST21NFCA_NUM_DEVICES 256 + +int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, + char *llc_name, int phy_headroom, int phy_tailroom, + int phy_payload, struct nfc_hci_dev **hdev); +void st21nfca_hci_remove(struct nfc_hci_dev *hdev); + +enum st21nfca_state { + ST21NFCA_ST_COLD, + ST21NFCA_ST_READY, +}; + +struct st21nfca_hci_info { + struct nfc_phy_ops *phy_ops; + void *phy_id; + + struct nfc_hci_dev *hdev; + + enum st21nfca_state state; + + struct mutex info_lock; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; + +} __packed; + +/* Reader RF commands */ +#define ST21NFCA_WR_XCHG_DATA 0x10 + +#define ST21NFCA_RF_READER_F_GATE 0x14 +#define ST21NFCA_RF_READER_F_DATARATE 0x01 +#define ST21NFCA_RF_READER_F_DATARATE_106 0x01 +#define ST21NFCA_RF_READER_F_DATARATE_212 0x02 +#define ST21NFCA_RF_READER_F_DATARATE_424 0x04 + +#endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index d9babe9..3b78b03 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -16,6 +16,7 @@ #include <linux/device.h> #include <linux/netdevice.h> #include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <linux/nfc.h> #include <linux/skbuff.h> #include <linux/delay.h> @@ -67,14 +68,14 @@ * only the SRX bit set, it means that all of the data has been received * (once what's in the fifo has been read). However, depending on timing * an interrupt status with only the SRX bit set may not be recived. In - * those cases, the timeout mechanism is used to wait 5 ms in case more - * data arrives. After 5 ms, it is assumed that all of the data has been + * those cases, the timeout mechanism is used to wait 20 ms in case more + * data arrives. After 20 ms, it is assumed that all of the data has been * received and the accumulated rx data is sent upstream. The * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose * (i.e., it indicates that some data has been received but we're not sure * if there is more coming so a timeout in this state means all data has - * been received and there isn't an error). The delay is 5 ms since delays - * over 2 ms have been observed during testing (a little extra just in case). + * been received and there isn't an error). The delay is 20 ms since delays + * of ~16 ms have been observed during testing. * * Type 2 write and sector select commands respond with a 4-bit ACK or NACK. * Having only 4 bits in the FIFO won't normally generate an interrupt so @@ -104,8 +105,11 @@ #define TRF7970A_SUPPORTED_PROTOCOLS \ (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_FELICA_MASK | \ NFC_PROTO_ISO15693_MASK) +#define TRF7970A_AUTOSUSPEND_DELAY 30000 /* 30 seconds */ + /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends * on what the current framing is, the address of the TX length byte 1 * register (0x1d), and the 2 byte length of the data to be transmitted. @@ -120,7 +124,7 @@ /* TX length is 3 nibbles long ==> 4KB - 1 bytes max */ #define TRF7970A_TX_MAX (4096 - 1) -#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT 5 +#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT 20 #define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT 3 #define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF 20 @@ -330,13 +334,15 @@ struct trf7970a { struct regulator *regulator; struct nfc_digital_dev *ddev; u32 quirks; - bool powering_up; bool aborting; struct sk_buff *tx_skb; struct sk_buff *rx_skb; nfc_digital_cmd_complete_t cb; void *cb_arg; + u8 chip_status_ctrl; u8 iso_ctrl; + u8 iso_ctrl_tech; + u8 modulator_sys_clk_ctrl; u8 special_fcn_reg1; int technology; int framing; @@ -681,7 +687,9 @@ static irqreturn_t trf7970a_irq(int irq, void *dev_id) trf->ignore_timeout = !cancel_delayed_work(&trf->timeout_work); trf7970a_drain_fifo(trf, status); - } else if (!(status & TRF7970A_IRQ_STATUS_TX)) { + } else if (status == TRF7970A_IRQ_STATUS_TX) { + trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + } else { trf7970a_send_err_upstream(trf, -EIO); } break; @@ -757,8 +765,8 @@ static int trf7970a_init(struct trf7970a *trf) if (ret) goto err_out; - ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, - TRF7970A_MODULATOR_DEPTH_OOK); + /* Must clear NFC Target Detection Level reg due to erratum */ + ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0); if (ret) goto err_out; @@ -774,12 +782,7 @@ static int trf7970a_init(struct trf7970a *trf) trf->special_fcn_reg1 = 0; - ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, - TRF7970A_CHIP_STATUS_RF_ON | - TRF7970A_CHIP_STATUS_VRS5_3); - if (ret) - goto err_out; - + trf->iso_ctrl = 0xff; return 0; err_out: @@ -791,53 +794,29 @@ static void trf7970a_switch_rf_off(struct trf7970a *trf) { dev_dbg(trf->dev, "Switching rf off\n"); - gpio_set_value(trf->en_gpio, 0); - gpio_set_value(trf->en2_gpio, 0); + trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON; + + trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, trf->chip_status_ctrl); trf->aborting = false; trf->state = TRF7970A_ST_OFF; + + pm_runtime_mark_last_busy(trf->dev); + pm_runtime_put_autosuspend(trf->dev); } -static int trf7970a_switch_rf_on(struct trf7970a *trf) +static void trf7970a_switch_rf_on(struct trf7970a *trf) { - unsigned long delay; - int ret; - dev_dbg(trf->dev, "Switching rf on\n"); - if (trf->powering_up) - usleep_range(5000, 6000); - - gpio_set_value(trf->en2_gpio, 1); - usleep_range(1000, 2000); - gpio_set_value(trf->en_gpio, 1); + pm_runtime_get_sync(trf->dev); - /* The delay between enabling the trf7970a and issuing the first - * command is significantly longer the very first time after powering - * up. Make sure the longer delay is only done the first time. - */ - if (trf->powering_up) { - delay = 20000; - trf->powering_up = false; - } else { - delay = 5000; - } - - usleep_range(delay, delay + 1000); - - ret = trf7970a_init(trf); - if (ret) - trf7970a_switch_rf_off(trf); - else - trf->state = TRF7970A_ST_IDLE; - - return ret; + trf->state = TRF7970A_ST_IDLE; } static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on) { struct trf7970a *trf = nfc_digital_get_drvdata(ddev); - int ret = 0; dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on); @@ -846,7 +825,7 @@ static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on) if (on) { switch (trf->state) { case TRF7970A_ST_OFF: - ret = trf7970a_switch_rf_on(trf); + trf7970a_switch_rf_on(trf); break; case TRF7970A_ST_IDLE: case TRF7970A_ST_IDLE_RX_BLOCKED: @@ -871,7 +850,7 @@ static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on) } mutex_unlock(&trf->lock); - return ret; + return 0; } static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech) @@ -882,10 +861,24 @@ static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech) switch (tech) { case NFC_DIGITAL_RF_TECH_106A: - trf->iso_ctrl = TRF7970A_ISO_CTRL_14443A_106; + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443A_106; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + break; + case NFC_DIGITAL_RF_TECH_106B: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443B_106; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + break; + case NFC_DIGITAL_RF_TECH_212F: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_212; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + break; + case NFC_DIGITAL_RF_TECH_424F: + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_424; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; break; case NFC_DIGITAL_RF_TECH_ISO15693: - trf->iso_ctrl = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; + trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; + trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; break; default: dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech); @@ -899,24 +892,31 @@ static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech) static int trf7970a_config_framing(struct trf7970a *trf, int framing) { + u8 iso_ctrl = trf->iso_ctrl_tech; + int ret; + dev_dbg(trf->dev, "framing: %d\n", framing); switch (framing) { case NFC_DIGITAL_FRAMING_NFCA_SHORT: case NFC_DIGITAL_FRAMING_NFCA_STANDARD: trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC; - trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; break; case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: case NFC_DIGITAL_FRAMING_NFCA_T4T: + case NFC_DIGITAL_FRAMING_NFCB: + case NFC_DIGITAL_FRAMING_NFCB_T4T: + case NFC_DIGITAL_FRAMING_NFCF: + case NFC_DIGITAL_FRAMING_NFCF_T3T: case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: case NFC_DIGITAL_FRAMING_ISO15693_T5T: trf->tx_cmd = TRF7970A_CMD_TRANSMIT; - trf->iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; + iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; break; case NFC_DIGITAL_FRAMING_NFCA_T2T: trf->tx_cmd = TRF7970A_CMD_TRANSMIT; - trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; break; default: dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing); @@ -925,24 +925,46 @@ static int trf7970a_config_framing(struct trf7970a *trf, int framing) trf->framing = framing; - return trf7970a_write(trf, TRF7970A_ISO_CTRL, trf->iso_ctrl); + if (iso_ctrl != trf->iso_ctrl) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl); + if (ret) + return ret; + + trf->iso_ctrl = iso_ctrl; + + ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, + trf->modulator_sys_clk_ctrl); + if (ret) + return ret; + } + + if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) { + ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, + trf->chip_status_ctrl | + TRF7970A_CHIP_STATUS_RF_ON); + if (ret) + return ret; + + trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON; + + usleep_range(5000, 6000); + } + + return 0; } static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param) { struct trf7970a *trf = nfc_digital_get_drvdata(ddev); - int ret = 0; + int ret; dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param); mutex_lock(&trf->lock); - if (trf->state == TRF7970A_ST_OFF) { - ret = trf7970a_switch_rf_on(trf); - if (ret) - goto err_out; - } + if (trf->state == TRF7970A_ST_OFF) + trf7970a_switch_rf_on(trf); switch (type) { case NFC_DIGITAL_CONFIG_RF_TECH: @@ -956,7 +978,6 @@ static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type, ret = -EINVAL; } -err_out: mutex_unlock(&trf->lock); return ret; } @@ -1191,7 +1212,18 @@ static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev) dev_dbg(trf->dev, "Abort process initiated\n"); mutex_lock(&trf->lock); - trf->aborting = true; + + switch (trf->state) { + case TRF7970A_ST_WAIT_FOR_TX_FIFO: + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + case TRF7970A_ST_WAIT_TO_ISSUE_EOF: + trf->aborting = true; + break; + default: + break; + } + mutex_unlock(&trf->lock); } @@ -1206,12 +1238,25 @@ static struct nfc_digital_ops trf7970a_nfc_ops = { .abort_cmd = trf7970a_abort_cmd, }; +static int trf7970a_get_autosuspend_delay(struct device_node *np) +{ + int autosuspend_delay, ret; + + ret = of_property_read_u32(np, "autosuspend-delay", &autosuspend_delay); + if (ret) + autosuspend_delay = TRF7970A_AUTOSUSPEND_DELAY; + + of_node_put(np); + + return autosuspend_delay; +} + static int trf7970a_probe(struct spi_device *spi) { struct device_node *np = spi->dev.of_node; const struct spi_device_id *id = spi_get_device_id(spi); struct trf7970a *trf; - int ret; + int uvolts, autosuspend_delay, ret; if (!np) { dev_err(&spi->dev, "No Device Tree entry\n"); @@ -1281,7 +1326,10 @@ static int trf7970a_probe(struct spi_device *spi) goto err_destroy_lock; } - trf->powering_up = true; + uvolts = regulator_get_voltage(trf->regulator); + + if (uvolts > 4000000) + trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3; trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops, TRF7970A_SUPPORTED_PROTOCOLS, @@ -1297,6 +1345,12 @@ static int trf7970a_probe(struct spi_device *spi) nfc_digital_set_drvdata(trf->ddev, trf); spi_set_drvdata(spi, trf); + autosuspend_delay = trf7970a_get_autosuspend_delay(np); + + pm_runtime_set_autosuspend_delay(trf->dev, autosuspend_delay); + pm_runtime_use_autosuspend(trf->dev); + pm_runtime_enable(trf->dev); + ret = nfc_digital_register_device(trf->ddev); if (ret) { dev_err(trf->dev, "Can't register NFC digital device: %d\n", @@ -1307,6 +1361,7 @@ static int trf7970a_probe(struct spi_device *spi) return 0; err_free_ddev: + pm_runtime_disable(trf->dev); nfc_digital_free_device(trf->ddev); err_disable_regulator: regulator_disable(trf->regulator); @@ -1321,15 +1376,16 @@ static int trf7970a_remove(struct spi_device *spi) mutex_lock(&trf->lock); - trf7970a_switch_rf_off(trf); - trf7970a_init(trf); - switch (trf->state) { case TRF7970A_ST_WAIT_FOR_TX_FIFO: case TRF7970A_ST_WAIT_FOR_RX_DATA: case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: case TRF7970A_ST_WAIT_TO_ISSUE_EOF: trf7970a_send_err_upstream(trf, -ECANCELED); + /* FALLTHROUGH */ + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + pm_runtime_put_sync(trf->dev); break; default: break; @@ -1337,6 +1393,8 @@ static int trf7970a_remove(struct spi_device *spi) mutex_unlock(&trf->lock); + pm_runtime_disable(trf->dev); + nfc_digital_unregister_device(trf->ddev); nfc_digital_free_device(trf->ddev); @@ -1347,6 +1405,70 @@ static int trf7970a_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int trf7970a_pm_runtime_suspend(struct device *dev) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct trf7970a *trf = spi_get_drvdata(spi); + int ret; + + dev_dbg(dev, "Runtime suspend\n"); + + if (trf->state != TRF7970A_ST_OFF) { + dev_dbg(dev, "Can't suspend - not in OFF state (%d)\n", + trf->state); + return -EBUSY; + } + + gpio_set_value(trf->en_gpio, 0); + gpio_set_value(trf->en2_gpio, 0); + + ret = regulator_disable(trf->regulator); + if (ret) + dev_err(dev, "%s - Can't disable VIN: %d\n", __func__, ret); + + return ret; +} + +static int trf7970a_pm_runtime_resume(struct device *dev) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct trf7970a *trf = spi_get_drvdata(spi); + int ret; + + dev_dbg(dev, "Runtime resume\n"); + + ret = regulator_enable(trf->regulator); + if (ret) { + dev_err(dev, "%s - Can't enable VIN: %d\n", __func__, ret); + return ret; + } + + usleep_range(5000, 6000); + + gpio_set_value(trf->en2_gpio, 1); + usleep_range(1000, 2000); + gpio_set_value(trf->en_gpio, 1); + + usleep_range(20000, 21000); + + ret = trf7970a_init(trf); + if (ret) { + dev_err(dev, "%s - Can't initialize: %d\n", __func__, ret); + return ret; + } + + pm_runtime_mark_last_busy(dev); + + return 0; +} +#endif + +static const struct dev_pm_ops trf7970a_pm_ops = { + SET_RUNTIME_PM_OPS(trf7970a_pm_runtime_suspend, + trf7970a_pm_runtime_resume, NULL) +}; + static const struct spi_device_id trf7970a_id_table[] = { { "trf7970a", TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA }, { } @@ -1360,6 +1482,7 @@ static struct spi_driver trf7970a_spi_driver = { .driver = { .name = "trf7970a", .owner = THIS_MODULE, + .pm = &trf7970a_pm_ops, }, }; diff --git a/drivers/of/base.c b/drivers/of/base.c index 6d4ee22..32e969d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1831,6 +1831,10 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!found) return -ENODEV; + /* At early boot, bail out and defer setup to of_init() */ + if (!of_kset) + return found ? 0 : -ENODEV; + /* Update the sysfs attribute */ sysfs_remove_bin_file(&np->kobj, &oldprop->attr); __of_add_property_sysfs(np, newprop); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 9a95831..fb4a598 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -14,6 +14,7 @@ #include <linux/netdevice.h> #include <linux/err.h> #include <linux/phy.h> +#include <linux/phy_fixed.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_mdio.h> @@ -22,27 +23,6 @@ MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); MODULE_LICENSE("GPL"); -static void of_set_phy_supported(struct phy_device *phydev, u32 max_speed) -{ - /* The default values for phydev->supported are provided by the PHY - * driver "features" member, we want to reset to sane defaults fist - * before supporting higher speeds. - */ - phydev->supported &= PHY_DEFAULT_FEATURES; - - switch (max_speed) { - default: - return; - - case SPEED_1000: - phydev->supported |= PHY_1000BT_FEATURES; - case SPEED_100: - phydev->supported |= PHY_100BT_FEATURES; - case SPEED_10: - phydev->supported |= PHY_10BT_FEATURES; - } -} - /* Extract the clause 22 phy ID from the compatible string of the form * ethernet-phy-idAAAA.BBBB */ static int of_get_phy_id(struct device_node *device, u32 *phy_id) @@ -66,7 +46,6 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi struct phy_device *phy; bool is_c45; int rc; - u32 max_speed = 0; u32 phy_id; is_c45 = of_device_is_compatible(child, @@ -103,17 +82,33 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi return 1; } - /* Set phydev->supported based on the "max-speed" property - * if present */ - if (!of_property_read_u32(child, "max-speed", &max_speed)) - of_set_phy_supported(phy, max_speed); - dev_dbg(&mdio->dev, "registered phy %s at address %i\n", child->name, addr); return 0; } +static int of_mdio_parse_addr(struct device *dev, const struct device_node *np) +{ + u32 addr; + int ret; + + ret = of_property_read_u32(np, "reg", &addr); + if (ret < 0) { + dev_err(dev, "%s has invalid PHY address\n", np->full_name); + return ret; + } + + /* A PHY must have a reg property in the range [0-31] */ + if (addr >= PHY_MAX_ADDR) { + dev_err(dev, "%s PHY address %i is too large\n", + np->full_name, addr); + return -EINVAL; + } + + return addr; +} + /** * of_mdiobus_register - Register mii_bus and create PHYs from the device tree * @mdio: pointer to mii_bus structure @@ -126,9 +121,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { struct device_node *child; const __be32 *paddr; - u32 addr; bool scanphys = false; - int rc, i, len; + int addr, rc, i; /* Mask out all PHYs from auto probing. Instead the PHYs listed in * the device tree are populated after the bus has been registered */ @@ -148,19 +142,9 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) /* Loop over the child nodes and register a phy_device for each one */ for_each_available_child_of_node(np, child) { - /* A PHY must have a reg property in the range [0-31] */ - paddr = of_get_property(child, "reg", &len); - if (!paddr || len < sizeof(*paddr)) { + addr = of_mdio_parse_addr(&mdio->dev, child); + if (addr < 0) { scanphys = true; - dev_err(&mdio->dev, "%s has invalid PHY address\n", - child->full_name); - continue; - } - - addr = be32_to_cpup(paddr); - if (addr >= PHY_MAX_ADDR) { - dev_err(&mdio->dev, "%s PHY address %i is too large\n", - child->full_name, addr); continue; } @@ -175,7 +159,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) /* auto scan for PHYs with empty reg property */ for_each_available_child_of_node(np, child) { /* Skip PHYs with reg property set */ - paddr = of_get_property(child, "reg", &len); + paddr = of_get_property(child, "reg", NULL); if (paddr) continue; @@ -198,6 +182,40 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) } EXPORT_SYMBOL(of_mdiobus_register); +/** + * of_mdiobus_link_phydev - Find a device node for a phy + * @mdio: pointer to mii_bus structure + * @phydev: phydev for which the of_node pointer should be set + * + * Walk the list of subnodes of a mdio bus and look for a node that matches the + * phy's address with its 'reg' property. If found, set the of_node pointer for + * the phy. This allows auto-probed pyh devices to be supplied with information + * passed in via DT. + */ +void of_mdiobus_link_phydev(struct mii_bus *mdio, + struct phy_device *phydev) +{ + struct device *dev = &phydev->dev; + struct device_node *child; + + if (dev->of_node || !mdio->dev.of_node) + return; + + for_each_available_child_of_node(mdio->dev.of_node, child) { + int addr; + + addr = of_mdio_parse_addr(&mdio->dev, child); + if (addr < 0) + continue; + + if (addr == phydev->addr) { + dev->of_node = child; + return; + } + } +} +EXPORT_SYMBOL(of_mdiobus_link_phydev); + /* Helper function for of_phy_find_device */ static int of_phy_match(struct device *dev, void *phy_np) { @@ -245,44 +263,6 @@ struct phy_device *of_phy_connect(struct net_device *dev, EXPORT_SYMBOL(of_phy_connect); /** - * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy - * @dev: pointer to net_device claiming the phy - * @hndlr: Link state callback for the network device - * @iface: PHY data interface type - * - * This function is a temporary stop-gap and will be removed soon. It is - * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers. Do - * not call this function from new drivers. - */ -struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, - void (*hndlr)(struct net_device *), - phy_interface_t iface) -{ - struct device_node *net_np; - char bus_id[MII_BUS_ID_SIZE + 3]; - struct phy_device *phy; - const __be32 *phy_id; - int sz; - - if (!dev->dev.parent) - return NULL; - - net_np = dev->dev.parent->of_node; - if (!net_np) - return NULL; - - phy_id = of_get_property(net_np, "fixed-link", &sz); - if (!phy_id || sz < sizeof(*phy_id)) - return NULL; - - sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0])); - - phy = phy_connect(dev, bus_id, hndlr, iface); - return IS_ERR(phy) ? NULL : phy; -} -EXPORT_SYMBOL(of_phy_connect_fixed_link); - -/** * of_phy_attach - Attach to a PHY without starting the state machine * @dev: pointer to net_device claiming the phy * @phy_np: Node pointer for the PHY @@ -301,3 +281,69 @@ struct phy_device *of_phy_attach(struct net_device *dev, return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_attach); + +#if defined(CONFIG_FIXED_PHY) +/* + * of_phy_is_fixed_link() and of_phy_register_fixed_link() must + * support two DT bindings: + * - the old DT binding, where 'fixed-link' was a property with 5 + * cells encoding various informations about the fixed PHY + * - the new DT binding, where 'fixed-link' is a sub-node of the + * Ethernet device. + */ +bool of_phy_is_fixed_link(struct device_node *np) +{ + struct device_node *dn; + int len; + + /* New binding */ + dn = of_get_child_by_name(np, "fixed-link"); + if (dn) { + of_node_put(dn); + return true; + } + + /* Old binding */ + if (of_get_property(np, "fixed-link", &len) && + len == (5 * sizeof(__be32))) + return true; + + return false; +} +EXPORT_SYMBOL(of_phy_is_fixed_link); + +int of_phy_register_fixed_link(struct device_node *np) +{ + struct fixed_phy_status status = {}; + struct device_node *fixed_link_node; + const __be32 *fixed_link_prop; + int len; + + /* New binding */ + fixed_link_node = of_get_child_by_name(np, "fixed-link"); + if (fixed_link_node) { + status.link = 1; + status.duplex = of_property_read_bool(np, "full-duplex"); + if (of_property_read_u32(fixed_link_node, "speed", &status.speed)) + return -EINVAL; + status.pause = of_property_read_bool(np, "pause"); + status.asym_pause = of_property_read_bool(np, "asym-pause"); + of_node_put(fixed_link_node); + return fixed_phy_register(PHY_POLL, &status, np); + } + + /* Old binding */ + fixed_link_prop = of_get_property(np, "fixed-link", &len); + if (fixed_link_prop && len == (5 * sizeof(__be32))) { + status.link = 1; + status.duplex = be32_to_cpu(fixed_link_prop[1]); + status.speed = be32_to_cpu(fixed_link_prop[2]); + status.pause = be32_to_cpu(fixed_link_prop[3]); + status.asym_pause = be32_to_cpu(fixed_link_prop[4]); + return fixed_phy_register(PHY_POLL, &status, np); + } + + return -ENODEV; +} +EXPORT_SYMBOL(of_phy_register_fixed_link); +#endif diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index d3d1cfd..e384e25 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -293,6 +293,58 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, return PCIBIOS_SUCCESSFUL; } +/* + * Remove windows, starting from the largest ones to the smallest + * ones. + */ +static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, + phys_addr_t base, size_t size) +{ + while (size) { + size_t sz = 1 << (fls(size) - 1); + + mvebu_mbus_del_window(base, sz); + base += sz; + size -= sz; + } +} + +/* + * MBus windows can only have a power of two size, but PCI BARs do not + * have this constraint. Therefore, we have to split the PCI BAR into + * areas each having a power of two size. We start from the largest + * one (i.e highest order bit set in the size). + */ +static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) +{ + size_t size_mapped = 0; + + while (size) { + size_t sz = 1 << (fls(size) - 1); + int ret; + + ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base, + sz, remap); + if (ret) { + dev_err(&port->pcie->pdev->dev, + "Could not create MBus window at 0x%x, size 0x%x: %d\n", + base, sz, ret); + mvebu_pcie_del_windows(port, base - size_mapped, + size_mapped); + return; + } + + size -= sz; + size_mapped += sz; + base += sz; + if (remap != MVEBU_MBUS_NO_REMAP) + remap += sz; + } +} + static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) { phys_addr_t iobase; @@ -304,8 +356,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) /* If a window was configured, remove it */ if (port->iowin_base) { - mvebu_mbus_del_window(port->iowin_base, - port->iowin_size); + mvebu_pcie_del_windows(port, port->iowin_base, + port->iowin_size); port->iowin_base = 0; port->iowin_size = 0; } @@ -331,11 +383,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) port->iowin_base = port->pcie->io.start + iobase; port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | (port->bridge.iolimitupper << 16)) - - iobase); + iobase) + 1; - mvebu_mbus_add_window_remap_by_id(port->io_target, port->io_attr, - port->iowin_base, port->iowin_size, - iobase); + mvebu_pcie_add_windows(port, port->io_target, port->io_attr, + port->iowin_base, port->iowin_size, + iobase); } static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) @@ -346,8 +398,8 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) /* If a window was configured, remove it */ if (port->memwin_base) { - mvebu_mbus_del_window(port->memwin_base, - port->memwin_size); + mvebu_pcie_del_windows(port, port->memwin_base, + port->memwin_size); port->memwin_base = 0; port->memwin_size = 0; } @@ -364,10 +416,11 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16); port->memwin_size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - - port->memwin_base; + port->memwin_base + 1; - mvebu_mbus_add_window_by_id(port->mem_target, port->mem_attr, - port->memwin_base, port->memwin_size); + mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr, + port->memwin_base, port->memwin_size, + MVEBU_MBUS_NO_REMAP); } /* @@ -743,14 +796,21 @@ static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, /* * On the PCI-to-PCI bridge side, the I/O windows must have at - * least a 64 KB size and be aligned on their size, and the - * memory windows must have at least a 1 MB size and be - * aligned on their size + * least a 64 KB size and the memory windows must have at + * least a 1 MB size. Moreover, MBus windows need to have a + * base address aligned on their size, and their size must be + * a power of two. This means that if the BAR doesn't have a + * power of two size, several MBus windows will actually be + * created. We need to ensure that the biggest MBus window + * (which will be the first one) is aligned on its size, which + * explains the rounddown_pow_of_two() being done here. */ if (res->flags & IORESOURCE_IO) - return round_up(start, max_t(resource_size_t, SZ_64K, size)); + return round_up(start, max_t(resource_size_t, SZ_64K, + rounddown_pow_of_two(size))); else if (res->flags & IORESOURCE_MEM) - return round_up(start, max_t(resource_size_t, SZ_1M, size)); + return round_up(start, max_t(resource_size_t, SZ_1M, + rounddown_pow_of_two(size))); else return start; } diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index 5849927..6efc2ec 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -282,8 +282,8 @@ static int board_added(struct slot *p_slot) return WRONG_BUS_FREQUENCY; } - bsp = ctrl->pci_dev->bus->cur_bus_speed; - msp = ctrl->pci_dev->bus->max_bus_speed; + bsp = ctrl->pci_dev->subordinate->cur_bus_speed; + msp = ctrl->pci_dev->subordinate->max_bus_speed; /* Check if there are other slots or devices on the same bus */ if (!list_empty(&ctrl->pci_dev->subordinate->devices)) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7325d43..759475e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3067,7 +3067,8 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev) if (!pci_is_pcie(dev)) return 1; - return pci_wait_for_pending(dev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_TRPND); + return pci_wait_for_pending(dev, pci_pcie_cap(dev) + PCI_EXP_DEVSTA, + PCI_EXP_DEVSTA_TRPND); } EXPORT_SYMBOL(pci_wait_for_pending_transaction); @@ -3109,7 +3110,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe) return 0; /* Wait for Transaction Pending bit clean */ - if (pci_wait_for_pending(dev, PCI_AF_STATUS, PCI_AF_STATUS_TP)) + if (pci_wait_for_pending(dev, pos + PCI_AF_STATUS, PCI_AF_STATUS_TP)) goto clear; dev_err(&dev->dev, "transaction is not cleared; " diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c index deb7f4b..438d4c7 100644 --- a/drivers/pnp/pnpbios/bioscalls.c +++ b/drivers/pnp/pnpbios/bioscalls.c @@ -37,7 +37,7 @@ __visible struct { * kernel begins at offset 3GB... */ -asmlinkage void pnp_bios_callfunc(void); +asmlinkage __visible void pnp_bios_callfunc(void); __asm__(".text \n" __ALIGN_STR "\n" diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index bd628a6..e5f13c4 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -569,6 +569,9 @@ static int hym8563_probe(struct i2c_client *client, if (IS_ERR(hym8563->rtc)) return PTR_ERR(hym8563->rtc); + /* the hym8563 alarm only supports a minute accuracy */ + hym8563->rtc->uie_unsupported = 1; + #ifdef CONFIG_COMMON_CLK hym8563_clkout_register_clk(hym8563); #endif diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 5c8f822..4cdb64b 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -206,7 +206,7 @@ static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_hour = bcd2bin(regs[2] & 0x3f); tm->tm_mday = bcd2bin(regs[3] & 0x3f); tm->tm_wday = regs[4] & 0x7; - tm->tm_mon = bcd2bin(regs[5] & 0x1f); + tm->tm_mon = bcd2bin(regs[5] & 0x1f) - 1; tm->tm_year = bcd2bin(regs[6]) + 100; return rtc_valid_tm(tm); @@ -229,7 +229,7 @@ static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm) regs[3] = bin2bcd(tm->tm_hour); regs[4] = bin2bcd(tm->tm_mday); regs[5] = tm->tm_wday; - regs[6] = bin2bcd(tm->tm_mon); + regs[6] = bin2bcd(tm->tm_mon + 1); regs[7] = bin2bcd(tm->tm_year - 100); msg.addr = client->addr; diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index 985b5dc..6bcfbbb 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -34,8 +34,9 @@ static ssize_t ctcm_buffer_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct net_device *ndev; - int bs1; + unsigned int bs1; struct ctcm_priv *priv = dev_get_drvdata(dev); + int rc; ndev = priv->channel[CTCM_READ]->netdev; if (!(priv && priv->channel[CTCM_READ] && ndev)) { @@ -43,7 +44,9 @@ static ssize_t ctcm_buffer_write(struct device *dev, return -ENODEV; } - sscanf(buf, "%u", &bs1); + rc = sscanf(buf, "%u", &bs1); + if (rc != 1) + goto einval; if (bs1 > CTCM_BUFSIZE_LIMIT) goto einval; if (bs1 < (576 + LL_HEADER_LENGTH + 2)) @@ -143,13 +146,14 @@ static ssize_t ctcm_proto_show(struct device *dev, static ssize_t ctcm_proto_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int value; + int value, rc; struct ctcm_priv *priv = dev_get_drvdata(dev); if (!priv) return -ENODEV; - sscanf(buf, "%u", &value); - if (!((value == CTCM_PROTO_S390) || + rc = sscanf(buf, "%d", &value); + if ((rc != 1) || + !((value == CTCM_PROTO_S390) || (value == CTCM_PROTO_LINUX) || (value == CTCM_PROTO_MPC) || (value == CTCM_PROTO_OS390))) diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 8d5d969..0a7d87c 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1943,14 +1943,16 @@ static ssize_t lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcs_card *card; - int value; + int value, rc; card = dev_get_drvdata(dev); if (!card) return 0; - sscanf(buf, "%u", &value); + rc = sscanf(buf, "%d", &value); + if (rc != 1) + return -EINVAL; /* TODO: sanity checks */ card->portno = value; @@ -1997,14 +1999,17 @@ static ssize_t lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcs_card *card; - int value; + unsigned int value; + int rc; card = dev_get_drvdata(dev); if (!card) return 0; - sscanf(buf, "%u", &value); + rc = sscanf(buf, "%u", &value); + if (rc != 1) + return -EINVAL; /* TODO: sanity checks */ card->lancmd_timeout = value; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 34993009..549e9fd 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1013,7 +1013,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev, card = CARD_FROM_CDEV(cdev); - if (!IS_ERR(irb)) + if (!card || !IS_ERR(irb)) return 0; switch (PTR_ERR(irb)) { @@ -1029,7 +1029,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev, QETH_CARD_TEXT(card, 2, "ckirberr"); QETH_CARD_TEXT_(card, 2, " rc%d", -ETIMEDOUT); if (intparm == QETH_RCD_PARM) { - if (card && (card->data.ccwdev == cdev)) { + if (card->data.ccwdev == cdev) { card->data.state = CH_STATE_DOWN; wake_up(&card->wait_q); } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c58f82a..14e0b58 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -63,7 +63,7 @@ void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf) int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr) { int count = 0, rc = 0; - int in[4]; + unsigned int in[4]; char c; rc = sscanf(buf, "%u.%u.%u.%u%c", diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 1185484..a669f2d 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -244,7 +244,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn) sk->sk_data_ready = tcp_sw_conn->old_data_ready; sk->sk_state_change = tcp_sw_conn->old_state_change; sk->sk_write_space = tcp_sw_conn->old_write_space; - sk->sk_no_check = 0; + sk->sk_no_check_tx = 0; write_unlock_bh(&sk->sk_callback_lock); } diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index fc67f56..788ed9b 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -1,10 +1,12 @@ # # Makefile for the SuperH specific drivers. # -obj-y := intc/ +obj-$(CONFIG_SUPERH) += intc/ +obj-$(CONFIG_ARCH_SHMOBILE_LEGACY) += intc/ +ifneq ($(CONFIG_COMMON_CLK),y) +obj-$(CONFIG_HAVE_CLK) += clk/ +endif +obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_HAVE_CLK) += clk/ -obj-$(CONFIG_MAPLE) += maple/ -obj-$(CONFIG_SUPERHYWAY) += superhyway/ - -obj-y += pm_runtime.o +obj-y += pm_runtime.o diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c index 8afa5a4..10c65eb 100644 --- a/drivers/sh/pm_runtime.c +++ b/drivers/sh/pm_runtime.c @@ -50,8 +50,25 @@ static struct pm_clk_notifier_block platform_bus_notifier = { .con_ids = { NULL, }, }; +static bool default_pm_on; + static int __init sh_pm_runtime_init(void) { + if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) { + if (!of_machine_is_compatible("renesas,emev2") && + !of_machine_is_compatible("renesas,r7s72100") && + !of_machine_is_compatible("renesas,r8a73a4") && + !of_machine_is_compatible("renesas,r8a7740") && + !of_machine_is_compatible("renesas,r8a7778") && + !of_machine_is_compatible("renesas,r8a7779") && + !of_machine_is_compatible("renesas,r8a7790") && + !of_machine_is_compatible("renesas,r8a7791") && + !of_machine_is_compatible("renesas,sh7372") && + !of_machine_is_compatible("renesas,sh73a0")) + return 0; + } + + default_pm_on = true; pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier); return 0; } @@ -59,7 +76,8 @@ core_initcall(sh_pm_runtime_init); static int __init sh_pm_runtime_late_init(void) { - pm_genpd_poweroff_unused(); + if (default_pm_on) + pm_genpd_poweroff_unused(); return 0; } late_initcall(sh_pm_runtime_late_init); diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 713af48..f6759dc 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -29,18 +29,6 @@ static int pxa2xx_spi_map_dma_buffer(struct driver_data *drv_data, struct sg_table *sgt; void *buf, *pbuf; - /* - * Some DMA controllers have problems transferring buffers that are - * not multiple of 4 bytes. So we truncate the transfer so that it - * is suitable for such controllers, and handle the trailing bytes - * manually after the DMA completes. - * - * REVISIT: It would be better if this information could be - * retrieved directly from the DMA device in a similar way than - * ->copy_align etc. is done. - */ - len = ALIGN(drv_data->len, 4); - if (dir == DMA_TO_DEVICE) { dmadev = drv_data->tx_chan->device->dev; sgt = &drv_data->tx_sgt; @@ -144,12 +132,8 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, if (!error) { pxa2xx_spi_unmap_dma_buffers(drv_data); - /* Handle the last bytes of unaligned transfer */ drv_data->tx += drv_data->tx_map_len; - drv_data->write(drv_data); - drv_data->rx += drv_data->rx_map_len; - drv_data->read(drv_data); msg->actual_length += drv_data->len; msg->state = pxa2xx_spi_next_transfer(drv_data); diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index b032e88..78c66e3 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -734,7 +734,7 @@ static int spi_qup_remove(struct platform_device *pdev) int ret; ret = pm_runtime_get_sync(&pdev->dev); - if (ret) + if (ret < 0) return ret; ret = spi_qup_set_state(controller, QUP_STATE_RESET); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4eb9bf0..939edf4 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -580,6 +580,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->master->set_cs(spi, !enable); } +#ifdef CONFIG_HAS_DMA static int spi_map_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, void *buf, size_t len, enum dma_data_direction dir) @@ -637,55 +638,12 @@ static void spi_unmap_buf(struct spi_master *master, struct device *dev, } } -static int spi_map_msg(struct spi_master *master, struct spi_message *msg) +static int __spi_map_msg(struct spi_master *master, struct spi_message *msg) { struct device *tx_dev, *rx_dev; struct spi_transfer *xfer; - void *tmp; - unsigned int max_tx, max_rx; int ret; - if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) { - max_tx = 0; - max_rx = 0; - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if ((master->flags & SPI_MASTER_MUST_TX) && - !xfer->tx_buf) - max_tx = max(xfer->len, max_tx); - if ((master->flags & SPI_MASTER_MUST_RX) && - !xfer->rx_buf) - max_rx = max(xfer->len, max_rx); - } - - if (max_tx) { - tmp = krealloc(master->dummy_tx, max_tx, - GFP_KERNEL | GFP_DMA); - if (!tmp) - return -ENOMEM; - master->dummy_tx = tmp; - memset(tmp, 0, max_tx); - } - - if (max_rx) { - tmp = krealloc(master->dummy_rx, max_rx, - GFP_KERNEL | GFP_DMA); - if (!tmp) - return -ENOMEM; - master->dummy_rx = tmp; - } - - if (max_tx || max_rx) { - list_for_each_entry(xfer, &msg->transfers, - transfer_list) { - if (!xfer->tx_buf) - xfer->tx_buf = master->dummy_tx; - if (!xfer->rx_buf) - xfer->rx_buf = master->dummy_rx; - } - } - } - if (!master->can_dma) return 0; @@ -742,6 +700,69 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) return 0; } +#else /* !CONFIG_HAS_DMA */ +static inline int __spi_map_msg(struct spi_master *master, + struct spi_message *msg) +{ + return 0; +} + +static inline int spi_unmap_msg(struct spi_master *master, + struct spi_message *msg) +{ + return 0; +} +#endif /* !CONFIG_HAS_DMA */ + +static int spi_map_msg(struct spi_master *master, struct spi_message *msg) +{ + struct spi_transfer *xfer; + void *tmp; + unsigned int max_tx, max_rx; + + if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) { + max_tx = 0; + max_rx = 0; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if ((master->flags & SPI_MASTER_MUST_TX) && + !xfer->tx_buf) + max_tx = max(xfer->len, max_tx); + if ((master->flags & SPI_MASTER_MUST_RX) && + !xfer->rx_buf) + max_rx = max(xfer->len, max_rx); + } + + if (max_tx) { + tmp = krealloc(master->dummy_tx, max_tx, + GFP_KERNEL | GFP_DMA); + if (!tmp) + return -ENOMEM; + master->dummy_tx = tmp; + memset(tmp, 0, max_tx); + } + + if (max_rx) { + tmp = krealloc(master->dummy_rx, max_rx, + GFP_KERNEL | GFP_DMA); + if (!tmp) + return -ENOMEM; + master->dummy_rx = tmp; + } + + if (max_tx || max_rx) { + list_for_each_entry(xfer, &msg->transfers, + transfer_list) { + if (!xfer->tx_buf) + xfer->tx_buf = master->dummy_tx; + if (!xfer->rx_buf) + xfer->rx_buf = master->dummy_rx; + } + } + } + + return __spi_map_msg(master, msg); +} /* * spi_transfer_one_message - Default implementation of transfer_one_message() @@ -1151,7 +1172,6 @@ static int spi_master_initialize_queue(struct spi_master *master) { int ret; - master->queued = true; master->transfer = spi_queued_transfer; if (!master->transfer_one_message) master->transfer_one_message = spi_transfer_one_message; @@ -1162,6 +1182,7 @@ static int spi_master_initialize_queue(struct spi_master *master) dev_err(&master->dev, "problem initializing queue\n"); goto err_init_queue; } + master->queued = true; ret = spi_start_queue(master); if (ret) { dev_err(&master->dev, "problem starting queue\n"); @@ -1171,8 +1192,8 @@ static int spi_master_initialize_queue(struct spi_master *master) return 0; err_start_queue: -err_init_queue: spi_destroy_queue(master); +err_init_queue: return ret; } @@ -1756,7 +1777,7 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master); */ int spi_setup(struct spi_device *spi) { - unsigned bad_bits; + unsigned bad_bits, ugly_bits; int status = 0; /* check mode to prevent that DUAL and QUAD set at the same time @@ -1776,6 +1797,15 @@ int spi_setup(struct spi_device *spi) * that aren't supported with their current master */ bad_bits = spi->mode & ~spi->master->mode_bits; + ugly_bits = bad_bits & + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD); + if (ugly_bits) { + dev_warn(&spi->dev, + "setup: ignoring unsupported mode bits %x\n", + ugly_bits); + spi->mode &= ~ugly_bits; + bad_bits &= ~ugly_bits; + } if (bad_bits) { dev_err(&spi->dev, "setup: unsupported mode bits %x\n", bad_bits); diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index 4144a75..c270c9a 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -517,7 +517,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node, of_node_put(port); if (port == imx_crtc->port) { ret = of_graph_parse_endpoint(ep, &endpoint); - return ret ? ret : endpoint.id; + return ret ? ret : endpoint.port; } } while (ep); @@ -675,6 +675,11 @@ static int imx_drm_platform_probe(struct platform_device *pdev) if (!remote || !of_device_is_available(remote)) { of_node_put(remote); continue; + } else if (!of_device_is_available(remote->parent)) { + dev_warn(&pdev->dev, "parent device of %s is not available\n", + remote->full_name); + of_node_put(remote); + continue; } ret = imx_drm_add_component(&pdev->dev, remote); diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index 575533f..a23f4f7 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -582,7 +582,7 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) tve->dev = dev; spin_lock_init(&tve->lock); - ddc_node = of_parse_phandle(np, "i2c-ddc-bus", 0); + ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); if (ddc_node) { tve->ddc = of_find_i2c_adapter_by_node(ddc_node); of_node_put(ddc_node); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 8c101cb..acc8184 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -1247,9 +1247,18 @@ static int vpfe_stop_streaming(struct vb2_queue *vq) struct vpfe_fh *fh = vb2_get_drv_priv(vq); struct vpfe_video_device *video = fh->video; - if (!vb2_is_streaming(vq)) - return 0; /* release all active buffers */ + if (video->cur_frm == video->next_frm) { + vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_ERROR); + } else { + if (video->cur_frm != NULL) + vb2_buffer_done(&video->cur_frm->vb, + VB2_BUF_STATE_ERROR); + if (video->next_frm != NULL) + vb2_buffer_done(&video->next_frm->vb, + VB2_BUF_STATE_ERROR); + } + while (!list_empty(&video->dma_queue)) { video->next_frm = list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list); diff --git a/drivers/staging/media/sn9c102/sn9c102_devtable.h b/drivers/staging/media/sn9c102/sn9c102_devtable.h index b3d2cc7..4ba5692 100644 --- a/drivers/staging/media/sn9c102/sn9c102_devtable.h +++ b/drivers/staging/media/sn9c102/sn9c102_devtable.h @@ -48,10 +48,8 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, diff --git a/drivers/staging/rtl8723au/os_dep/os_intfs.c b/drivers/staging/rtl8723au/os_dep/os_intfs.c index 57eca7a..4fe751f 100644 --- a/drivers/staging/rtl8723au/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723au/os_dep/os_intfs.c @@ -953,8 +953,6 @@ static int netdev_close(struct net_device *pnetdev) #endif /* CONFIG_8723AU_P2P */ rtw_scan_abort23a(padapter); - /* set this at the end */ - padapter->rtw_wdev->iftype = NL80211_IFTYPE_MONITOR; RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-871x_drv - drv_close\n")); DBG_8723A("-871x_drv - drv_close, bup =%d\n", padapter->bup); diff --git a/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c b/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c index c49160e..07e542e 100644 --- a/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c +++ b/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c @@ -26,7 +26,7 @@ unsigned int ffaddr2pipehdl23a(struct dvobj_priv *pdvobj, u32 addr) if (addr == RECV_BULK_IN_ADDR) { pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[0]); } else if (addr == RECV_INT_IN_ADDR) { - pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[1]); + pipe = usb_rcvintpipe(pusbd, pdvobj->RtInPipe[1]); } else if (addr < HW_QUEUE_ENTRY) { ep_num = pdvobj->Queue2Pipe[addr]; pipe = usb_sndbulkpipe(pusbd, ep_num); diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index f76f95c..723319e 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -84,7 +84,7 @@ static int prism2_domibset_uint32(wlandevice_t *wlandev, u32 did, u32 data) } static int prism2_domibset_pstr32(wlandevice_t *wlandev, - u32 did, u8 len, u8 *data) + u32 did, u8 len, const u8 *data) { struct p80211msg_dot11req_mibset msg; p80211item_pstr32_t *mibitem = @@ -298,7 +298,7 @@ static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { wlandevice_t *wlandev = dev->ml_priv; struct p80211msg_lnxreq_commsquality quality; diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 78cab13..46588c8 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1593,7 +1593,9 @@ int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * Initiator is expecting a NopIN ping reply.. */ if (hdr->itt != RESERVED_ITT) { - BUG_ON(!cmd); + if (!cmd) + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); spin_lock_bh(&conn->cmd_lock); list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 6960f22..302eb3b 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -775,6 +775,7 @@ struct iscsi_np { int np_ip_proto; int np_sock_type; enum np_thread_state_table np_thread_state; + bool enabled; enum iscsi_timer_flags_table np_login_timer_flags; u32 np_exports; enum np_flags_table np_flags; diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 8739b98..ca31fa1 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -436,7 +436,7 @@ static int iscsi_login_zero_tsih_s2( } off = mrdsl % PAGE_SIZE; if (!off) - return 0; + goto check_prot; if (mrdsl < PAGE_SIZE) mrdsl = PAGE_SIZE; @@ -452,6 +452,31 @@ static int iscsi_login_zero_tsih_s2( ISCSI_LOGIN_STATUS_NO_RESOURCES); return -1; } + /* + * ISER currently requires that ImmediateData + Unsolicited + * Data be disabled when protection / signature MRs are enabled. + */ +check_prot: + if (sess->se_sess->sup_prot_ops & + (TARGET_PROT_DOUT_STRIP | TARGET_PROT_DOUT_PASS | + TARGET_PROT_DOUT_INSERT)) { + + sprintf(buf, "ImmediateData=No"); + if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, + ISCSI_LOGIN_STATUS_NO_RESOURCES); + return -1; + } + + sprintf(buf, "InitialR2T=Yes"); + if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, + ISCSI_LOGIN_STATUS_NO_RESOURCES); + return -1; + } + pr_debug("Forcing ImmediateData=No + InitialR2T=Yes for" + " T10-PI enabled ISER session\n"); + } } return 0; @@ -984,6 +1009,7 @@ int iscsi_target_setup_login_socket( } np->np_transport = t; + np->enabled = true; return 0; } diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index eb96b20..ca18118 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -184,6 +184,7 @@ static void iscsit_clear_tpg_np_login_thread( return; } + tpg_np->tpg_np->enabled = false; iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg, shutdown); } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 65001e1..26416c1 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -798,10 +798,10 @@ int se_dev_set_emulate_write_cache(struct se_device *dev, int flag) pr_err("emulate_write_cache not supported for pSCSI\n"); return -EINVAL; } - if (dev->transport->get_write_cache) { - pr_warn("emulate_write_cache cannot be changed when underlying" - " HW reports WriteCacheEnabled, ignoring request\n"); - return 0; + if (flag && + dev->transport->get_write_cache) { + pr_err("emulate_write_cache not supported for this device\n"); + return -EINVAL; } dev->dev_attrib.emulate_write_cache = flag; @@ -936,6 +936,10 @@ int se_dev_set_pi_prot_type(struct se_device *dev, int flag) return 0; } if (!dev->transport->init_prot || !dev->transport->free_prot) { + /* 0 is only allowed value for non-supporting backends */ + if (flag == 0) + return 0; + pr_err("DIF protection not supported by backend: %s\n", dev->transport->name); return -ENOSYS; diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index d4b9869..789aa9e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1113,6 +1113,7 @@ void transport_init_se_cmd( init_completion(&cmd->cmd_wait_comp); init_completion(&cmd->task_stop_comp); spin_lock_init(&cmd->t_state_lock); + kref_init(&cmd->cmd_kref); cmd->transport_state = CMD_T_DEV_ACTIVE; cmd->se_tfo = tfo; @@ -2357,7 +2358,6 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd, unsigned long flags; int ret = 0; - kref_init(&se_cmd->cmd_kref); /* * Add a second kref if the fabric caller is expecting to handle * fabric acknowledgement that requires two target_put_sess_cmd() diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 01cf37f..f5fd515 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -90,18 +90,18 @@ static void ft_free_cmd(struct ft_cmd *cmd) { struct fc_frame *fp; struct fc_lport *lport; - struct se_session *se_sess; + struct ft_sess *sess; if (!cmd) return; - se_sess = cmd->sess->se_sess; + sess = cmd->sess; fp = cmd->req_frame; lport = fr_dev(fp); if (fr_seq(fp)) lport->tt.seq_release(fr_seq(fp)); fc_frame_free(fp); - percpu_ida_free(&se_sess->sess_tag_pool, cmd->se_cmd.map_tag); - ft_sess_put(cmd->sess); /* undo get from lookup at recv */ + percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag); + ft_sess_put(sess); /* undo get from lookup at recv */ } void ft_release_cmd(struct se_cmd *se_cmd) diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c index 96109a9..84b4bfb 100644 --- a/drivers/xen/events/events_fifo.c +++ b/drivers/xen/events/events_fifo.c @@ -66,7 +66,22 @@ static DEFINE_PER_CPU(struct evtchn_fifo_queue, cpu_queue); static event_word_t *event_array[MAX_EVENT_ARRAY_PAGES] __read_mostly; static unsigned event_array_pages __read_mostly; +/* + * sync_set_bit() and friends must be unsigned long aligned on non-x86 + * platforms. + */ +#if !defined(CONFIG_X86) && BITS_PER_LONG > 32 + +#define BM(w) (unsigned long *)((unsigned long)w & ~0x7UL) +#define EVTCHN_FIFO_BIT(b, w) \ + (((unsigned long)w & 0x4UL) ? (EVTCHN_FIFO_ ##b + 32) : EVTCHN_FIFO_ ##b) + +#else + #define BM(w) ((unsigned long *)(w)) +#define EVTCHN_FIFO_BIT(b, w) EVTCHN_FIFO_ ##b + +#endif static inline event_word_t *event_word_from_port(unsigned port) { @@ -161,33 +176,38 @@ static void evtchn_fifo_bind_to_cpu(struct irq_info *info, unsigned cpu) static void evtchn_fifo_clear_pending(unsigned port) { event_word_t *word = event_word_from_port(port); - sync_clear_bit(EVTCHN_FIFO_PENDING, BM(word)); + sync_clear_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word)); } static void evtchn_fifo_set_pending(unsigned port) { event_word_t *word = event_word_from_port(port); - sync_set_bit(EVTCHN_FIFO_PENDING, BM(word)); + sync_set_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word)); } static bool evtchn_fifo_is_pending(unsigned port) { event_word_t *word = event_word_from_port(port); - return sync_test_bit(EVTCHN_FIFO_PENDING, BM(word)); + return sync_test_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word)); } static bool evtchn_fifo_test_and_set_mask(unsigned port) { event_word_t *word = event_word_from_port(port); - return sync_test_and_set_bit(EVTCHN_FIFO_MASKED, BM(word)); + return sync_test_and_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word)); } static void evtchn_fifo_mask(unsigned port) { event_word_t *word = event_word_from_port(port); - sync_set_bit(EVTCHN_FIFO_MASKED, BM(word)); + sync_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word)); } +static bool evtchn_fifo_is_masked(unsigned port) +{ + event_word_t *word = event_word_from_port(port); + return sync_test_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word)); +} /* * Clear MASKED, spinning if BUSY is set. */ @@ -211,7 +231,7 @@ static void evtchn_fifo_unmask(unsigned port) BUG_ON(!irqs_disabled()); clear_masked(word); - if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))) { + if (evtchn_fifo_is_pending(port)) { struct evtchn_unmask unmask = { .port = port }; (void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask); } @@ -243,7 +263,7 @@ static void handle_irq_for_port(unsigned port) static void consume_one_event(unsigned cpu, struct evtchn_fifo_control_block *control_block, - unsigned priority, uint32_t *ready) + unsigned priority, unsigned long *ready) { struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu); uint32_t head; @@ -273,10 +293,9 @@ static void consume_one_event(unsigned cpu, * copy of the ready word. */ if (head == 0) - clear_bit(priority, BM(ready)); + clear_bit(priority, ready); - if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word)) - && !sync_test_bit(EVTCHN_FIFO_MASKED, BM(word))) + if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) handle_irq_for_port(port); q->head[priority] = head; @@ -285,7 +304,7 @@ static void consume_one_event(unsigned cpu, static void evtchn_fifo_handle_events(unsigned cpu) { struct evtchn_fifo_control_block *control_block; - uint32_t ready; + unsigned long ready; unsigned q; control_block = per_cpu(cpu_control_block, cpu); diff --git a/fs/affs/super.c b/fs/affs/super.c index 6d589f2..895ac7d 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -340,8 +340,6 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) &blocksize,&sbi->s_prefix, sbi->s_volume, &mount_flags)) { printk(KERN_ERR "AFFS: Error parsing options\n"); - kfree(sbi->s_prefix); - kfree(sbi); return -EINVAL; } /* N.B. after this point s_prefix must be released */ diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 2caf36a..cc87c1a 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -179,7 +179,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry) spin_lock(&active->d_lock); /* Already gone? */ - if (!d_count(active)) + if ((int) d_count(active) <= 0) goto next; qstr = &active->d_name; @@ -230,7 +230,7 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry) spin_lock(&expiring->d_lock); - /* Bad luck, we've already been dentry_iput */ + /* We've already been dentry_iput or unlinked */ if (!expiring->d_inode) goto next; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 2ad7de9..2f6d7b1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3120,6 +3120,8 @@ process_slot: } else if (type == BTRFS_FILE_EXTENT_INLINE) { u64 skip = 0; u64 trim = 0; + u64 aligned_end = 0; + if (off > key.offset) { skip = off - key.offset; new_key.offset += skip; @@ -3136,9 +3138,11 @@ process_slot: size -= skip + trim; datal -= skip + trim; + aligned_end = ALIGN(new_key.offset + datal, + root->sectorsize); ret = btrfs_drop_extents(trans, root, inode, new_key.offset, - new_key.offset + datal, + aligned_end, 1); if (ret) { if (ret != -EOPNOTSUPP) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index eb6537a..fd38b50 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1668,7 +1668,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino, goto out; } - if (key.type == BTRFS_INODE_REF_KEY) { + if (found_key.type == BTRFS_INODE_REF_KEY) { struct btrfs_inode_ref *iref; iref = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_ref); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index aadc2b6..a22d667 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1737,6 +1737,9 @@ cifs_inode_needs_reval(struct inode *inode) if (cifs_i->time == 0) return true; + if (!cifs_sb->actimeo) + return true; + if (!time_in_range(jiffies, cifs_i->time, cifs_i->time + cifs_sb->actimeo)) return true; diff --git a/fs/dcache.c b/fs/dcache.c index 40707d8..42ae01e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -246,16 +246,8 @@ static void __d_free(struct rcu_head *head) kmem_cache_free(dentry_cache, dentry); } -/* - * no locks, please. - */ -static void d_free(struct dentry *dentry) +static void dentry_free(struct dentry *dentry) { - BUG_ON((int)dentry->d_lockref.count > 0); - this_cpu_dec(nr_dentry); - if (dentry->d_op && dentry->d_op->d_release) - dentry->d_op->d_release(dentry); - /* if dentry was never visible to RCU, immediate free is OK */ if (!(dentry->d_flags & DCACHE_RCUACCESS)) __d_free(&dentry->d_u.d_rcu); @@ -403,56 +395,6 @@ static void dentry_lru_add(struct dentry *dentry) d_lru_add(dentry); } -/* - * Remove a dentry with references from the LRU. - * - * If we are on the shrink list, then we can get to try_prune_one_dentry() and - * lose our last reference through the parent walk. In this case, we need to - * remove ourselves from the shrink list, not the LRU. - */ -static void dentry_lru_del(struct dentry *dentry) -{ - if (dentry->d_flags & DCACHE_LRU_LIST) { - if (dentry->d_flags & DCACHE_SHRINK_LIST) - return d_shrink_del(dentry); - d_lru_del(dentry); - } -} - -/** - * d_kill - kill dentry and return parent - * @dentry: dentry to kill - * @parent: parent dentry - * - * The dentry must already be unhashed and removed from the LRU. - * - * If this is the root of the dentry tree, return NULL. - * - * dentry->d_lock and parent->d_lock must be held by caller, and are dropped by - * d_kill. - */ -static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent) - __releases(dentry->d_lock) - __releases(parent->d_lock) - __releases(dentry->d_inode->i_lock) -{ - list_del(&dentry->d_u.d_child); - /* - * Inform d_walk() that we are no longer attached to the - * dentry tree - */ - dentry->d_flags |= DCACHE_DENTRY_KILLED; - if (parent) - spin_unlock(&parent->d_lock); - dentry_iput(dentry); - /* - * dentry_iput drops the locks, at which point nobody (except - * transient RCU lookups) can reach this dentry. - */ - d_free(dentry); - return parent; -} - /** * d_drop - drop a dentry * @dentry: dentry to drop @@ -510,7 +452,14 @@ dentry_kill(struct dentry *dentry, int unlock_on_failure) __releases(dentry->d_lock) { struct inode *inode; - struct dentry *parent; + struct dentry *parent = NULL; + bool can_free = true; + + if (unlikely(dentry->d_flags & DCACHE_DENTRY_KILLED)) { + can_free = dentry->d_flags & DCACHE_MAY_FREE; + spin_unlock(&dentry->d_lock); + goto out; + } inode = dentry->d_inode; if (inode && !spin_trylock(&inode->i_lock)) { @@ -521,9 +470,7 @@ relock: } return dentry; /* try again with same dentry */ } - if (IS_ROOT(dentry)) - parent = NULL; - else + if (!IS_ROOT(dentry)) parent = dentry->d_parent; if (parent && !spin_trylock(&parent->d_lock)) { if (inode) @@ -543,10 +490,40 @@ relock: if ((dentry->d_flags & DCACHE_OP_PRUNE) && !d_unhashed(dentry)) dentry->d_op->d_prune(dentry); - dentry_lru_del(dentry); + if (dentry->d_flags & DCACHE_LRU_LIST) { + if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) + d_lru_del(dentry); + } /* if it was on the hash then remove it */ __d_drop(dentry); - return d_kill(dentry, parent); + list_del(&dentry->d_u.d_child); + /* + * Inform d_walk() that we are no longer attached to the + * dentry tree + */ + dentry->d_flags |= DCACHE_DENTRY_KILLED; + if (parent) + spin_unlock(&parent->d_lock); + dentry_iput(dentry); + /* + * dentry_iput drops the locks, at which point nobody (except + * transient RCU lookups) can reach this dentry. + */ + BUG_ON((int)dentry->d_lockref.count > 0); + this_cpu_dec(nr_dentry); + if (dentry->d_op && dentry->d_op->d_release) + dentry->d_op->d_release(dentry); + + spin_lock(&dentry->d_lock); + if (dentry->d_flags & DCACHE_SHRINK_LIST) { + dentry->d_flags |= DCACHE_MAY_FREE; + can_free = false; + } + spin_unlock(&dentry->d_lock); +out: + if (likely(can_free)) + dentry_free(dentry); + return parent; } /* @@ -815,65 +792,13 @@ restart: } EXPORT_SYMBOL(d_prune_aliases); -/* - * Try to throw away a dentry - free the inode, dput the parent. - * Requires dentry->d_lock is held, and dentry->d_count == 0. - * Releases dentry->d_lock. - * - * This may fail if locks cannot be acquired no problem, just try again. - */ -static struct dentry * try_prune_one_dentry(struct dentry *dentry) - __releases(dentry->d_lock) -{ - struct dentry *parent; - - parent = dentry_kill(dentry, 0); - /* - * If dentry_kill returns NULL, we have nothing more to do. - * if it returns the same dentry, trylocks failed. In either - * case, just loop again. - * - * Otherwise, we need to prune ancestors too. This is necessary - * to prevent quadratic behavior of shrink_dcache_parent(), but - * is also expected to be beneficial in reducing dentry cache - * fragmentation. - */ - if (!parent) - return NULL; - if (parent == dentry) - return dentry; - - /* Prune ancestors. */ - dentry = parent; - while (dentry) { - if (lockref_put_or_lock(&dentry->d_lockref)) - return NULL; - dentry = dentry_kill(dentry, 1); - } - return NULL; -} - static void shrink_dentry_list(struct list_head *list) { - struct dentry *dentry; + struct dentry *dentry, *parent; - rcu_read_lock(); - for (;;) { - dentry = list_entry_rcu(list->prev, struct dentry, d_lru); - if (&dentry->d_lru == list) - break; /* empty */ - - /* - * Get the dentry lock, and re-verify that the dentry is - * this on the shrinking list. If it is, we know that - * DCACHE_SHRINK_LIST and DCACHE_LRU_LIST are set. - */ + while (!list_empty(list)) { + dentry = list_entry(list->prev, struct dentry, d_lru); spin_lock(&dentry->d_lock); - if (dentry != list_entry(list->prev, struct dentry, d_lru)) { - spin_unlock(&dentry->d_lock); - continue; - } - /* * The dispose list is isolated and dentries are not accounted * to the LRU here, so we can simply remove it from the list @@ -885,30 +810,38 @@ static void shrink_dentry_list(struct list_head *list) * We found an inuse dentry which was not removed from * the LRU because of laziness during lookup. Do not free it. */ - if (dentry->d_lockref.count) { + if ((int)dentry->d_lockref.count > 0) { spin_unlock(&dentry->d_lock); continue; } - rcu_read_unlock(); + parent = dentry_kill(dentry, 0); /* - * If 'try_to_prune()' returns a dentry, it will - * be the same one we passed in, and d_lock will - * have been held the whole time, so it will not - * have been added to any other lists. We failed - * to get the inode lock. - * - * We just add it back to the shrink list. + * If dentry_kill returns NULL, we have nothing more to do. */ - dentry = try_prune_one_dentry(dentry); + if (!parent) + continue; - rcu_read_lock(); - if (dentry) { + if (unlikely(parent == dentry)) { + /* + * trylocks have failed and d_lock has been held the + * whole time, so it could not have been added to any + * other lists. Just add it back to the shrink list. + */ d_shrink_add(dentry, list); spin_unlock(&dentry->d_lock); + continue; } + /* + * We need to prune ancestors too. This is necessary to prevent + * quadratic behavior of shrink_dcache_parent(), but is also + * expected to be beneficial in reducing dentry cache + * fragmentation. + */ + dentry = parent; + while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) + dentry = dentry_kill(dentry, 1); } - rcu_read_unlock(); } static enum lru_status @@ -1261,34 +1194,23 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry) if (data->start == dentry) goto out; - /* - * move only zero ref count dentries to the dispose list. - * - * Those which are presently on the shrink list, being processed - * by shrink_dentry_list(), shouldn't be moved. Otherwise the - * loop in shrink_dcache_parent() might not make any progress - * and loop forever. - */ - if (dentry->d_lockref.count) { - dentry_lru_del(dentry); - } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) { - /* - * We can't use d_lru_shrink_move() because we - * need to get the global LRU lock and do the - * LRU accounting. - */ - d_lru_del(dentry); - d_shrink_add(dentry, &data->dispose); + if (dentry->d_flags & DCACHE_SHRINK_LIST) { data->found++; - ret = D_WALK_NORETRY; + } else { + if (dentry->d_flags & DCACHE_LRU_LIST) + d_lru_del(dentry); + if (!dentry->d_lockref.count) { + d_shrink_add(dentry, &data->dispose); + data->found++; + } } /* * We can return to the caller if we have found some (this * ensures forward progress). We'll be coming back to find * the rest. */ - if (data->found && need_resched()) - ret = D_WALK_QUIT; + if (!list_empty(&data->dispose)) + ret = need_resched() ? D_WALK_QUIT : D_WALK_NORETRY; out: return ret; } @@ -1318,45 +1240,35 @@ void shrink_dcache_parent(struct dentry *parent) } EXPORT_SYMBOL(shrink_dcache_parent); -static enum d_walk_ret umount_collect(void *_data, struct dentry *dentry) +static enum d_walk_ret umount_check(void *_data, struct dentry *dentry) { - struct select_data *data = _data; - enum d_walk_ret ret = D_WALK_CONTINUE; + /* it has busy descendents; complain about those instead */ + if (!list_empty(&dentry->d_subdirs)) + return D_WALK_CONTINUE; - if (dentry->d_lockref.count) { - dentry_lru_del(dentry); - if (likely(!list_empty(&dentry->d_subdirs))) - goto out; - if (dentry == data->start && dentry->d_lockref.count == 1) - goto out; - printk(KERN_ERR - "BUG: Dentry %p{i=%lx,n=%s}" - " still in use (%d)" - " [unmount of %s %s]\n", + /* root with refcount 1 is fine */ + if (dentry == _data && dentry->d_lockref.count == 1) + return D_WALK_CONTINUE; + + printk(KERN_ERR "BUG: Dentry %p{i=%lx,n=%pd} " + " still in use (%d) [unmount of %s %s]\n", dentry, dentry->d_inode ? dentry->d_inode->i_ino : 0UL, - dentry->d_name.name, + dentry, dentry->d_lockref.count, dentry->d_sb->s_type->name, dentry->d_sb->s_id); - BUG(); - } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) { - /* - * We can't use d_lru_shrink_move() because we - * need to get the global LRU lock and do the - * LRU accounting. - */ - if (dentry->d_flags & DCACHE_LRU_LIST) - d_lru_del(dentry); - d_shrink_add(dentry, &data->dispose); - data->found++; - ret = D_WALK_NORETRY; - } -out: - if (data->found && need_resched()) - ret = D_WALK_QUIT; - return ret; + WARN_ON(1); + return D_WALK_CONTINUE; +} + +static void do_one_tree(struct dentry *dentry) +{ + shrink_dcache_parent(dentry); + d_walk(dentry, dentry, umount_check, NULL); + d_drop(dentry); + dput(dentry); } /* @@ -1366,40 +1278,15 @@ void shrink_dcache_for_umount(struct super_block *sb) { struct dentry *dentry; - if (down_read_trylock(&sb->s_umount)) - BUG(); + WARN(down_read_trylock(&sb->s_umount), "s_umount should've been locked"); dentry = sb->s_root; sb->s_root = NULL; - for (;;) { - struct select_data data; - - INIT_LIST_HEAD(&data.dispose); - data.start = dentry; - data.found = 0; - - d_walk(dentry, &data, umount_collect, NULL); - if (!data.found) - break; - - shrink_dentry_list(&data.dispose); - cond_resched(); - } - d_drop(dentry); - dput(dentry); + do_one_tree(dentry); while (!hlist_bl_empty(&sb->s_anon)) { - struct select_data data; - dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash); - - INIT_LIST_HEAD(&data.dispose); - data.start = NULL; - data.found = 0; - - d_walk(dentry, &data, umount_collect, NULL); - if (data.found) - shrink_dentry_list(&data.dispose); - cond_resched(); + dentry = dget(hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash)); + do_one_tree(dentry); } } @@ -1647,8 +1534,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) unsigned add_flags = d_flags_for_inode(inode); spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_ENTRY_TYPE; - dentry->d_flags |= add_flags; + __d_set_type(dentry, add_flags); if (inode) hlist_add_head(&dentry->d_alias, &inode->i_dentry); dentry->d_inode = inode; @@ -657,10 +657,10 @@ int setup_arg_pages(struct linux_binprm *bprm, unsigned long rlim_stack; #ifdef CONFIG_STACK_GROWSUP - /* Limit stack size to 1GB */ + /* Limit stack size */ stack_base = rlimit_max(RLIMIT_STACK); - if (stack_base > (1 << 30)) - stack_base = 1 << 30; + if (stack_base > STACK_SIZE_MAX) + stack_base = STACK_SIZE_MAX; /* Make sure we didn't let the argument array grow too large. */ if (vma->vm_end - vma->vm_start > stack_base) diff --git a/fs/fuse/control.c b/fs/fuse/control.c index a0b0855..205e0d5 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -348,7 +348,7 @@ int __init fuse_ctl_init(void) return register_filesystem(&fuse_ctl_fs_type); } -void fuse_ctl_cleanup(void) +void __exit fuse_ctl_cleanup(void) { unregister_filesystem(&fuse_ctl_fs_type); } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5b4e035..4219835 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -679,6 +679,14 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, return create_new_entry(fc, req, dir, entry, S_IFLNK); } +static inline void fuse_update_ctime(struct inode *inode) +{ + if (!IS_NOCMTIME(inode)) { + inode->i_ctime = current_fs_time(inode->i_sb); + mark_inode_dirty_sync(inode); + } +} + static int fuse_unlink(struct inode *dir, struct dentry *entry) { int err; @@ -713,6 +721,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) fuse_invalidate_attr(inode); fuse_invalidate_attr(dir); fuse_invalidate_entry_cache(entry); + fuse_update_ctime(inode); } else if (err == -EINTR) fuse_invalidate_entry(entry); return err; @@ -743,23 +752,26 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) return err; } -static int fuse_rename(struct inode *olddir, struct dentry *oldent, - struct inode *newdir, struct dentry *newent) +static int fuse_rename_common(struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags, int opcode, size_t argsize) { int err; - struct fuse_rename_in inarg; + struct fuse_rename2_in inarg; struct fuse_conn *fc = get_fuse_conn(olddir); - struct fuse_req *req = fuse_get_req_nopages(fc); + struct fuse_req *req; + req = fuse_get_req_nopages(fc); if (IS_ERR(req)) return PTR_ERR(req); - memset(&inarg, 0, sizeof(inarg)); + memset(&inarg, 0, argsize); inarg.newdir = get_node_id(newdir); - req->in.h.opcode = FUSE_RENAME; + inarg.flags = flags; + req->in.h.opcode = opcode; req->in.h.nodeid = get_node_id(olddir); req->in.numargs = 3; - req->in.args[0].size = sizeof(inarg); + req->in.args[0].size = argsize; req->in.args[0].value = &inarg; req->in.args[1].size = oldent->d_name.len + 1; req->in.args[1].value = oldent->d_name.name; @@ -771,15 +783,22 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, if (!err) { /* ctime changes */ fuse_invalidate_attr(oldent->d_inode); + fuse_update_ctime(oldent->d_inode); + + if (flags & RENAME_EXCHANGE) { + fuse_invalidate_attr(newent->d_inode); + fuse_update_ctime(newent->d_inode); + } fuse_invalidate_attr(olddir); if (olddir != newdir) fuse_invalidate_attr(newdir); /* newent will end up negative */ - if (newent->d_inode) { + if (!(flags & RENAME_EXCHANGE) && newent->d_inode) { fuse_invalidate_attr(newent->d_inode); fuse_invalidate_entry_cache(newent); + fuse_update_ctime(newent->d_inode); } } else if (err == -EINTR) { /* If request was interrupted, DEITY only knows if the @@ -795,6 +814,36 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, return err; } +static int fuse_rename(struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + return fuse_rename_common(olddir, oldent, newdir, newent, 0, + FUSE_RENAME, sizeof(struct fuse_rename_in)); +} + +static int fuse_rename2(struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + struct fuse_conn *fc = get_fuse_conn(olddir); + int err; + + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) + return -EINVAL; + + if (fc->no_rename2 || fc->minor < 23) + return -EINVAL; + + err = fuse_rename_common(olddir, oldent, newdir, newent, flags, + FUSE_RENAME2, sizeof(struct fuse_rename2_in)); + if (err == -ENOSYS) { + fc->no_rename2 = 1; + err = -EINVAL; + } + return err; + +} + static int fuse_link(struct dentry *entry, struct inode *newdir, struct dentry *newent) { @@ -829,6 +878,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, inc_nlink(inode); spin_unlock(&fc->lock); fuse_invalidate_attr(inode); + fuse_update_ctime(inode); } else if (err == -EINTR) { fuse_invalidate_attr(inode); } @@ -846,6 +896,8 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, attr->size = i_size_read(inode); attr->mtime = inode->i_mtime.tv_sec; attr->mtimensec = inode->i_mtime.tv_nsec; + attr->ctime = inode->i_ctime.tv_sec; + attr->ctimensec = inode->i_ctime.tv_nsec; } stat->dev = inode->i_sb->s_dev; @@ -1504,7 +1556,7 @@ static bool update_mtime(unsigned ivalid, bool trust_local_mtime) } static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, - bool trust_local_mtime) + bool trust_local_cmtime) { unsigned ivalid = iattr->ia_valid; @@ -1523,13 +1575,18 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, if (!(ivalid & ATTR_ATIME_SET)) arg->valid |= FATTR_ATIME_NOW; } - if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) { + if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_cmtime)) { arg->valid |= FATTR_MTIME; arg->mtime = iattr->ia_mtime.tv_sec; arg->mtimensec = iattr->ia_mtime.tv_nsec; - if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime) + if (!(ivalid & ATTR_MTIME_SET) && !trust_local_cmtime) arg->valid |= FATTR_MTIME_NOW; } + if ((ivalid & ATTR_CTIME) && trust_local_cmtime) { + arg->valid |= FATTR_CTIME; + arg->ctime = iattr->ia_ctime.tv_sec; + arg->ctimensec = iattr->ia_ctime.tv_nsec; + } } /* @@ -1597,39 +1654,38 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req, /* * Flush inode->i_mtime to the server */ -int fuse_flush_mtime(struct file *file, bool nofail) +int fuse_flush_times(struct inode *inode, struct fuse_file *ff) { - struct inode *inode = file->f_mapping->host; - struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req = NULL; + struct fuse_req *req; struct fuse_setattr_in inarg; struct fuse_attr_out outarg; int err; - if (nofail) { - req = fuse_get_req_nofail_nopages(fc, file); - } else { - req = fuse_get_req_nopages(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - } + req = fuse_get_req_nopages(fc); + if (IS_ERR(req)) + return PTR_ERR(req); memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); - inarg.valid |= FATTR_MTIME; + inarg.valid = FATTR_MTIME; inarg.mtime = inode->i_mtime.tv_sec; inarg.mtimensec = inode->i_mtime.tv_nsec; - + if (fc->minor >= 23) { + inarg.valid |= FATTR_CTIME; + inarg.ctime = inode->i_ctime.tv_sec; + inarg.ctimensec = inode->i_ctime.tv_nsec; + } + if (ff) { + inarg.valid |= FATTR_FH; + inarg.fh = ff->fh; + } fuse_setattr_fill(fc, req, inode, &inarg, &outarg); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); - if (!err) - clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); - return err; } @@ -1653,7 +1709,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, bool is_wb = fc->writeback_cache; loff_t oldsize; int err; - bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode); + bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode); if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) attr->ia_valid |= ATTR_FORCE; @@ -1678,11 +1734,13 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, if (is_truncate) { fuse_set_nowrite(inode); set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); + if (trust_local_cmtime && attr->ia_size != inode->i_size) + attr->ia_valid |= ATTR_MTIME | ATTR_CTIME; } memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); - iattr_to_fattr(attr, &inarg, trust_local_mtime); + iattr_to_fattr(attr, &inarg, trust_local_cmtime); if (file) { struct fuse_file *ff = file->private_data; inarg.valid |= FATTR_FH; @@ -1711,9 +1769,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, spin_lock(&fc->lock); /* the kernel maintains i_mtime locally */ - if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) { - inode->i_mtime = attr->ia_mtime; - clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); + if (trust_local_cmtime) { + if (attr->ia_valid & ATTR_MTIME) + inode->i_mtime = attr->ia_mtime; + if (attr->ia_valid & ATTR_CTIME) + inode->i_ctime = attr->ia_ctime; + /* FIXME: clear I_DIRTY_SYNC? */ } fuse_change_attributes_common(inode, &outarg.attr, @@ -1810,8 +1871,10 @@ static int fuse_setxattr(struct dentry *entry, const char *name, fc->no_setxattr = 1; err = -EOPNOTSUPP; } - if (!err) + if (!err) { fuse_invalidate_attr(inode); + fuse_update_ctime(inode); + } return err; } @@ -1941,20 +2004,11 @@ static int fuse_removexattr(struct dentry *entry, const char *name) fc->no_removexattr = 1; err = -EOPNOTSUPP; } - if (!err) + if (!err) { fuse_invalidate_attr(inode); - return err; -} - -static int fuse_update_time(struct inode *inode, struct timespec *now, - int flags) -{ - if (flags & S_MTIME) { - inode->i_mtime = *now; - set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state); - BUG_ON(!S_ISREG(inode->i_mode)); + fuse_update_ctime(inode); } - return 0; + return err; } static const struct inode_operations fuse_dir_inode_operations = { @@ -1964,6 +2018,7 @@ static const struct inode_operations fuse_dir_inode_operations = { .unlink = fuse_unlink, .rmdir = fuse_rmdir, .rename = fuse_rename, + .rename2 = fuse_rename2, .link = fuse_link, .setattr = fuse_setattr, .create = fuse_create, @@ -1996,7 +2051,6 @@ static const struct inode_operations fuse_common_inode_operations = { .getxattr = fuse_getxattr, .listxattr = fuse_listxattr, .removexattr = fuse_removexattr, - .update_time = fuse_update_time, }; static const struct inode_operations fuse_symlink_inode_operations = { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 13f8bde..96d513e 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -223,6 +223,8 @@ void fuse_finish_open(struct inode *inode, struct file *file) i_size_write(inode, 0); spin_unlock(&fc->lock); fuse_invalidate_attr(inode); + if (fc->writeback_cache) + file_update_time(file); } if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache) fuse_link_write_file(file); @@ -232,18 +234,26 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) { struct fuse_conn *fc = get_fuse_conn(inode); int err; + bool lock_inode = (file->f_flags & O_TRUNC) && + fc->atomic_o_trunc && + fc->writeback_cache; err = generic_file_open(inode, file); if (err) return err; + if (lock_inode) + mutex_lock(&inode->i_mutex); + err = fuse_do_open(fc, get_node_id(inode), file, isdir); - if (err) - return err; - fuse_finish_open(inode, file); + if (!err) + fuse_finish_open(inode, file); - return 0; + if (lock_inode) + mutex_unlock(&inode->i_mutex); + + return err; } static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode) @@ -314,10 +324,7 @@ static int fuse_release(struct inode *inode, struct file *file) /* see fuse_vma_close() for !writeback_cache case */ if (fc->writeback_cache) - filemap_write_and_wait(file->f_mapping); - - if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) - fuse_flush_mtime(file, true); + write_inode_now(inode, 1); fuse_release_common(file, FUSE_RELEASE); @@ -439,7 +446,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) if (fc->no_flush) return 0; - err = filemap_write_and_wait(file->f_mapping); + err = write_inode_now(inode, 1); if (err) return err; @@ -480,13 +487,6 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, if (is_bad_inode(inode)) return -EIO; - err = filemap_write_and_wait_range(inode->i_mapping, start, end); - if (err) - return err; - - if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) - return 0; - mutex_lock(&inode->i_mutex); /* @@ -494,17 +494,17 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, * wait for all outstanding writes, before sending the FSYNC * request. */ - err = write_inode_now(inode, 0); + err = filemap_write_and_wait_range(inode->i_mapping, start, end); if (err) goto out; fuse_sync_writes(inode); + err = sync_inode_metadata(inode, 1); + if (err) + goto out; - if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) { - int err = fuse_flush_mtime(file, false); - if (err) - goto out; - } + if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) + goto out; req = fuse_get_req_nopages(fc); if (IS_ERR(req)) { @@ -1659,13 +1659,13 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req) fuse_writepage_free(fc, req); } -static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc, - struct fuse_inode *fi) +static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc, + struct fuse_inode *fi) { struct fuse_file *ff = NULL; spin_lock(&fc->lock); - if (!WARN_ON(list_empty(&fi->write_files))) { + if (!list_empty(&fi->write_files)) { ff = list_entry(fi->write_files.next, struct fuse_file, write_entry); fuse_file_get(ff); @@ -1675,6 +1675,29 @@ static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc, return ff; } +static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc, + struct fuse_inode *fi) +{ + struct fuse_file *ff = __fuse_write_file_get(fc, fi); + WARN_ON(!ff); + return ff; +} + +int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_file *ff; + int err; + + ff = __fuse_write_file_get(fc, fi); + err = fuse_flush_times(inode, ff); + if (ff) + fuse_file_put(ff, 0); + + return err; +} + static int fuse_writepage_locked(struct page *page) { struct address_space *mapping = page->mapping; @@ -2972,6 +2995,9 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) || (mode & FALLOC_FL_PUNCH_HOLE); + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) + return -EOPNOTSUPP; + if (fc->no_fallocate) return -EOPNOTSUPP; @@ -3017,12 +3043,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, if (!(mode & FALLOC_FL_KEEP_SIZE)) { bool changed = fuse_write_update_size(inode, offset + length); - if (changed && fc->writeback_cache) { - struct fuse_inode *fi = get_fuse_inode(inode); - - inode->i_mtime = current_fs_time(inode->i_sb); - set_bit(FUSE_I_MTIME_DIRTY, &fi->state); - } + if (changed && fc->writeback_cache) + file_update_time(file); } if (mode & FALLOC_FL_PUNCH_HOLE) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index a257ed8e..7aa5c75 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -119,8 +119,6 @@ enum { FUSE_I_INIT_RDPLUS, /** An operation changing file size is in progress */ FUSE_I_SIZE_UNSTABLE, - /** i_mtime has been updated locally; a flush to userspace needed */ - FUSE_I_MTIME_DIRTY, }; struct fuse_conn; @@ -544,6 +542,9 @@ struct fuse_conn { /** Is fallocate not implemented by fs? */ unsigned no_fallocate:1; + /** Is rename with flags implemented by fs? */ + unsigned no_rename2:1; + /** Use enhanced/automatic page cache invalidation. */ unsigned auto_inval_data:1; @@ -725,7 +726,7 @@ int fuse_dev_init(void); void fuse_dev_cleanup(void); int fuse_ctl_init(void); -void fuse_ctl_cleanup(void); +void __exit fuse_ctl_cleanup(void); /** * Allocate a request @@ -891,7 +892,8 @@ int fuse_dev_release(struct inode *inode, struct file *file); bool fuse_write_update_size(struct inode *inode, loff_t pos); -int fuse_flush_mtime(struct file *file, bool nofail); +int fuse_flush_times(struct inode *inode, struct fuse_file *ff); +int fuse_write_inode(struct inode *inode, struct writeback_control *wbc); int fuse_do_setattr(struct inode *inode, struct iattr *attr, struct file *file); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 8d61169..754dcf2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -175,9 +175,9 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) { inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_nsec = attr->mtimensec; + inode->i_ctime.tv_sec = attr->ctime; + inode->i_ctime.tv_nsec = attr->ctimensec; } - inode->i_ctime.tv_sec = attr->ctime; - inode->i_ctime.tv_nsec = attr->ctimensec; if (attr->blksize != 0) inode->i_blkbits = ilog2(attr->blksize); @@ -256,6 +256,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) inode->i_size = attr->size; inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_nsec = attr->mtimensec; + inode->i_ctime.tv_sec = attr->ctime; + inode->i_ctime.tv_nsec = attr->ctimensec; if (S_ISREG(inode->i_mode)) { fuse_init_common(inode); fuse_init_file_inode(inode); @@ -303,7 +305,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, if ((inode->i_state & I_NEW)) { inode->i_flags |= S_NOATIME; - if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) + if (!fc->writeback_cache || !S_ISREG(attr->mode)) inode->i_flags |= S_NOCMTIME; inode->i_generation = generation; inode->i_data.backing_dev_info = &fc->bdi; @@ -788,6 +790,7 @@ static const struct super_operations fuse_super_operations = { .alloc_inode = fuse_alloc_inode, .destroy_inode = fuse_destroy_inode, .evict_inode = fuse_evict_inode, + .write_inode = fuse_write_inode, .drop_inode = generic_delete_inode, .remount_fs = fuse_remount_fs, .put_super = fuse_put_super, @@ -890,6 +893,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) fc->async_dio = 1; if (arg->flags & FUSE_WRITEBACK_CACHE) fc->writeback_cache = 1; + if (arg->time_gran && arg->time_gran <= 1000000000) + fc->sb->s_time_gran = arg->time_gran; + else + fc->sb->s_time_gran = 1000000000; + } else { ra_pages = fc->max_read / PAGE_CACHE_SIZE; fc->no_lock = 1; @@ -996,7 +1004,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) if (sb->s_flags & MS_MANDLOCK) goto err; - sb->s_flags &= ~MS_NOSEC; + sb->s_flags &= ~(MS_NOSEC | MS_I_VERSION); if (!parse_fuse_opt((char *) data, &d, is_bdev)) goto err; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 2040275..e19d4c0 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -1030,6 +1030,11 @@ static int __init init_hugetlbfs_fs(void) int error; int i; + if (!hugepages_supported()) { + pr_info("hugetlbfs: disabling because there are no supported hugepage sizes\n"); + return -ENOTSUPP; + } + error = bdi_init(&hugetlbfs_backing_dev_info); if (error) return error; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index e01ea4a..5e9a80c 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -610,6 +610,7 @@ static void kernfs_put_open_node(struct kernfs_node *kn, static int kernfs_fop_open(struct inode *inode, struct file *file) { struct kernfs_node *kn = file->f_path.dentry->d_fsdata; + struct kernfs_root *root = kernfs_root(kn); const struct kernfs_ops *ops; struct kernfs_open_file *of; bool has_read, has_write, has_mmap; @@ -624,14 +625,16 @@ static int kernfs_fop_open(struct inode *inode, struct file *file) has_write = ops->write || ops->mmap; has_mmap = ops->mmap; - /* check perms and supported operations */ - if ((file->f_mode & FMODE_WRITE) && - (!(inode->i_mode & S_IWUGO) || !has_write)) - goto err_out; + /* see the flag definition for details */ + if (root->flags & KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK) { + if ((file->f_mode & FMODE_WRITE) && + (!(inode->i_mode & S_IWUGO) || !has_write)) + goto err_out; - if ((file->f_mode & FMODE_READ) && - (!(inode->i_mode & S_IRUGO) || !has_read)) - goto err_out; + if ((file->f_mode & FMODE_READ) && + (!(inode->i_mode & S_IRUGO) || !has_read)) + goto err_out; + } /* allocate a kernfs_open_file for the file */ error = -ENOMEM; @@ -389,18 +389,6 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_ops = NULL; fl->fl_lmops = NULL; - /* Ensure that fl->fl_filp has compatible f_mode */ - switch (l->l_type) { - case F_RDLCK: - if (!(filp->f_mode & FMODE_READ)) - return -EBADF; - break; - case F_WRLCK: - if (!(filp->f_mode & FMODE_WRITE)) - return -EBADF; - break; - } - return assign_type(fl, l->l_type); } @@ -2034,6 +2022,22 @@ static int do_lock_file_wait(struct file *filp, unsigned int cmd, return error; } +/* Ensure that fl->fl_filp has compatible f_mode for F_SETLK calls */ +static int +check_fmode_for_setlk(struct file_lock *fl) +{ + switch (fl->fl_type) { + case F_RDLCK: + if (!(fl->fl_file->f_mode & FMODE_READ)) + return -EBADF; + break; + case F_WRLCK: + if (!(fl->fl_file->f_mode & FMODE_WRITE)) + return -EBADF; + } + return 0; +} + /* Apply the lock described by l to an open file descriptor. * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */ @@ -2071,6 +2075,10 @@ again: if (error) goto out; + error = check_fmode_for_setlk(file_lock); + if (error) + goto out; + /* * If the cmd is requesting file-private locks, then set the * FL_OFDLCK flag and override the owner. @@ -2206,6 +2214,10 @@ again: if (error) goto out; + error = check_fmode_for_setlk(file_lock); + if (error) + goto out; + /* * If the cmd is requesting file-private locks, then set the * FL_OFDLCK flag and override the owner. @@ -1542,7 +1542,7 @@ static inline int walk_component(struct nameidata *nd, struct path *path, inode = path->dentry->d_inode; } err = -ENOENT; - if (!inode) + if (!inode || d_is_negative(path->dentry)) goto out_path_put; if (should_follow_link(path->dentry, follow)) { @@ -2249,7 +2249,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) mutex_unlock(&dir->d_inode->i_mutex); done: - if (!dentry->d_inode) { + if (!dentry->d_inode || d_is_negative(dentry)) { error = -ENOENT; dput(dentry); goto out; @@ -2994,7 +2994,7 @@ retry_lookup: finish_lookup: /* we _can_ be in RCU mode here */ error = -ENOENT; - if (d_is_negative(path->dentry)) { + if (!inode || d_is_negative(path->dentry)) { path_to_nameidata(path, nd); goto out; } diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 6f3f392..b6f4601 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -402,8 +402,10 @@ sort_pacl(struct posix_acl *pacl) * by uid/gid. */ int i, j; - if (pacl->a_count <= 4) - return; /* no users or groups */ + /* no users or groups */ + if (!pacl || pacl->a_count <= 4) + return; + i = 1; while (pacl->a_entries[i].e_tag == ACL_USER) i++; @@ -530,13 +532,12 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) /* * ACLs with no ACEs are treated differently in the inheritable - * and effective cases: when there are no inheritable ACEs, we - * set a zero-length default posix acl: + * and effective cases: when there are no inheritable ACEs, + * calls ->set_acl with a NULL ACL structure. */ - if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT)) { - pacl = posix_acl_alloc(0, GFP_KERNEL); - return pacl ? pacl : ERR_PTR(-ENOMEM); - } + if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT)) + return NULL; + /* * When there are no effective ACEs, the following will end * up setting a 3-element effective posix ACL with all diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3ba6597..32b699b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1078,6 +1078,18 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) return NULL; } clp->cl_name.len = name.len; + INIT_LIST_HEAD(&clp->cl_sessions); + idr_init(&clp->cl_stateids); + atomic_set(&clp->cl_refcount, 0); + clp->cl_cb_state = NFSD4_CB_UNKNOWN; + INIT_LIST_HEAD(&clp->cl_idhash); + INIT_LIST_HEAD(&clp->cl_openowners); + INIT_LIST_HEAD(&clp->cl_delegations); + INIT_LIST_HEAD(&clp->cl_lru); + INIT_LIST_HEAD(&clp->cl_callbacks); + INIT_LIST_HEAD(&clp->cl_revoked); + spin_lock_init(&clp->cl_lock); + rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); return clp; } @@ -1095,6 +1107,7 @@ free_client(struct nfs4_client *clp) WARN_ON_ONCE(atomic_read(&ses->se_ref)); free_session(ses); } + rpc_destroy_wait_queue(&clp->cl_cb_waitq); free_svc_cred(&clp->cl_cred); kfree(clp->cl_name.data); idr_destroy(&clp->cl_stateids); @@ -1347,7 +1360,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, if (clp == NULL) return NULL; - INIT_LIST_HEAD(&clp->cl_sessions); ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred); if (ret) { spin_lock(&nn->client_lock); @@ -1355,20 +1367,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name, spin_unlock(&nn->client_lock); return NULL; } - idr_init(&clp->cl_stateids); - atomic_set(&clp->cl_refcount, 0); - clp->cl_cb_state = NFSD4_CB_UNKNOWN; - INIT_LIST_HEAD(&clp->cl_idhash); - INIT_LIST_HEAD(&clp->cl_openowners); - INIT_LIST_HEAD(&clp->cl_delegations); - INIT_LIST_HEAD(&clp->cl_lru); - INIT_LIST_HEAD(&clp->cl_callbacks); - INIT_LIST_HEAD(&clp->cl_revoked); - spin_lock_init(&clp->cl_lock); nfsd4_init_callback(&clp->cl_cb_null); clp->cl_time = get_seconds(); clear_bit(0, &clp->cl_cb_slot_busy); - rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); copy_verf(clp, verf); rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa); gen_confirm(clp); diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 4e565c8..732648b 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -698,6 +698,8 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) } group->overflow_event = &oevent->fse; + if (force_o_largefile()) + event_f_flags |= O_LARGEFILE; group->fanotify_data.f_flags = event_f_flags; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS spin_lock_init(&group->fanotify_data.access_lock); diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index af3f7aa..ee1f884 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -472,11 +472,15 @@ bail: void dlm_destroy_master_caches(void) { - if (dlm_lockname_cache) + if (dlm_lockname_cache) { kmem_cache_destroy(dlm_lockname_cache); + dlm_lockname_cache = NULL; + } - if (dlm_lockres_cache) + if (dlm_lockres_cache) { kmem_cache_destroy(dlm_lockres_cache); + dlm_lockres_cache = NULL; + } } static void dlm_lockres_release(struct kref *kref) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 9e363e4..0855f77 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -246,6 +246,12 @@ posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p) umode_t mode = 0; int not_equiv = 0; + /* + * A null ACL can always be presented as mode bits. + */ + if (!acl) + return 0; + FOREACH_ACL_ENTRY(pa, acl, pe) { switch (pa->e_tag) { case ACL_USER_OBJ: diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 28cc1acd..e9ef59b 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -47,12 +47,13 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) ssize_t count; char *buf; - /* acquire buffer and ensure that it's >= PAGE_SIZE */ + /* acquire buffer and ensure that it's >= PAGE_SIZE and clear */ count = seq_get_buf(sf, &buf); if (count < PAGE_SIZE) { seq_commit(sf, -1); return 0; } + memset(buf, 0, PAGE_SIZE); /* * Invoke show(). Control may reach here via seq file lseek even diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index a66ad61..8794423 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -63,7 +63,8 @@ int __init sysfs_init(void) { int err; - sysfs_root = kernfs_create_root(NULL, 0, NULL); + sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK, + NULL); if (IS_ERR(sysfs_root)) return PTR_ERR(sysfs_root); diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index 01b6a01..abda112 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -213,7 +213,7 @@ xfs_attr_calc_size( * Out of line attribute, cannot double split, but * make room for the attribute value itself. */ - uint dblocks = XFS_B_TO_FSB(mp, valuelen); + uint dblocks = xfs_attr3_rmt_blocks(mp, valuelen); nblks += dblocks; nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK); } @@ -698,11 +698,22 @@ xfs_attr_leaf_addname(xfs_da_args_t *args) trace_xfs_attr_leaf_replace(args); + /* save the attribute state for later removal*/ args->op_flags |= XFS_DA_OP_RENAME; /* an atomic rename */ args->blkno2 = args->blkno; /* set 2nd entry info*/ args->index2 = args->index; args->rmtblkno2 = args->rmtblkno; args->rmtblkcnt2 = args->rmtblkcnt; + args->rmtvaluelen2 = args->rmtvaluelen; + + /* + * clear the remote attr state now that it is saved so that the + * values reflect the state of the attribute we are about to + * add, not the attribute we just found and will remove later. + */ + args->rmtblkno = 0; + args->rmtblkcnt = 0; + args->rmtvaluelen = 0; } /* @@ -794,6 +805,7 @@ xfs_attr_leaf_addname(xfs_da_args_t *args) args->blkno = args->blkno2; args->rmtblkno = args->rmtblkno2; args->rmtblkcnt = args->rmtblkcnt2; + args->rmtvaluelen = args->rmtvaluelen2; if (args->rmtblkno) { error = xfs_attr_rmtval_remove(args); if (error) @@ -999,13 +1011,22 @@ restart: trace_xfs_attr_node_replace(args); + /* save the attribute state for later removal*/ args->op_flags |= XFS_DA_OP_RENAME; /* atomic rename op */ args->blkno2 = args->blkno; /* set 2nd entry info*/ args->index2 = args->index; args->rmtblkno2 = args->rmtblkno; args->rmtblkcnt2 = args->rmtblkcnt; + args->rmtvaluelen2 = args->rmtvaluelen; + + /* + * clear the remote attr state now that it is saved so that the + * values reflect the state of the attribute we are about to + * add, not the attribute we just found and will remove later. + */ args->rmtblkno = 0; args->rmtblkcnt = 0; + args->rmtvaluelen = 0; } retval = xfs_attr3_leaf_add(blk->bp, state->args); @@ -1133,6 +1154,7 @@ restart: args->blkno = args->blkno2; args->rmtblkno = args->rmtblkno2; args->rmtblkcnt = args->rmtblkcnt2; + args->rmtvaluelen = args->rmtvaluelen2; if (args->rmtblkno) { error = xfs_attr_rmtval_remove(args); if (error) diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index fe9587f..511c2834 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -1229,6 +1229,7 @@ xfs_attr3_leaf_add_work( name_rmt->valueblk = 0; args->rmtblkno = 1; args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); + args->rmtvaluelen = args->valuelen; } xfs_trans_log_buf(args->trans, bp, XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index), @@ -2167,11 +2168,11 @@ xfs_attr3_leaf_lookup_int( if (!xfs_attr_namesp_match(args->flags, entry->flags)) continue; args->index = probe; - args->valuelen = be32_to_cpu(name_rmt->valuelen); + args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen); args->rmtblkno = be32_to_cpu(name_rmt->valueblk); args->rmtblkcnt = xfs_attr3_rmt_blocks( args->dp->i_mount, - args->valuelen); + args->rmtvaluelen); return XFS_ERROR(EEXIST); } } @@ -2220,19 +2221,19 @@ xfs_attr3_leaf_getvalue( name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); ASSERT(name_rmt->namelen == args->namelen); ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0); - valuelen = be32_to_cpu(name_rmt->valuelen); + args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen); args->rmtblkno = be32_to_cpu(name_rmt->valueblk); args->rmtblkcnt = xfs_attr3_rmt_blocks(args->dp->i_mount, - valuelen); + args->rmtvaluelen); if (args->flags & ATTR_KERNOVAL) { - args->valuelen = valuelen; + args->valuelen = args->rmtvaluelen; return 0; } - if (args->valuelen < valuelen) { - args->valuelen = valuelen; + if (args->valuelen < args->rmtvaluelen) { + args->valuelen = args->rmtvaluelen; return XFS_ERROR(ERANGE); } - args->valuelen = valuelen; + args->valuelen = args->rmtvaluelen; } return 0; } @@ -2519,7 +2520,7 @@ xfs_attr3_leaf_clearflag( ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0); name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); name_rmt->valueblk = cpu_to_be32(args->rmtblkno); - name_rmt->valuelen = cpu_to_be32(args->valuelen); + name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen); xfs_trans_log_buf(args->trans, bp, XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); } @@ -2677,7 +2678,7 @@ xfs_attr3_leaf_flipflags( ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0); name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index); name_rmt->valueblk = cpu_to_be32(args->rmtblkno); - name_rmt->valuelen = cpu_to_be32(args->valuelen); + name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen); xfs_trans_log_buf(args->trans, bp1, XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt))); } diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c index 01db96f..833fe5d9 100644 --- a/fs/xfs/xfs_attr_list.c +++ b/fs/xfs/xfs_attr_list.c @@ -447,6 +447,7 @@ xfs_attr3_leaf_list_int( args.dp = context->dp; args.whichfork = XFS_ATTR_FORK; args.valuelen = valuelen; + args.rmtvaluelen = valuelen; args.value = kmem_alloc(valuelen, KM_SLEEP | KM_NOFS); args.rmtblkno = be32_to_cpu(name_rmt->valueblk); args.rmtblkcnt = xfs_attr3_rmt_blocks( diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c index 6e37823..d2e6e94 100644 --- a/fs/xfs/xfs_attr_remote.c +++ b/fs/xfs/xfs_attr_remote.c @@ -337,7 +337,7 @@ xfs_attr_rmtval_get( struct xfs_buf *bp; xfs_dablk_t lblkno = args->rmtblkno; __uint8_t *dst = args->value; - int valuelen = args->valuelen; + int valuelen; int nmap; int error; int blkcnt = args->rmtblkcnt; @@ -347,7 +347,9 @@ xfs_attr_rmtval_get( trace_xfs_attr_rmtval_get(args); ASSERT(!(args->flags & ATTR_KERNOVAL)); + ASSERT(args->rmtvaluelen == args->valuelen); + valuelen = args->rmtvaluelen; while (valuelen > 0) { nmap = ATTR_RMTVALUE_MAPSIZE; error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, @@ -415,7 +417,7 @@ xfs_attr_rmtval_set( * attributes have headers, we can't just do a straight byte to FSB * conversion and have to take the header space into account. */ - blkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); + blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen); error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff, XFS_ATTR_FORK); if (error) @@ -480,7 +482,7 @@ xfs_attr_rmtval_set( */ lblkno = args->rmtblkno; blkcnt = args->rmtblkcnt; - valuelen = args->valuelen; + valuelen = args->rmtvaluelen; while (valuelen > 0) { struct xfs_buf *bp; xfs_daddr_t dblkno; diff --git a/fs/xfs/xfs_da_btree.h b/fs/xfs/xfs_da_btree.h index 6e95ea7..201c609 100644 --- a/fs/xfs/xfs_da_btree.h +++ b/fs/xfs/xfs_da_btree.h @@ -60,10 +60,12 @@ typedef struct xfs_da_args { int index; /* index of attr of interest in blk */ xfs_dablk_t rmtblkno; /* remote attr value starting blkno */ int rmtblkcnt; /* remote attr value block count */ + int rmtvaluelen; /* remote attr value length in bytes */ xfs_dablk_t blkno2; /* blkno of 2nd attr leaf of interest */ int index2; /* index of 2nd attr in blk */ xfs_dablk_t rmtblkno2; /* remote attr value starting blkno */ int rmtblkcnt2; /* remote attr value block count */ + int rmtvaluelen2; /* remote attr value length in bytes */ int op_flags; /* operation flags */ enum xfs_dacmp cmpresult; /* name compare result for lookups */ } xfs_da_args_t; diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c index 1399e18..753e467 100644 --- a/fs/xfs/xfs_export.c +++ b/fs/xfs/xfs_export.c @@ -237,7 +237,7 @@ xfs_fs_nfs_commit_metadata( if (!lsn) return 0; - return _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL); + return -_xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL); } const struct export_operations xfs_export_operations = { diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 951a232..830c1c9 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -155,7 +155,7 @@ xfs_dir_fsync( if (!lsn) return 0; - return _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL); + return -_xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL); } STATIC int @@ -295,7 +295,7 @@ xfs_file_aio_read( xfs_rw_ilock(ip, XFS_IOLOCK_EXCL); if (inode->i_mapping->nrpages) { - ret = -filemap_write_and_wait_range( + ret = filemap_write_and_wait_range( VFS_I(ip)->i_mapping, pos, -1); if (ret) { @@ -837,7 +837,7 @@ xfs_file_fallocate( unsigned blksize_mask = (1 << inode->i_blkbits) - 1; if (offset & blksize_mask || len & blksize_mask) { - error = -EINVAL; + error = EINVAL; goto out_unlock; } @@ -846,7 +846,7 @@ xfs_file_fallocate( * in which case it is effectively a truncate operation */ if (offset + len >= i_size_read(inode)) { - error = -EINVAL; + error = EINVAL; goto out_unlock; } diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index ef1ca01..36d6303 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -72,8 +72,8 @@ xfs_initxattrs( int error = 0; for (xattr = xattr_array; xattr->name != NULL; xattr++) { - error = xfs_attr_set(ip, xattr->name, xattr->value, - xattr->value_len, ATTR_SECURE); + error = -xfs_attr_set(ip, xattr->name, xattr->value, + xattr->value_len, ATTR_SECURE); if (error < 0) break; } @@ -93,8 +93,8 @@ xfs_init_security( struct inode *dir, const struct qstr *qstr) { - return security_inode_init_security(inode, dir, qstr, - &xfs_initxattrs, NULL); + return -security_inode_init_security(inode, dir, qstr, + &xfs_initxattrs, NULL); } static void @@ -124,15 +124,15 @@ xfs_cleanup_inode( xfs_dentry_to_name(&teardown, dentry, 0); xfs_remove(XFS_I(dir), &teardown, XFS_I(inode)); - iput(inode); } STATIC int -xfs_vn_mknod( +xfs_generic_create( struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t rdev) + dev_t rdev, + bool tmpfile) /* unnamed file */ { struct inode *inode; struct xfs_inode *ip = NULL; @@ -156,8 +156,12 @@ xfs_vn_mknod( if (error) return error; - xfs_dentry_to_name(&name, dentry, mode); - error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip); + if (!tmpfile) { + xfs_dentry_to_name(&name, dentry, mode); + error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip); + } else { + error = xfs_create_tmpfile(XFS_I(dir), dentry, mode, &ip); + } if (unlikely(error)) goto out_free_acl; @@ -169,18 +173,22 @@ xfs_vn_mknod( #ifdef CONFIG_XFS_POSIX_ACL if (default_acl) { - error = xfs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT); + error = -xfs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT); if (error) goto out_cleanup_inode; } if (acl) { - error = xfs_set_acl(inode, acl, ACL_TYPE_ACCESS); + error = -xfs_set_acl(inode, acl, ACL_TYPE_ACCESS); if (error) goto out_cleanup_inode; } #endif - d_instantiate(dentry, inode); + if (tmpfile) + d_tmpfile(dentry, inode); + else + d_instantiate(dentry, inode); + out_free_acl: if (default_acl) posix_acl_release(default_acl); @@ -189,11 +197,23 @@ xfs_vn_mknod( return -error; out_cleanup_inode: - xfs_cleanup_inode(dir, inode, dentry); + if (!tmpfile) + xfs_cleanup_inode(dir, inode, dentry); + iput(inode); goto out_free_acl; } STATIC int +xfs_vn_mknod( + struct inode *dir, + struct dentry *dentry, + umode_t mode, + dev_t rdev) +{ + return xfs_generic_create(dir, dentry, mode, rdev, false); +} + +STATIC int xfs_vn_create( struct inode *dir, struct dentry *dentry, @@ -353,6 +373,7 @@ xfs_vn_symlink( out_cleanup_inode: xfs_cleanup_inode(dir, inode, dentry); + iput(inode); out: return -error; } @@ -1053,25 +1074,7 @@ xfs_vn_tmpfile( struct dentry *dentry, umode_t mode) { - int error; - struct xfs_inode *ip; - struct inode *inode; - - error = xfs_create_tmpfile(XFS_I(dir), dentry, mode, &ip); - if (unlikely(error)) - return -error; - - inode = VFS_I(ip); - - error = xfs_init_security(inode, dir, &dentry->d_name); - if (unlikely(error)) { - iput(inode); - return -error; - } - - d_tmpfile(dentry, inode); - - return 0; + return xfs_generic_create(dir, dentry, mode, 0, true); } static const struct inode_operations xfs_inode_operations = { diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 08624dc..a5f8bd9 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -616,11 +616,13 @@ xfs_log_mount( int error = 0; int min_logfsbs; - if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) - xfs_notice(mp, "Mounting Filesystem"); - else { + if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) { + xfs_notice(mp, "Mounting V%d Filesystem", + XFS_SB_VERSION_NUM(&mp->m_sb)); + } else { xfs_notice(mp, -"Mounting filesystem in no-recovery mode. Filesystem will be inconsistent."); +"Mounting V%d filesystem in no-recovery mode. Filesystem will be inconsistent.", + XFS_SB_VERSION_NUM(&mp->m_sb)); ASSERT(mp->m_flags & XFS_MOUNT_RDONLY); } diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 993cb19..944f3d9 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -743,8 +743,6 @@ xfs_mountfs( new_size *= mp->m_sb.sb_inodesize / XFS_DINODE_MIN_SIZE; if (mp->m_sb.sb_inoalignmt >= XFS_B_TO_FSBT(mp, new_size)) mp->m_inode_cluster_size = new_size; - xfs_info(mp, "Using inode cluster size of %d bytes", - mp->m_inode_cluster_size); } /* diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 348e4d2..dc977b6 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -843,22 +843,17 @@ xfs_qm_init_quotainfo( qinf = mp->m_quotainfo = kmem_zalloc(sizeof(xfs_quotainfo_t), KM_SLEEP); - if ((error = list_lru_init(&qinf->qi_lru))) { - kmem_free(qinf); - mp->m_quotainfo = NULL; - return error; - } + error = -list_lru_init(&qinf->qi_lru); + if (error) + goto out_free_qinf; /* * See if quotainodes are setup, and if not, allocate them, * and change the superblock accordingly. */ - if ((error = xfs_qm_init_quotainos(mp))) { - list_lru_destroy(&qinf->qi_lru); - kmem_free(qinf); - mp->m_quotainfo = NULL; - return error; - } + error = xfs_qm_init_quotainos(mp); + if (error) + goto out_free_lru; INIT_RADIX_TREE(&qinf->qi_uquota_tree, GFP_NOFS); INIT_RADIX_TREE(&qinf->qi_gquota_tree, GFP_NOFS); @@ -918,7 +913,7 @@ xfs_qm_init_quotainfo( qinf->qi_isoftlimit = be64_to_cpu(ddqp->d_ino_softlimit); qinf->qi_rtbhardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit); qinf->qi_rtbsoftlimit = be64_to_cpu(ddqp->d_rtb_softlimit); - + xfs_qm_dqdestroy(dqp); } else { qinf->qi_btimelimit = XFS_QM_BTIMELIMIT; @@ -935,6 +930,13 @@ xfs_qm_init_quotainfo( qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE; register_shrinker(&qinf->qi_shrinker); return 0; + +out_free_lru: + list_lru_destroy(&qinf->qi_lru); +out_free_qinf: + kmem_free(qinf); + mp->m_quotainfo = NULL; + return error; } diff --git a/fs/xfs/xfs_sb.c b/fs/xfs/xfs_sb.c index 0c0e41b..8baf61a 100644 --- a/fs/xfs/xfs_sb.c +++ b/fs/xfs/xfs_sb.c @@ -201,10 +201,6 @@ xfs_mount_validate_sb( * write validation, we don't need to check feature masks. */ if (check_version && XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) { - xfs_alert(mp, -"Version 5 superblock detected. This kernel has EXPERIMENTAL support enabled!\n" -"Use of these features in this kernel is at your own risk!"); - if (xfs_sb_has_compat_feature(sbp, XFS_SB_FEAT_COMPAT_UNKNOWN)) { xfs_warn(mp, diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 2053767..3494eff 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1433,11 +1433,11 @@ xfs_fs_fill_super( if (error) goto out_free_fsname; - error = xfs_init_mount_workqueues(mp); + error = -xfs_init_mount_workqueues(mp); if (error) goto out_close_devices; - error = xfs_icsb_init_counters(mp); + error = -xfs_icsb_init_counters(mp); if (error) goto out_destroy_workqueues; diff --git a/include/asm-generic/resource.h b/include/asm-generic/resource.h index b4ea8f5..5e752b9 100644 --- a/include/asm-generic/resource.h +++ b/include/asm-generic/resource.h @@ -12,7 +12,7 @@ [RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, \ [RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, \ [RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, \ - [RLIMIT_STACK] = { _STK_LIM, _STK_LIM_MAX }, \ + [RLIMIT_STACK] = { _STK_LIM, RLIM_INFINITY }, \ [RLIMIT_CORE] = { 0, RLIM_INFINITY }, \ [RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, \ [RLIMIT_NPROC] = { 0, 0 }, \ diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 49376ae..6dfd64b 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -637,6 +637,22 @@ {0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 940ece4..012d58f 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -191,8 +191,8 @@ INTEL_VGA_DEVICE(0x0A06, info), /* ULT GT1 mobile */ \ INTEL_VGA_DEVICE(0x0A16, info), /* ULT GT2 mobile */ \ INTEL_VGA_DEVICE(0x0A26, info), /* ULT GT3 mobile */ \ - INTEL_VGA_DEVICE(0x0A0E, info), /* ULT GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0A1E, info), /* ULT GT2 reserved */ \ + INTEL_VGA_DEVICE(0x0A0E, info), /* ULX GT1 mobile */ \ + INTEL_VGA_DEVICE(0x0A1E, info), /* ULX GT2 mobile */ \ INTEL_VGA_DEVICE(0x0A2E, info), /* ULT GT3 reserved */ \ INTEL_VGA_DEVICE(0x0D06, info), /* CRW GT1 mobile */ \ INTEL_VGA_DEVICE(0x0D16, info), /* CRW GT2 mobile */ \ diff --git a/include/dt-bindings/clk/at91.h b/include/dt-bindings/clock/at91.h index 0b4cb99..0b4cb99 100644 --- a/include/dt-bindings/clk/at91.h +++ b/include/dt-bindings/clock/at91.h diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h index 8598f8e..a495a95 100644 --- a/include/linux/ath9k_platform.h +++ b/include/linux/ath9k_platform.h @@ -36,6 +36,8 @@ struct ath9k_platform_data { int (*get_mac_revision)(void); int (*external_reset)(void); + + bool use_eeprom; }; #endif /* _LINUX_ATH9K_PLATFORM_H */ diff --git a/include/linux/can/core.h b/include/linux/can/core.h index 78c6c52..a087500 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -10,8 +10,8 @@ * */ -#ifndef CAN_CORE_H -#define CAN_CORE_H +#ifndef _CAN_CORE_H +#define _CAN_CORE_H #include <linux/can.h> #include <linux/skbuff.h> @@ -58,4 +58,4 @@ extern void can_rx_unregister(struct net_device *dev, canid_t can_id, extern int can_send(struct sk_buff *skb, int loop); extern int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); -#endif /* CAN_CORE_H */ +#endif /* !_CAN_CORE_H */ diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 3ce5e52..6992afc 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -10,8 +10,8 @@ * */ -#ifndef CAN_DEV_H -#define CAN_DEV_H +#ifndef _CAN_DEV_H +#define _CAN_DEV_H #include <linux/can.h> #include <linux/can/netlink.h> @@ -132,4 +132,4 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf); -#endif /* CAN_DEV_H */ +#endif /* !_CAN_DEV_H */ diff --git a/include/linux/can/led.h b/include/linux/can/led.h index 9c1167ba..e0475c5 100644 --- a/include/linux/can/led.h +++ b/include/linux/can/led.h @@ -6,8 +6,8 @@ * published by the Free Software Foundation. */ -#ifndef CAN_LED_H -#define CAN_LED_H +#ifndef _CAN_LED_H +#define _CAN_LED_H #include <linux/if.h> #include <linux/leds.h> @@ -48,4 +48,4 @@ static inline void can_led_notifier_exit(void) #endif -#endif +#endif /* !_CAN_LED_H */ diff --git a/include/linux/can/platform/cc770.h b/include/linux/can/platform/cc770.h index 7702641..78b2d44 100644 --- a/include/linux/can/platform/cc770.h +++ b/include/linux/can/platform/cc770.h @@ -1,5 +1,5 @@ -#ifndef _CAN_PLATFORM_CC770_H_ -#define _CAN_PLATFORM_CC770_H_ +#ifndef _CAN_PLATFORM_CC770_H +#define _CAN_PLATFORM_CC770_H /* CPU Interface Register (0x02) */ #define CPUIF_CEN 0x01 /* Clock Out Enable */ @@ -30,4 +30,4 @@ struct cc770_platform_data { u8 bcr; /* Bus Configuration Register */ }; -#endif /* !_CAN_PLATFORM_CC770_H_ */ +#endif /* !_CAN_PLATFORM_CC770_H */ diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index dc029db..d44fcae 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -1,5 +1,5 @@ -#ifndef __CAN_PLATFORM_MCP251X_H__ -#define __CAN_PLATFORM_MCP251X_H__ +#ifndef _CAN_PLATFORM_MCP251X_H +#define _CAN_PLATFORM_MCP251X_H /* * @@ -18,4 +18,4 @@ struct mcp251x_platform_data { unsigned long oscillator_frequency; }; -#endif /* __CAN_PLATFORM_MCP251X_H__ */ +#endif /* !_CAN_PLATFORM_MCP251X_H */ diff --git a/include/linux/can/platform/rcar_can.h b/include/linux/can/platform/rcar_can.h new file mode 100644 index 0000000..0f4a2f3 --- /dev/null +++ b/include/linux/can/platform/rcar_can.h @@ -0,0 +1,17 @@ +#ifndef _CAN_PLATFORM_RCAR_CAN_H_ +#define _CAN_PLATFORM_RCAR_CAN_H_ + +#include <linux/types.h> + +/* Clock Select Register settings */ +enum CLKR { + CLKR_CLKP1 = 0, /* Peripheral clock (clkp1) */ + CLKR_CLKP2 = 1, /* Peripheral clock (clkp2) */ + CLKR_CLKEXT = 3 /* Externally input clock */ +}; + +struct rcar_can_platform_data { + enum CLKR clock_select; /* Clock source select */ +}; + +#endif /* !_CAN_PLATFORM_RCAR_CAN_H_ */ diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h index 96f8fcc..93570b6 100644 --- a/include/linux/can/platform/sja1000.h +++ b/include/linux/can/platform/sja1000.h @@ -1,5 +1,5 @@ -#ifndef _CAN_PLATFORM_SJA1000_H_ -#define _CAN_PLATFORM_SJA1000_H_ +#ifndef _CAN_PLATFORM_SJA1000_H +#define _CAN_PLATFORM_SJA1000_H /* clock divider register */ #define CDR_CLKOUT_MASK 0x07 @@ -32,4 +32,4 @@ struct sja1000_platform_data { u8 cdr; /* clock divider register */ }; -#endif /* !_CAN_PLATFORM_SJA1000_H_ */ +#endif /* !_CAN_PLATFORM_SJA1000_H */ diff --git a/include/linux/can/platform/ti_hecc.h b/include/linux/can/platform/ti_hecc.h index af17cb3..a52f47c 100644 --- a/include/linux/can/platform/ti_hecc.h +++ b/include/linux/can/platform/ti_hecc.h @@ -1,5 +1,5 @@ -#ifndef __CAN_PLATFORM_TI_HECC_H__ -#define __CAN_PLATFORM_TI_HECC_H__ +#ifndef _CAN_PLATFORM_TI_HECC_H +#define _CAN_PLATFORM_TI_HECC_H /* * TI HECC (High End CAN Controller) driver platform header @@ -41,4 +41,4 @@ struct ti_hecc_platform_data { u32 version; void (*transceiver_switch) (int); }; -#endif +#endif /* !_CAN_PLATFORM_TI_HECC_H */ diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index f9bbbb4..cc00d15 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -7,8 +7,8 @@ * */ -#ifndef CAN_SKB_H -#define CAN_SKB_H +#ifndef _CAN_SKB_H +#define _CAN_SKB_H #include <linux/types.h> #include <linux/skbuff.h> @@ -80,4 +80,4 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb) return skb; } -#endif /* CAN_SKB_H */ +#endif /* !_CAN_SKB_H */ diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c251585..d60904b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -473,6 +473,7 @@ struct cftype { }; extern struct cgroup_root cgrp_dfl_root; +extern struct css_set init_css_set; static inline bool cgroup_on_dfl(const struct cgroup *cgrp) { @@ -700,6 +701,20 @@ static inline struct cgroup_subsys_state *task_css(struct task_struct *task, return task_css_check(task, subsys_id, false); } +/** + * task_css_is_root - test whether a task belongs to the root css + * @task: the target task + * @subsys_id: the target subsystem ID + * + * Test whether @task belongs to the root css on the specified subsystem. + * May be invoked in any context. + */ +static inline bool task_css_is_root(struct task_struct *task, int subsys_id) +{ + return task_css_check(task, subsys_id, true) == + init_css_set.subsys[subsys_id]; +} + static inline struct cgroup *task_cgroup(struct task_struct *task, int subsys_id) { diff --git a/include/linux/crc7.h b/include/linux/crc7.h index 1786e77..d590765 100644 --- a/include/linux/crc7.h +++ b/include/linux/crc7.h @@ -2,13 +2,13 @@ #define _LINUX_CRC7_H #include <linux/types.h> -extern const u8 crc7_syndrome_table[256]; +extern const u8 crc7_be_syndrome_table[256]; -static inline u8 crc7_byte(u8 crc, u8 data) +static inline u8 crc7_be_byte(u8 crc, u8 data) { - return crc7_syndrome_table[(crc << 1) ^ data]; + return crc7_be_syndrome_table[crc ^ data]; } -extern u8 crc7(u8 crc, const u8 *buffer, size_t len); +extern u8 crc7_be(u8 crc, const u8 *buffer, size_t len); #endif diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 3b9bfdb..3c7ec32 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -221,6 +221,8 @@ struct dentry_operations { #define DCACHE_SYMLINK_TYPE 0x00300000 /* Symlink */ #define DCACHE_FILE_TYPE 0x00400000 /* Other file type */ +#define DCACHE_MAY_FREE 0x00800000 + extern seqlock_t rename_lock; static inline int dname_external(const struct dentry *dentry) diff --git a/include/linux/filter.h b/include/linux/filter.h index 4457b38..f0c2ad4 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -37,9 +37,6 @@ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ -/* Placeholder/dummy for 0 */ -#define BPF_0 0 - /* Register numbers */ enum { BPF_REG_0 = 0, @@ -79,56 +76,211 @@ enum { /* BPF program can access up to 512 bytes of stack space. */ #define MAX_BPF_STACK 512 -/* bpf_add|sub|...: a += x, bpf_mov: a = x */ -#define BPF_ALU64_REG(op, a, x) \ - ((struct sock_filter_int) {BPF_ALU64|BPF_OP(op)|BPF_X, a, x, 0, 0}) -#define BPF_ALU32_REG(op, a, x) \ - ((struct sock_filter_int) {BPF_ALU|BPF_OP(op)|BPF_X, a, x, 0, 0}) - -/* bpf_add|sub|...: a += imm, bpf_mov: a = imm */ -#define BPF_ALU64_IMM(op, a, imm) \ - ((struct sock_filter_int) {BPF_ALU64|BPF_OP(op)|BPF_K, a, 0, 0, imm}) -#define BPF_ALU32_IMM(op, a, imm) \ - ((struct sock_filter_int) {BPF_ALU|BPF_OP(op)|BPF_K, a, 0, 0, imm}) - -/* R0 = *(uint *) (skb->data + off) */ -#define BPF_LD_ABS(size, off) \ - ((struct sock_filter_int) {BPF_LD|BPF_SIZE(size)|BPF_ABS, 0, 0, 0, off}) - -/* R0 = *(uint *) (skb->data + x + off) */ -#define BPF_LD_IND(size, x, off) \ - ((struct sock_filter_int) {BPF_LD|BPF_SIZE(size)|BPF_IND, 0, x, 0, off}) - -/* a = *(uint *) (x + off) */ -#define BPF_LDX_MEM(sz, a, x, off) \ - ((struct sock_filter_int) {BPF_LDX|BPF_SIZE(sz)|BPF_MEM, a, x, off, 0}) - -/* if (a 'op' x) goto pc+off */ -#define BPF_JMP_REG(op, a, x, off) \ - ((struct sock_filter_int) {BPF_JMP|BPF_OP(op)|BPF_X, a, x, off, 0}) - -/* if (a 'op' imm) goto pc+off */ -#define BPF_JMP_IMM(op, a, imm, off) \ - ((struct sock_filter_int) {BPF_JMP|BPF_OP(op)|BPF_K, a, 0, off, imm}) - -#define BPF_EXIT_INSN() \ - ((struct sock_filter_int) {BPF_JMP|BPF_EXIT, 0, 0, 0, 0}) - -static inline int size_to_bpf(int size) -{ - switch (size) { - case 1: - return BPF_B; - case 2: - return BPF_H; - case 4: - return BPF_W; - case 8: - return BPF_DW; - default: - return -EINVAL; - } -} +/* Helper macros for filter block array initializers. */ + +/* ALU ops on registers, bpf_add|sub|...: A += X */ + +#define BPF_ALU64_REG(OP, A, X) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .a_reg = A, \ + .x_reg = X, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU32_REG(OP, A, X) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ + .a_reg = A, \ + .x_reg = X, \ + .off = 0, \ + .imm = 0 }) + +/* ALU ops on immediates, bpf_add|sub|...: A += IMM */ + +#define BPF_ALU64_IMM(OP, A, IMM) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .a_reg = A, \ + .x_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_ALU32_IMM(OP, A, IMM) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ + .a_reg = A, \ + .x_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */ + +#define BPF_ENDIAN(TYPE, A, LEN) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \ + .a_reg = A, \ + .x_reg = 0, \ + .off = 0, \ + .imm = LEN }) + +/* Short form of mov, A = X */ + +#define BPF_MOV64_REG(A, X) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .a_reg = A, \ + .x_reg = X, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV32_REG(A, X) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU | BPF_MOV | BPF_X, \ + .a_reg = A, \ + .x_reg = X, \ + .off = 0, \ + .imm = 0 }) + +/* Short form of mov, A = IMM */ + +#define BPF_MOV64_IMM(A, IMM) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .a_reg = A, \ + .x_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV32_IMM(A, IMM) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU | BPF_MOV | BPF_K, \ + .a_reg = A, \ + .x_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Short form of mov based on type, BPF_X: A = X, BPF_K: A = IMM */ + +#define BPF_MOV64_RAW(TYPE, A, X, IMM) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ + .a_reg = A, \ + .x_reg = X, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV32_RAW(TYPE, A, X, IMM) \ + ((struct sock_filter_int) { \ + .code = BPF_ALU | BPF_MOV | BPF_SRC(TYPE), \ + .a_reg = A, \ + .x_reg = X, \ + .off = 0, \ + .imm = IMM }) + +/* Direct packet access, R0 = *(uint *) (skb->data + OFF) */ + +#define BPF_LD_ABS(SIZE, OFF) \ + ((struct sock_filter_int) { \ + .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ + .a_reg = 0, \ + .x_reg = 0, \ + .off = 0, \ + .imm = OFF }) + +/* Indirect packet access, R0 = *(uint *) (skb->data + X + OFF) */ + +#define BPF_LD_IND(SIZE, X, OFF) \ + ((struct sock_filter_int) { \ + .code = BPF_LD | BPF_SIZE(SIZE) | BPF_IND, \ + .a_reg = 0, \ + .x_reg = X, \ + .off = 0, \ + .imm = OFF }) + +/* Memory store, A = *(uint *) (X + OFF), and vice versa */ + +#define BPF_LDX_MEM(SIZE, A, X, OFF) \ + ((struct sock_filter_int) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .a_reg = A, \ + .x_reg = X, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_MEM(SIZE, A, X, OFF) \ + ((struct sock_filter_int) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .a_reg = A, \ + .x_reg = X, \ + .off = OFF, \ + .imm = 0 }) + +/* Conditional jumps against registers, if (A 'op' X) goto pc + OFF */ + +#define BPF_JMP_REG(OP, A, X, OFF) \ + ((struct sock_filter_int) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .a_reg = A, \ + .x_reg = X, \ + .off = OFF, \ + .imm = 0 }) + +/* Conditional jumps against immediates, if (A 'op' IMM) goto pc + OFF */ + +#define BPF_JMP_IMM(OP, A, IMM, OFF) \ + ((struct sock_filter_int) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .a_reg = A, \ + .x_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Function call */ + +#define BPF_EMIT_CALL(FUNC) \ + ((struct sock_filter_int) { \ + .code = BPF_JMP | BPF_CALL, \ + .a_reg = 0, \ + .x_reg = 0, \ + .off = 0, \ + .imm = ((FUNC) - __bpf_call_base) }) + +/* Raw code statement block */ + +#define BPF_RAW_INSN(CODE, A, X, OFF, IMM) \ + ((struct sock_filter_int) { \ + .code = CODE, \ + .a_reg = A, \ + .x_reg = X, \ + .off = OFF, \ + .imm = IMM }) + +/* Program exit */ + +#define BPF_EXIT_INSN() \ + ((struct sock_filter_int) { \ + .code = BPF_JMP | BPF_EXIT, \ + .a_reg = 0, \ + .x_reg = 0, \ + .off = 0, \ + .imm = 0 }) + +#define bytes_to_bpf_size(bytes) \ +({ \ + int bpf_size = -EINVAL; \ + \ + if (bytes == sizeof(u8)) \ + bpf_size = BPF_B; \ + else if (bytes == sizeof(u16)) \ + bpf_size = BPF_H; \ + else if (bytes == sizeof(u32)) \ + bpf_size = BPF_W; \ + else if (bytes == sizeof(u64)) \ + bpf_size = BPF_DW; \ + \ + bpf_size; \ +}) /* Macro to invoke filter function. */ #define SK_RUN_FILTER(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) @@ -184,16 +336,14 @@ static inline unsigned int sk_filter_size(unsigned int proglen) int sk_filter(struct sock *sk, struct sk_buff *skb); -u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx, - const struct sock_filter_int *insni); -u32 sk_run_filter_int_skb(const struct sk_buff *ctx, - const struct sock_filter_int *insni); +void sk_filter_select_runtime(struct sk_filter *fp); +void sk_filter_free(struct sk_filter *fp); int sk_convert_filter(struct sock_filter *prog, int len, struct sock_filter_int *new_prog, int *new_len); int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog *fprog); + struct sock_fprog_kern *fprog); void sk_unattached_filter_destroy(struct sk_filter *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); @@ -202,11 +352,48 @@ int sk_detach_filter(struct sock *sk); int sk_chk_filter(struct sock_filter *filter, unsigned int flen); int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned int len); -void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to); void sk_filter_charge(struct sock *sk, struct sk_filter *fp); void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); +u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); +void bpf_int_jit_compile(struct sk_filter *fp); + +#define BPF_ANC BIT(15) + +static inline u16 bpf_anc_helper(const struct sock_filter *ftest) +{ + BUG_ON(ftest->code & BPF_ANC); + + switch (ftest->code) { + case BPF_LD | BPF_W | BPF_ABS: + case BPF_LD | BPF_H | BPF_ABS: + case BPF_LD | BPF_B | BPF_ABS: +#define BPF_ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE: \ + return BPF_ANC | SKF_AD_##CODE + switch (ftest->k) { + BPF_ANCILLARY(PROTOCOL); + BPF_ANCILLARY(PKTTYPE); + BPF_ANCILLARY(IFINDEX); + BPF_ANCILLARY(NLATTR); + BPF_ANCILLARY(NLATTR_NEST); + BPF_ANCILLARY(MARK); + BPF_ANCILLARY(QUEUE); + BPF_ANCILLARY(HATYPE); + BPF_ANCILLARY(RXHASH); + BPF_ANCILLARY(CPU); + BPF_ANCILLARY(ALU_XOR_X); + BPF_ANCILLARY(VLAN_TAG); + BPF_ANCILLARY(VLAN_TAG_PRESENT); + BPF_ANCILLARY(PAY_OFFSET); + BPF_ANCILLARY(RANDOM); + } + /* Fallthrough. */ + default: + return ftest->code; + } +} + #ifdef CONFIG_BPF_JIT #include <stdarg.h> #include <linux/linkage.h> @@ -226,86 +413,20 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen, } #else #include <linux/slab.h> + static inline void bpf_jit_compile(struct sk_filter *fp) { } + static inline void bpf_jit_free(struct sk_filter *fp) { kfree(fp); } -#endif +#endif /* CONFIG_BPF_JIT */ static inline int bpf_tell_extensions(void) { return SKF_AD_MAX; } -enum { - BPF_S_RET_K = 1, - BPF_S_RET_A, - BPF_S_ALU_ADD_K, - BPF_S_ALU_ADD_X, - BPF_S_ALU_SUB_K, - BPF_S_ALU_SUB_X, - BPF_S_ALU_MUL_K, - BPF_S_ALU_MUL_X, - BPF_S_ALU_DIV_X, - BPF_S_ALU_MOD_K, - BPF_S_ALU_MOD_X, - BPF_S_ALU_AND_K, - BPF_S_ALU_AND_X, - BPF_S_ALU_OR_K, - BPF_S_ALU_OR_X, - BPF_S_ALU_XOR_K, - BPF_S_ALU_XOR_X, - BPF_S_ALU_LSH_K, - BPF_S_ALU_LSH_X, - BPF_S_ALU_RSH_K, - BPF_S_ALU_RSH_X, - BPF_S_ALU_NEG, - BPF_S_LD_W_ABS, - BPF_S_LD_H_ABS, - BPF_S_LD_B_ABS, - BPF_S_LD_W_LEN, - BPF_S_LD_W_IND, - BPF_S_LD_H_IND, - BPF_S_LD_B_IND, - BPF_S_LD_IMM, - BPF_S_LDX_W_LEN, - BPF_S_LDX_B_MSH, - BPF_S_LDX_IMM, - BPF_S_MISC_TAX, - BPF_S_MISC_TXA, - BPF_S_ALU_DIV_K, - BPF_S_LD_MEM, - BPF_S_LDX_MEM, - BPF_S_ST, - BPF_S_STX, - BPF_S_JMP_JA, - BPF_S_JMP_JEQ_K, - BPF_S_JMP_JEQ_X, - BPF_S_JMP_JGE_K, - BPF_S_JMP_JGE_X, - BPF_S_JMP_JGT_K, - BPF_S_JMP_JGT_X, - BPF_S_JMP_JSET_K, - BPF_S_JMP_JSET_X, - /* Ancillary data */ - BPF_S_ANC_PROTOCOL, - BPF_S_ANC_PKTTYPE, - BPF_S_ANC_IFINDEX, - BPF_S_ANC_NLATTR, - BPF_S_ANC_NLATTR_NEST, - BPF_S_ANC_MARK, - BPF_S_ANC_QUEUE, - BPF_S_ANC_HATYPE, - BPF_S_ANC_RXHASH, - BPF_S_ANC_CPU, - BPF_S_ANC_ALU_XOR_X, - BPF_S_ANC_VLAN_TAG, - BPF_S_ANC_VLAN_TAG_PRESENT, - BPF_S_ANC_PAY_OFFSET, - BPF_S_ANC_RANDOM, -}; - #endif /* __LINUX_FILTER_H__ */ diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 5b337cf..b65166d 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -412,6 +412,16 @@ static inline spinlock_t *huge_pte_lockptr(struct hstate *h, return &mm->page_table_lock; } +static inline bool hugepages_supported(void) +{ + /* + * Some platform decide whether they support huge pages at boot + * time. On these, such as powerpc, HPAGE_SHIFT is set to 0 when + * there is no such support + */ + return HPAGE_SHIFT != 0; +} + #else /* CONFIG_HUGETLB_PAGE */ struct hstate {}; #define alloc_huge_page_node(h, nid) NULL diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f194ccb..6bff13f 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1711,6 +1711,7 @@ enum ieee80211_eid { WLAN_EID_RRM_ENABLED_CAPABILITIES = 70, WLAN_EID_MULTIPLE_BSSID = 71, WLAN_EID_BSS_COEX_2040 = 72, + WLAN_EID_BSS_INTOLERANT_CHL_REPORT = 73, WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74, WLAN_EID_RIC_DESCRIPTOR = 75, WLAN_EID_MMIE = 76, diff --git a/include/linux/if_link.h b/include/linux/if_link.h index a86784d..119130e 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -10,8 +10,9 @@ struct ifla_vf_info { __u8 mac[32]; __u32 vlan; __u32 qos; - __u32 tx_rate; __u32 spoofchk; __u32 linkstate; + __u32 min_tx_rate; + __u32 max_tx_rate; }; #endif /* _LINUX_IF_LINK_H */ diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 7c8b20b1..6b2c7cf 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -56,6 +56,10 @@ struct macvlan_dev { int numqueues; netdev_features_t tap_features; int minor; + int nest_level; +#ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; +#endif }; static inline void macvlan_count_rx(const struct macvlan_dev *vlan, diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8c0fb7f..4967916 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -73,7 +73,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb) /* found in socket.c */ extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *)); -static inline int is_vlan_dev(struct net_device *dev) +static inline bool is_vlan_dev(struct net_device *dev) { return dev->priv_flags & IFF_802_1Q_VLAN; } @@ -159,6 +159,7 @@ struct vlan_dev_priv { #ifdef CONFIG_NET_POLL_CONTROLLER struct netpoll *netpoll; #endif + unsigned int nest_level; }; static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) @@ -197,6 +198,12 @@ extern void vlan_vids_del_by_dev(struct net_device *dev, const struct net_device *by_dev); extern bool vlan_uses_dev(const struct net_device *dev); + +static inline int vlan_get_encap_level(struct net_device *dev) +{ + BUG_ON(!is_vlan_dev(dev)); + return vlan_dev_priv(dev)->nest_level; +} #else static inline struct net_device * __vlan_find_dev_deep_rcu(struct net_device *real_dev, @@ -263,6 +270,11 @@ static inline bool vlan_uses_dev(const struct net_device *dev) { return false; } +static inline int vlan_get_encap_level(struct net_device *dev) +{ + BUG(); + return 0; +} #endif static inline bool vlan_hw_offload_capable(netdev_features_t features, @@ -483,4 +495,5 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb, */ skb->protocol = htons(ETH_P_802_2); } + #endif /* !(_LINUX_IF_VLAN_H_) */ diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 97ac926..051c850 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -272,6 +272,11 @@ static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m) return -EINVAL; } +static inline int irq_force_affinity(unsigned int irq, const struct cpumask *cpumask) +{ + return 0; +} + static inline int irq_can_set_affinity(unsigned int irq) { return 0; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index b0122dc..ca1be5c 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -50,7 +50,24 @@ enum kernfs_node_flag { /* @flags for kernfs_create_root() */ enum kernfs_root_flag { - KERNFS_ROOT_CREATE_DEACTIVATED = 0x0001, + /* + * kernfs_nodes are created in the deactivated state and invisible. + * They require explicit kernfs_activate() to become visible. This + * can be used to make related nodes become visible atomically + * after all nodes are created successfully. + */ + KERNFS_ROOT_CREATE_DEACTIVATED = 0x0001, + + /* + * For regular flies, if the opener has CAP_DAC_OVERRIDE, open(2) + * succeeds regardless of the RW permissions. sysfs had an extra + * layer of enforcement where open(2) fails with -EACCES regardless + * of CAP_DAC_OVERRIDE if the permission doesn't have the + * respective read or write access at all (none of S_IRUGO or + * S_IWUGO) or the respective operation isn't implemented. The + * following flag enables that behavior. + */ + KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK = 0x0002, }; /* type-specific structures for kernfs_node union members */ diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 34a513a..a6a42dd 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -12,9 +12,9 @@ #endif #ifdef __cplusplus -#define CPP_ASMLINKAGE extern "C" __visible +#define CPP_ASMLINKAGE extern "C" #else -#define CPP_ASMLINKAGE __visible +#define CPP_ASMLINKAGE #endif #ifndef asmlinkage diff --git a/include/linux/mfd/rtsx_common.h b/include/linux/mfd/rtsx_common.h index 7c36cc5..443176e 100644 --- a/include/linux/mfd/rtsx_common.h +++ b/include/linux/mfd/rtsx_common.h @@ -45,7 +45,6 @@ struct platform_device; struct rtsx_slot { struct platform_device *p_dev; void (*card_event)(struct platform_device *p_dev); - void (*done_transfer)(struct platform_device *p_dev); }; #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 8d6bbd6..a383597 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -943,12 +943,6 @@ void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr); int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout); int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, int num_sg, bool read, int timeout); -int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read); -int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read); -int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int sg_count, bool read); int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len); int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len); int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index c0468e6..ca38871 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -449,7 +449,6 @@ struct mlx4_caps { int reserved_qps_base[MLX4_NUM_QP_REGION]; int log_num_macs; int log_num_vlans; - int log_num_prios; enum mlx4_port_type port_type[MLX4_MAX_PORTS + 1]; u8 supported_type[MLX4_MAX_PORTS + 1]; u8 suggested_type[MLX4_MAX_PORTS + 1]; diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index b66e761..7040dc9 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -421,6 +421,17 @@ struct mlx4_wqe_inline_seg { __be32 byte_count; }; +enum mlx4_update_qp_attr { + MLX4_UPDATE_QP_SMAC = 1 << 0, +}; + +struct mlx4_update_qp_params { + u8 smac_index; +}; + +int mlx4_update_qp(struct mlx4_dev *dev, struct mlx4_qp *qp, + enum mlx4_update_qp_attr attr, + struct mlx4_update_qp_params *params); int mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, enum mlx4_qp_state cur_state, enum mlx4_qp_state new_state, struct mlx4_qp_context *context, enum mlx4_qp_optpar optpar, diff --git a/include/linux/mm.h b/include/linux/mm.h index bf9811e..d677706 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -370,6 +370,8 @@ static inline int is_vmalloc_or_module_addr(const void *x) } #endif +extern void kvfree(const void *addr); + static inline void compound_lock(struct page *page) { #ifdef CONFIG_TRANSPARENT_HUGEPAGE diff --git a/include/linux/net.h b/include/linux/net.h index 94734a6..17d8339 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -248,24 +248,17 @@ do { \ bool __net_get_random_once(void *buf, int nbytes, bool *done, struct static_key *done_key); -#ifdef HAVE_JUMP_LABEL -#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \ - { .enabled = ATOMIC_INIT(0), .entries = (void *)1 }) -#else /* !HAVE_JUMP_LABEL */ -#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE -#endif /* HAVE_JUMP_LABEL */ - #define net_get_random_once(buf, nbytes) \ ({ \ bool ___ret = false; \ static bool ___done = false; \ - static struct static_key ___done_key = \ - ___NET_RANDOM_STATIC_KEY_INIT; \ - if (!static_key_true(&___done_key)) \ + static struct static_key ___once_key = \ + STATIC_KEY_INIT_TRUE; \ + if (static_key_true(&___once_key)) \ ___ret = __net_get_random_once(buf, \ nbytes, \ &___done, \ - &___done_key); \ + &___once_key); \ ___ret; \ }) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2dea98c..774e539 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -850,7 +850,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * SR-IOV management functions. * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac); * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos); - * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate); + * int (*ndo_set_vf_rate)(struct net_device *dev, int vf, int min_tx_rate, + * int max_tx_rate); * int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_config)(struct net_device *dev, * int vf, struct ifla_vf_info *ivf); @@ -1044,8 +1045,9 @@ struct net_device_ops { int queue, u8 *mac); int (*ndo_set_vf_vlan)(struct net_device *dev, int queue, u16 vlan, u8 qos); - int (*ndo_set_vf_tx_rate)(struct net_device *dev, - int vf, int rate); + int (*ndo_set_vf_rate)(struct net_device *dev, + int vf, int min_tx_rate, + int max_tx_rate); int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting); int (*ndo_get_vf_config)(struct net_device *dev, @@ -1141,6 +1143,7 @@ struct net_device_ops { netdev_tx_t (*ndo_dfwd_start_xmit) (struct sk_buff *skb, struct net_device *dev, void *priv); + int (*ndo_get_lock_subclass)(struct net_device *dev); }; /** @@ -2948,7 +2951,12 @@ static inline void netif_addr_lock(struct net_device *dev) static inline void netif_addr_lock_nested(struct net_device *dev) { - spin_lock_nested(&dev->addr_list_lock, SINGLE_DEPTH_NESTING); + int subclass = SINGLE_DEPTH_NESTING; + + if (dev->netdev_ops->ndo_get_lock_subclass) + subclass = dev->netdev_ops->ndo_get_lock_subclass(dev); + + spin_lock_nested(&dev->addr_list_lock, subclass); } static inline void netif_addr_lock_bh(struct net_device *dev) @@ -2995,6 +3003,15 @@ int __hw_addr_sync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); +int __hw_addr_sync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*sync)(struct net_device *, const unsigned char *), + int (*unsync)(struct net_device *, + const unsigned char *)); +void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*unsync)(struct net_device *, + const unsigned char *)); void __hw_addr_init(struct netdev_hw_addr_list *list); /* Functions used for device addresses handling */ @@ -3015,6 +3032,38 @@ void dev_uc_unsync(struct net_device *to, struct net_device *from); void dev_uc_flush(struct net_device *dev); void dev_uc_init(struct net_device *dev); +/** + * __dev_uc_sync - Synchonize device's unicast list + * @dev: device to sync + * @sync: function to call if address should be added + * @unsync: function to call if address should be removed + * + * Add newly added addresses to the interface, and release + * addresses that have been deleted. + **/ +static inline int __dev_uc_sync(struct net_device *dev, + int (*sync)(struct net_device *, + const unsigned char *), + int (*unsync)(struct net_device *, + const unsigned char *)) +{ + return __hw_addr_sync_dev(&dev->uc, dev, sync, unsync); +} + +/** + * __dev_uc_unsync - Remove synchonized addresses from device + * @dev: device to sync + * @unsync: function to call if address should be removed + * + * Remove all addresses that were added to the device by dev_uc_sync(). + **/ +static inline void __dev_uc_unsync(struct net_device *dev, + int (*unsync)(struct net_device *, + const unsigned char *)) +{ + __hw_addr_unsync_dev(&dev->uc, dev, unsync); +} + /* Functions used for multicast addresses handling */ int dev_mc_add(struct net_device *dev, const unsigned char *addr); int dev_mc_add_global(struct net_device *dev, const unsigned char *addr); @@ -3027,6 +3076,38 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from); void dev_mc_flush(struct net_device *dev); void dev_mc_init(struct net_device *dev); +/** + * __dev_mc_sync - Synchonize device's multicast list + * @dev: device to sync + * @sync: function to call if address should be added + * @unsync: function to call if address should be removed + * + * Add newly added addresses to the interface, and release + * addresses that have been deleted. + **/ +static inline int __dev_mc_sync(struct net_device *dev, + int (*sync)(struct net_device *, + const unsigned char *), + int (*unsync)(struct net_device *, + const unsigned char *)) +{ + return __hw_addr_sync_dev(&dev->mc, dev, sync, unsync); +} + +/** + * __dev_mc_unsync - Remove synchonized addresses from device + * @dev: device to sync + * @unsync: function to call if address should be removed + * + * Remove all addresses that were added to the device by dev_mc_sync(). + **/ +static inline void __dev_mc_unsync(struct net_device *dev, + int (*unsync)(struct net_device *, + const unsigned char *)) +{ + __hw_addr_unsync_dev(&dev->mc, dev, unsync); +} + /* Functions used for secondary unicast and multicast support */ void dev_set_rx_mode(struct net_device *dev); void __dev_set_rx_mode(struct net_device *dev); @@ -3048,10 +3129,19 @@ extern int weight_p; extern int bpf_jit_enable; bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev); +struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter); struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, struct list_head **iter); /* iterate through upper list, must be called under RCU read lock */ +#define netdev_for_each_upper_dev_rcu(dev, updev, iter) \ + for (iter = &(dev)->adj_list.upper, \ + updev = netdev_upper_get_next_dev_rcu(dev, &(iter)); \ + updev; \ + updev = netdev_upper_get_next_dev_rcu(dev, &(iter))) + +/* iterate through upper list, must be called under RCU read lock */ #define netdev_for_each_all_upper_dev_rcu(dev, updev, iter) \ for (iter = &(dev)->all_adj_list.upper, \ updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)); \ @@ -3075,6 +3165,14 @@ void *netdev_lower_get_next_private_rcu(struct net_device *dev, priv; \ priv = netdev_lower_get_next_private_rcu(dev, &(iter))) +void *netdev_lower_get_next(struct net_device *dev, + struct list_head **iter); +#define netdev_for_each_lower_dev(dev, ldev, iter) \ + for (iter = &(dev)->adj_list.lower, \ + ldev = netdev_lower_get_next(dev, &(iter)); \ + ldev; \ + ldev = netdev_lower_get_next(dev, &(iter))) + void *netdev_adjacent_get_private(struct list_head *adj_list); void *netdev_lower_get_first_private_rcu(struct net_device *dev); struct net_device *netdev_master_upper_dev_get(struct net_device *dev); @@ -3090,6 +3188,8 @@ void netdev_upper_dev_unlink(struct net_device *dev, void netdev_adjacent_rename_links(struct net_device *dev, char *oldname); void *netdev_lower_dev_get_private(struct net_device *dev, struct net_device *lower_dev); +int dev_get_nest_level(struct net_device *dev, + bool (*type_check)(struct net_device *dev)); int skb_checksum_help(struct sk_buff *skb); struct sk_buff *__skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path); @@ -3153,6 +3253,20 @@ const char *netdev_drivername(const struct net_device *dev); void linkwatch_run_queue(void); +static inline netdev_features_t netdev_intersect_features(netdev_features_t f1, + netdev_features_t f2) +{ + if (f1 & NETIF_F_GEN_CSUM) + f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + if (f2 & NETIF_F_GEN_CSUM) + f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + f1 &= f2; + if (f1 & NETIF_F_GEN_CSUM) + f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + + return f1; +} + static inline netdev_features_t netdev_get_wanted_features( struct net_device *dev) { diff --git a/include/linux/netfilter/nfnetlink_acct.h b/include/linux/netfilter/nfnetlink_acct.h index b2e85e5..6ec9757 100644 --- a/include/linux/netfilter/nfnetlink_acct.h +++ b/include/linux/netfilter/nfnetlink_acct.h @@ -3,11 +3,17 @@ #include <uapi/linux/netfilter/nfnetlink_acct.h> +enum { + NFACCT_NO_QUOTA = -1, + NFACCT_UNDERQUOTA, + NFACCT_OVERQUOTA, +}; struct nf_acct; struct nf_acct *nfnl_acct_find_get(const char *filter_name); void nfnl_acct_put(struct nf_acct *acct); void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct); - +extern int nfnl_acct_overquota(const struct sk_buff *skb, + struct nf_acct *nfacct); #endif /* _NFNL_ACCT_H */ diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index c8d7f39..20163b9 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -80,6 +80,22 @@ enum { IEEE802154_ATTR_FRAME_RETRIES, + IEEE802154_ATTR_LLSEC_ENABLED, + IEEE802154_ATTR_LLSEC_SECLEVEL, + IEEE802154_ATTR_LLSEC_KEY_MODE, + IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT, + IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED, + IEEE802154_ATTR_LLSEC_KEY_ID, + IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + IEEE802154_ATTR_LLSEC_KEY_BYTES, + IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES, + IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, + IEEE802154_ATTR_LLSEC_FRAME_TYPE, + IEEE802154_ATTR_LLSEC_CMD_FRAME_ID, + IEEE802154_ATTR_LLSEC_SECLEVELS, + IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, + IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, + __IEEE802154_ATTR_MAX, }; @@ -134,6 +150,21 @@ enum { IEEE802154_SET_MACPARAMS, + IEEE802154_LLSEC_GETPARAMS, + IEEE802154_LLSEC_SETPARAMS, + IEEE802154_LLSEC_LIST_KEY, + IEEE802154_LLSEC_ADD_KEY, + IEEE802154_LLSEC_DEL_KEY, + IEEE802154_LLSEC_LIST_DEV, + IEEE802154_LLSEC_ADD_DEV, + IEEE802154_LLSEC_DEL_DEV, + IEEE802154_LLSEC_LIST_DEVKEY, + IEEE802154_LLSEC_ADD_DEVKEY, + IEEE802154_LLSEC_DEL_DEVKEY, + IEEE802154_LLSEC_LIST_SECLEVEL, + IEEE802154_LLSEC_ADD_SECLEVEL, + IEEE802154_LLSEC_DEL_SECLEVEL, + __IEEE802154_CMD_MAX, }; diff --git a/include/linux/of.h b/include/linux/of.h index 3bad8d1..e6f0988 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -349,7 +349,7 @@ int of_device_is_stdout_path(struct device_node *dn); #else /* CONFIG_OF */ -static inline const char* of_node_full_name(struct device_node *np) +static inline const char* of_node_full_name(const struct device_node *np) { return "<no-node>"; } diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 881a7c3..a70c949 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -22,12 +22,12 @@ extern struct phy_device *of_phy_connect(struct net_device *dev, struct phy_device *of_phy_attach(struct net_device *dev, struct device_node *phy_np, u32 flags, phy_interface_t iface); -extern struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, - void (*hndlr)(struct net_device *), - phy_interface_t iface); extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); +extern void of_mdiobus_link_phydev(struct mii_bus *mdio, + struct phy_device *phydev); + #else /* CONFIG_OF */ static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { @@ -59,17 +59,30 @@ static inline struct phy_device *of_phy_attach(struct net_device *dev, return NULL; } -static inline struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, - void (*hndlr)(struct net_device *), - phy_interface_t iface) +static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) { return NULL; } -static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) +static inline void of_mdiobus_link_phydev(struct mii_bus *mdio, + struct phy_device *phydev) { - return NULL; } #endif /* CONFIG_OF */ +#if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY) +extern int of_phy_register_fixed_link(struct device_node *np); +extern bool of_phy_is_fixed_link(struct device_node *np); +#else +static inline int of_phy_register_fixed_link(struct device_node *np) +{ + return -ENOSYS; +} +static inline bool of_phy_is_fixed_link(struct device_node *np) +{ + return false; +} +#endif + + #endif /* __LINUX_OF_MDIO_H */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3356abc..3ef6ea1 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -402,6 +402,8 @@ struct perf_event { struct ring_buffer *rb; struct list_head rb_entry; + unsigned long rcu_batches; + int rcu_pending; /* poll related */ wait_queue_head_t waitq; diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 509d8f5..4f2478b 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -9,15 +9,26 @@ struct fixed_phy_status { int asym_pause; }; +struct device_node; + #ifdef CONFIG_FIXED_PHY extern int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status); +extern int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np); #else static inline int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status) { return -ENODEV; } +static inline int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np) +{ + return -ENODEV; +} #endif /* CONFIG_FIXED_PHY */ /* diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h new file mode 100644 index 0000000..1730312 --- /dev/null +++ b/include/linux/platform_data/st21nfca.h @@ -0,0 +1,32 @@ +/* + * Driver include for the ST21NFCA NFC chip. + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _ST21NFCA_HCI_H_ +#define _ST21NFCA_HCI_H_ + +#include <linux/i2c.h> + +#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci" + +struct st21nfca_nfc_platform_data { + unsigned int gpio_irq; + unsigned int gpio_ena; + unsigned int irq_polarity; +}; + +#endif /* _ST21NFCA_HCI_H_ */ diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h index 4d09f6e..20bcb55 100644 --- a/include/linux/rfkill-gpio.h +++ b/include/linux/rfkill-gpio.h @@ -27,21 +27,11 @@ * struct rfkill_gpio_platform_data - platform data for rfkill gpio device. * for unused gpio's, the expected value is -1. * @name: name for the gpio rf kill instance - * @reset_gpio: GPIO which is used for reseting rfkill switch - * @shutdown_gpio: GPIO which is used for shutdown of rfkill switch - * @power_clk_name: [optional] name of clk to turn off while blocked - * @gpio_runtime_close: clean up platform specific gpio configuration - * @gpio_runtime_setup: set up platform specific gpio configuration */ struct rfkill_gpio_platform_data { char *name; - int reset_gpio; - int shutdown_gpio; - const char *power_clk_name; enum rfkill_type type; - void (*gpio_runtime_close)(struct platform_device *); - int (*gpio_runtime_setup)(struct platform_device *); }; #endif /* __RFKILL_GPIO_H */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 8e3e66a..953937e 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -4,6 +4,7 @@ #include <linux/mutex.h> #include <linux/netdevice.h> +#include <linux/wait.h> #include <uapi/linux/rtnetlink.h> extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); @@ -22,6 +23,10 @@ extern void rtnl_lock(void); extern void rtnl_unlock(void); extern int rtnl_trylock(void); extern int rtnl_is_locked(void); + +extern wait_queue_head_t netdev_unregistering_wq; +extern struct mutex net_mutex; + #ifdef CONFIG_PROVE_LOCKING extern int lockdep_rtnl_is_held(void); #else diff --git a/include/linux/sched.h b/include/linux/sched.h index 25f54c7..221b2bd 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -220,7 +220,7 @@ print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq); #define TASK_PARKED 512 #define TASK_STATE_MAX 1024 -#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP" +#define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWP" extern char ___assert_task_state[1 - 2*!!( sizeof(TASK_STATE_TO_CHAR_STR)-1 != ilog2(TASK_STATE_MAX)+1)]; @@ -1153,9 +1153,12 @@ struct sched_dl_entity { * * @dl_boosted tells if we are boosted due to DI. If so we are * outside bandwidth enforcement mechanism (but only until we - * exit the critical section). + * exit the critical section); + * + * @dl_yielded tells if task gave up the cpu before consuming + * all its available runtime during the last job. */ - int dl_throttled, dl_new, dl_boosted; + int dl_throttled, dl_new, dl_boosted, dl_yielded; /* * Bandwidth enforcement timer. Each -deadline task has its diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index f2f7398..d82abd4 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -101,4 +101,13 @@ struct kmem_cache { struct kmem_cache_node *node[MAX_NUMNODES]; }; +#ifdef CONFIG_SYSFS +#define SLAB_SUPPORTS_SYSFS +void sysfs_slab_remove(struct kmem_cache *); +#else +static inline void sysfs_slab_remove(struct kmem_cache *s) +{ +} +#endif + #endif /* _LINUX_SLUB_DEF_H */ diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 07ef9b8..4568a5c 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -33,6 +33,7 @@ struct ssb_sprom { u8 et1phyaddr; /* MII address for enet1 */ u8 et0mdcport; /* MDIO for enet0 */ u8 et1mdcport; /* MDIO for enet1 */ + u16 dev_id; /* Device ID overriding e.g. PCI ID */ u16 board_rev; /* Board revision number from SPROM. */ u16 board_num; /* Board number from SPROM. */ u16 board_type; /* Board type from SPROM. */ diff --git a/include/linux/tcp.h b/include/linux/tcp.h index bc35e470..a051321 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -197,7 +197,8 @@ struct tcp_sock { u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ - syn_data_acked:1;/* data in SYN is acked by SYN-ACK */ + syn_data_acked:1,/* data in SYN is acked by SYN-ACK */ + is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */ u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ /* RTT measurement */ @@ -209,6 +210,8 @@ struct tcp_sock { u32 packets_out; /* Packets which are "in flight" */ u32 retrans_out; /* Retransmitted packets out */ + u32 max_packets_out; /* max packets_out in last window */ + u32 max_packets_seq; /* right edge of max_packets_out flight */ u16 urg_data; /* Saved octet of OOB data and control flags */ u8 ecn_flags; /* ECN status bits. */ @@ -230,7 +233,6 @@ struct tcp_sock { u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ u32 snd_cwnd_used; u32 snd_cwnd_stamp; - u32 lsnd_pending; /* packets inflight or unsent since last xmit */ u32 prior_cwnd; /* Congestion window at start of Recovery. */ u32 prr_delivered; /* Number of newly delivered packets to * receiver in Recovery. */ diff --git a/include/linux/udp.h b/include/linux/udp.h index 42278bb..247cfdc 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -47,7 +47,9 @@ struct udp_sock { #define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node int pending; /* Any pending frames ? */ unsigned int corkflag; /* Cork is required */ - __u16 encap_type; /* Is this an Encapsulation socket? */ + __u8 encap_type; /* Is this an Encapsulation socket? */ + unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ + no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */ /* * Following member retains the information to create a UDP header * when the socket is uncorked. @@ -76,6 +78,26 @@ static inline struct udp_sock *udp_sk(const struct sock *sk) return (struct udp_sock *)sk; } +static inline void udp_set_no_check6_tx(struct sock *sk, bool val) +{ + udp_sk(sk)->no_check6_tx = val; +} + +static inline void udp_set_no_check6_rx(struct sock *sk, bool val) +{ + udp_sk(sk)->no_check6_rx = val; +} + +static inline bool udp_get_no_check6_tx(struct sock *sk) +{ + return udp_sk(sk)->no_check6_tx; +} + +static inline bool udp_get_no_check6_rx(struct sock *sk) +{ + return udp_sk(sk)->no_check6_rx; +} + #define udp_portaddr_for_each_entry(__sk, node, list) \ hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node) diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 55b6fee..7c9b484 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -52,6 +52,10 @@ #define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */ #define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */ +/* Initial NTB length */ +#define CDC_NCM_NTB_DEF_SIZE_TX 16384 /* bytes */ +#define CDC_NCM_NTB_DEF_SIZE_RX 16384 /* bytes */ + /* Minimum value for MaxDatagramSize, ch. 6.2.9 */ #define CDC_NCM_MIN_DATAGRAM_SIZE 1514 /* bytes */ @@ -72,16 +76,9 @@ /* Restart the timer, if amount of datagrams is less than given value */ #define CDC_NCM_RESTART_TIMER_DATAGRAM_CNT 3 #define CDC_NCM_TIMER_PENDING_CNT 2 -#define CDC_NCM_TIMER_INTERVAL (400UL * NSEC_PER_USEC) - -/* The following macro defines the minimum header space */ -#define CDC_NCM_MIN_HDR_SIZE \ - (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \ - (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) - -#define CDC_NCM_NDP_SIZE \ - (sizeof(struct usb_cdc_ncm_ndp16) + \ - (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) +#define CDC_NCM_TIMER_INTERVAL_USEC 400UL +#define CDC_NCM_TIMER_INTERVAL_MIN 5UL +#define CDC_NCM_TIMER_INTERVAL_MAX (U32_MAX / NSEC_PER_USEC) #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) @@ -107,6 +104,9 @@ struct cdc_ncm_ctx { spinlock_t mtx; atomic_t stop; + u32 timer_interval; + u32 max_ndp_size; + u32 tx_timer_pending; u32 tx_curr_frame_num; u32 rx_max; @@ -118,7 +118,18 @@ struct cdc_ncm_ctx { u16 tx_ndp_modulus; u16 tx_seq; u16 rx_seq; - u16 connected; + u16 min_tx_pkt; + + /* statistics */ + u32 tx_curr_frame_payload; + u32 tx_reason_ntb_full; + u32 tx_reason_ndp_full; + u32 tx_reason_timeout; + u32 tx_reason_max_datagram; + u64 tx_overhead; + u64 tx_ntbs; + u64 rx_overhead; + u64 rx_ntbs; }; u8 cdc_ncm_select_altsetting(struct usb_interface *intf); diff --git a/include/net/af_ieee802154.h b/include/net/af_ieee802154.h index f79ae2a..085940f 100644 --- a/include/net/af_ieee802154.h +++ b/include/net/af_ieee802154.h @@ -57,6 +57,14 @@ struct sockaddr_ieee802154 { /* get/setsockopt */ #define SOL_IEEE802154 0 -#define WPAN_WANTACK 0 +#define WPAN_WANTACK 0 +#define WPAN_SECURITY 1 +#define WPAN_SECURITY_LEVEL 2 + +#define WPAN_SECURITY_DEFAULT 0 +#define WPAN_SECURITY_OFF 1 +#define WPAN_SECURITY_ON 2 + +#define WPAN_SECURITY_LEVEL_DEFAULT (-1) #endif diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4261a67..16587dc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1054,6 +1054,17 @@ struct hci_cp_write_page_scan_activity { __le16 window; } __packed; +#define HCI_OP_READ_TX_POWER 0x0c2d +struct hci_cp_read_tx_power { + __le16 handle; + __u8 type; +} __packed; +struct hci_rp_read_tx_power { + __u8 status; + __le16 handle; + __s8 tx_power; +} __packed; + #define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46 struct hci_rp_read_page_scan_type { __u8 status; @@ -1064,6 +1075,16 @@ struct hci_rp_read_page_scan_type { #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 +#define HCI_OP_READ_RSSI 0x1405 +struct hci_cp_read_rssi { + __le16 handle; +} __packed; +struct hci_rp_read_rssi { + __u8 status; + __le16 handle; + __s8 rssi; +} __packed; + #define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 struct hci_rp_read_local_amp_info { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d73f418..b386bf1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -145,6 +145,10 @@ struct oob_data { /* Default LE RPA expiry time, 15 minutes */ #define HCI_DEFAULT_RPA_TIMEOUT (15 * 60) +/* Default min/max age of connection information (1s/3s) */ +#define DEFAULT_CONN_INFO_MIN_AGE 1000 +#define DEFAULT_CONN_INFO_MAX_AGE 3000 + struct amp_assoc { __u16 len; __u16 offset; @@ -200,6 +204,8 @@ struct hci_dev { __u16 le_conn_min_interval; __u16 le_conn_max_interval; __u16 discov_interleaved_timeout; + __u16 conn_info_min_age; + __u16 conn_info_max_age; __u8 ssp_debug_mode; __u16 devid_source; @@ -374,8 +380,13 @@ struct hci_conn { __u16 setting; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __s8 rssi; + __s8 tx_power; + __s8 max_tx_power; unsigned long flags; + unsigned long conn_info_timestamp; + __u8 remote_cap; __u8 remote_auth; __u8 remote_id; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d4b571c..bcffc9a 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -181,6 +181,9 @@ struct mgmt_cp_load_link_keys { } __packed; #define MGMT_LOAD_LINK_KEYS_SIZE 3 +#define MGMT_LTK_UNAUTHENTICATED 0x00 +#define MGMT_LTK_AUTHENTICATED 0x01 + struct mgmt_ltk_info { struct mgmt_addr_info addr; __u8 type; @@ -409,6 +412,18 @@ struct mgmt_cp_load_irks { } __packed; #define MGMT_LOAD_IRKS_SIZE 2 +#define MGMT_OP_GET_CONN_INFO 0x0031 +struct mgmt_cp_get_conn_info { + struct mgmt_addr_info addr; +} __packed; +#define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE +struct mgmt_rp_get_conn_info { + struct mgmt_addr_info addr; + __s8 rssi; + __s8 tx_power; + __s8 max_tx_power; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 2611cc3..578b831 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -173,7 +173,7 @@ struct rfcomm_dlc { struct sk_buff_head tx_queue; struct timer_list timer; - spinlock_t lock; + struct mutex lock; unsigned long state; unsigned long flags; atomic_t refcnt; @@ -244,8 +244,8 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); void rfcomm_dlc_accept(struct rfcomm_dlc *d); struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel); -#define rfcomm_dlc_lock(d) spin_lock(&d->lock) -#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) +#define rfcomm_dlc_lock(d) mutex_lock(&d->lock) +#define rfcomm_dlc_unlock(d) mutex_unlock(&d->lock) static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) { diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f2c3186..e46c437 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -341,8 +341,8 @@ struct vif_params { * @seq_len: length of @seq. */ struct key_params { - u8 *key; - u8 *seq; + const u8 *key; + const u8 *seq; int key_len; int seq_len; u32 cipher; @@ -458,7 +458,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, */ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, - enum nl80211_iftype); + enum nl80211_iftype iftype); /** * ieee80211_chandef_rate_flags - returns rate flags for a channel @@ -694,8 +694,10 @@ struct cfg80211_ap_settings { * * @chandef: defines the channel to use after the switch * @beacon_csa: beacon data while performing the switch - * @counter_offset_beacon: offset for the counter within the beacon (tail) - * @counter_offset_presp: offset for the counter within the probe response + * @counter_offsets_beacon: offsets of the counters within the beacon (tail) + * @counter_offsets_presp: offsets of the counters within the probe response + * @n_counter_offsets_beacon: number of csa counters the beacon (tail) + * @n_counter_offsets_presp: number of csa counters in the probe response * @beacon_after: beacon data to be used on the new channel * @radar_required: whether radar detection is required on the new channel * @block_tx: whether transmissions should be blocked while changing @@ -704,7 +706,10 @@ struct cfg80211_ap_settings { struct cfg80211_csa_settings { struct cfg80211_chan_def chandef; struct cfg80211_beacon_data beacon_csa; - u16 counter_offset_beacon, counter_offset_presp; + const u16 *counter_offsets_beacon; + const u16 *counter_offsets_presp; + unsigned int n_counter_offsets_beacon; + unsigned int n_counter_offsets_presp; struct cfg80211_beacon_data beacon_after; bool radar_required; bool block_tx; @@ -868,36 +873,38 @@ int cfg80211_check_station_change(struct wiphy *wiphy, * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled + * @STATION_INFO_EXPECTED_THROUGHPUT: @expected_throughput filled */ enum station_info_flags { - STATION_INFO_INACTIVE_TIME = 1<<0, - STATION_INFO_RX_BYTES = 1<<1, - STATION_INFO_TX_BYTES = 1<<2, - STATION_INFO_LLID = 1<<3, - STATION_INFO_PLID = 1<<4, - STATION_INFO_PLINK_STATE = 1<<5, - STATION_INFO_SIGNAL = 1<<6, - STATION_INFO_TX_BITRATE = 1<<7, - STATION_INFO_RX_PACKETS = 1<<8, - STATION_INFO_TX_PACKETS = 1<<9, - STATION_INFO_TX_RETRIES = 1<<10, - STATION_INFO_TX_FAILED = 1<<11, - STATION_INFO_RX_DROP_MISC = 1<<12, - STATION_INFO_SIGNAL_AVG = 1<<13, - STATION_INFO_RX_BITRATE = 1<<14, - STATION_INFO_BSS_PARAM = 1<<15, - STATION_INFO_CONNECTED_TIME = 1<<16, - STATION_INFO_ASSOC_REQ_IES = 1<<17, - STATION_INFO_STA_FLAGS = 1<<18, - STATION_INFO_BEACON_LOSS_COUNT = 1<<19, - STATION_INFO_T_OFFSET = 1<<20, - STATION_INFO_LOCAL_PM = 1<<21, - STATION_INFO_PEER_PM = 1<<22, - STATION_INFO_NONPEER_PM = 1<<23, - STATION_INFO_RX_BYTES64 = 1<<24, - STATION_INFO_TX_BYTES64 = 1<<25, - STATION_INFO_CHAIN_SIGNAL = 1<<26, - STATION_INFO_CHAIN_SIGNAL_AVG = 1<<27, + STATION_INFO_INACTIVE_TIME = BIT(0), + STATION_INFO_RX_BYTES = BIT(1), + STATION_INFO_TX_BYTES = BIT(2), + STATION_INFO_LLID = BIT(3), + STATION_INFO_PLID = BIT(4), + STATION_INFO_PLINK_STATE = BIT(5), + STATION_INFO_SIGNAL = BIT(6), + STATION_INFO_TX_BITRATE = BIT(7), + STATION_INFO_RX_PACKETS = BIT(8), + STATION_INFO_TX_PACKETS = BIT(9), + STATION_INFO_TX_RETRIES = BIT(10), + STATION_INFO_TX_FAILED = BIT(11), + STATION_INFO_RX_DROP_MISC = BIT(12), + STATION_INFO_SIGNAL_AVG = BIT(13), + STATION_INFO_RX_BITRATE = BIT(14), + STATION_INFO_BSS_PARAM = BIT(15), + STATION_INFO_CONNECTED_TIME = BIT(16), + STATION_INFO_ASSOC_REQ_IES = BIT(17), + STATION_INFO_STA_FLAGS = BIT(18), + STATION_INFO_BEACON_LOSS_COUNT = BIT(19), + STATION_INFO_T_OFFSET = BIT(20), + STATION_INFO_LOCAL_PM = BIT(21), + STATION_INFO_PEER_PM = BIT(22), + STATION_INFO_NONPEER_PM = BIT(23), + STATION_INFO_RX_BYTES64 = BIT(24), + STATION_INFO_TX_BYTES64 = BIT(25), + STATION_INFO_CHAIN_SIGNAL = BIT(26), + STATION_INFO_CHAIN_SIGNAL_AVG = BIT(27), + STATION_INFO_EXPECTED_THROUGHPUT = BIT(28), }; /** @@ -1019,6 +1026,8 @@ struct sta_bss_parameters { * @local_pm: local mesh STA power save mode * @peer_pm: peer mesh STA power save mode * @nonpeer_pm: non-peer mesh STA power save mode + * @expected_throughput: expected throughput in kbps (including 802.11 headers) + * towards this station. */ struct station_info { u32 filled; @@ -1057,6 +1066,8 @@ struct station_info { enum nl80211_mesh_power_mode peer_pm; enum nl80211_mesh_power_mode nonpeer_pm; + u32 expected_throughput; + /* * Note: Add a new enum station_info_flags value for each new field and * use it to check which fields are initialized. @@ -1064,6 +1075,19 @@ struct station_info { }; /** + * cfg80211_get_station - retrieve information about a given station + * @dev: the device where the station is supposed to be connected to + * @mac_addr: the mac address of the station of interest + * @sinfo: pointer to the structure to fill with the information + * + * Returns 0 on success and sinfo is filled with the available information + * otherwise returns a negative error code and the content of sinfo has to be + * considered undefined. + */ +int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo); + +/** * enum monitor_flags - monitor flags * * Monitor interface configuration flags. Note that these must be the bits @@ -1164,7 +1188,7 @@ struct bss_parameters { int use_cts_prot; int use_short_preamble; int use_short_slot_time; - u8 *basic_rates; + const u8 *basic_rates; u8 basic_rates_len; int ap_isolate; int ht_opmode; @@ -1694,10 +1718,10 @@ struct cfg80211_disassoc_request { * @ht_capa_mask: The bits of ht_capa which are to be used. */ struct cfg80211_ibss_params { - u8 *ssid; - u8 *bssid; + const u8 *ssid; + const u8 *bssid; struct cfg80211_chan_def chandef; - u8 *ie; + const u8 *ie; u8 ssid_len, ie_len; u16 beacon_interval; u32 basic_rates; @@ -1806,8 +1830,8 @@ struct cfg80211_bitrate_mask { * @pmkid: The PMK material itself. */ struct cfg80211_pmksa { - u8 *bssid; - u8 *pmkid; + const u8 *bssid; + const u8 *pmkid; }; /** @@ -1822,7 +1846,7 @@ struct cfg80211_pmksa { * memory, free @mask only! */ struct cfg80211_pkt_pattern { - u8 *mask, *pattern; + const u8 *mask, *pattern; int pattern_len; int pkt_offset; }; @@ -1986,6 +2010,8 @@ struct cfg80211_update_ft_ies_params { * @len: buffer length * @no_cck: don't use cck rates for this frame * @dont_wait_for_ack: tells the low level not to wait for an ack + * @n_csa_offsets: length of csa_offsets array + * @csa_offsets: array of all the csa offsets in the frame */ struct cfg80211_mgmt_tx_params { struct ieee80211_channel *chan; @@ -1995,6 +2021,8 @@ struct cfg80211_mgmt_tx_params { size_t len; bool no_cck; bool dont_wait_for_ack; + int n_csa_offsets; + const u16 *csa_offsets; }; /** @@ -2336,28 +2364,29 @@ struct cfg80211_ops { int (*add_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params); + const u8 *mac, + struct station_parameters *params); int (*del_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac); + const u8 *mac); int (*change_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params); + const u8 *mac, + struct station_parameters *params); int (*get_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo); + const u8 *mac, struct station_info *sinfo); int (*dump_station)(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo); + int idx, u8 *mac, struct station_info *sinfo); int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop); + const u8 *dst, const u8 *next_hop); int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst); + const u8 *dst); int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop); + const u8 *dst, const u8 *next_hop); int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop, - struct mpath_info *pinfo); + u8 *dst, u8 *next_hop, struct mpath_info *pinfo); int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *dst, u8 *next_hop, - struct mpath_info *pinfo); + int idx, u8 *dst, u8 *next_hop, + struct mpath_info *pinfo); int (*get_mesh_config)(struct wiphy *wiphy, struct net_device *dev, struct mesh_config *conf); @@ -2487,11 +2516,11 @@ struct cfg80211_ops { struct cfg80211_gtk_rekey_data *data); int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation oper); + const u8 *peer, enum nl80211_tdls_operation oper); int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie); @@ -2638,6 +2667,7 @@ struct ieee80211_iface_limit { * between infrastructure and AP types must match. This is required * only in special cases. * @radar_detect_widths: bitmap of channel widths supported for radar detection + * @radar_detect_regions: bitmap of regions supported for radar detection * * With this structure the driver can describe which interface * combinations it supports concurrently. @@ -2695,6 +2725,7 @@ struct ieee80211_iface_combination { u8 n_limits; bool beacon_int_infra_match; u8 radar_detect_widths; + u8 radar_detect_regions; }; struct ieee80211_txrx_stypes { @@ -2925,6 +2956,17 @@ struct wiphy_vendor_command { * (including P2P GO) or 0 to indicate no such limit is advertised. The * driver is allowed to advertise a theoretical limit that it can reach in * some cases, but may not always reach. + * + * @max_num_csa_counters: Number of supported csa_counters in beacons + * and probe responses. This value should be set if the driver + * wishes to limit the number of csa counters. Default (0) means + * infinite. + * @max_adj_channel_rssi_comp: max offset of between the channel on which the + * frame was sent and the channel on which the frame was heard for which + * the reported rssi is still valid. If a driver is able to compensate the + * low rssi when a frame is heard on different channel, then it should set + * this variable to the maximal offset for which it can compensate. + * This value should be set in MHz. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -3042,6 +3084,9 @@ struct wiphy { u16 max_ap_assoc_sta; + u8 max_num_csa_counters; + u8 max_adj_channel_rssi_comp; + char priv[0] __aligned(NETDEV_ALIGN); }; @@ -3270,7 +3315,7 @@ struct wireless_dev { struct cfg80211_ibss_params ibss; struct cfg80211_connect_params connect; struct cfg80211_cached_keys *keys; - u8 *ie; + const u8 *ie; size_t ie_len; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -3511,7 +3556,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, * Return: 0 on success, or a negative error code. */ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, u8 *bssid, bool qos); + enum nl80211_iftype iftype, const u8 *bssid, + bool qos); /** * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame @@ -4312,7 +4358,7 @@ void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss, * and not try to connect to any AP any more. */ void cfg80211_disconnected(struct net_device *dev, u16 reason, - u8 *ie, size_t ie_len, gfp_t gfp); + const u8 *ie, size_t ie_len, gfp_t gfp); /** * cfg80211_ready_on_channel - notification of remain_on_channel start @@ -4768,6 +4814,35 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, void *data), void *data); +/* + * cfg80211_stop_iface - trigger interface disconnection + * + * @wiphy: the wiphy + * @wdev: wireless device + * @gfp: context flags + * + * Trigger interface to be stopped as if AP was stopped, IBSS/mesh left, STA + * disconnected. + * + * Note: This doesn't need any locks and is asynchronous. + */ +void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, + gfp_t gfp); + +/** + * cfg80211_shutdown_all_interfaces - shut down all interfaces for a wiphy + * @wiphy: the wiphy to shut down + * + * This function shuts down all interfaces belonging to this wiphy by + * calling dev_close() (and treating non-netdev interfaces as needed). + * It shouldn't really be used unless there are some fatal device errors + * that really can't be recovered in any other way. + * + * Callers must hold the RTNL and be able to deal with callbacks into + * the driver while the function is running. + */ +void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/net/ieee802154.h b/include/net/ieee802154.h index c7ae0ac..0aa7122 100644 --- a/include/net/ieee802154.h +++ b/include/net/ieee802154.h @@ -79,6 +79,15 @@ #define IEEE802154_SCF_KEY_SHORT_INDEX 2 #define IEEE802154_SCF_KEY_HW_INDEX 3 +#define IEEE802154_SCF_SECLEVEL_NONE 0 +#define IEEE802154_SCF_SECLEVEL_MIC32 1 +#define IEEE802154_SCF_SECLEVEL_MIC64 2 +#define IEEE802154_SCF_SECLEVEL_MIC128 3 +#define IEEE802154_SCF_SECLEVEL_ENC 4 +#define IEEE802154_SCF_SECLEVEL_ENC_MIC32 5 +#define IEEE802154_SCF_SECLEVEL_ENC_MIC64 6 +#define IEEE802154_SCF_SECLEVEL_ENC_MIC128 7 + /* MAC footer size */ #define IEEE802154_MFR_SIZE 2 /* 2 octets */ diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 5a719ca..3b53c8e 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -27,6 +27,7 @@ #ifndef IEEE802154_NETDEVICE_H #define IEEE802154_NETDEVICE_H +#include <net/ieee802154.h> #include <net/af_ieee802154.h> #include <linux/netdevice.h> #include <linux/skbuff.h> @@ -114,6 +115,34 @@ int ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr); int ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr); +/* parses the full 802.15.4 header a given skb and stores them into hdr, + * performing pan id decompression and length checks to be suitable for use in + * header_ops.parse + */ +int ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr); + +int ieee802154_max_payload(const struct ieee802154_hdr *hdr); + +static inline int +ieee802154_sechdr_authtag_len(const struct ieee802154_sechdr *sec) +{ + switch (sec->level) { + case IEEE802154_SCF_SECLEVEL_MIC32: + case IEEE802154_SCF_SECLEVEL_ENC_MIC32: + return 4; + case IEEE802154_SCF_SECLEVEL_MIC64: + case IEEE802154_SCF_SECLEVEL_ENC_MIC64: + return 8; + case IEEE802154_SCF_SECLEVEL_MIC128: + case IEEE802154_SCF_SECLEVEL_ENC_MIC128: + return 16; + case IEEE802154_SCF_SECLEVEL_NONE: + case IEEE802154_SCF_SECLEVEL_ENC: + default: + return 0; + } +} + static inline int ieee802154_hdr_length(struct sk_buff *skb) { struct ieee802154_hdr hdr; @@ -193,8 +222,12 @@ static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa, */ struct ieee802154_mac_cb { u8 lqi; - u8 flags; - u8 seq; + u8 type; + bool ackreq; + bool secen; + bool secen_override; + u8 seclevel; + bool seclevel_override; struct ieee802154_addr source; struct ieee802154_addr dest; }; @@ -204,25 +237,96 @@ static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb) return (struct ieee802154_mac_cb *)skb->cb; } -#define MAC_CB_FLAG_TYPEMASK ((1 << 3) - 1) - -#define MAC_CB_FLAG_ACKREQ (1 << 3) -#define MAC_CB_FLAG_SECEN (1 << 4) - -static inline bool mac_cb_is_ackreq(struct sk_buff *skb) +static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb) { - return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ; -} + BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb)); -static inline bool mac_cb_is_secen(struct sk_buff *skb) -{ - return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN; + memset(skb->cb, 0, sizeof(struct ieee802154_mac_cb)); + return mac_cb(skb); } -static inline int mac_cb_type(struct sk_buff *skb) -{ - return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK; -} +#define IEEE802154_LLSEC_KEY_SIZE 16 + +struct ieee802154_llsec_key_id { + u8 mode; + u8 id; + union { + struct ieee802154_addr device_addr; + __le32 short_source; + __le64 extended_source; + }; +}; + +struct ieee802154_llsec_key { + u8 frame_types; + u32 cmd_frame_ids; + u8 key[IEEE802154_LLSEC_KEY_SIZE]; +}; + +struct ieee802154_llsec_key_entry { + struct list_head list; + + struct ieee802154_llsec_key_id id; + struct ieee802154_llsec_key *key; +}; + +struct ieee802154_llsec_device_key { + struct list_head list; + + struct ieee802154_llsec_key_id key_id; + u32 frame_counter; +}; + +enum { + IEEE802154_LLSEC_DEVKEY_IGNORE, + IEEE802154_LLSEC_DEVKEY_RESTRICT, + IEEE802154_LLSEC_DEVKEY_RECORD, + + __IEEE802154_LLSEC_DEVKEY_MAX, +}; + +struct ieee802154_llsec_device { + struct list_head list; + + __le16 pan_id; + __le16 short_addr; + __le64 hwaddr; + u32 frame_counter; + bool seclevel_exempt; + + u8 key_mode; + struct list_head keys; +}; + +struct ieee802154_llsec_seclevel { + struct list_head list; + + u8 frame_type; + u8 cmd_frame_id; + bool device_override; + u32 sec_levels; +}; + +struct ieee802154_llsec_params { + bool enabled; + + __be32 frame_counter; + u8 out_level; + struct ieee802154_llsec_key_id out_key; + + __le64 default_key_source; + + __le16 pan_id; + __le64 hwaddr; + __le64 coord_hwaddr; + __le16 coord_shortaddr; +}; + +struct ieee802154_llsec_table { + struct list_head keys; + struct list_head devices; + struct list_head security_levels; +}; #define IEEE802154_MAC_SCAN_ED 0 #define IEEE802154_MAC_SCAN_ACTIVE 1 @@ -242,6 +346,53 @@ struct ieee802154_mac_params { }; struct wpan_phy; + +enum { + IEEE802154_LLSEC_PARAM_ENABLED = 1 << 0, + IEEE802154_LLSEC_PARAM_FRAME_COUNTER = 1 << 1, + IEEE802154_LLSEC_PARAM_OUT_LEVEL = 1 << 2, + IEEE802154_LLSEC_PARAM_OUT_KEY = 1 << 3, + IEEE802154_LLSEC_PARAM_KEY_SOURCE = 1 << 4, + IEEE802154_LLSEC_PARAM_PAN_ID = 1 << 5, + IEEE802154_LLSEC_PARAM_HWADDR = 1 << 6, + IEEE802154_LLSEC_PARAM_COORD_HWADDR = 1 << 7, + IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = 1 << 8, +}; + +struct ieee802154_llsec_ops { + int (*get_params)(struct net_device *dev, + struct ieee802154_llsec_params *params); + int (*set_params)(struct net_device *dev, + const struct ieee802154_llsec_params *params, + int changed); + + int (*add_key)(struct net_device *dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key); + int (*del_key)(struct net_device *dev, + const struct ieee802154_llsec_key_id *id); + + int (*add_dev)(struct net_device *dev, + const struct ieee802154_llsec_device *llsec_dev); + int (*del_dev)(struct net_device *dev, __le64 dev_addr); + + int (*add_devkey)(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); + int (*del_devkey)(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); + + int (*add_seclevel)(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); + int (*del_seclevel)(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); + + void (*lock_table)(struct net_device *dev); + void (*get_table)(struct net_device *dev, + struct ieee802154_llsec_table **t); + void (*unlock_table)(struct net_device *dev); +}; /* * This should be located at net_device->ml_priv * @@ -272,6 +423,8 @@ struct ieee802154_mlme_ops { void (*get_mac_params)(struct net_device *dev, struct ieee802154_mac_params *params); + struct ieee802154_llsec_ops *llsec; + /* The fields below are required. */ struct wpan_phy *(*get_phy)(const struct net_device *dev); diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 6efe73c..823ec7b 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -41,14 +41,13 @@ struct inet_peer { struct rcu_head gc_rcu; }; /* - * Once inet_peer is queued for deletion (refcnt == -1), following fields - * are not available: rid, ip_id_count + * Once inet_peer is queued for deletion (refcnt == -1), following field + * is not available: rid * We can share memory with rcu_head to help keep inet_peer small. */ union { struct { atomic_t rid; /* Frag reception counter */ - atomic_t ip_id_count; /* IP ID for the next packet */ }; struct rcu_head rcu; struct inet_peer *gc_next; @@ -165,7 +164,7 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); void inetpeer_invalidate_tree(struct inet_peer_base *); /* - * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, + * temporary check to make sure we dont access rid, tcp_ts, * tcp_ts_stamp if no refcount is taken on inet_peer */ static inline void inet_peer_refcheck(const struct inet_peer *p) @@ -173,20 +172,4 @@ static inline void inet_peer_refcheck(const struct inet_peer *p) WARN_ON_ONCE(atomic_read(&p->refcnt) <= 0); } - -/* can be called with or without local BH being disabled */ -static inline int inet_getid(struct inet_peer *p, int more) -{ - int old, new; - more++; - inet_peer_refcheck(p); - do { - old = atomic_read(&p->ip_id_count); - new = old + more; - if (!new) - new = 1; - } while (atomic_cmpxchg(&p->ip_id_count, old, new) != old); - return new; -} - #endif /* _NET_INETPEER_H */ diff --git a/include/net/ip.h b/include/net/ip.h index 512bcd5..0e795df 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -208,7 +208,7 @@ static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_o void inet_get_local_port_range(struct net *net, int *low, int *high); -#if CONFIG_SYSCTL +#ifdef CONFIG_SYSCTL static inline int inet_is_local_reserved_port(struct net *net, int port) { if (!net->ipv4.sysctl_local_reserved_ports) @@ -309,9 +309,19 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb) } } -void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more); +#define IP_IDENTS_SZ 2048u +extern atomic_t *ip_idents; -static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk) +static inline u32 ip_idents_reserve(u32 hash, int segs) +{ + atomic_t *id_ptr = ip_idents + hash % IP_IDENTS_SZ; + + return atomic_add_return(segs, id_ptr) - segs; +} + +void __ip_select_ident(struct iphdr *iph, int segs); + +static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs) { struct iphdr *iph = ip_hdr(skb); @@ -321,24 +331,20 @@ static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, s * does not change, they drop every other packet in * a TCP stream using header compression. */ - iph->id = (sk && inet_sk(sk)->inet_daddr) ? - htons(inet_sk(sk)->inet_id++) : 0; - } else - __ip_select_ident(iph, dst, 0); -} - -static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk, int more) -{ - struct iphdr *iph = ip_hdr(skb); - - if ((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) { if (sk && inet_sk(sk)->inet_daddr) { iph->id = htons(inet_sk(sk)->inet_id); - inet_sk(sk)->inet_id += 1 + more; - } else + inet_sk(sk)->inet_id += segs; + } else { iph->id = 0; - } else - __ip_select_ident(iph, dst, more); + } + } else { + __ip_select_ident(iph, segs); + } +} + +static inline void ip_select_ident(struct sk_buff *skb, struct sock *sk) +{ + ip_select_ident_segs(skb, sk, 1); } static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto) diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 38e41e4..1d09b46 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -127,6 +127,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg); void rt6_ifdown(struct net *net, struct net_device *dev); void rt6_mtu_change(struct net_device *dev, unsigned int mtu); void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); +void rt6_clean_tohost(struct net *net, struct in6_addr *gateway); /* diff --git a/include/net/ipv6.h b/include/net/ipv6.h index ba810d0..574337f 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -668,8 +668,6 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); } -void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); - int ip6_dst_hoplimit(struct dst_entry *dst); static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 451c1bf..421b6ec 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -189,6 +189,43 @@ struct ieee80211_chanctx_conf { }; /** + * enum ieee80211_chanctx_switch_mode - channel context switch mode + * @CHANCTX_SWMODE_REASSIGN_VIF: Both old and new contexts already + * exist (and will continue to exist), but the virtual interface + * needs to be switched from one to the other. + * @CHANCTX_SWMODE_SWAP_CONTEXTS: The old context exists but will stop + * to exist with this call, the new context doesn't exist but + * will be active after this call, the virtual interface switches + * from the old to the new (note that the driver may of course + * implement this as an on-the-fly chandef switch of the existing + * hardware context, but the mac80211 pointer for the old context + * will cease to exist and only the new one will later be used + * for changes/removal.) + */ +enum ieee80211_chanctx_switch_mode { + CHANCTX_SWMODE_REASSIGN_VIF, + CHANCTX_SWMODE_SWAP_CONTEXTS, +}; + +/** + * struct ieee80211_vif_chanctx_switch - vif chanctx switch information + * + * This is structure is used to pass information about a vif that + * needs to switch from one chanctx to another. The + * &ieee80211_chanctx_switch_mode defines how the switch should be + * done. + * + * @vif: the vif that should be switched from old_ctx to new_ctx + * @old_ctx: the old context to which the vif was assigned + * @new_ctx: the new context to which the vif must be assigned + */ +struct ieee80211_vif_chanctx_switch { + struct ieee80211_vif *vif; + struct ieee80211_chanctx_conf *old_ctx; + struct ieee80211_chanctx_conf *new_ctx; +}; + +/** * enum ieee80211_bss_change - BSS change notification flags * * These flags are used with the bss_info_changed() callback @@ -1113,7 +1150,9 @@ enum ieee80211_vif_flags { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively - * @csa_active: marks whether a channel switch is going on + * @csa_active: marks whether a channel switch is going on. Internally it is + * write-protected by sdata_lock and local->mtx so holding either is fine + * for read access. * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed @@ -1374,6 +1413,7 @@ struct ieee80211_sta_rates { * the station moves to associated state. * @smps_mode: current SMPS mode (off, static or dynamic) * @rates: rate control selection table + * @tdls: indicates whether the STA is a TDLS peer */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1388,6 +1428,7 @@ struct ieee80211_sta { enum ieee80211_sta_rx_bandwidth bandwidth; enum ieee80211_smps_mode smps_mode; struct ieee80211_sta_rates __rcu *rates; + bool tdls; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); @@ -2732,6 +2773,11 @@ enum ieee80211_roc_type { * to vif. Possible use is for hw queue remapping. * @unassign_vif_chanctx: Notifies device driver about channel context being * unbound from vif. + * @switch_vif_chanctx: switch a number of vifs from one chanctx to + * another, as specified in the list of + * @ieee80211_vif_chanctx_switch passed to the driver, according + * to the mode defined in &ieee80211_chanctx_switch_mode. + * * @start_ap: Start operation on the AP interface, this is called after all the * information in bss_conf is set and beacon can be retrieved. A channel * context is bound before this is called. Note that if the driver uses @@ -2765,6 +2811,10 @@ enum ieee80211_roc_type { * information in bss_conf is set up and the beacon can be retrieved. A * channel context is bound before this is called. * @leave_ibss: Leave the IBSS again. + * + * @get_expected_throughput: extract the expected throughput towards the + * specified station. The returned value is expressed in Kbps. It returns 0 + * if the RC algorithm does not have proper data to provide. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2944,6 +2994,10 @@ struct ieee80211_ops { void (*unassign_vif_chanctx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx); + int (*switch_vif_chanctx)(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode); void (*restart_complete)(struct ieee80211_hw *hw); @@ -2958,6 +3012,7 @@ struct ieee80211_ops { int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + u32 (*get_expected_throughput)(struct ieee80211_sta *sta); }; /** @@ -3407,6 +3462,47 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, */ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); +#define IEEE80211_MAX_CSA_COUNTERS_NUM 2 + +/** + * struct ieee80211_mutable_offsets - mutable beacon offsets + * @tim_offset: position of TIM element + * @tim_length: size of TIM element + * @csa_counter_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets + * to CSA counters. This array can contain zero values which + * should be ignored. + */ +struct ieee80211_mutable_offsets { + u16 tim_offset; + u16 tim_length; + + u16 csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM]; +}; + +/** + * ieee80211_beacon_get_template - beacon template generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @offs: &struct ieee80211_mutable_offsets pointer to struct that will + * receive the offsets that may be updated by the driver. + * + * If the driver implements beaconing modes, it must use this function to + * obtain the beacon template. + * + * This function should be used if the beacon frames are generated by the + * device, and then the driver must use the returned beacon as the template + * The driver or the device are responsible to update the DTIM and, when + * applicable, the CSA count. + * + * The driver is responsible for freeing the returned skb. + * + * Return: The beacon template. %NULL on error. + */ +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_mutable_offsets *offs); + /** * ieee80211_beacon_get_tim - beacon generation function * @hw: pointer obtained from ieee80211_alloc_hw(). @@ -3418,16 +3514,12 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); * Set to 0 if invalid (in non-AP modes). * * If the driver implements beaconing modes, it must use this function to - * obtain the beacon frame/template. + * obtain the beacon frame. * * If the beacon frames are generated by the host system (i.e., not in * hardware/firmware), the driver uses this function to get each beacon - * frame from mac80211 -- it is responsible for calling this function - * before the beacon is needed (e.g. based on hardware interrupt). - * - * If the beacon frames are generated by the device, then the driver - * must use the returned beacon as the template and change the TIM IE - * according to the current DTIM parameters/TIM bitmap. + * frame from mac80211 -- it is responsible for calling this function exactly + * once before the beacon is needed (e.g. based on hardware interrupt). * * The driver is responsible for freeing the returned skb. * @@ -3453,6 +3545,20 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, } /** + * ieee80211_csa_update_counter - request mac80211 to decrement the csa counter + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * The csa counter should be updated after each beacon transmission. + * This function is called implicitly when + * ieee80211_beacon_get/ieee80211_beacon_get_tim are called, however if the + * beacon frames are generated by the device, the driver should call this + * function after each beacon transmission to sync mac80211's csa counters. + * + * Return: new csa counter value + */ +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif); + +/** * ieee80211_csa_finish - notify mac80211 about channel switch * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @@ -4480,6 +4586,8 @@ struct rate_control_ops { void (*add_sta_debugfs)(void *priv, void *priv_sta, struct dentry *dir); void (*remove_sta_debugfs)(void *priv, void *priv_sta); + + u32 (*get_expected_throughput)(void *priv_sta); }; static inline int rate_supported(struct ieee80211_sta *sta, diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index 07eaaf6..a71dd33 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -48,6 +48,8 @@ unsigned int nf_nat_setup_info(struct nf_conn *ct, extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum); +struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct); + /* Is this tuple already taken? (not by us)*/ int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, const struct nf_conn *ignored_conntrack); diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e6bc14d..7ee6ce6 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -72,21 +72,23 @@ static inline void nft_data_debug(const struct nft_data *data) * struct nft_ctx - nf_tables rule/set context * * @net: net namespace - * @skb: netlink skb - * @nlh: netlink message header * @afi: address family info * @table: the table the chain is contained in * @chain: the chain the rule is contained in * @nla: netlink attributes + * @portid: netlink portID of the original message + * @seq: netlink sequence number + * @report: notify via unicast netlink message */ struct nft_ctx { struct net *net; - const struct sk_buff *skb; - const struct nlmsghdr *nlh; - const struct nft_af_info *afi; - const struct nft_table *table; - const struct nft_chain *chain; + struct nft_af_info *afi; + struct nft_table *table; + struct nft_chain *chain; const struct nlattr * const *nla; + u32 portid; + u32 seq; + bool report; }; struct nft_data_desc { @@ -146,6 +148,44 @@ struct nft_set_iter { }; /** + * struct nft_set_desc - description of set elements + * + * @klen: key length + * @dlen: data length + * @size: number of set elements + */ +struct nft_set_desc { + unsigned int klen; + unsigned int dlen; + unsigned int size; +}; + +/** + * enum nft_set_class - performance class + * + * @NFT_LOOKUP_O_1: constant, O(1) + * @NFT_LOOKUP_O_LOG_N: logarithmic, O(log N) + * @NFT_LOOKUP_O_N: linear, O(N) + */ +enum nft_set_class { + NFT_SET_CLASS_O_1, + NFT_SET_CLASS_O_LOG_N, + NFT_SET_CLASS_O_N, +}; + +/** + * struct nft_set_estimate - estimation of memory and performance + * characteristics + * + * @size: required memory + * @class: lookup performance class + */ +struct nft_set_estimate { + unsigned int size; + enum nft_set_class class; +}; + +/** * struct nft_set_ops - nf_tables set operations * * @lookup: look up an element within the set @@ -174,7 +214,11 @@ struct nft_set_ops { struct nft_set_iter *iter); unsigned int (*privsize)(const struct nlattr * const nla[]); + bool (*estimate)(const struct nft_set_desc *desc, + u32 features, + struct nft_set_estimate *est); int (*init)(const struct nft_set *set, + const struct nft_set_desc *desc, const struct nlattr * const nla[]); void (*destroy)(const struct nft_set *set); @@ -194,6 +238,8 @@ void nft_unregister_set(struct nft_set_ops *ops); * @name: name of the set * @ktype: key type (numeric type defined by userspace, not used in the kernel) * @dtype: data type (verdict or numeric type defined by userspace) + * @size: maximum set size + * @nelems: number of elements * @ops: set ops * @flags: set flags * @klen: key length @@ -206,6 +252,8 @@ struct nft_set { char name[IFNAMSIZ]; u32 ktype; u32 dtype; + u32 size; + u32 nelems; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; u16 flags; @@ -222,6 +270,8 @@ static inline void *nft_set_priv(const struct nft_set *set) struct nft_set *nf_tables_set_lookup(const struct nft_table *table, const struct nlattr *nla); +struct nft_set *nf_tables_set_lookup_byid(const struct net *net, + const struct nlattr *nla); /** * struct nft_set_binding - nf_tables set binding @@ -341,18 +391,75 @@ struct nft_rule { }; /** - * struct nft_rule_trans - nf_tables rule update in transaction + * struct nft_trans - nf_tables object update in transaction * + * @rcu_head: rcu head to defer release of transaction data * @list: used internally - * @ctx: rule context - * @rule: rule that needs to be updated + * @msg_type: message type + * @ctx: transaction context + * @data: internal information related to the transaction */ -struct nft_rule_trans { +struct nft_trans { + struct rcu_head rcu_head; struct list_head list; + int msg_type; struct nft_ctx ctx; + char data[0]; +}; + +struct nft_trans_rule { struct nft_rule *rule; }; +#define nft_trans_rule(trans) \ + (((struct nft_trans_rule *)trans->data)->rule) + +struct nft_trans_set { + struct nft_set *set; + u32 set_id; +}; + +#define nft_trans_set(trans) \ + (((struct nft_trans_set *)trans->data)->set) +#define nft_trans_set_id(trans) \ + (((struct nft_trans_set *)trans->data)->set_id) + +struct nft_trans_chain { + bool update; + char name[NFT_CHAIN_MAXNAMELEN]; + struct nft_stats __percpu *stats; + u8 policy; +}; + +#define nft_trans_chain_update(trans) \ + (((struct nft_trans_chain *)trans->data)->update) +#define nft_trans_chain_name(trans) \ + (((struct nft_trans_chain *)trans->data)->name) +#define nft_trans_chain_stats(trans) \ + (((struct nft_trans_chain *)trans->data)->stats) +#define nft_trans_chain_policy(trans) \ + (((struct nft_trans_chain *)trans->data)->policy) + +struct nft_trans_table { + bool update; + bool enable; +}; + +#define nft_trans_table_update(trans) \ + (((struct nft_trans_table *)trans->data)->update) +#define nft_trans_table_enable(trans) \ + (((struct nft_trans_table *)trans->data)->enable) + +struct nft_trans_elem { + struct nft_set *set; + struct nft_set_elem elem; +}; + +#define nft_trans_elem_set(trans) \ + (((struct nft_trans_elem *)trans->data)->set) +#define nft_trans_elem(trans) \ + (((struct nft_trans_elem *)trans->data)->elem) + static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; @@ -385,6 +492,7 @@ static inline void *nft_userdata(const struct nft_rule *rule) enum nft_chain_flags { NFT_BASE_CHAIN = 0x1, + NFT_CHAIN_INACTIVE = 0x2, }; /** diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h new file mode 100644 index 0000000..0ee47c3 --- /dev/null +++ b/include/net/netfilter/nft_meta.h @@ -0,0 +1,36 @@ +#ifndef _NFT_META_H_ +#define _NFT_META_H_ + +struct nft_meta { + enum nft_meta_keys key:8; + union { + enum nft_registers dreg:8; + enum nft_registers sreg:8; + }; +}; + +extern const struct nla_policy nft_meta_policy[]; + +int nft_meta_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + +int nft_meta_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + +int nft_meta_get_dump(struct sk_buff *skb, + const struct nft_expr *expr); + +int nft_meta_set_dump(struct sk_buff *skb, + const struct nft_expr *expr); + +void nft_meta_get_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt); + +void nft_meta_set_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt); + +#endif diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 7655cfe..bdf55c3 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -36,6 +36,7 @@ enum { NFC_DIGITAL_RF_TECH_212F, NFC_DIGITAL_RF_TECH_424F, NFC_DIGITAL_RF_TECH_ISO15693, + NFC_DIGITAL_RF_TECH_106B, NFC_DIGITAL_RF_TECH_LAST, }; @@ -62,6 +63,9 @@ enum { NFC_DIGITAL_FRAMING_ISO15693_INVENTORY, NFC_DIGITAL_FRAMING_ISO15693_T5T, + NFC_DIGITAL_FRAMING_NFCB, + NFC_DIGITAL_FRAMING_NFCB_T4T, + NFC_DIGITAL_FRAMING_LAST, }; diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 03c4650..61286db 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -27,6 +27,7 @@ struct nfc_hci_dev; struct nfc_hci_ops { int (*open) (struct nfc_hci_dev *hdev); void (*close) (struct nfc_hci_dev *hdev); + int (*load_session) (struct nfc_hci_dev *hdev); int (*hci_ready) (struct nfc_hci_dev *hdev); /* * xmit must always send the complete buffer before diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 2e8b40c..6c583e2 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -264,4 +264,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type); int nfc_remove_se(struct nfc_dev *dev, u32 se_idx); struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx); +void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, + u8 payload_type, u8 direction); + #endif /* __NET_NFC_H */ diff --git a/include/net/protocol.h b/include/net/protocol.h index a7e986b..d6fcc1f 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -86,7 +86,6 @@ struct inet_protosw { struct proto *prot; const struct proto_ops *ops; - char no_check; /* checksum on rcv/xmit/none? */ unsigned char flags; /* See INET_PROTOSW_* below. */ }; #define INET_PROTOSW_REUSE 0x01 /* Are ports automatically reusable? */ diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h index f257486..3f36d45 100644 --- a/include/net/secure_seq.h +++ b/include/net/secure_seq.h @@ -3,8 +3,6 @@ #include <linux/types.h> -__u32 secure_ip_id(__be32 daddr); -__u32 secure_ipv6_id(const __be32 daddr[4]); u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, __be16 dport); diff --git a/include/net/sock.h b/include/net/sock.h index 21569cf..07b7fcd 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -243,7 +243,8 @@ struct cg_proto; * @sk_sndbuf: size of send buffer in bytes * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings - * @sk_no_check: %SO_NO_CHECK setting, whether or not checkup packets + * @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets + * @sk_no_check_rx: allow zero checksum in RX packets * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) * @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK) * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) @@ -371,7 +372,8 @@ struct sock { struct sk_buff_head sk_write_queue; kmemcheck_bitfield_begin(flags); unsigned int sk_shutdown : 2, - sk_no_check : 2, + sk_no_check_tx : 1, + sk_no_check_rx : 1, sk_userlocks : 4, sk_protocol : 8, sk_type : 16; diff --git a/include/net/tcp.h b/include/net/tcp.h index f5d6ca4..e80abe4 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -971,8 +971,9 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp) /* We follow the spirit of RFC2861 to validate cwnd but implement a more * flexible approach. The RFC suggests cwnd should not be raised unless - * it was fully used previously. But we allow cwnd to grow as long as the - * application has used half the cwnd. + * it was fully used previously. And that's exactly what we do in + * congestion avoidance mode. But in slow start we allow cwnd to grow + * as long as the application has used half the cwnd. * Example : * cwnd is 10 (IW10), but application sends 9 frames. * We allow cwnd to reach 18 when all frames are ACKed. @@ -985,7 +986,11 @@ static inline bool tcp_is_cwnd_limited(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - return tp->snd_cwnd < 2 * tp->lsnd_pending; + /* If in slow start, ensure cwnd grows to twice what was ACKed. */ + if (tp->snd_cwnd <= tp->snd_ssthresh) + return tp->snd_cwnd < 2 * tp->max_packets_out; + + return tp->is_cwnd_limited; } static inline void tcp_check_probe_timer(struct sock *sk) diff --git a/include/net/tso.h b/include/net/tso.h new file mode 100644 index 0000000..47e5444 --- /dev/null +++ b/include/net/tso.h @@ -0,0 +1,20 @@ +#ifndef _TSO_H +#define _TSO_H + +#include <net/ip.h> + +struct tso_t { + int next_frag_idx; + void *data; + size_t size; + u16 ip_id; + u32 tcp_seq; +}; + +int tso_count_descs(struct sk_buff *skb); +void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, + int size, bool is_last); +void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size); +void tso_start(struct sk_buff *skb, struct tso_t *tso); + +#endif /* _TSO_H */ diff --git a/include/net/udp.h b/include/net/udp.h index a24f0f3..5eb8687 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -95,15 +95,6 @@ static inline struct udp_hslot *udp_hashslot2(struct udp_table *table, return &table->hash2[hash & table->mask]; } -/* Note: this must match 'valbool' in sock_setsockopt */ -#define UDP_CSUM_NOXMIT 1 - -/* Used by SunRPC/xprt layer. */ -#define UDP_CSUM_NORCV 2 - -/* Default, as per the RFC, is to always do csums. */ -#define UDP_CSUM_DEFAULT 0 - extern struct proto udp_prot; extern atomic_long_t udp_memory_allocated; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 116e9c7..721e9c3 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -691,13 +691,6 @@ struct xfrm_spi_skb_cb { #define XFRM_SPI_SKB_CB(__skb) ((struct xfrm_spi_skb_cb *)&((__skb)->cb[0])) -/* Audit Information */ -struct xfrm_audit { - u32 secid; - kuid_t loginuid; - unsigned int sessionid; -}; - #ifdef CONFIG_AUDITSYSCALL static inline struct audit_buffer *xfrm_audit_start(const char *op) { @@ -713,30 +706,24 @@ static inline struct audit_buffer *xfrm_audit_start(const char *op) return audit_buf; } -static inline void xfrm_audit_helper_usrinfo(kuid_t auid, unsigned int ses, u32 secid, +static inline void xfrm_audit_helper_usrinfo(bool task_valid, struct audit_buffer *audit_buf) { - char *secctx; - u32 secctx_len; - - audit_log_format(audit_buf, " auid=%u ses=%u", - from_kuid(&init_user_ns, auid), ses); - if (secid != 0 && - security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) { - audit_log_format(audit_buf, " subj=%s", secctx); - security_release_secctx(secctx, secctx_len); - } else - audit_log_task_context(audit_buf); -} - -void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid, - unsigned int ses, u32 secid); -void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid, - unsigned int ses, u32 secid); -void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid, - unsigned int ses, u32 secid); -void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid, - unsigned int ses, u32 secid); + const unsigned int auid = from_kuid(&init_user_ns, task_valid ? + audit_get_loginuid(current) : + INVALID_UID); + const unsigned int ses = task_valid ? audit_get_sessionid(current) : + (unsigned int) -1; + + audit_log_format(audit_buf, " auid=%u ses=%u", auid, ses); + audit_log_task_context(audit_buf); +} + +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid); +void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, + bool task_valid); +void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid); +void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid); void xfrm_audit_state_replay_overflow(struct xfrm_state *x, struct sk_buff *skb); void xfrm_audit_state_replay(struct xfrm_state *x, struct sk_buff *skb, @@ -749,22 +736,22 @@ void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb, #else static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int ses, u32 secid) + bool task_valid) { } static inline void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int ses, u32 secid) + bool task_valid) { } static inline void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, unsigned int ses, u32 secid) + bool task_valid) { } static inline void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, unsigned int ses, u32 secid) + bool task_valid) { } @@ -1508,7 +1495,7 @@ struct xfrmk_spdinfo { struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); int xfrm_state_delete(struct xfrm_state *x); -int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info); +int xfrm_state_flush(struct net *net, u8 proto, bool task_valid); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); @@ -1603,7 +1590,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, int *err); struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 id, int delete, int *err); -int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info); +int xfrm_policy_flush(struct net *net, u8 type, bool task_valid); u32 xfrm_get_acqseq(void); int verify_spi_info(u8 proto, u32 min, u32 max); int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); diff --git a/include/trace/events/module.h b/include/trace/events/module.h index ed0b2c5..7c5cbfe 100644 --- a/include/trace/events/module.h +++ b/include/trace/events/module.h @@ -80,7 +80,7 @@ DECLARE_EVENT_CLASS(module_refcnt, TP_fast_assign( __entry->ip = ip; - __entry->refcnt = __this_cpu_read(mod->refptr->incs) + __this_cpu_read(mod->refptr->decs); + __entry->refcnt = __this_cpu_read(mod->refptr->incs) - __this_cpu_read(mod->refptr->decs); __assign_str(name, mod->name); ), diff --git a/include/uapi/asm-generic/resource.h b/include/uapi/asm-generic/resource.h index f863428..c6d10af 100644 --- a/include/uapi/asm-generic/resource.h +++ b/include/uapi/asm-generic/resource.h @@ -57,12 +57,5 @@ # define RLIM_INFINITY (~0UL) #endif -/* - * RLIMIT_STACK default maximum - some architectures override it: - */ -#ifndef _STK_LIM_MAX -# define _STK_LIM_MAX RLIM_INFINITY -#endif - #endif /* _UAPI_ASM_GENERIC_RESOURCE_H */ diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 6db6678..3336406 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -697,9 +697,11 @@ __SYSCALL(__NR_finit_module, sys_finit_module) __SYSCALL(__NR_sched_setattr, sys_sched_setattr) #define __NR_sched_getattr 275 __SYSCALL(__NR_sched_getattr, sys_sched_getattr) +#define __NR_renameat2 276 +__SYSCALL(__NR_renameat2, sys_renameat2) #undef __NR_syscalls -#define __NR_syscalls 276 +#define __NR_syscalls 277 /* * All syscalls below here should go away really, diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index dfa4c86..b21ea45 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -331,9 +331,17 @@ enum { #define AUDIT_FAIL_PRINTK 1 #define AUDIT_FAIL_PANIC 2 +/* + * These bits disambiguate different calling conventions that share an + * ELF machine type, bitness, and endianness + */ +#define __AUDIT_ARCH_CONVENTION_MASK 0x30000000 +#define __AUDIT_ARCH_CONVENTION_MIPS64_N32 0x20000000 + /* distinguish syscall tables */ #define __AUDIT_ARCH_64BIT 0x80000000 #define __AUDIT_ARCH_LE 0x40000000 + #define AUDIT_ARCH_ALPHA (EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ARMEB (EM_ARM) @@ -346,7 +354,11 @@ enum { #define AUDIT_ARCH_MIPS (EM_MIPS) #define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE) #define AUDIT_ARCH_MIPS64 (EM_MIPS|__AUDIT_ARCH_64BIT) +#define AUDIT_ARCH_MIPS64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|\ + __AUDIT_ARCH_CONVENTION_MIPS64_N32) #define AUDIT_ARCH_MIPSEL64 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) +#define AUDIT_ARCH_MIPSEL64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE\ + __AUDIT_ARCH_CONVENTION_MIPS64_N32) #define AUDIT_ARCH_OPENRISC (EM_OPENRISC) #define AUDIT_ARCH_PARISC (EM_PARISC) #define AUDIT_ARCH_PARISC64 (EM_PARISC|__AUDIT_ARCH_64BIT) diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h index 5d9d1d14..41892f7 100644 --- a/include/uapi/linux/can.h +++ b/include/uapi/linux/can.h @@ -42,8 +42,8 @@ * DAMAGE. */ -#ifndef CAN_H -#define CAN_H +#ifndef _UAPI_CAN_H +#define _UAPI_CAN_H #include <linux/types.h> #include <linux/socket.h> @@ -191,4 +191,4 @@ struct can_filter { #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ -#endif /* CAN_H */ +#endif /* !_UAPI_CAN_H */ diff --git a/include/uapi/linux/can/bcm.h b/include/uapi/linux/can/bcm.h index 382251a..89ddb9d 100644 --- a/include/uapi/linux/can/bcm.h +++ b/include/uapi/linux/can/bcm.h @@ -41,8 +41,8 @@ * DAMAGE. */ -#ifndef CAN_BCM_H -#define CAN_BCM_H +#ifndef _UAPI_CAN_BCM_H +#define _UAPI_CAN_BCM_H #include <linux/types.h> #include <linux/can.h> @@ -95,4 +95,4 @@ enum { #define TX_RESET_MULTI_IDX 0x0200 #define RX_RTR_FRAME 0x0400 -#endif /* CAN_BCM_H */ +#endif /* !_UAPI_CAN_BCM_H */ diff --git a/include/uapi/linux/can/error.h b/include/uapi/linux/can/error.h index b632045..c247446 100644 --- a/include/uapi/linux/can/error.h +++ b/include/uapi/linux/can/error.h @@ -41,8 +41,8 @@ * DAMAGE. */ -#ifndef CAN_ERROR_H -#define CAN_ERROR_H +#ifndef _UAPI_CAN_ERROR_H +#define _UAPI_CAN_ERROR_H #define CAN_ERR_DLC 8 /* dlc for error message frames */ @@ -120,4 +120,4 @@ /* controller specific additional information / data[5..7] */ -#endif /* CAN_ERROR_H */ +#endif /* _UAPI_CAN_ERROR_H */ diff --git a/include/uapi/linux/can/gw.h b/include/uapi/linux/can/gw.h index 844c896..3e6184c 100644 --- a/include/uapi/linux/can/gw.h +++ b/include/uapi/linux/can/gw.h @@ -41,8 +41,8 @@ * DAMAGE. */ -#ifndef CAN_GW_H -#define CAN_GW_H +#ifndef _UAPI_CAN_GW_H +#define _UAPI_CAN_GW_H #include <linux/types.h> #include <linux/can.h> @@ -200,4 +200,4 @@ enum { * Beware of sending unpacked or aligned structs! */ -#endif +#endif /* !_UAPI_CAN_GW_H */ diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h index 7e2e186..813d11f 100644 --- a/include/uapi/linux/can/netlink.h +++ b/include/uapi/linux/can/netlink.h @@ -15,8 +15,8 @@ * GNU General Public License for more details. */ -#ifndef CAN_NETLINK_H -#define CAN_NETLINK_H +#ifndef _UAPI_CAN_NETLINK_H +#define _UAPI_CAN_NETLINK_H #include <linux/types.h> @@ -130,4 +130,4 @@ enum { #define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) -#endif /* CAN_NETLINK_H */ +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h index c7d8c33..78ec76f 100644 --- a/include/uapi/linux/can/raw.h +++ b/include/uapi/linux/can/raw.h @@ -42,8 +42,8 @@ * DAMAGE. */ -#ifndef CAN_RAW_H -#define CAN_RAW_H +#ifndef _UAPI_CAN_RAW_H +#define _UAPI_CAN_RAW_H #include <linux/can.h> @@ -59,4 +59,4 @@ enum { CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */ }; -#endif +#endif /* !_UAPI_CAN_RAW_H */ diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index cf4750e..40b5ca8 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -96,6 +96,11 @@ * * 7.23 * - add FUSE_WRITEBACK_CACHE + * - add time_gran to fuse_init_out + * - add reserved space to fuse_init_out + * - add FATTR_CTIME + * - add ctime and ctimensec to fuse_setattr_in + * - add FUSE_RENAME2 request */ #ifndef _LINUX_FUSE_H @@ -191,6 +196,7 @@ struct fuse_file_lock { #define FATTR_ATIME_NOW (1 << 7) #define FATTR_MTIME_NOW (1 << 8) #define FATTR_LOCKOWNER (1 << 9) +#define FATTR_CTIME (1 << 10) /** * Flags returned by the OPEN request @@ -348,6 +354,7 @@ enum fuse_opcode { FUSE_BATCH_FORGET = 42, FUSE_FALLOCATE = 43, FUSE_READDIRPLUS = 44, + FUSE_RENAME2 = 45, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -426,6 +433,12 @@ struct fuse_rename_in { uint64_t newdir; }; +struct fuse_rename2_in { + uint64_t newdir; + uint32_t flags; + uint32_t padding; +}; + struct fuse_link_in { uint64_t oldnodeid; }; @@ -438,10 +451,10 @@ struct fuse_setattr_in { uint64_t lock_owner; uint64_t atime; uint64_t mtime; - uint64_t unused2; + uint64_t ctime; uint32_t atimensec; uint32_t mtimensec; - uint32_t unused3; + uint32_t ctimensec; uint32_t mode; uint32_t unused4; uint32_t uid; @@ -559,6 +572,9 @@ struct fuse_init_in { uint32_t flags; }; +#define FUSE_COMPAT_INIT_OUT_SIZE 8 +#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 + struct fuse_init_out { uint32_t major; uint32_t minor; @@ -567,6 +583,8 @@ struct fuse_init_out { uint16_t max_background; uint16_t congestion_threshold; uint32_t max_write; + uint32_t time_gran; + uint32_t unused[9]; }; #define CUSE_INIT_INFO_MAX 4096 diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 9a7f7ac..622e7910 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -399,9 +399,10 @@ enum { IFLA_VF_UNSPEC, IFLA_VF_MAC, /* Hardware queue specific attributes */ IFLA_VF_VLAN, - IFLA_VF_TX_RATE, /* TX Bandwidth Allocation */ + IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ + IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ __IFLA_VF_MAX, }; @@ -423,6 +424,12 @@ struct ifla_vf_tx_rate { __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ }; +struct ifla_vf_rate { + __u32 vf; + __u32 min_tx_rate; /* Min Bandwidth in Mbps */ + __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +}; + struct ifla_vf_spoofchk { __u32 vf; __u32 setting; diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h index 8adb681..21caa26 100644 --- a/include/uapi/linux/l2tp.h +++ b/include/uapi/linux/l2tp.h @@ -124,6 +124,8 @@ enum { L2TP_ATTR_STATS, /* nested */ L2TP_ATTR_IP6_SADDR, /* struct in6_addr */ L2TP_ATTR_IP6_DADDR, /* struct in6_addr */ + L2TP_ATTR_UDP_ZERO_CSUM6_TX, /* u8 */ + L2TP_ATTR_UDP_ZERO_CSUM6_RX, /* u8 */ __L2TP_ATTR_MAX, }; diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index d3ef583..4a1d7e9 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -24,6 +24,7 @@ enum { NDA_PORT, NDA_VNI, NDA_IFINDEX, + NDA_MASTER, __NDA_MAX }; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index c88ccbf..2a88f64 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -212,6 +212,29 @@ enum nft_set_flags { }; /** + * enum nft_set_policies - set selection policy + * + * @NFT_SET_POL_PERFORMANCE: prefer high performance over low memory use + * @NFT_SET_POL_MEMORY: prefer low memory use over high performance + */ +enum nft_set_policies { + NFT_SET_POL_PERFORMANCE, + NFT_SET_POL_MEMORY, +}; + +/** + * enum nft_set_desc_attributes - set element description + * + * @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32) + */ +enum nft_set_desc_attributes { + NFTA_SET_DESC_UNSPEC, + NFTA_SET_DESC_SIZE, + __NFTA_SET_DESC_MAX +}; +#define NFTA_SET_DESC_MAX (__NFTA_SET_DESC_MAX - 1) + +/** * enum nft_set_attributes - nf_tables set netlink attributes * * @NFTA_SET_TABLE: table name (NLA_STRING) @@ -221,6 +244,9 @@ enum nft_set_flags { * @NFTA_SET_KEY_LEN: key data length (NLA_U32) * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32) * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32) + * @NFTA_SET_POLICY: selection policy (NLA_U32) + * @NFTA_SET_DESC: set description (NLA_NESTED) + * @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -231,6 +257,9 @@ enum nft_set_attributes { NFTA_SET_KEY_LEN, NFTA_SET_DATA_TYPE, NFTA_SET_DATA_LEN, + NFTA_SET_POLICY, + NFTA_SET_DESC, + NFTA_SET_ID, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) @@ -266,12 +295,14 @@ enum nft_set_elem_attributes { * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING) * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING) * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes) + * @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32) */ enum nft_set_elem_list_attributes { NFTA_SET_ELEM_LIST_UNSPEC, NFTA_SET_ELEM_LIST_TABLE, NFTA_SET_ELEM_LIST_SET, NFTA_SET_ELEM_LIST_ELEMENTS, + NFTA_SET_ELEM_LIST_SET_ID, __NFTA_SET_ELEM_LIST_MAX }; #define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1) @@ -457,12 +488,14 @@ enum nft_cmp_attributes { * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING) * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers) * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32) */ enum nft_lookup_attributes { NFTA_LOOKUP_UNSPEC, NFTA_LOOKUP_SET, NFTA_LOOKUP_SREG, NFTA_LOOKUP_DREG, + NFTA_LOOKUP_SET_ID, __NFTA_LOOKUP_MAX }; #define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) @@ -536,6 +569,8 @@ enum nft_exthdr_attributes { * @NFT_META_SECMARK: packet secmark (skb->secmark) * @NFT_META_NFPROTO: netfilter protocol * @NFT_META_L4PROTO: layer 4 protocol number + * @NFT_META_BRI_IIFNAME: packet input bridge interface name + * @NFT_META_BRI_OIFNAME: packet output bridge interface name */ enum nft_meta_keys { NFT_META_LEN, @@ -555,6 +590,8 @@ enum nft_meta_keys { NFT_META_SECMARK, NFT_META_NFPROTO, NFT_META_L4PROTO, + NFT_META_BRI_IIFNAME, + NFT_META_BRI_OIFNAME, }; /** diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h index 596ddd4..354a7e5 100644 --- a/include/uapi/linux/netfilter/nfnetlink.h +++ b/include/uapi/linux/netfilter/nfnetlink.h @@ -20,6 +20,8 @@ enum nfnetlink_groups { #define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_NFTABLES, #define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + NFNLGRP_ACCT_QUOTA, +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA __NFNLGRP_MAX, }; #define NFNLGRP_MAX (__NFNLGRP_MAX - 1) diff --git a/include/uapi/linux/netfilter/nfnetlink_acct.h b/include/uapi/linux/netfilter/nfnetlink_acct.h index c7b6269..51404ec 100644 --- a/include/uapi/linux/netfilter/nfnetlink_acct.h +++ b/include/uapi/linux/netfilter/nfnetlink_acct.h @@ -10,15 +10,24 @@ enum nfnl_acct_msg_types { NFNL_MSG_ACCT_GET, NFNL_MSG_ACCT_GET_CTRZERO, NFNL_MSG_ACCT_DEL, + NFNL_MSG_ACCT_OVERQUOTA, NFNL_MSG_ACCT_MAX }; +enum nfnl_acct_flags { + NFACCT_F_QUOTA_PKTS = (1 << 0), + NFACCT_F_QUOTA_BYTES = (1 << 1), + NFACCT_F_OVERQUOTA = (1 << 2), /* can't be set from userspace */ +}; + enum nfnl_acct_type { NFACCT_UNSPEC, NFACCT_NAME, NFACCT_PKTS, NFACCT_BYTES, NFACCT_USE, + NFACCT_FLAGS, + NFACCT_QUOTA, __NFACCT_MAX }; #define NFACCT_MAX (__NFACCT_MAX - 1) diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 9789dc9..9b19b44 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -273,11 +273,19 @@ struct sockaddr_nfc_llcp { * First byte is the adapter index * Second byte contains flags * - 0x01 - Direction (0=RX, 1=TX) - * - 0x02-0x80 - Reserved + * - 0x02-0x04 - Payload type (000=LLCP, 001=NCI, 010=HCI, 011=Digital, + * 100=Proprietary) + * - 0x05-0x80 - Reserved **/ -#define NFC_LLCP_RAW_HEADER_SIZE 2 -#define NFC_LLCP_DIRECTION_RX 0x00 -#define NFC_LLCP_DIRECTION_TX 0x01 +#define NFC_RAW_HEADER_SIZE 2 +#define NFC_DIRECTION_RX 0x00 +#define NFC_DIRECTION_TX 0x01 + +#define RAW_PAYLOAD_LLCP 0 +#define RAW_PAYLOAD_NCI 1 +#define RAW_PAYLOAD_HCI 2 +#define RAW_PAYLOAD_DIGITAL 3 +#define RAW_PAYLOAD_PROPRIETARY 4 /* socket option names */ #define NFC_LLCP_RW 0 diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 406010d..be9519b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -503,6 +503,9 @@ * TX status event pertaining to the TX request. * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * management frames at CCK rate or not in 2GHz band. + * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA + * counters which will be updated to the current value. This attribute + * is used during CSA period. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. @@ -1525,10 +1528,10 @@ enum nl80211_commands { * operation). * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information * for the time while performing a channel switch. - * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter - * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). - * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter - * field in the probe response (%NL80211_ATTR_PROBE_RESP). + * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel + * switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel + * switch counters in the probe response (%NL80211_ATTR_PROBE_RESP). * * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. * As specified in the &enum nl80211_rxmgmt_flags. @@ -1576,6 +1579,11 @@ enum nl80211_commands { * advertise values that cannot always be met. In such cases, an attempt * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. * + * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which + * should be updated when the frame is transmitted. + * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum + * supported number of csa counters. + * * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. * @@ -1920,6 +1928,9 @@ enum nl80211_attrs { NL80211_ATTR_IFACE_SOCKET_OWNER, + NL80211_ATTR_CSA_C_OFFSETS_TX, + NL80211_ATTR_MAX_CSA_COUNTERS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2188,6 +2199,8 @@ enum nl80211_sta_bss_param { * Contains a nested array of signal strength attributes (u8, dBm) * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. + * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the + * 802.11 header (u32, kbps) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2219,6 +2232,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_BYTES64, NL80211_STA_INFO_CHAIN_SIGNAL, NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + NL80211_STA_INFO_EXPECTED_THROUGHPUT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -3688,6 +3702,8 @@ enum nl80211_iface_limit_attrs { * different channels may be used within this group. * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap * of supported channel widths for radar detection. + * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap + * of supported regulatory regions for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -3721,6 +3737,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, /* keep last */ NUM_NL80211_IFACE_COMB, @@ -3894,6 +3911,8 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only + * here to reserve the value for API/ABI compatibility) * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station * mode @@ -3938,7 +3957,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, - /* bit 4 is reserved - don't use */ + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, NL80211_FEATURE_SAE = 1 << 5, NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_SCAN_FLUSH = 1 << 7, diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 970553c..0b979ee 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -395,7 +395,9 @@ struct ovs_key_nd { * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying * the actions to take for packets that match the key. Always present in * notifications. Required for %OVS_FLOW_CMD_NEW requests, optional for - * %OVS_FLOW_CMD_SET requests. + * %OVS_FLOW_CMD_SET requests. An %OVS_FLOW_CMD_SET without + * %OVS_FLOW_ATTR_ACTIONS will not modify the actions. To clear the actions, + * an %OVS_FLOW_ATTR_ACTIONS without any nested attributes must be given. * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this * flow. Present in notifications if the stats would be nonzero. Ignored in * requests. diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h index e2bcfd7..16574ea 100644 --- a/include/uapi/linux/udp.h +++ b/include/uapi/linux/udp.h @@ -29,6 +29,8 @@ struct udphdr { /* UDP socket options */ #define UDP_CORK 1 /* Never send partially complete segments */ #define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */ +#define UDP_NO_CHECK6_TX 101 /* Disable sending checksum for UDP6X */ +#define UDP_NO_CHECK6_RX 102 /* Disable accpeting checksum for UDP6 */ /* UDP encapsulation types */ #define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ diff --git a/init/main.c b/init/main.c index 9c7fd4c..48655ce 100644 --- a/init/main.c +++ b/init/main.c @@ -476,7 +476,7 @@ static void __init mm_init(void) vmalloc_init(); } -asmlinkage void __init start_kernel(void) +asmlinkage __visible void __init start_kernel(void) { char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 9fcdaa7..3f1ca93 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -348,7 +348,7 @@ struct cgrp_cset_link { * reference-counted, to improve performance when child cgroups * haven't been created. */ -static struct css_set init_css_set = { +struct css_set init_css_set = { .refcount = ATOMIC_INIT(1), .cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links), .tasks = LIST_HEAD_INIT(init_css_set.tasks), @@ -1495,7 +1495,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, */ if (!use_task_css_set_links) cgroup_enable_task_cg_lists(); -retry: + mutex_lock(&cgroup_tree_mutex); mutex_lock(&cgroup_mutex); @@ -1503,7 +1503,7 @@ retry: ret = parse_cgroupfs_options(data, &opts); if (ret) goto out_unlock; - +retry: /* look for a matching existing root */ if (!opts.subsys_mask && !opts.none && !opts.name) { cgrp_dfl_root_visible = true; @@ -1562,9 +1562,9 @@ retry: if (!atomic_inc_not_zero(&root->cgrp.refcnt)) { mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_tree_mutex); - kfree(opts.release_agent); - kfree(opts.name); msleep(10); + mutex_lock(&cgroup_tree_mutex); + mutex_lock(&cgroup_mutex); goto retry; } diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 2bc4a22..345628c 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -21,6 +21,7 @@ #include <linux/uaccess.h> #include <linux/freezer.h> #include <linux/seq_file.h> +#include <linux/mutex.h> /* * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is @@ -42,9 +43,10 @@ enum freezer_state_flags { struct freezer { struct cgroup_subsys_state css; unsigned int state; - spinlock_t lock; }; +static DEFINE_MUTEX(freezer_mutex); + static inline struct freezer *css_freezer(struct cgroup_subsys_state *css) { return css ? container_of(css, struct freezer, css) : NULL; @@ -93,7 +95,6 @@ freezer_css_alloc(struct cgroup_subsys_state *parent_css) if (!freezer) return ERR_PTR(-ENOMEM); - spin_lock_init(&freezer->lock); return &freezer->css; } @@ -110,14 +111,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css) struct freezer *freezer = css_freezer(css); struct freezer *parent = parent_freezer(freezer); - /* - * The following double locking and freezing state inheritance - * guarantee that @cgroup can never escape ancestors' freezing - * states. See css_for_each_descendant_pre() for details. - */ - if (parent) - spin_lock_irq(&parent->lock); - spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING); + mutex_lock(&freezer_mutex); freezer->state |= CGROUP_FREEZER_ONLINE; @@ -126,10 +120,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css) atomic_inc(&system_freezing_cnt); } - spin_unlock(&freezer->lock); - if (parent) - spin_unlock_irq(&parent->lock); - + mutex_unlock(&freezer_mutex); return 0; } @@ -144,14 +135,14 @@ static void freezer_css_offline(struct cgroup_subsys_state *css) { struct freezer *freezer = css_freezer(css); - spin_lock_irq(&freezer->lock); + mutex_lock(&freezer_mutex); if (freezer->state & CGROUP_FREEZING) atomic_dec(&system_freezing_cnt); freezer->state = 0; - spin_unlock_irq(&freezer->lock); + mutex_unlock(&freezer_mutex); } static void freezer_css_free(struct cgroup_subsys_state *css) @@ -175,7 +166,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css, struct task_struct *task; bool clear_frozen = false; - spin_lock_irq(&freezer->lock); + mutex_lock(&freezer_mutex); /* * Make the new tasks conform to the current state of @new_css. @@ -197,21 +188,13 @@ static void freezer_attach(struct cgroup_subsys_state *new_css, } } - spin_unlock_irq(&freezer->lock); - - /* - * Propagate FROZEN clearing upwards. We may race with - * update_if_frozen(), but as long as both work bottom-up, either - * update_if_frozen() sees child's FROZEN cleared or we clear the - * parent's FROZEN later. No parent w/ !FROZEN children can be - * left FROZEN. - */ + /* propagate FROZEN clearing upwards */ while (clear_frozen && (freezer = parent_freezer(freezer))) { - spin_lock_irq(&freezer->lock); freezer->state &= ~CGROUP_FROZEN; clear_frozen = freezer->state & CGROUP_FREEZING; - spin_unlock_irq(&freezer->lock); } + + mutex_unlock(&freezer_mutex); } /** @@ -228,9 +211,6 @@ static void freezer_fork(struct task_struct *task) { struct freezer *freezer; - rcu_read_lock(); - freezer = task_freezer(task); - /* * The root cgroup is non-freezable, so we can skip locking the * freezer. This is safe regardless of race with task migration. @@ -238,24 +218,18 @@ static void freezer_fork(struct task_struct *task) * to do. If we lost and root is the new cgroup, noop is still the * right thing to do. */ - if (!parent_freezer(freezer)) - goto out; + if (task_css_is_root(task, freezer_cgrp_id)) + return; - /* - * Grab @freezer->lock and freeze @task after verifying @task still - * belongs to @freezer and it's freezing. The former is for the - * case where we have raced against task migration and lost and - * @task is already in a different cgroup which may not be frozen. - * This isn't strictly necessary as freeze_task() is allowed to be - * called spuriously but let's do it anyway for, if nothing else, - * documentation. - */ - spin_lock_irq(&freezer->lock); - if (freezer == task_freezer(task) && (freezer->state & CGROUP_FREEZING)) + mutex_lock(&freezer_mutex); + rcu_read_lock(); + + freezer = task_freezer(task); + if (freezer->state & CGROUP_FREEZING) freeze_task(task); - spin_unlock_irq(&freezer->lock); -out: + rcu_read_unlock(); + mutex_unlock(&freezer_mutex); } /** @@ -281,22 +255,24 @@ static void update_if_frozen(struct cgroup_subsys_state *css) struct css_task_iter it; struct task_struct *task; - WARN_ON_ONCE(!rcu_read_lock_held()); - - spin_lock_irq(&freezer->lock); + lockdep_assert_held(&freezer_mutex); if (!(freezer->state & CGROUP_FREEZING) || (freezer->state & CGROUP_FROZEN)) - goto out_unlock; + return; /* are all (live) children frozen? */ + rcu_read_lock(); css_for_each_child(pos, css) { struct freezer *child = css_freezer(pos); if ((child->state & CGROUP_FREEZER_ONLINE) && - !(child->state & CGROUP_FROZEN)) - goto out_unlock; + !(child->state & CGROUP_FROZEN)) { + rcu_read_unlock(); + return; + } } + rcu_read_unlock(); /* are all tasks frozen? */ css_task_iter_start(css, &it); @@ -317,21 +293,29 @@ static void update_if_frozen(struct cgroup_subsys_state *css) freezer->state |= CGROUP_FROZEN; out_iter_end: css_task_iter_end(&it); -out_unlock: - spin_unlock_irq(&freezer->lock); } static int freezer_read(struct seq_file *m, void *v) { struct cgroup_subsys_state *css = seq_css(m), *pos; + mutex_lock(&freezer_mutex); rcu_read_lock(); /* update states bottom-up */ - css_for_each_descendant_post(pos, css) + css_for_each_descendant_post(pos, css) { + if (!css_tryget(pos)) + continue; + rcu_read_unlock(); + update_if_frozen(pos); + rcu_read_lock(); + css_put(pos); + } + rcu_read_unlock(); + mutex_unlock(&freezer_mutex); seq_puts(m, freezer_state_strs(css_freezer(css)->state)); seq_putc(m, '\n'); @@ -373,7 +357,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze, unsigned int state) { /* also synchronizes against task migration, see freezer_attach() */ - lockdep_assert_held(&freezer->lock); + lockdep_assert_held(&freezer_mutex); if (!(freezer->state & CGROUP_FREEZER_ONLINE)) return; @@ -414,31 +398,29 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) * descendant will try to inherit its parent's FREEZING state as * CGROUP_FREEZING_PARENT. */ + mutex_lock(&freezer_mutex); rcu_read_lock(); css_for_each_descendant_pre(pos, &freezer->css) { struct freezer *pos_f = css_freezer(pos); struct freezer *parent = parent_freezer(pos_f); - spin_lock_irq(&pos_f->lock); + if (!css_tryget(pos)) + continue; + rcu_read_unlock(); - if (pos_f == freezer) { + if (pos_f == freezer) freezer_apply_state(pos_f, freeze, CGROUP_FREEZING_SELF); - } else { - /* - * Our update to @parent->state is already visible - * which is all we need. No need to lock @parent. - * For more info on synchronization, see - * freezer_post_create(). - */ + else freezer_apply_state(pos_f, parent->state & CGROUP_FREEZING, CGROUP_FREEZING_PARENT); - } - spin_unlock_irq(&pos_f->lock); + rcu_read_lock(); + css_put(pos); } rcu_read_unlock(); + mutex_unlock(&freezer_mutex); } static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft, diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 6cb20d2..019d450 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -120,7 +120,7 @@ void context_tracking_user_enter(void) * instead of preempt_schedule() to exit user context if needed before * calling the scheduler. */ -asmlinkage void __sched notrace preempt_schedule_context(void) +asmlinkage __visible void __sched notrace preempt_schedule_context(void) { enum ctx_state prev_ctx; diff --git a/kernel/events/core.c b/kernel/events/core.c index f83a71a..440eefc 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1443,6 +1443,11 @@ group_sched_out(struct perf_event *group_event, cpuctx->exclusive = 0; } +struct remove_event { + struct perf_event *event; + bool detach_group; +}; + /* * Cross CPU call to remove a performance event * @@ -1451,12 +1456,15 @@ group_sched_out(struct perf_event *group_event, */ static int __perf_remove_from_context(void *info) { - struct perf_event *event = info; + struct remove_event *re = info; + struct perf_event *event = re->event; struct perf_event_context *ctx = event->ctx; struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); raw_spin_lock(&ctx->lock); event_sched_out(event, cpuctx, ctx); + if (re->detach_group) + perf_group_detach(event); list_del_event(event, ctx); if (!ctx->nr_events && cpuctx->task_ctx == ctx) { ctx->is_active = 0; @@ -1481,10 +1489,14 @@ static int __perf_remove_from_context(void *info) * When called from perf_event_exit_task, it's OK because the * context has been detached from its task. */ -static void perf_remove_from_context(struct perf_event *event) +static void perf_remove_from_context(struct perf_event *event, bool detach_group) { struct perf_event_context *ctx = event->ctx; struct task_struct *task = ctx->task; + struct remove_event re = { + .event = event, + .detach_group = detach_group, + }; lockdep_assert_held(&ctx->mutex); @@ -1493,12 +1505,12 @@ static void perf_remove_from_context(struct perf_event *event) * Per cpu events are removed via an smp call and * the removal is always successful. */ - cpu_function_call(event->cpu, __perf_remove_from_context, event); + cpu_function_call(event->cpu, __perf_remove_from_context, &re); return; } retry: - if (!task_function_call(task, __perf_remove_from_context, event)) + if (!task_function_call(task, __perf_remove_from_context, &re)) return; raw_spin_lock_irq(&ctx->lock); @@ -1515,6 +1527,8 @@ retry: * Since the task isn't running, its safe to remove the event, us * holding the ctx->lock ensures the task won't get scheduled in. */ + if (detach_group) + perf_group_detach(event); list_del_event(event, ctx); raw_spin_unlock_irq(&ctx->lock); } @@ -3178,7 +3192,8 @@ static void free_event_rcu(struct rcu_head *head) } static void ring_buffer_put(struct ring_buffer *rb); -static void ring_buffer_detach(struct perf_event *event, struct ring_buffer *rb); +static void ring_buffer_attach(struct perf_event *event, + struct ring_buffer *rb); static void unaccount_event_cpu(struct perf_event *event, int cpu) { @@ -3238,8 +3253,6 @@ static void free_event(struct perf_event *event) unaccount_event(event); if (event->rb) { - struct ring_buffer *rb; - /* * Can happen when we close an event with re-directed output. * @@ -3247,12 +3260,7 @@ static void free_event(struct perf_event *event) * over us; possibly making our ring_buffer_put() the last. */ mutex_lock(&event->mmap_mutex); - rb = event->rb; - if (rb) { - rcu_assign_pointer(event->rb, NULL); - ring_buffer_detach(event, rb); - ring_buffer_put(rb); /* could be last */ - } + ring_buffer_attach(event, NULL); mutex_unlock(&event->mmap_mutex); } @@ -3281,10 +3289,7 @@ int perf_event_release_kernel(struct perf_event *event) * to trigger the AB-BA case. */ mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); - raw_spin_lock_irq(&ctx->lock); - perf_group_detach(event); - raw_spin_unlock_irq(&ctx->lock); - perf_remove_from_context(event); + perf_remove_from_context(event, true); mutex_unlock(&ctx->mutex); free_event(event); @@ -3839,28 +3844,47 @@ unlock: static void ring_buffer_attach(struct perf_event *event, struct ring_buffer *rb) { + struct ring_buffer *old_rb = NULL; unsigned long flags; - if (!list_empty(&event->rb_entry)) - return; + if (event->rb) { + /* + * Should be impossible, we set this when removing + * event->rb_entry and wait/clear when adding event->rb_entry. + */ + WARN_ON_ONCE(event->rcu_pending); - spin_lock_irqsave(&rb->event_lock, flags); - if (list_empty(&event->rb_entry)) - list_add(&event->rb_entry, &rb->event_list); - spin_unlock_irqrestore(&rb->event_lock, flags); -} + old_rb = event->rb; + event->rcu_batches = get_state_synchronize_rcu(); + event->rcu_pending = 1; -static void ring_buffer_detach(struct perf_event *event, struct ring_buffer *rb) -{ - unsigned long flags; + spin_lock_irqsave(&old_rb->event_lock, flags); + list_del_rcu(&event->rb_entry); + spin_unlock_irqrestore(&old_rb->event_lock, flags); + } - if (list_empty(&event->rb_entry)) - return; + if (event->rcu_pending && rb) { + cond_synchronize_rcu(event->rcu_batches); + event->rcu_pending = 0; + } + + if (rb) { + spin_lock_irqsave(&rb->event_lock, flags); + list_add_rcu(&event->rb_entry, &rb->event_list); + spin_unlock_irqrestore(&rb->event_lock, flags); + } + + rcu_assign_pointer(event->rb, rb); - spin_lock_irqsave(&rb->event_lock, flags); - list_del_init(&event->rb_entry); - wake_up_all(&event->waitq); - spin_unlock_irqrestore(&rb->event_lock, flags); + if (old_rb) { + ring_buffer_put(old_rb); + /* + * Since we detached before setting the new rb, so that we + * could attach the new rb, we could have missed a wakeup. + * Provide it now. + */ + wake_up_all(&event->waitq); + } } static void ring_buffer_wakeup(struct perf_event *event) @@ -3929,7 +3953,7 @@ static void perf_mmap_close(struct vm_area_struct *vma) { struct perf_event *event = vma->vm_file->private_data; - struct ring_buffer *rb = event->rb; + struct ring_buffer *rb = ring_buffer_get(event); struct user_struct *mmap_user = rb->mmap_user; int mmap_locked = rb->mmap_locked; unsigned long size = perf_data_size(rb); @@ -3937,18 +3961,14 @@ static void perf_mmap_close(struct vm_area_struct *vma) atomic_dec(&rb->mmap_count); if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) - return; + goto out_put; - /* Detach current event from the buffer. */ - rcu_assign_pointer(event->rb, NULL); - ring_buffer_detach(event, rb); + ring_buffer_attach(event, NULL); mutex_unlock(&event->mmap_mutex); /* If there's still other mmap()s of this buffer, we're done. */ - if (atomic_read(&rb->mmap_count)) { - ring_buffer_put(rb); /* can't be last */ - return; - } + if (atomic_read(&rb->mmap_count)) + goto out_put; /* * No other mmap()s, detach from all other events that might redirect @@ -3978,11 +3998,9 @@ again: * still restart the iteration to make sure we're not now * iterating the wrong list. */ - if (event->rb == rb) { - rcu_assign_pointer(event->rb, NULL); - ring_buffer_detach(event, rb); - ring_buffer_put(rb); /* can't be last, we still have one */ - } + if (event->rb == rb) + ring_buffer_attach(event, NULL); + mutex_unlock(&event->mmap_mutex); put_event(event); @@ -4007,6 +4025,7 @@ again: vma->vm_mm->pinned_vm -= mmap_locked; free_uid(mmap_user); +out_put: ring_buffer_put(rb); /* could be last */ } @@ -4124,7 +4143,6 @@ again: vma->vm_mm->pinned_vm += extra; ring_buffer_attach(event, rb); - rcu_assign_pointer(event->rb, rb); perf_event_init_userpage(event); perf_event_update_userpage(event); @@ -5408,6 +5426,9 @@ struct swevent_htable { /* Recursion avoidance in each contexts */ int recursion[PERF_NR_CONTEXTS]; + + /* Keeps track of cpu being initialized/exited */ + bool online; }; static DEFINE_PER_CPU(struct swevent_htable, swevent_htable); @@ -5654,8 +5675,14 @@ static int perf_swevent_add(struct perf_event *event, int flags) hwc->state = !(flags & PERF_EF_START); head = find_swevent_head(swhash, event); - if (WARN_ON_ONCE(!head)) + if (!head) { + /* + * We can race with cpu hotplug code. Do not + * WARN if the cpu just got unplugged. + */ + WARN_ON_ONCE(swhash->online); return -EINVAL; + } hlist_add_head_rcu(&event->hlist_entry, head); @@ -6914,7 +6941,7 @@ err_size: static int perf_event_set_output(struct perf_event *event, struct perf_event *output_event) { - struct ring_buffer *rb = NULL, *old_rb = NULL; + struct ring_buffer *rb = NULL; int ret = -EINVAL; if (!output_event) @@ -6942,8 +6969,6 @@ set: if (atomic_read(&event->mmap_count)) goto unlock; - old_rb = event->rb; - if (output_event) { /* get the rb we want to redirect to */ rb = ring_buffer_get(output_event); @@ -6951,23 +6976,7 @@ set: goto unlock; } - if (old_rb) - ring_buffer_detach(event, old_rb); - - if (rb) - ring_buffer_attach(event, rb); - - rcu_assign_pointer(event->rb, rb); - - if (old_rb) { - ring_buffer_put(old_rb); - /* - * Since we detached before setting the new rb, so that we - * could attach the new rb, we could have missed a wakeup. - * Provide it now. - */ - wake_up_all(&event->waitq); - } + ring_buffer_attach(event, rb); ret = 0; unlock: @@ -7018,6 +7027,9 @@ SYSCALL_DEFINE5(perf_event_open, if (attr.freq) { if (attr.sample_freq > sysctl_perf_event_sample_rate) return -EINVAL; + } else { + if (attr.sample_period & (1ULL << 63)) + return -EINVAL; } /* @@ -7165,7 +7177,7 @@ SYSCALL_DEFINE5(perf_event_open, struct perf_event_context *gctx = group_leader->ctx; mutex_lock(&gctx->mutex); - perf_remove_from_context(group_leader); + perf_remove_from_context(group_leader, false); /* * Removing from the context ends up with disabled @@ -7175,7 +7187,7 @@ SYSCALL_DEFINE5(perf_event_open, perf_event__state_init(group_leader); list_for_each_entry(sibling, &group_leader->sibling_list, group_entry) { - perf_remove_from_context(sibling); + perf_remove_from_context(sibling, false); perf_event__state_init(sibling); put_ctx(gctx); } @@ -7305,7 +7317,7 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) mutex_lock(&src_ctx->mutex); list_for_each_entry_safe(event, tmp, &src_ctx->event_list, event_entry) { - perf_remove_from_context(event); + perf_remove_from_context(event, false); unaccount_event_cpu(event, src_cpu); put_ctx(src_ctx); list_add(&event->migrate_entry, &events); @@ -7367,13 +7379,7 @@ __perf_event_exit_task(struct perf_event *child_event, struct perf_event_context *child_ctx, struct task_struct *child) { - if (child_event->parent) { - raw_spin_lock_irq(&child_ctx->lock); - perf_group_detach(child_event); - raw_spin_unlock_irq(&child_ctx->lock); - } - - perf_remove_from_context(child_event); + perf_remove_from_context(child_event, !!child_event->parent); /* * It can happen that the parent exits first, and has events @@ -7724,6 +7730,8 @@ int perf_event_init_context(struct task_struct *child, int ctxn) * swapped under us. */ parent_ctx = perf_pin_task_context(parent, ctxn); + if (!parent_ctx) + return 0; /* * No need to check if parent_ctx != NULL here; since we saw @@ -7835,6 +7843,7 @@ static void perf_event_init_cpu(int cpu) struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); mutex_lock(&swhash->hlist_mutex); + swhash->online = true; if (swhash->hlist_refcount > 0) { struct swevent_hlist *hlist; @@ -7857,14 +7866,14 @@ static void perf_pmu_rotate_stop(struct pmu *pmu) static void __perf_event_exit_context(void *__info) { + struct remove_event re = { .detach_group = false }; struct perf_event_context *ctx = __info; - struct perf_event *event; perf_pmu_rotate_stop(ctx->pmu); rcu_read_lock(); - list_for_each_entry_rcu(event, &ctx->event_list, event_entry) - __perf_remove_from_context(event); + list_for_each_entry_rcu(re.event, &ctx->event_list, event_entry) + __perf_remove_from_context(&re); rcu_read_unlock(); } @@ -7892,6 +7901,7 @@ static void perf_event_exit_cpu(int cpu) perf_event_exit_cpu_context(cpu); mutex_lock(&swhash->hlist_mutex); + swhash->online = false; swevent_hlist_release(swhash); mutex_unlock(&swhash->hlist_mutex); } diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 6b715c0..e0501fe 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -990,11 +990,8 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, /* Remove an active timer from the queue: */ ret = remove_hrtimer(timer, base); - /* Switch the timer base, if necessary: */ - new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED); - if (mode & HRTIMER_MODE_REL) { - tim = ktime_add_safe(tim, new_base->get_time()); + tim = ktime_add_safe(tim, base->get_time()); /* * CONFIG_TIME_LOW_RES is a temporary way for architectures * to signal that they simply return xtime in @@ -1009,6 +1006,9 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, hrtimer_set_expires_range_ns(timer, tim, delta_ns); + /* Switch the timer base, if necessary: */ + new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED); + timer_stats_hrtimer_set_start_info(timer); leftmost = enqueue_hrtimer(timer, new_base); diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index b0e9467..d24e433 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -4188,7 +4188,7 @@ void debug_show_held_locks(struct task_struct *task) } EXPORT_SYMBOL_GPL(debug_show_held_locks); -asmlinkage void lockdep_sys_exit(void) +asmlinkage __visible void lockdep_sys_exit(void) { struct task_struct *curr = current; diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 18fb7a2..1ea328a 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1586,7 +1586,7 @@ swsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm, return -ENOMEM; } -asmlinkage int swsusp_save(void) +asmlinkage __visible int swsusp_save(void) { unsigned int nr_pages, nr_highmem; diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index a45b509..7228258 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1674,7 +1674,7 @@ EXPORT_SYMBOL(printk_emit); * * See the vsnprintf() documentation for format string extensions over C99. */ -asmlinkage int printk(const char *fmt, ...) +asmlinkage __visible int printk(const char *fmt, ...) { va_list args; int r; @@ -1737,7 +1737,7 @@ void early_vprintk(const char *fmt, va_list ap) } } -asmlinkage void early_printk(const char *fmt, ...) +asmlinkage __visible void early_printk(const char *fmt, ...) { va_list ap; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 268a45e..204d3d2 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2192,7 +2192,7 @@ static inline void post_schedule(struct rq *rq) * schedule_tail - first thing a freshly forked thread must call. * @prev: the thread we just switched away from. */ -asmlinkage void schedule_tail(struct task_struct *prev) +asmlinkage __visible void schedule_tail(struct task_struct *prev) __releases(rq->lock) { struct rq *rq = this_rq(); @@ -2592,8 +2592,14 @@ pick_next_task(struct rq *rq, struct task_struct *prev) if (likely(prev->sched_class == class && rq->nr_running == rq->cfs.h_nr_running)) { p = fair_sched_class.pick_next_task(rq, prev); - if (likely(p && p != RETRY_TASK)) - return p; + if (unlikely(p == RETRY_TASK)) + goto again; + + /* assumes fair_sched_class->next == idle_sched_class */ + if (unlikely(!p)) + p = idle_sched_class.pick_next_task(rq, prev); + + return p; } again: @@ -2741,7 +2747,7 @@ static inline void sched_submit_work(struct task_struct *tsk) blk_schedule_flush_plug(tsk); } -asmlinkage void __sched schedule(void) +asmlinkage __visible void __sched schedule(void) { struct task_struct *tsk = current; @@ -2751,7 +2757,7 @@ asmlinkage void __sched schedule(void) EXPORT_SYMBOL(schedule); #ifdef CONFIG_CONTEXT_TRACKING -asmlinkage void __sched schedule_user(void) +asmlinkage __visible void __sched schedule_user(void) { /* * If we come here after a random call to set_need_resched(), @@ -2783,7 +2789,7 @@ void __sched schedule_preempt_disabled(void) * off of preempt_enable. Kernel preemptions off return from interrupt * occur there and call schedule directly. */ -asmlinkage void __sched notrace preempt_schedule(void) +asmlinkage __visible void __sched notrace preempt_schedule(void) { /* * If there is a non-zero preempt_count or interrupts are disabled, @@ -2813,7 +2819,7 @@ EXPORT_SYMBOL(preempt_schedule); * Note, that this is called and return with irqs disabled. This will * protect us against recursive calling from irq. */ -asmlinkage void __sched preempt_schedule_irq(void) +asmlinkage __visible void __sched preempt_schedule_irq(void) { enum ctx_state prev_state; @@ -3124,6 +3130,7 @@ __setparam_dl(struct task_struct *p, const struct sched_attr *attr) dl_se->dl_bw = to_ratio(dl_se->dl_period, dl_se->dl_runtime); dl_se->dl_throttled = 0; dl_se->dl_new = 1; + dl_se->dl_yielded = 0; } static void __setscheduler_params(struct task_struct *p, @@ -3639,6 +3646,7 @@ SYSCALL_DEFINE2(sched_setparam, pid_t, pid, struct sched_param __user *, param) * sys_sched_setattr - same as above, but with extended sched_attr * @pid: the pid in question. * @uattr: structure containing the extended parameters. + * @flags: for future extension. */ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr, unsigned int, flags) @@ -3783,6 +3791,7 @@ err_size: * @pid: the pid in question. * @uattr: structure containing the extended parameters. * @size: sizeof(attr) for fwd/bwd comp. + * @flags: for future extension. */ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, unsigned int, size, unsigned int, flags) @@ -6017,6 +6026,8 @@ sd_numa_init(struct sched_domain_topology_level *tl, int cpu) , .last_balance = jiffies, .balance_interval = sd_weight, + .max_newidle_lb_cost = 0, + .next_decay_max_lb_cost = jiffies, }; SD_INIT_NAME(sd, NUMA); sd->private = &tl->data; diff --git a/kernel/sched/cpudeadline.c b/kernel/sched/cpudeadline.c index 5b9bb42..ab001b5 100644 --- a/kernel/sched/cpudeadline.c +++ b/kernel/sched/cpudeadline.c @@ -210,7 +210,5 @@ int cpudl_init(struct cpudl *cp) */ void cpudl_cleanup(struct cpudl *cp) { - /* - * nothing to do for the moment - */ + free_cpumask_var(cp->free_cpus); } diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c index 8b836b3..3031bac 100644 --- a/kernel/sched/cpupri.c +++ b/kernel/sched/cpupri.c @@ -70,8 +70,7 @@ int cpupri_find(struct cpupri *cp, struct task_struct *p, int idx = 0; int task_pri = convert_prio(p->prio); - if (task_pri >= MAX_RT_PRIO) - return 0; + BUG_ON(task_pri >= CPUPRI_NR_PRIORITIES); for (idx = 0; idx < task_pri; idx++) { struct cpupri_vec *vec = &cp->pri_to_cpu[idx]; diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index a95097c..72fdf06 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -332,50 +332,50 @@ out: * softirq as those do not count in task exec_runtime any more. */ static void irqtime_account_process_tick(struct task_struct *p, int user_tick, - struct rq *rq) + struct rq *rq, int ticks) { - cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy); + cputime_t scaled = cputime_to_scaled(cputime_one_jiffy); + u64 cputime = (__force u64) cputime_one_jiffy; u64 *cpustat = kcpustat_this_cpu->cpustat; if (steal_account_process_tick()) return; + cputime *= ticks; + scaled *= ticks; + if (irqtime_account_hi_update()) { - cpustat[CPUTIME_IRQ] += (__force u64) cputime_one_jiffy; + cpustat[CPUTIME_IRQ] += cputime; } else if (irqtime_account_si_update()) { - cpustat[CPUTIME_SOFTIRQ] += (__force u64) cputime_one_jiffy; + cpustat[CPUTIME_SOFTIRQ] += cputime; } else if (this_cpu_ksoftirqd() == p) { /* * ksoftirqd time do not get accounted in cpu_softirq_time. * So, we have to handle it separately here. * Also, p->stime needs to be updated for ksoftirqd. */ - __account_system_time(p, cputime_one_jiffy, one_jiffy_scaled, - CPUTIME_SOFTIRQ); + __account_system_time(p, cputime, scaled, CPUTIME_SOFTIRQ); } else if (user_tick) { - account_user_time(p, cputime_one_jiffy, one_jiffy_scaled); + account_user_time(p, cputime, scaled); } else if (p == rq->idle) { - account_idle_time(cputime_one_jiffy); + account_idle_time(cputime); } else if (p->flags & PF_VCPU) { /* System time or guest time */ - account_guest_time(p, cputime_one_jiffy, one_jiffy_scaled); + account_guest_time(p, cputime, scaled); } else { - __account_system_time(p, cputime_one_jiffy, one_jiffy_scaled, - CPUTIME_SYSTEM); + __account_system_time(p, cputime, scaled, CPUTIME_SYSTEM); } } static void irqtime_account_idle_ticks(int ticks) { - int i; struct rq *rq = this_rq(); - for (i = 0; i < ticks; i++) - irqtime_account_process_tick(current, 0, rq); + irqtime_account_process_tick(current, 0, rq, ticks); } #else /* CONFIG_IRQ_TIME_ACCOUNTING */ static inline void irqtime_account_idle_ticks(int ticks) {} static inline void irqtime_account_process_tick(struct task_struct *p, int user_tick, - struct rq *rq) {} + struct rq *rq, int nr_ticks) {} #endif /* CONFIG_IRQ_TIME_ACCOUNTING */ /* @@ -464,7 +464,7 @@ void account_process_tick(struct task_struct *p, int user_tick) return; if (sched_clock_irqtime) { - irqtime_account_process_tick(p, user_tick, rq); + irqtime_account_process_tick(p, user_tick, rq, 1); return; } diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index b080957..800e99b 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -528,6 +528,7 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer) sched_clock_tick(); update_rq_clock(rq); dl_se->dl_throttled = 0; + dl_se->dl_yielded = 0; if (p->on_rq) { enqueue_task_dl(rq, p, ENQUEUE_REPLENISH); if (task_has_dl_policy(rq->curr)) @@ -893,10 +894,10 @@ static void yield_task_dl(struct rq *rq) * We make the task go to sleep until its current deadline by * forcing its runtime to zero. This way, update_curr_dl() stops * it and the bandwidth timer will wake it up and will give it - * new scheduling parameters (thanks to dl_new=1). + * new scheduling parameters (thanks to dl_yielded=1). */ if (p->dl.runtime > 0) { - rq->curr->dl.dl_new = 1; + rq->curr->dl.dl_yielded = 1; p->dl.runtime = 0; } update_curr_dl(rq); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7570dd9..0fdb96d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6653,6 +6653,7 @@ static int idle_balance(struct rq *this_rq) int this_cpu = this_rq->cpu; idle_enter_fair(this_rq); + /* * We must set idle_stamp _before_ calling idle_balance(), such that we * measure the duration of idle_balance() as idle time. @@ -6705,14 +6706,16 @@ static int idle_balance(struct rq *this_rq) raw_spin_lock(&this_rq->lock); + if (curr_cost > this_rq->max_idle_balance_cost) + this_rq->max_idle_balance_cost = curr_cost; + /* - * While browsing the domains, we released the rq lock. - * A task could have be enqueued in the meantime + * While browsing the domains, we released the rq lock, a task could + * have been enqueued in the meantime. Since we're not going idle, + * pretend we pulled a task. */ - if (this_rq->cfs.h_nr_running && !pulled_task) { + if (this_rq->cfs.h_nr_running && !pulled_task) pulled_task = 1; - goto out; - } if (pulled_task || time_after(jiffies, this_rq->next_balance)) { /* @@ -6722,9 +6725,6 @@ static int idle_balance(struct rq *this_rq) this_rq->next_balance = next_balance; } - if (curr_cost > this_rq->max_idle_balance_cost) - this_rq->max_idle_balance_cost = curr_cost; - out: /* Is there a task of a high priority class? */ if (this_rq->nr_running != this_rq->cfs.h_nr_running && diff --git a/kernel/seccomp.c b/kernel/seccomp.c index b35c215..44e6948 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -54,8 +54,7 @@ struct seccomp_filter { atomic_t usage; struct seccomp_filter *prev; - unsigned short len; /* Instruction count */ - struct sock_filter_int insnsi[]; + struct sk_filter *prog; }; /* Limit any path through the tree to 256KB worth of instructions. */ @@ -104,60 +103,59 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) u32 k = ftest->k; switch (code) { - case BPF_S_LD_W_ABS: + case BPF_LD | BPF_W | BPF_ABS: ftest->code = BPF_LDX | BPF_W | BPF_ABS; /* 32-bit aligned and not out of bounds. */ if (k >= sizeof(struct seccomp_data) || k & 3) return -EINVAL; continue; - case BPF_S_LD_W_LEN: + case BPF_LD | BPF_W | BPF_LEN: ftest->code = BPF_LD | BPF_IMM; ftest->k = sizeof(struct seccomp_data); continue; - case BPF_S_LDX_W_LEN: + case BPF_LDX | BPF_W | BPF_LEN: ftest->code = BPF_LDX | BPF_IMM; ftest->k = sizeof(struct seccomp_data); continue; /* Explicitly include allowed calls. */ - case BPF_S_RET_K: - case BPF_S_RET_A: - case BPF_S_ALU_ADD_K: - case BPF_S_ALU_ADD_X: - case BPF_S_ALU_SUB_K: - case BPF_S_ALU_SUB_X: - case BPF_S_ALU_MUL_K: - case BPF_S_ALU_MUL_X: - case BPF_S_ALU_DIV_X: - case BPF_S_ALU_AND_K: - case BPF_S_ALU_AND_X: - case BPF_S_ALU_OR_K: - case BPF_S_ALU_OR_X: - case BPF_S_ALU_XOR_K: - case BPF_S_ALU_XOR_X: - case BPF_S_ALU_LSH_K: - case BPF_S_ALU_LSH_X: - case BPF_S_ALU_RSH_K: - case BPF_S_ALU_RSH_X: - case BPF_S_ALU_NEG: - case BPF_S_LD_IMM: - case BPF_S_LDX_IMM: - case BPF_S_MISC_TAX: - case BPF_S_MISC_TXA: - case BPF_S_ALU_DIV_K: - case BPF_S_LD_MEM: - case BPF_S_LDX_MEM: - case BPF_S_ST: - case BPF_S_STX: - case BPF_S_JMP_JA: - case BPF_S_JMP_JEQ_K: - case BPF_S_JMP_JEQ_X: - case BPF_S_JMP_JGE_K: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JSET_K: - case BPF_S_JMP_JSET_X: - sk_decode_filter(ftest, ftest); + case BPF_RET | BPF_K: + case BPF_RET | BPF_A: + case BPF_ALU | BPF_ADD | BPF_K: + case BPF_ALU | BPF_ADD | BPF_X: + case BPF_ALU | BPF_SUB | BPF_K: + case BPF_ALU | BPF_SUB | BPF_X: + case BPF_ALU | BPF_MUL | BPF_K: + case BPF_ALU | BPF_MUL | BPF_X: + case BPF_ALU | BPF_DIV | BPF_K: + case BPF_ALU | BPF_DIV | BPF_X: + case BPF_ALU | BPF_AND | BPF_K: + case BPF_ALU | BPF_AND | BPF_X: + case BPF_ALU | BPF_OR | BPF_K: + case BPF_ALU | BPF_OR | BPF_X: + case BPF_ALU | BPF_XOR | BPF_K: + case BPF_ALU | BPF_XOR | BPF_X: + case BPF_ALU | BPF_LSH | BPF_K: + case BPF_ALU | BPF_LSH | BPF_X: + case BPF_ALU | BPF_RSH | BPF_K: + case BPF_ALU | BPF_RSH | BPF_X: + case BPF_ALU | BPF_NEG: + case BPF_LD | BPF_IMM: + case BPF_LDX | BPF_IMM: + case BPF_MISC | BPF_TAX: + case BPF_MISC | BPF_TXA: + case BPF_LD | BPF_MEM: + case BPF_LDX | BPF_MEM: + case BPF_ST: + case BPF_STX: + case BPF_JMP | BPF_JA: + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JSET | BPF_K: + case BPF_JMP | BPF_JSET | BPF_X: continue; default: return -EINVAL; @@ -189,7 +187,8 @@ static u32 seccomp_run_filters(int syscall) * value always takes priority (ignoring the DATA). */ for (f = current->seccomp.filter; f; f = f->prev) { - u32 cur_ret = sk_run_filter_int_seccomp(&sd, f->insnsi); + u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd); + if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) ret = cur_ret; } @@ -215,7 +214,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) return -EINVAL; for (filter = current->seccomp.filter; filter; filter = filter->prev) - total_insns += filter->len + 4; /* include a 4 instr penalty */ + total_insns += filter->prog->len + 4; /* include a 4 instr penalty */ if (total_insns > MAX_INSNS_PER_PATH) return -ENOMEM; @@ -256,19 +255,25 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) /* Allocate a new seccomp_filter */ ret = -ENOMEM; - filter = kzalloc(sizeof(struct seccomp_filter) + - sizeof(struct sock_filter_int) * new_len, + filter = kzalloc(sizeof(struct seccomp_filter), GFP_KERNEL|__GFP_NOWARN); if (!filter) goto free_prog; - ret = sk_convert_filter(fp, fprog->len, filter->insnsi, &new_len); - if (ret) + filter->prog = kzalloc(sk_filter_size(new_len), + GFP_KERNEL|__GFP_NOWARN); + if (!filter->prog) goto free_filter; + + ret = sk_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len); + if (ret) + goto free_filter_prog; kfree(fp); atomic_set(&filter->usage, 1); - filter->len = new_len; + filter->prog->len = new_len; + + sk_filter_select_runtime(filter->prog); /* * If there is an existing filter, make it the prev and don't drop its @@ -278,6 +283,8 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) current->seccomp.filter = filter; return 0; +free_filter_prog: + kfree(filter->prog); free_filter: kfree(filter); free_prog: @@ -330,6 +337,7 @@ void put_seccomp_filter(struct task_struct *tsk) while (orig && atomic_dec_and_test(&orig->usage)) { struct seccomp_filter *freeme = orig; orig = orig->prev; + sk_filter_free(freeme->prog); kfree(freeme); } } diff --git a/kernel/softirq.c b/kernel/softirq.c index 33e4648..92f24f5 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -223,7 +223,7 @@ static inline bool lockdep_softirq_start(void) { return false; } static inline void lockdep_softirq_end(bool in_hardirq) { } #endif -asmlinkage void __do_softirq(void) +asmlinkage __visible void __do_softirq(void) { unsigned long end = jiffies + MAX_SOFTIRQ_TIME; unsigned long old_flags = current->flags; @@ -299,7 +299,7 @@ restart: tsk_restore_flags(current, old_flags, PF_MEMALLOC); } -asmlinkage void do_softirq(void) +asmlinkage __visible void do_softirq(void) { __u32 pending; unsigned long flags; diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index ac5b23c..6620e58 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -188,7 +188,6 @@ static int tracepoint_add_func(struct tracepoint *tp, WARN_ON_ONCE(1); return PTR_ERR(old); } - release_probes(old); /* * rcu_assign_pointer has a smp_wmb() which makes sure that the new @@ -200,6 +199,7 @@ static int tracepoint_add_func(struct tracepoint *tp, rcu_assign_pointer(tp->funcs, tp_funcs); if (!static_key_enabled(&tp->key)) static_key_slow_inc(&tp->key); + release_probes(old); return 0; } @@ -221,7 +221,6 @@ static int tracepoint_remove_func(struct tracepoint *tp, WARN_ON_ONCE(1); return PTR_ERR(old); } - release_probes(old); if (!tp_funcs) { /* Removed last function */ @@ -232,6 +231,7 @@ static int tracepoint_remove_func(struct tracepoint *tp, static_key_slow_dec(&tp->key); } rcu_assign_pointer(tp->funcs, tp_funcs); + release_probes(old); return 0; } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 0ee63af..8edc871 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1916,6 +1916,12 @@ static void send_mayday(struct work_struct *work) /* mayday mayday mayday */ if (list_empty(&pwq->mayday_node)) { + /* + * If @pwq is for an unbound wq, its base ref may be put at + * any time due to an attribute change. Pin @pwq until the + * rescuer is done with it. + */ + get_pwq(pwq); list_add_tail(&pwq->mayday_node, &wq->maydays); wake_up_process(wq->rescuer->task); } @@ -2398,6 +2404,7 @@ static int rescuer_thread(void *__rescuer) struct worker *rescuer = __rescuer; struct workqueue_struct *wq = rescuer->rescue_wq; struct list_head *scheduled = &rescuer->scheduled; + bool should_stop; set_user_nice(current, RESCUER_NICE_LEVEL); @@ -2409,11 +2416,15 @@ static int rescuer_thread(void *__rescuer) repeat: set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) { - __set_current_state(TASK_RUNNING); - rescuer->task->flags &= ~PF_WQ_WORKER; - return 0; - } + /* + * By the time the rescuer is requested to stop, the workqueue + * shouldn't have any work pending, but @wq->maydays may still have + * pwq(s) queued. This can happen by non-rescuer workers consuming + * all the work items before the rescuer got to them. Go through + * @wq->maydays processing before acting on should_stop so that the + * list is always empty on exit. + */ + should_stop = kthread_should_stop(); /* see whether any pwq is asking for help */ spin_lock_irq(&wq_mayday_lock); @@ -2445,6 +2456,12 @@ repeat: process_scheduled_works(rescuer); /* + * Put the reference grabbed by send_mayday(). @pool won't + * go away while we're holding its lock. + */ + put_pwq(pwq); + + /* * Leave this pool. If keep_working() is %true, notify a * regular worker; otherwise, we end up with 0 concurrency * and stalling the execution. @@ -2459,6 +2476,12 @@ repeat: spin_unlock_irq(&wq_mayday_lock); + if (should_stop) { + __set_current_state(TASK_RUNNING); + rescuer->task->flags &= ~PF_WQ_WORKER; + return 0; + } + /* rescuers should never participate in concurrency management */ WARN_ON_ONCE(!(rescuer->flags & WORKER_NOT_RUNNING)); schedule(); @@ -4100,7 +4123,8 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, if (!pwq) { pr_warning("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n", wq->name); - goto out_unlock; + mutex_lock(&wq->mutex); + goto use_dfl_pwq; } /* @@ -10,42 +10,47 @@ #include <linux/crc7.h> -/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ -const u8 crc7_syndrome_table[256] = { - 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, - 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, - 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, - 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, - 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, - 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, - 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, - 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, - 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, - 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, - 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, - 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, - 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, - 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, - 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, - 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, - 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, - 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, - 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, - 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, - 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, - 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, - 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, - 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, - 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, - 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, - 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, - 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, - 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, - 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, - 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, - 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +/* + * Table for CRC-7 (polynomial x^7 + x^3 + 1). + * This is a big-endian CRC (msbit is highest power of x), + * aligned so the msbit of the byte is the x^6 coefficient + * and the lsbit is not used. + */ +const u8 crc7_be_syndrome_table[256] = { + 0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, + 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee, + 0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, + 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc, + 0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, + 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a, + 0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, + 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8, + 0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, + 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26, + 0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, + 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14, + 0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, + 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42, + 0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, + 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70, + 0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, + 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c, + 0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, + 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e, + 0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, + 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08, + 0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, + 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a, + 0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, + 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4, + 0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, + 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96, + 0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, + 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0, + 0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, + 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2 }; -EXPORT_SYMBOL(crc7_syndrome_table); +EXPORT_SYMBOL(crc7_be_syndrome_table); /** * crc7 - update the CRC7 for the data buffer @@ -55,14 +60,17 @@ EXPORT_SYMBOL(crc7_syndrome_table); * Context: any * * Returns the updated CRC7 value. + * The CRC7 is left-aligned in the byte (the lsbit is always 0), as that + * makes the computation easier, and all callers want it in that form. + * */ -u8 crc7(u8 crc, const u8 *buffer, size_t len) +u8 crc7_be(u8 crc, const u8 *buffer, size_t len) { while (len--) - crc = crc7_byte(crc, *buffer++); + crc = crc7_be_byte(crc, *buffer++); return crc; } -EXPORT_SYMBOL(crc7); +EXPORT_SYMBOL(crc7_be); MODULE_DESCRIPTION("CRC7 calculations"); MODULE_LICENSE("GPL"); diff --git a/lib/dump_stack.c b/lib/dump_stack.c index f23b63f..6745c62 100644 --- a/lib/dump_stack.c +++ b/lib/dump_stack.c @@ -23,7 +23,7 @@ static void __dump_stack(void) #ifdef CONFIG_SMP static atomic_t dump_lock = ATOMIC_INIT(-1); -asmlinkage void dump_stack(void) +asmlinkage __visible void dump_stack(void) { int was_locked; int old; @@ -55,7 +55,7 @@ retry: preempt_enable(); } #else -asmlinkage void dump_stack(void) +asmlinkage __visible void dump_stack(void) { __dump_stack(); } diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 3603ebc..ea60ad8 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -22,12 +22,14 @@ #include <linux/netdevice.h> #include <linux/if_vlan.h> +/* General test specific settings */ #define MAX_SUBTESTS 3 +#define MAX_TESTRUNS 10000 #define MAX_DATA 128 #define MAX_INSNS 512 #define MAX_K 0xffffFFFF -/* define few constants used to init test 'skb' */ +/* Few constants used to init test 'skb' */ #define SKB_TYPE 3 #define SKB_MARK 0x1234aaaa #define SKB_HASH 0x1234aaab @@ -36,31 +38,37 @@ #define SKB_DEV_IFINDEX 577 #define SKB_DEV_TYPE 588 -/* redefine REGs to make tests less verbose */ -#define R0 BPF_REG_0 -#define R1 BPF_REG_1 -#define R2 BPF_REG_2 -#define R3 BPF_REG_3 -#define R4 BPF_REG_4 -#define R5 BPF_REG_5 -#define R6 BPF_REG_6 -#define R7 BPF_REG_7 -#define R8 BPF_REG_8 -#define R9 BPF_REG_9 -#define R10 BPF_REG_10 +/* Redefine REGs to make tests less verbose */ +#define R0 BPF_REG_0 +#define R1 BPF_REG_1 +#define R2 BPF_REG_2 +#define R3 BPF_REG_3 +#define R4 BPF_REG_4 +#define R5 BPF_REG_5 +#define R6 BPF_REG_6 +#define R7 BPF_REG_7 +#define R8 BPF_REG_8 +#define R9 BPF_REG_9 +#define R10 BPF_REG_10 + +/* Flags that can be passed to test cases */ +#define FLAG_NO_DATA BIT(0) +#define FLAG_EXPECTED_FAIL BIT(1) + +enum { + CLASSIC = BIT(6), /* Old BPF instructions only. */ + INTERNAL = BIT(7), /* Extended instruction set. */ +}; + +#define TEST_TYPE_MASK (CLASSIC | INTERNAL) struct bpf_test { const char *descr; union { struct sock_filter insns[MAX_INSNS]; struct sock_filter_int insns_int[MAX_INSNS]; - }; - enum { - NO_DATA, - EXPECTED_FAIL, - SKB, - SKB_INT - } data_type; + } u; + __u8 aux; __u8 data[MAX_DATA]; struct { int data_size; @@ -71,7 +79,7 @@ struct bpf_test { static struct bpf_test tests[] = { { "TAX", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 1), BPF_STMT(BPF_MISC | BPF_TAX, 0), BPF_STMT(BPF_LD | BPF_IMM, 2), @@ -84,25 +92,25 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 10, 20, 30, 40, 50 }, { { 2, 10 }, { 3, 20 }, { 4, 30 } }, }, { "TXA", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_MISC | BPF_TXA, 0), BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) /* A == len * 2 */ }, - SKB, + CLASSIC, { 10, 20, 30, 40, 50 }, { { 1, 2 }, { 3, 6 }, { 4, 8 } }, }, { "ADD_SUB_MUL_K", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 1), BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 2), BPF_STMT(BPF_LDX | BPF_IMM, 3), @@ -111,13 +119,13 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_ALU | BPF_MUL | BPF_K, 3), BPF_STMT(BPF_RET | BPF_A, 0) }, - 0, + CLASSIC | FLAG_NO_DATA, { }, { { 0, 0xfffffffd } } }, { "DIV_KX", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 8), BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 2), BPF_STMT(BPF_MISC | BPF_TAX, 0), @@ -129,13 +137,13 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - 0, + CLASSIC | FLAG_NO_DATA, { }, { { 0, 0x40000001 } } }, { "AND_OR_LSH_K", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 0xff), BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0), BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 27), @@ -145,47 +153,59 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - 0, + CLASSIC | FLAG_NO_DATA, { }, { { 0, 0x800000ff }, { 1, 0x800000ff } }, }, { + "LD_IMM_0", + .u.insns = { + BPF_STMT(BPF_LD | BPF_IMM, 0), /* ld #0 */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0), + BPF_STMT(BPF_RET | BPF_K, 0), + BPF_STMT(BPF_RET | BPF_K, 1), + }, + CLASSIC, + { }, + { { 1, 1 } }, + }, + { "LD_IND", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_LD | BPF_H | BPF_IND, MAX_K), BPF_STMT(BPF_RET | BPF_K, 1) }, - SKB, + CLASSIC, { }, { { 1, 0 }, { 10, 0 }, { 60, 0 } }, }, { "LD_ABS", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 1000), BPF_STMT(BPF_RET | BPF_K, 1) }, - SKB, + CLASSIC, { }, { { 1, 0 }, { 10, 0 }, { 60, 0 } }, }, { "LD_ABS_LL", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_LL_OFF), BPF_STMT(BPF_MISC | BPF_TAX, 0), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_LL_OFF + 1), BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 1, 2, 3 }, { { 1, 0 }, { 2, 3 } }, }, { "LD_IND_LL", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, SKF_LL_OFF - 1), BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), @@ -193,26 +213,26 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 1, 2, 3, 0xff }, { { 1, 1 }, { 3, 3 }, { 4, 0xff } }, }, { "LD_ABS_NET", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_NET_OFF), BPF_STMT(BPF_MISC | BPF_TAX, 0), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_NET_OFF + 1), BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 }, { { 15, 0 }, { 16, 3 } }, }, { "LD_IND_NET", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, SKF_NET_OFF - 15), BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), @@ -220,13 +240,13 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 }, { { 14, 0 }, { 15, 1 }, { 17, 3 } }, }, { "LD_PKTTYPE", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_PKTTYPE), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0), @@ -241,46 +261,46 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_RET | BPF_K, 1), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, 3 }, { 10, 3 } }, }, { "LD_MARK", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_MARK), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, SKB_MARK}, { 10, SKB_MARK} }, }, { "LD_RXHASH", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_RXHASH), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, SKB_HASH}, { 10, SKB_HASH} }, }, { "LD_QUEUE", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_QUEUE), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, SKB_QUEUE_MAP }, { 10, SKB_QUEUE_MAP } }, }, { "LD_PROTOCOL", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 1), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 20, 1, 0), BPF_STMT(BPF_RET | BPF_K, 0), @@ -293,18 +313,18 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_MISC | BPF_TXA, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 10, 20, 30 }, { { 10, ETH_P_IP }, { 100, ETH_P_IP } }, }, { "LD_VLAN_TAG", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT }, @@ -313,12 +333,12 @@ static struct bpf_test tests[] = { }, { "LD_VLAN_TAG_PRESENT", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }, @@ -327,29 +347,29 @@ static struct bpf_test tests[] = { }, { "LD_IFINDEX", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_IFINDEX), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, SKB_DEV_IFINDEX }, { 10, SKB_DEV_IFINDEX } }, }, { "LD_HATYPE", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_HATYPE), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, SKB_DEV_TYPE }, { 10, SKB_DEV_TYPE } }, }, { "LD_CPU", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_CPU), BPF_STMT(BPF_MISC | BPF_TAX, 0), @@ -358,13 +378,13 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, 0 }, { 10, 0 } }, }, { "LD_NLATTR", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_IMM, 1), BPF_STMT(BPF_MISC | BPF_TXA, 0), BPF_STMT(BPF_LDX | BPF_IMM, 3), @@ -372,13 +392,13 @@ static struct bpf_test tests[] = { SKF_AD_OFF + SKF_AD_NLATTR), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 0xff, 4, 0, 2, 0, 4, 0, 3, 0 }, { { 4, 0 }, { 20, 5 } }, }, { "LD_NLATTR_NEST", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 1), BPF_STMT(BPF_LDX | BPF_IMM, 3), BPF_STMT(BPF_LD | BPF_W | BPF_ABS, @@ -406,13 +426,13 @@ static struct bpf_test tests[] = { SKF_AD_OFF + SKF_AD_NLATTR_NEST), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { 0xff, 12, 0, 1, 0, 4, 0, 2, 0, 4, 0, 3, 0 }, { { 4, 0 }, { 20, 9 } }, }, { "LD_PAYLOAD_OFF", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_PAY_OFFSET), BPF_STMT(BPF_LD | BPF_W | BPF_ABS, @@ -425,7 +445,7 @@ static struct bpf_test tests[] = { SKF_AD_OFF + SKF_AD_PAY_OFFSET), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, /* 00:00:00:00:00:00 > 00:00:00:00:00:00, ethtype IPv4 (0x0800), * length 98: 127.0.0.1 > 127.0.0.1: ICMP echo request, * id 9737, seq 1, length 64 @@ -439,20 +459,20 @@ static struct bpf_test tests[] = { }, { "LD_ANC_XOR", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 10), BPF_STMT(BPF_LDX | BPF_IMM, 300), BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_ALU_XOR_X), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 4, 10 ^ 300 }, { 20, 10 ^ 300 } }, }, { "SPILL_FILL", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_LD | BPF_IMM, 2), BPF_STMT(BPF_ALU | BPF_RSH, 1), @@ -468,39 +488,39 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, + CLASSIC, { }, { { 1, 0x80000001 }, { 2, 0x80000002 }, { 60, 0x80000000 ^ 60 } } }, { "JEQ", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 1), BPF_STMT(BPF_RET | BPF_K, 1), BPF_STMT(BPF_RET | BPF_K, MAX_K) }, - SKB, + CLASSIC, { 3, 3, 3, 3, 3 }, { { 1, 0 }, { 3, 1 }, { 4, MAX_K } }, }, { "JGT", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), BPF_JUMP(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 1), BPF_STMT(BPF_RET | BPF_K, 1), BPF_STMT(BPF_RET | BPF_K, MAX_K) }, - SKB, + CLASSIC, { 4, 4, 4, 3, 3 }, { { 2, 0 }, { 3, 1 }, { 4, MAX_K } }, }, { "JGE", - .insns = { + .u.insns = { BPF_STMT(BPF_LDX | BPF_LEN, 0), BPF_STMT(BPF_LD | BPF_B | BPF_IND, MAX_K), BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 1, 1, 0), @@ -513,13 +533,13 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_RET | BPF_K, 40), BPF_STMT(BPF_RET | BPF_K, MAX_K) }, - SKB, + CLASSIC, { 1, 2, 3, 4, 5 }, { { 1, 20 }, { 3, 40 }, { 5, MAX_K } }, }, { "JSET", - .insns = { + .u.insns = { BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0), BPF_JUMP(BPF_JMP | BPF_JA, 1, 1, 1), BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0), @@ -545,39 +565,39 @@ static struct bpf_test tests[] = { BPF_STMT(BPF_RET | BPF_K, 30), BPF_STMT(BPF_RET | BPF_K, MAX_K) }, - SKB, + CLASSIC, { 0, 0xAA, 0x55, 1 }, { { 4, 10 }, { 5, 20 }, { 6, MAX_K } }, }, { "tcpdump port 22", - .insns = { - { 0x28, 0, 0, 0x0000000c }, - { 0x15, 0, 8, 0x000086dd }, - { 0x30, 0, 0, 0x00000014 }, - { 0x15, 2, 0, 0x00000084 }, - { 0x15, 1, 0, 0x00000006 }, - { 0x15, 0, 17, 0x00000011 }, - { 0x28, 0, 0, 0x00000036 }, - { 0x15, 14, 0, 0x00000016 }, - { 0x28, 0, 0, 0x00000038 }, - { 0x15, 12, 13, 0x00000016 }, - { 0x15, 0, 12, 0x00000800 }, - { 0x30, 0, 0, 0x00000017 }, - { 0x15, 2, 0, 0x00000084 }, - { 0x15, 1, 0, 0x00000006 }, - { 0x15, 0, 8, 0x00000011 }, - { 0x28, 0, 0, 0x00000014 }, - { 0x45, 6, 0, 0x00001fff }, - { 0xb1, 0, 0, 0x0000000e }, - { 0x48, 0, 0, 0x0000000e }, - { 0x15, 2, 0, 0x00000016 }, - { 0x48, 0, 0, 0x00000010 }, - { 0x15, 0, 1, 0x00000016 }, - { 0x06, 0, 0, 0x0000ffff }, - { 0x06, 0, 0, 0x00000000 }, + .u.insns = { + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x86dd, 0, 8), /* IPv6 */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 20), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x84, 2, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 17), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 54), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 14, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 56), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 12, 13), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 12), /* IPv4 */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 23), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x84, 2, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 8), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20), + BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 6, 0), + BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14), + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 14), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 16), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 1), + BPF_STMT(BPF_RET | BPF_K, 0xffff), + BPF_STMT(BPF_RET | BPF_K, 0), }, - SKB, + CLASSIC, /* 3c:07:54:43:e5:76 > 10:bf:48:d6:43:d6, ethertype IPv4(0x0800) * length 114: 10.1.1.149.49700 > 10.1.2.10.22: Flags [P.], * seq 1305692979:1305693027, ack 3650467037, win 65535, @@ -596,46 +616,46 @@ static struct bpf_test tests[] = { }, { "tcpdump complex", - .insns = { + .u.insns = { /* tcpdump -nei eth0 'tcp port 22 and (((ip[2:2] - * ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and * (len > 115 or len < 30000000000)' -d */ - { 0x28, 0, 0, 0x0000000c }, - { 0x15, 30, 0, 0x000086dd }, - { 0x15, 0, 29, 0x00000800 }, - { 0x30, 0, 0, 0x00000017 }, - { 0x15, 0, 27, 0x00000006 }, - { 0x28, 0, 0, 0x00000014 }, - { 0x45, 25, 0, 0x00001fff }, - { 0xb1, 0, 0, 0x0000000e }, - { 0x48, 0, 0, 0x0000000e }, - { 0x15, 2, 0, 0x00000016 }, - { 0x48, 0, 0, 0x00000010 }, - { 0x15, 0, 20, 0x00000016 }, - { 0x28, 0, 0, 0x00000010 }, - { 0x02, 0, 0, 0x00000001 }, - { 0x30, 0, 0, 0x0000000e }, - { 0x54, 0, 0, 0x0000000f }, - { 0x64, 0, 0, 0x00000002 }, - { 0x07, 0, 0, 0x00000005 }, - { 0x60, 0, 0, 0x00000001 }, - { 0x1c, 0, 0, 0x00000000 }, - { 0x02, 0, 0, 0x00000005 }, - { 0xb1, 0, 0, 0x0000000e }, - { 0x50, 0, 0, 0x0000001a }, - { 0x54, 0, 0, 0x000000f0 }, - { 0x74, 0, 0, 0x00000002 }, - { 0x07, 0, 0, 0x00000009 }, - { 0x60, 0, 0, 0x00000005 }, - { 0x1d, 4, 0, 0x00000000 }, - { 0x80, 0, 0, 0x00000000 }, - { 0x25, 1, 0, 0x00000073 }, - { 0x35, 1, 0, 0xfc23ac00 }, - { 0x06, 0, 0, 0x0000ffff }, - { 0x06, 0, 0, 0x00000000 }, + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x86dd, 30, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x800, 0, 29), + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 23), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 0, 27), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20), + BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 25, 0), + BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14), + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 14), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 16), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 20), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 16), + BPF_STMT(BPF_ST, 1), + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 14), + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf), + BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 2), + BPF_STMT(BPF_MISC | BPF_TAX, 0x5), /* libpcap emits K on TAX */ + BPF_STMT(BPF_LD | BPF_MEM, 1), + BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0), + BPF_STMT(BPF_ST, 5), + BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14), + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 26), + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0), + BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 2), + BPF_STMT(BPF_MISC | BPF_TAX, 0x9), /* libpcap emits K on TAX */ + BPF_STMT(BPF_LD | BPF_MEM, 5), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, 4, 0), + BPF_STMT(BPF_LD | BPF_LEN, 0), + BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, 0x73, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0xfc23ac00, 1, 0), + BPF_STMT(BPF_RET | BPF_K, 0xffff), + BPF_STMT(BPF_RET | BPF_K, 0), }, - SKB, + CLASSIC, { 0x10, 0xbf, 0x48, 0xd6, 0x43, 0xd6, 0x3c, 0x07, 0x54, 0x43, 0xe5, 0x76, 0x08, 0x00, @@ -649,18 +669,18 @@ static struct bpf_test tests[] = { }, { "RET_A", - .insns = { + .u.insns = { /* check that unitialized X and A contain zeros */ BPF_STMT(BPF_MISC | BPF_TXA, 0), BPF_STMT(BPF_RET | BPF_A, 0) }, - SKB, - {}, + CLASSIC, + { }, { {1, 0}, {2, 0} }, }, { "INT: ADD trivial", - .insns_int = { + .u.insns_int = { BPF_ALU64_IMM(BPF_MOV, R1, 1), BPF_ALU64_IMM(BPF_ADD, R1, 2), BPF_ALU64_IMM(BPF_MOV, R2, 3), @@ -670,13 +690,13 @@ static struct bpf_test tests[] = { BPF_ALU64_REG(BPF_MOV, R0, R1), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 0xfffffffd } } }, { "INT: MUL_X", - .insns_int = { + .u.insns_int = { BPF_ALU64_IMM(BPF_MOV, R0, -1), BPF_ALU64_IMM(BPF_MOV, R1, -1), BPF_ALU64_IMM(BPF_MOV, R2, 3), @@ -686,13 +706,13 @@ static struct bpf_test tests[] = { BPF_ALU64_IMM(BPF_MOV, R0, 1), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 1 } } }, { "INT: MUL_X2", - .insns_int = { + .u.insns_int = { BPF_ALU32_IMM(BPF_MOV, R0, -1), BPF_ALU32_IMM(BPF_MOV, R1, -1), BPF_ALU32_IMM(BPF_MOV, R2, 3), @@ -703,13 +723,13 @@ static struct bpf_test tests[] = { BPF_ALU32_IMM(BPF_MOV, R0, 1), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 1 } } }, { "INT: MUL32_X", - .insns_int = { + .u.insns_int = { BPF_ALU32_IMM(BPF_MOV, R0, -1), BPF_ALU64_IMM(BPF_MOV, R1, -1), BPF_ALU32_IMM(BPF_MOV, R2, 3), @@ -720,7 +740,7 @@ static struct bpf_test tests[] = { BPF_ALU32_IMM(BPF_MOV, R0, 1), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 1 } } }, @@ -730,7 +750,7 @@ static struct bpf_test tests[] = { * different asm code. */ "INT: ADD 64-bit", - .insns_int = { + .u.insns_int = { BPF_ALU64_IMM(BPF_MOV, R0, 0), BPF_ALU64_IMM(BPF_MOV, R1, 1), BPF_ALU64_IMM(BPF_MOV, R2, 2), @@ -882,13 +902,13 @@ static struct bpf_test tests[] = { BPF_ALU64_REG(BPF_MOV, R0, R9), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 2957380 } } }, { "INT: ADD 32-bit", - .insns_int = { + .u.insns_int = { BPF_ALU32_IMM(BPF_MOV, R0, 20), BPF_ALU32_IMM(BPF_MOV, R1, 1), BPF_ALU32_IMM(BPF_MOV, R2, 2), @@ -1028,13 +1048,13 @@ static struct bpf_test tests[] = { BPF_ALU32_REG(BPF_MOV, R0, R9), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 2957380 } } }, { /* Mainly checking JIT here. */ "INT: SUB", - .insns_int = { + .u.insns_int = { BPF_ALU64_IMM(BPF_MOV, R0, 0), BPF_ALU64_IMM(BPF_MOV, R1, 1), BPF_ALU64_IMM(BPF_MOV, R2, 2), @@ -1161,13 +1181,13 @@ static struct bpf_test tests[] = { BPF_ALU64_REG(BPF_SUB, R0, R9), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 11 } } }, { /* Mainly checking JIT here. */ "INT: XOR", - .insns_int = { + .u.insns_int = { BPF_ALU64_REG(BPF_SUB, R0, R0), BPF_ALU64_REG(BPF_XOR, R1, R1), BPF_JMP_REG(BPF_JEQ, R0, R1, 1), @@ -1227,13 +1247,13 @@ static struct bpf_test tests[] = { BPF_ALU64_IMM(BPF_MOV, R0, 1), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 1 } } }, { /* Mainly checking JIT here. */ "INT: MUL", - .insns_int = { + .u.insns_int = { BPF_ALU64_IMM(BPF_MOV, R0, 11), BPF_ALU64_IMM(BPF_MOV, R1, 1), BPF_ALU64_IMM(BPF_MOV, R2, 2), @@ -1289,13 +1309,13 @@ static struct bpf_test tests[] = { BPF_ALU64_REG(BPF_MOV, R0, R2), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, 0x35d97ef2 } } }, { "INT: ALU MIX", - .insns_int = { + .u.insns_int = { BPF_ALU64_IMM(BPF_MOV, R0, 11), BPF_ALU64_IMM(BPF_ADD, R0, -1), BPF_ALU64_IMM(BPF_MOV, R2, 2), @@ -1309,13 +1329,13 @@ static struct bpf_test tests[] = { BPF_ALU64_IMM(BPF_MOV, R0, -1), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { }, { { 0, -1 } } }, { "INT: DIV + ABS", - .insns_int = { + .u.insns_int = { BPF_ALU64_REG(BPF_MOV, R6, R1), BPF_LD_ABS(BPF_B, 3), BPF_ALU64_IMM(BPF_MOV, R2, 2), @@ -1326,79 +1346,354 @@ static struct bpf_test tests[] = { BPF_LD_IND(BPF_B, R8, -70), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { 10, 20, 30, 40, 50 }, { { 4, 0 }, { 5, 10 } } }, { "INT: DIV by zero", - .insns_int = { + .u.insns_int = { BPF_ALU64_REG(BPF_MOV, R6, R1), BPF_ALU64_IMM(BPF_MOV, R7, 0), BPF_LD_ABS(BPF_B, 3), BPF_ALU32_REG(BPF_DIV, R0, R7), BPF_EXIT_INSN(), }, - SKB_INT, + INTERNAL, { 10, 20, 30, 40, 50 }, { { 3, 0 }, { 4, 0 } } }, { "check: missing ret", - .insns = { + .u.insns = { BPF_STMT(BPF_LD | BPF_IMM, 1), }, - EXPECTED_FAIL, + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, { } }, { "check: div_k_0", - .insns = { + .u.insns = { BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0), BPF_STMT(BPF_RET | BPF_K, 0) }, - EXPECTED_FAIL, + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, { } }, { "check: unknown insn", - .insns = { + .u.insns = { /* seccomp insn, rejected in socket filter */ BPF_STMT(BPF_LDX | BPF_W | BPF_ABS, 0), BPF_STMT(BPF_RET | BPF_K, 0) }, - EXPECTED_FAIL, + CLASSIC | FLAG_EXPECTED_FAIL, { }, { } }, { "check: out of range spill/fill", - .insns = { + .u.insns = { BPF_STMT(BPF_STX, 16), BPF_STMT(BPF_RET | BPF_K, 0) }, - EXPECTED_FAIL, + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, { }, { } }, + { + "JUMPS + HOLES", + .u.insns = { + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 15), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90c2894d, 3, 4), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90c2894d, 1, 2), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JGE, 0, 14, 15), + BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 14), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, 0x2ac28349, 2, 3), + BPF_JUMP(BPF_JMP | BPF_JEQ, 0x2ac28349, 1, 2), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JGE, 0, 14, 15), + BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 14), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90d2ff41, 2, 3), + BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90d2ff41, 1, 2), + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0), + BPF_STMT(BPF_RET | BPF_A, 0), + BPF_STMT(BPF_RET | BPF_A, 0), + }, + CLASSIC, + { 0x00, 0x1b, 0x21, 0x3c, 0x9d, 0xf8, + 0x90, 0xe2, 0xba, 0x0a, 0x56, 0xb4, + 0x08, 0x00, + 0x45, 0x00, 0x00, 0x28, 0x00, 0x00, + 0x20, 0x00, 0x40, 0x11, 0x00, 0x00, /* IP header */ + 0xc0, 0xa8, 0x33, 0x01, + 0xc0, 0xa8, 0x33, 0x02, + 0xbb, 0xb6, + 0xa9, 0xfa, + 0x00, 0x14, 0x00, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc }, + { { 88, 0x001b } } + }, + { + "check: RET X", + .u.insns = { + BPF_STMT(BPF_RET | BPF_X, 0), + }, + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, + { }, + { }, + }, + { + "check: LDX + RET X", + .u.insns = { + BPF_STMT(BPF_LDX | BPF_IMM, 42), + BPF_STMT(BPF_RET | BPF_X, 0), + }, + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, + { }, + { }, + }, + { /* Mainly checking JIT here. */ + "M[]: alt STX + LDX", + .u.insns = { + BPF_STMT(BPF_LDX | BPF_IMM, 100), + BPF_STMT(BPF_STX, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 0), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 1), + BPF_STMT(BPF_LDX | BPF_MEM, 1), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 2), + BPF_STMT(BPF_LDX | BPF_MEM, 2), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 3), + BPF_STMT(BPF_LDX | BPF_MEM, 3), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 4), + BPF_STMT(BPF_LDX | BPF_MEM, 4), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 5), + BPF_STMT(BPF_LDX | BPF_MEM, 5), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 6), + BPF_STMT(BPF_LDX | BPF_MEM, 6), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 7), + BPF_STMT(BPF_LDX | BPF_MEM, 7), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 8), + BPF_STMT(BPF_LDX | BPF_MEM, 8), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 9), + BPF_STMT(BPF_LDX | BPF_MEM, 9), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 10), + BPF_STMT(BPF_LDX | BPF_MEM, 10), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 11), + BPF_STMT(BPF_LDX | BPF_MEM, 11), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 12), + BPF_STMT(BPF_LDX | BPF_MEM, 12), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 13), + BPF_STMT(BPF_LDX | BPF_MEM, 13), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 14), + BPF_STMT(BPF_LDX | BPF_MEM, 14), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_STX, 15), + BPF_STMT(BPF_LDX | BPF_MEM, 15), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1), + BPF_STMT(BPF_MISC | BPF_TAX, 0), + BPF_STMT(BPF_RET | BPF_A, 0), + }, + CLASSIC | FLAG_NO_DATA, + { }, + { { 0, 116 } }, + }, + { /* Mainly checking JIT here. */ + "M[]: full STX + full LDX", + .u.insns = { + BPF_STMT(BPF_LDX | BPF_IMM, 0xbadfeedb), + BPF_STMT(BPF_STX, 0), + BPF_STMT(BPF_LDX | BPF_IMM, 0xecabedae), + BPF_STMT(BPF_STX, 1), + BPF_STMT(BPF_LDX | BPF_IMM, 0xafccfeaf), + BPF_STMT(BPF_STX, 2), + BPF_STMT(BPF_LDX | BPF_IMM, 0xbffdcedc), + BPF_STMT(BPF_STX, 3), + BPF_STMT(BPF_LDX | BPF_IMM, 0xfbbbdccb), + BPF_STMT(BPF_STX, 4), + BPF_STMT(BPF_LDX | BPF_IMM, 0xfbabcbda), + BPF_STMT(BPF_STX, 5), + BPF_STMT(BPF_LDX | BPF_IMM, 0xaedecbdb), + BPF_STMT(BPF_STX, 6), + BPF_STMT(BPF_LDX | BPF_IMM, 0xadebbade), + BPF_STMT(BPF_STX, 7), + BPF_STMT(BPF_LDX | BPF_IMM, 0xfcfcfaec), + BPF_STMT(BPF_STX, 8), + BPF_STMT(BPF_LDX | BPF_IMM, 0xbcdddbdc), + BPF_STMT(BPF_STX, 9), + BPF_STMT(BPF_LDX | BPF_IMM, 0xfeefdfac), + BPF_STMT(BPF_STX, 10), + BPF_STMT(BPF_LDX | BPF_IMM, 0xcddcdeea), + BPF_STMT(BPF_STX, 11), + BPF_STMT(BPF_LDX | BPF_IMM, 0xaccfaebb), + BPF_STMT(BPF_STX, 12), + BPF_STMT(BPF_LDX | BPF_IMM, 0xbdcccdcf), + BPF_STMT(BPF_STX, 13), + BPF_STMT(BPF_LDX | BPF_IMM, 0xaaedecde), + BPF_STMT(BPF_STX, 14), + BPF_STMT(BPF_LDX | BPF_IMM, 0xfaeacdad), + BPF_STMT(BPF_STX, 15), + BPF_STMT(BPF_LDX | BPF_MEM, 0), + BPF_STMT(BPF_MISC | BPF_TXA, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 1), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 2), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 3), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 4), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 5), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 6), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 7), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 8), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 9), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 10), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 11), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 12), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 13), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 14), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_LDX | BPF_MEM, 15), + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + BPF_STMT(BPF_RET | BPF_A, 0), + }, + CLASSIC | FLAG_NO_DATA, + { }, + { { 0, 0x2a5a5e5 } }, + }, + { + "check: SKF_AD_MAX", + .u.insns = { + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + SKF_AD_OFF + SKF_AD_MAX), + BPF_STMT(BPF_RET | BPF_A, 0), + }, + CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL, + { }, + { }, + }, + { /* Passes checker but fails during runtime. */ + "LD [SKF_AD_OFF-1]", + .u.insns = { + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + SKF_AD_OFF - 1), + BPF_STMT(BPF_RET | BPF_K, 1), + }, + CLASSIC, + { }, + { { 1, 0 } }, + }, }; -static int get_length(struct sock_filter *fp) -{ - int len = 0; - - while (fp->code != 0 || fp->k != 0) { - fp++; - len++; - } - - return len; -} +static struct net_device dev; -struct net_device dev; -struct sk_buff *populate_skb(char *buf, int size) +static struct sk_buff *populate_skb(char *buf, int size) { struct sk_buff *skb; @@ -1410,6 +1705,8 @@ struct sk_buff *populate_skb(char *buf, int size) return NULL; memcpy(__skb_put(skb, size), buf, size); + + /* Initialize a fake skb with test pattern. */ skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IP); skb->pkt_type = SKB_TYPE; @@ -1425,43 +1722,148 @@ struct sk_buff *populate_skb(char *buf, int size) return skb; } -static int run_one(struct sk_filter *fp, struct bpf_test *t) +static void *generate_test_data(struct bpf_test *test, int sub) { - u64 start, finish, res, cnt = 100000; - int err_cnt = 0, err, i, j; - u32 ret = 0; - void *data; + if (test->aux & FLAG_NO_DATA) + return NULL; - for (i = 0; i < MAX_SUBTESTS; i++) { - if (t->test[i].data_size == 0 && - t->test[i].result == 0) + /* Test case expects an skb, so populate one. Various + * subtests generate skbs of different sizes based on + * the same data. + */ + return populate_skb(test->data, test->test[sub].data_size); +} + +static void release_test_data(const struct bpf_test *test, void *data) +{ + if (test->aux & FLAG_NO_DATA) + return; + + kfree_skb(data); +} + +static int probe_filter_length(struct sock_filter *fp) +{ + int len = 0; + + for (len = MAX_INSNS - 1; len > 0; --len) + if (fp[len].code != 0 || fp[len].k != 0) break; - if (t->data_type == SKB || - t->data_type == SKB_INT) { - data = populate_skb(t->data, t->test[i].data_size); - if (!data) - return -ENOMEM; - } else { - data = NULL; + + return len + 1; +} + +static struct sk_filter *generate_filter(int which, int *err) +{ + struct sk_filter *fp; + struct sock_fprog_kern fprog; + unsigned int flen = probe_filter_length(tests[which].u.insns); + __u8 test_type = tests[which].aux & TEST_TYPE_MASK; + + switch (test_type) { + case CLASSIC: + fprog.filter = tests[which].u.insns; + fprog.len = flen; + + *err = sk_unattached_filter_create(&fp, &fprog); + if (tests[which].aux & FLAG_EXPECTED_FAIL) { + if (*err == -EINVAL) { + pr_cont("PASS\n"); + /* Verifier rejected filter as expected. */ + *err = 0; + return NULL; + } else { + pr_cont("UNEXPECTED_PASS\n"); + /* Verifier didn't reject the test that's + * bad enough, just return! + */ + *err = -EINVAL; + return NULL; + } + } + /* We don't expect to fail. */ + if (*err) { + pr_cont("FAIL to attach err=%d len=%d\n", + *err, fprog.len); + return NULL; } + break; + + case INTERNAL: + fp = kzalloc(sk_filter_size(flen), GFP_KERNEL); + if (fp == NULL) { + pr_cont("UNEXPECTED_FAIL no memory left\n"); + *err = -ENOMEM; + return NULL; + } + + fp->len = flen; + memcpy(fp->insnsi, tests[which].u.insns_int, + fp->len * sizeof(struct sock_filter_int)); - start = ktime_to_us(ktime_get()); - for (j = 0; j < cnt; j++) - ret = SK_RUN_FILTER(fp, data); - finish = ktime_to_us(ktime_get()); + sk_filter_select_runtime(fp); + break; + } + + *err = 0; + return fp; +} + +static void release_filter(struct sk_filter *fp, int which) +{ + __u8 test_type = tests[which].aux & TEST_TYPE_MASK; - res = (finish - start) * 1000; - do_div(res, cnt); + switch (test_type) { + case CLASSIC: + sk_unattached_filter_destroy(fp); + break; + case INTERNAL: + sk_filter_free(fp); + break; + } +} - err = ret != t->test[i].result; - if (!err) - pr_cont("%lld ", res); +static int __run_one(const struct sk_filter *fp, const void *data, + int runs, u64 *duration) +{ + u64 start, finish; + int ret, i; - if (t->data_type == SKB || t->data_type == SKB_INT) - kfree_skb(data); + start = ktime_to_us(ktime_get()); - if (err) { - pr_cont("ret %d != %d ", ret, t->test[i].result); + for (i = 0; i < runs; i++) + ret = SK_RUN_FILTER(fp, data); + + finish = ktime_to_us(ktime_get()); + + *duration = (finish - start) * 1000ULL; + do_div(*duration, runs); + + return ret; +} + +static int run_one(const struct sk_filter *fp, struct bpf_test *test) +{ + int err_cnt = 0, i, runs = MAX_TESTRUNS; + + for (i = 0; i < MAX_SUBTESTS; i++) { + void *data; + u64 duration; + u32 ret; + + if (test->test[i].data_size == 0 && + test->test[i].result == 0) + break; + + data = generate_test_data(test, i); + ret = __run_one(fp, data, runs, &duration); + release_test_data(test, data); + + if (ret == test->test[i].result) { + pr_cont("%lld ", duration); + } else { + pr_cont("ret %d != %d ", ret, + test->test[i].result); err_cnt++; } } @@ -1471,65 +1873,37 @@ static int run_one(struct sk_filter *fp, struct bpf_test *t) static __init int test_bpf(void) { - struct sk_filter *fp, *fp_ext = NULL; - struct sock_fprog fprog; - int err, i, err_cnt = 0; + int i, err_cnt = 0, pass_cnt = 0; for (i = 0; i < ARRAY_SIZE(tests); i++) { - pr_info("#%d %s ", i, tests[i].descr); + struct sk_filter *fp; + int err; - fprog.filter = tests[i].insns; - fprog.len = get_length(fprog.filter); + pr_info("#%d %s ", i, tests[i].descr); - if (tests[i].data_type == SKB_INT) { - fp_ext = kzalloc(4096, GFP_KERNEL); - if (!fp_ext) - return -ENOMEM; - fp = fp_ext; - memcpy(fp_ext->insns, tests[i].insns_int, - fprog.len * 8); - fp->len = fprog.len; - fp->bpf_func = sk_run_filter_int_skb; - } else { - err = sk_unattached_filter_create(&fp, &fprog); - if (tests[i].data_type == EXPECTED_FAIL) { - if (err == -EINVAL) { - pr_cont("PASS\n"); - continue; - } else { - pr_cont("UNEXPECTED_PASS\n"); - /* verifier didn't reject the test - * that's bad enough, just return - */ - return -EINVAL; - } + fp = generate_filter(i, &err); + if (fp == NULL) { + if (err == 0) { + pass_cnt++; + continue; } - if (err) { - pr_cont("FAIL to attach err=%d len=%d\n", - err, fprog.len); - return err; - } - } + return err; + } err = run_one(fp, &tests[i]); - - if (tests[i].data_type != SKB_INT) - sk_unattached_filter_destroy(fp); - else - kfree(fp); + release_filter(fp, i); if (err) { - pr_cont("FAIL %d\n", err); + pr_cont("FAIL (%d times)\n", err); err_cnt++; } else { pr_cont("PASS\n"); + pass_cnt++; } } - if (err_cnt) - return -EINVAL; - else - return 0; + pr_info("Summary: %d PASSED, %d FAILED\n", pass_cnt, err_cnt); + return err_cnt ? -EINVAL : 0; } static int __init test_bpf_init(void) @@ -1543,4 +1917,5 @@ static void __exit test_bpf_exit(void) module_init(test_bpf_init); module_exit(test_bpf_exit); + MODULE_LICENSE("GPL"); @@ -581,3 +581,18 @@ config PGTABLE_MAPPING config GENERIC_EARLY_IOREMAP bool + +config MAX_STACK_SIZE_MB + int "Maximum user stack size for 32-bit processes (MB)" + default 80 + range 8 256 if METAG + range 8 2048 + depends on STACK_GROWSUP && (!64BIT || COMPAT) + help + This is the maximum stack size in Megabytes in the VM layout of 32-bit + user processes when the stack grows upwards (currently only on parisc + and metag arch). The stack will be located at the highest memory + address minus the given value, unless the RLIMIT_STACK hard limit is + changed to a smaller value in which case that is used. + + A sane initial value is 80 MB. diff --git a/mm/compaction.c b/mm/compaction.c index 37f9762..627dc2e 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -671,16 +671,20 @@ static void isolate_freepages(struct zone *zone, struct compact_control *cc) { struct page *page; - unsigned long high_pfn, low_pfn, pfn, z_end_pfn, end_pfn; + unsigned long high_pfn, low_pfn, pfn, z_end_pfn; int nr_freepages = cc->nr_freepages; struct list_head *freelist = &cc->freepages; /* * Initialise the free scanner. The starting point is where we last - * scanned from (or the end of the zone if starting). The low point - * is the end of the pageblock the migration scanner is using. + * successfully isolated from, zone-cached value, or the end of the + * zone when isolating for the first time. We need this aligned to + * the pageblock boundary, because we do pfn -= pageblock_nr_pages + * in the for loop. + * The low boundary is the end of the pageblock the migration scanner + * is using. */ - pfn = cc->free_pfn; + pfn = cc->free_pfn & ~(pageblock_nr_pages-1); low_pfn = ALIGN(cc->migrate_pfn + 1, pageblock_nr_pages); /* @@ -700,6 +704,7 @@ static void isolate_freepages(struct zone *zone, for (; pfn >= low_pfn && cc->nr_migratepages > nr_freepages; pfn -= pageblock_nr_pages) { unsigned long isolated; + unsigned long end_pfn; /* * This can iterate a massively long zone without finding any @@ -734,13 +739,10 @@ static void isolate_freepages(struct zone *zone, isolated = 0; /* - * As pfn may not start aligned, pfn+pageblock_nr_page - * may cross a MAX_ORDER_NR_PAGES boundary and miss - * a pfn_valid check. Ensure isolate_freepages_block() - * only scans within a pageblock + * Take care when isolating in last pageblock of a zone which + * ends in the middle of a pageblock. */ - end_pfn = ALIGN(pfn + 1, pageblock_nr_pages); - end_pfn = min(end_pfn, z_end_pfn); + end_pfn = min(pfn + pageblock_nr_pages, z_end_pfn); isolated = isolate_freepages_block(cc, pfn, end_pfn, freelist, false); nr_freepages += isolated; diff --git a/mm/filemap.c b/mm/filemap.c index 5020b28..088358c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -257,9 +257,11 @@ static int filemap_check_errors(struct address_space *mapping) { int ret = 0; /* Check for outstanding write errors */ - if (test_and_clear_bit(AS_ENOSPC, &mapping->flags)) + if (test_bit(AS_ENOSPC, &mapping->flags) && + test_and_clear_bit(AS_ENOSPC, &mapping->flags)) ret = -ENOSPC; - if (test_and_clear_bit(AS_EIO, &mapping->flags)) + if (test_bit(AS_EIO, &mapping->flags) && + test_and_clear_bit(AS_EIO, &mapping->flags)) ret = -EIO; return ret; } @@ -906,8 +908,8 @@ EXPORT_SYMBOL(page_cache_prev_hole); * Looks up the page cache slot at @mapping & @offset. If there is a * page cache page, it is returned with an increased refcount. * - * If the slot holds a shadow entry of a previously evicted page, it - * is returned. + * If the slot holds a shadow entry of a previously evicted page, or a + * swap entry from shmem/tmpfs, it is returned. * * Otherwise, %NULL is returned. */ @@ -928,9 +930,9 @@ repeat: if (radix_tree_deref_retry(page)) goto repeat; /* - * Otherwise, shmem/tmpfs must be storing a swap entry - * here as an exceptional entry: so return it without - * attempting to raise page count. + * A shadow entry of a recently evicted page, + * or a swap entry from shmem/tmpfs. Return + * it without attempting to raise page count. */ goto out; } @@ -983,8 +985,8 @@ EXPORT_SYMBOL(find_get_page); * page cache page, it is returned locked and with an increased * refcount. * - * If the slot holds a shadow entry of a previously evicted page, it - * is returned. + * If the slot holds a shadow entry of a previously evicted page, or a + * swap entry from shmem/tmpfs, it is returned. * * Otherwise, %NULL is returned. * @@ -1099,8 +1101,8 @@ EXPORT_SYMBOL(find_or_create_page); * with ascending indexes. There may be holes in the indices due to * not-present pages. * - * Any shadow entries of evicted pages are included in the returned - * array. + * Any shadow entries of evicted pages, or swap entries from + * shmem/tmpfs, are included in the returned array. * * find_get_entries() returns the number of pages and shadow entries * which were found. @@ -1128,9 +1130,9 @@ repeat: if (radix_tree_deref_retry(page)) goto restart; /* - * Otherwise, we must be storing a swap entry - * here as an exceptional entry: so return it - * without attempting to raise page count. + * A shadow entry of a recently evicted page, + * or a swap entry from shmem/tmpfs. Return + * it without attempting to raise page count. */ goto export; } @@ -1198,9 +1200,9 @@ repeat: goto restart; } /* - * Otherwise, shmem/tmpfs must be storing a swap entry - * here as an exceptional entry: so skip over it - - * we only reach this from invalidate_mapping_pages(). + * A shadow entry of a recently evicted page, + * or a swap entry from shmem/tmpfs. Skip + * over it. */ continue; } @@ -1265,9 +1267,9 @@ repeat: goto restart; } /* - * Otherwise, shmem/tmpfs must be storing a swap entry - * here as an exceptional entry: so stop looking for - * contiguous pages. + * A shadow entry of a recently evicted page, + * or a swap entry from shmem/tmpfs. Stop + * looking for contiguous pages. */ break; } @@ -1341,10 +1343,17 @@ repeat: goto restart; } /* - * This function is never used on a shmem/tmpfs - * mapping, so a swap entry won't be found here. + * A shadow entry of a recently evicted page. + * + * Those entries should never be tagged, but + * this tree walk is lockless and the tags are + * looked up in bulk, one radix tree node at a + * time, so there is a sizable window for page + * reclaim to evict a page we saw tagged. + * + * Skip over it. */ - BUG(); + continue; } if (!page_cache_get_speculative(page)) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 2461929..c82290b 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1981,11 +1981,7 @@ static int __init hugetlb_init(void) { int i; - /* Some platform decide whether they support huge pages at boot - * time. On these, such as powerpc, HPAGE_SHIFT is set to 0 when - * there is no such support - */ - if (HPAGE_SHIFT == 0) + if (!hugepages_supported()) return 0; if (!size_to_hstate(default_hstate_size)) { @@ -2112,6 +2108,9 @@ static int hugetlb_sysctl_handler_common(bool obey_mempolicy, unsigned long tmp; int ret; + if (!hugepages_supported()) + return -ENOTSUPP; + tmp = h->max_huge_pages; if (write && h->order >= MAX_ORDER) @@ -2165,6 +2164,9 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, unsigned long tmp; int ret; + if (!hugepages_supported()) + return -ENOTSUPP; + tmp = h->nr_overcommit_huge_pages; if (write && h->order >= MAX_ORDER) @@ -2190,6 +2192,8 @@ out: void hugetlb_report_meminfo(struct seq_file *m) { struct hstate *h = &default_hstate; + if (!hugepages_supported()) + return; seq_printf(m, "HugePages_Total: %5lu\n" "HugePages_Free: %5lu\n" @@ -2206,6 +2210,8 @@ void hugetlb_report_meminfo(struct seq_file *m) int hugetlb_report_node_meminfo(int nid, char *buf) { struct hstate *h = &default_hstate; + if (!hugepages_supported()) + return 0; return sprintf(buf, "Node %d HugePages_Total: %5u\n" "Node %d HugePages_Free: %5u\n" @@ -2220,6 +2226,9 @@ void hugetlb_show_meminfo(void) struct hstate *h; int nid; + if (!hugepages_supported()) + return; + for_each_node_state(nid, N_MEMORY) for_each_hstate(h) pr_info("Node %d hugepages_total=%u hugepages_free=%u hugepages_surp=%u hugepages_size=%lukB\n", diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 91d67ea..8d2fcdf 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1775,10 +1775,9 @@ void __init kmemleak_init(void) int i; unsigned long flags; - kmemleak_early_log = 0; - #ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF if (!kmemleak_skip_disable) { + kmemleak_early_log = 0; kmemleak_disable(); return; } @@ -1796,6 +1795,7 @@ void __init kmemleak_init(void) /* the kernel is still in UP mode, so disabling the IRQs is enough */ local_irq_save(flags); + kmemleak_early_log = 0; if (kmemleak_error) { local_irq_restore(flags); return; diff --git a/mm/madvise.c b/mm/madvise.c index 539eeb9..a402f8f 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -195,7 +195,7 @@ static void force_shm_swapin_readahead(struct vm_area_struct *vma, for (; start < end; start += PAGE_SIZE) { index = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; - page = find_get_page(mapping, index); + page = find_get_entry(mapping, index); if (!radix_tree_exceptional_entry(page)) { if (page) page_cache_release(page); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 29501f0..5177c6d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1077,9 +1077,18 @@ static struct mem_cgroup *get_mem_cgroup_from_mm(struct mm_struct *mm) rcu_read_lock(); do { - memcg = mem_cgroup_from_task(rcu_dereference(mm->owner)); - if (unlikely(!memcg)) + /* + * Page cache insertions can happen withou an + * actual mm context, e.g. during disk probing + * on boot, loopback IO, acct() writes etc. + */ + if (unlikely(!mm)) memcg = root_mem_cgroup; + else { + memcg = mem_cgroup_from_task(rcu_dereference(mm->owner)); + if (unlikely(!memcg)) + memcg = root_mem_cgroup; + } } while (!css_tryget(&memcg->css)); rcu_read_unlock(); return memcg; @@ -3958,17 +3967,9 @@ int mem_cgroup_charge_file(struct page *page, struct mm_struct *mm, return 0; } - /* - * Page cache insertions can happen without an actual mm - * context, e.g. during disk probing on boot. - */ - if (unlikely(!mm)) - memcg = root_mem_cgroup; - else { - memcg = mem_cgroup_try_charge_mm(mm, gfp_mask, 1, true); - if (!memcg) - return -ENOMEM; - } + memcg = mem_cgroup_try_charge_mm(mm, gfp_mask, 1, true); + if (!memcg) + return -ENOMEM; __mem_cgroup_commit_charge(memcg, page, 1, type, false); return 0; } @@ -6686,16 +6687,20 @@ static struct page *mc_handle_file_pte(struct vm_area_struct *vma, pgoff = pte_to_pgoff(ptent); /* page is moved even if it's not RSS of this task(page-faulted). */ - page = find_get_page(mapping, pgoff); - #ifdef CONFIG_SWAP /* shmem/tmpfs may report page out on swap: account for that too. */ - if (radix_tree_exceptional_entry(page)) { - swp_entry_t swap = radix_to_swp_entry(page); - if (do_swap_account) - *entry = swap; - page = find_get_page(swap_address_space(swap), swap.val); - } + if (shmem_mapping(mapping)) { + page = find_get_entry(mapping, pgoff); + if (radix_tree_exceptional_entry(page)) { + swp_entry_t swp = radix_to_swp_entry(page); + if (do_swap_account) + *entry = swp; + page = find_get_page(swap_address_space(swp), swp.val); + } + } else + page = find_get_page(mapping, pgoff); +#else + page = find_get_page(mapping, pgoff); #endif return page; } diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 35ef28a..9ccef39 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1081,15 +1081,16 @@ int memory_failure(unsigned long pfn, int trapno, int flags) return 0; } else if (PageHuge(hpage)) { /* - * Check "just unpoisoned", "filter hit", and - * "race with other subpage." + * Check "filter hit" and "race with other subpage." */ lock_page(hpage); - if (!PageHWPoison(hpage) - || (hwpoison_filter(p) && TestClearPageHWPoison(p)) - || (p != hpage && TestSetPageHWPoison(hpage))) { - atomic_long_sub(nr_pages, &num_poisoned_pages); - return 0; + if (PageHWPoison(hpage)) { + if ((hwpoison_filter(p) && TestClearPageHWPoison(p)) + || (p != hpage && TestSetPageHWPoison(hpage))) { + atomic_long_sub(nr_pages, &num_poisoned_pages); + unlock_page(hpage); + return 0; + } } set_page_hwpoison_huge_page(hpage); res = dequeue_hwpoisoned_huge_page(hpage); @@ -1152,6 +1153,8 @@ int memory_failure(unsigned long pfn, int trapno, int flags) */ if (!PageHWPoison(p)) { printk(KERN_ERR "MCE %#lx: just unpoisoned\n", pfn); + atomic_long_sub(nr_pages, &num_poisoned_pages); + put_page(hpage); res = 0; goto out; } diff --git a/mm/mremap.c b/mm/mremap.c index 0843feb..05f1180 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -194,10 +194,17 @@ unsigned long move_page_tables(struct vm_area_struct *vma, break; if (pmd_trans_huge(*old_pmd)) { int err = 0; - if (extent == HPAGE_PMD_SIZE) + if (extent == HPAGE_PMD_SIZE) { + VM_BUG_ON(vma->vm_file || !vma->anon_vma); + /* See comment in move_ptes() */ + if (need_rmap_locks) + anon_vma_lock_write(vma->anon_vma); err = move_huge_pmd(vma, new_vma, old_addr, new_addr, old_end, old_pmd, new_pmd); + if (need_rmap_locks) + anon_vma_unlock_write(vma->anon_vma); + } if (err > 0) { need_flush = true; continue; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index ef41349..a4317da 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -593,14 +593,14 @@ unsigned long bdi_dirty_limit(struct backing_dev_info *bdi, unsigned long dirty) * (5) the closer to setpoint, the smaller |df/dx| (and the reverse) * => fast response on large errors; small oscillation near setpoint */ -static inline long long pos_ratio_polynom(unsigned long setpoint, +static long long pos_ratio_polynom(unsigned long setpoint, unsigned long dirty, unsigned long limit) { long long pos_ratio; long x; - x = div_s64(((s64)setpoint - (s64)dirty) << RATELIMIT_CALC_SHIFT, + x = div64_s64(((s64)setpoint - (s64)dirty) << RATELIMIT_CALC_SHIFT, limit - setpoint + 1); pos_ratio = x; pos_ratio = pos_ratio * x >> RATELIMIT_CALC_SHIFT; @@ -842,7 +842,7 @@ static unsigned long bdi_position_ratio(struct backing_dev_info *bdi, x_intercept = bdi_setpoint + span; if (bdi_dirty < x_intercept - span / 4) { - pos_ratio = div_u64(pos_ratio * (x_intercept - bdi_dirty), + pos_ratio = div64_u64(pos_ratio * (x_intercept - bdi_dirty), x_intercept - bdi_setpoint + 1); } else pos_ratio /= 4; diff --git a/mm/percpu.c b/mm/percpu.c index 63e24fb..2ddf9a9 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -610,7 +610,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void) chunk->map = pcpu_mem_zalloc(PCPU_DFL_MAP_ALLOC * sizeof(chunk->map[0])); if (!chunk->map) { - kfree(chunk); + pcpu_mem_free(chunk, pcpu_chunk_struct_size); return NULL; } @@ -166,7 +166,7 @@ typedef unsigned char freelist_idx_t; typedef unsigned short freelist_idx_t; #endif -#define SLAB_OBJ_MAX_NUM (1 << sizeof(freelist_idx_t) * BITS_PER_BYTE) +#define SLAB_OBJ_MAX_NUM ((1 << sizeof(freelist_idx_t) * BITS_PER_BYTE) - 1) /* * true if a page was allocated from pfmemalloc reserves for network-based @@ -2572,13 +2572,13 @@ static void *alloc_slabmgmt(struct kmem_cache *cachep, return freelist; } -static inline freelist_idx_t get_free_obj(struct page *page, unsigned char idx) +static inline freelist_idx_t get_free_obj(struct page *page, unsigned int idx) { return ((freelist_idx_t *)page->freelist)[idx]; } static inline void set_free_obj(struct page *page, - unsigned char idx, freelist_idx_t val) + unsigned int idx, freelist_idx_t val) { ((freelist_idx_t *)(page->freelist))[idx] = val; } @@ -91,6 +91,7 @@ __kmem_cache_alias(const char *name, size_t size, size_t align, #define CACHE_CREATE_MASK (SLAB_CORE_FLAGS | SLAB_DEBUG_FLAGS | SLAB_CACHE_FLAGS) int __kmem_cache_shutdown(struct kmem_cache *); +void slab_kmem_cache_release(struct kmem_cache *); struct seq_file; struct file; diff --git a/mm/slab_common.c b/mm/slab_common.c index f3cfccf..102cc6f 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -323,6 +323,12 @@ static int kmem_cache_destroy_memcg_children(struct kmem_cache *s) } #endif /* CONFIG_MEMCG_KMEM */ +void slab_kmem_cache_release(struct kmem_cache *s) +{ + kfree(s->name); + kmem_cache_free(kmem_cache, s); +} + void kmem_cache_destroy(struct kmem_cache *s) { get_online_cpus(); @@ -352,8 +358,11 @@ void kmem_cache_destroy(struct kmem_cache *s) rcu_barrier(); memcg_free_cache_params(s); - kfree(s->name); - kmem_cache_free(kmem_cache, s); +#ifdef SLAB_SUPPORTS_SYSFS + sysfs_slab_remove(s); +#else + slab_kmem_cache_release(s); +#endif goto out_put_cpus; out_unlock: @@ -210,14 +210,11 @@ enum track_item { TRACK_ALLOC, TRACK_FREE }; #ifdef CONFIG_SYSFS static int sysfs_slab_add(struct kmem_cache *); static int sysfs_slab_alias(struct kmem_cache *, const char *); -static void sysfs_slab_remove(struct kmem_cache *); static void memcg_propagate_slab_attrs(struct kmem_cache *s); #else static inline int sysfs_slab_add(struct kmem_cache *s) { return 0; } static inline int sysfs_slab_alias(struct kmem_cache *s, const char *p) { return 0; } -static inline void sysfs_slab_remove(struct kmem_cache *s) { } - static inline void memcg_propagate_slab_attrs(struct kmem_cache *s) { } #endif @@ -3238,24 +3235,7 @@ static inline int kmem_cache_close(struct kmem_cache *s) int __kmem_cache_shutdown(struct kmem_cache *s) { - int rc = kmem_cache_close(s); - - if (!rc) { - /* - * Since slab_attr_store may take the slab_mutex, we should - * release the lock while removing the sysfs entry in order to - * avoid a deadlock. Because this is pretty much the last - * operation we do and the lock will be released shortly after - * that in slab_common.c, we could just move sysfs_slab_remove - * to a later point in common code. We should do that when we - * have a common sysfs framework for all allocators. - */ - mutex_unlock(&slab_mutex); - sysfs_slab_remove(s); - mutex_lock(&slab_mutex); - } - - return rc; + return kmem_cache_close(s); } /******************************************************************** @@ -5071,15 +5051,18 @@ static void memcg_propagate_slab_attrs(struct kmem_cache *s) #ifdef CONFIG_MEMCG_KMEM int i; char *buffer = NULL; + struct kmem_cache *root_cache; - if (!is_root_cache(s)) + if (is_root_cache(s)) return; + root_cache = s->memcg_params->root_cache; + /* * This mean this cache had no attribute written. Therefore, no point * in copying default values around */ - if (!s->max_attr_size) + if (!root_cache->max_attr_size) return; for (i = 0; i < ARRAY_SIZE(slab_attrs); i++) { @@ -5101,7 +5084,7 @@ static void memcg_propagate_slab_attrs(struct kmem_cache *s) */ if (buffer) buf = buffer; - else if (s->max_attr_size < ARRAY_SIZE(mbuf)) + else if (root_cache->max_attr_size < ARRAY_SIZE(mbuf)) buf = mbuf; else { buffer = (char *) get_zeroed_page(GFP_KERNEL); @@ -5110,7 +5093,7 @@ static void memcg_propagate_slab_attrs(struct kmem_cache *s) buf = buffer; } - attr->show(s->memcg_params->root_cache, buf); + attr->show(root_cache, buf); attr->store(s, buf, strlen(buf)); } @@ -5119,6 +5102,11 @@ static void memcg_propagate_slab_attrs(struct kmem_cache *s) #endif } +static void kmem_cache_release(struct kobject *k) +{ + slab_kmem_cache_release(to_slab(k)); +} + static const struct sysfs_ops slab_sysfs_ops = { .show = slab_attr_show, .store = slab_attr_store, @@ -5126,6 +5114,7 @@ static const struct sysfs_ops slab_sysfs_ops = { static struct kobj_type slab_ktype = { .sysfs_ops = &slab_sysfs_ops, + .release = kmem_cache_release, }; static int uevent_filter(struct kset *kset, struct kobject *kobj) @@ -5252,7 +5241,7 @@ out_put_kobj: goto out; } -static void sysfs_slab_remove(struct kmem_cache *s) +void sysfs_slab_remove(struct kmem_cache *s) { if (slab_state < FULL) /* diff --git a/mm/truncate.c b/mm/truncate.c index e5cc39a..6a78c81 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -484,14 +484,6 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping, unsigned long count = 0; int i; - /* - * Note: this function may get called on a shmem/tmpfs mapping: - * pagevec_lookup() might then return 0 prematurely (because it - * got a gangful of swap entries); but it's hardly worth worrying - * about - it can rarely have anything to free from such a mapping - * (most pages are dirty), and already skips over any difficulties. - */ - pagevec_init(&pvec, 0); while (index <= end && pagevec_lookup_entries(&pvec, mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1, @@ -10,6 +10,7 @@ #include <linux/swapops.h> #include <linux/mman.h> #include <linux/hugetlb.h> +#include <linux/vmalloc.h> #include <asm/uaccess.h> @@ -387,6 +388,15 @@ unsigned long vm_mmap(struct file *file, unsigned long addr, } EXPORT_SYMBOL(vm_mmap); +void kvfree(const void *addr) +{ + if (is_vmalloc_addr(addr)) + vfree(addr); + else + kfree(addr); +} +EXPORT_SYMBOL(kvfree); + struct address_space *page_mapping(struct page *page) { struct address_space *mapping = page->mapping; diff --git a/mm/vmscan.c b/mm/vmscan.c index 3f56c8d..32c661d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1916,6 +1916,24 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc, get_lru_size(lruvec, LRU_INACTIVE_FILE); /* + * Prevent the reclaimer from falling into the cache trap: as + * cache pages start out inactive, every cache fault will tip + * the scan balance towards the file LRU. And as the file LRU + * shrinks, so does the window for rotation from references. + * This means we have a runaway feedback loop where a tiny + * thrashing file LRU becomes infinitely more attractive than + * anon pages. Try to detect this based on file LRU size. + */ + if (global_reclaim(sc)) { + unsigned long free = zone_page_state(zone, NR_FREE_PAGES); + + if (unlikely(file + free <= high_wmark_pages(zone))) { + scan_balance = SCAN_ANON; + goto out; + } + } + + /* * There is enough inactive page cache, do not reclaim * anything from the anonymous working set right now. */ diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 175273f..44ebd5c 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -169,6 +169,7 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_mvrp; + vlan->nest_level = dev_get_nest_level(real_dev, is_vlan_dev) + 1; err = register_netdevice(dev); if (err < 0) goto out_uninit_mvrp; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 8f025af..ad2ac3c 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -493,48 +493,10 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change) } } -static int vlan_calculate_locking_subclass(struct net_device *real_dev) -{ - int subclass = 0; - - while (is_vlan_dev(real_dev)) { - subclass++; - real_dev = vlan_dev_priv(real_dev)->real_dev; - } - - return subclass; -} - -static void vlan_dev_mc_sync(struct net_device *to, struct net_device *from) -{ - int err = 0, subclass; - - subclass = vlan_calculate_locking_subclass(to); - - spin_lock_nested(&to->addr_list_lock, subclass); - err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len); - if (!err) - __dev_set_rx_mode(to); - spin_unlock(&to->addr_list_lock); -} - -static void vlan_dev_uc_sync(struct net_device *to, struct net_device *from) -{ - int err = 0, subclass; - - subclass = vlan_calculate_locking_subclass(to); - - spin_lock_nested(&to->addr_list_lock, subclass); - err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len); - if (!err) - __dev_set_rx_mode(to); - spin_unlock(&to->addr_list_lock); -} - static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) { - vlan_dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); - vlan_dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); + dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); + dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); } /* @@ -562,6 +524,11 @@ static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass); } +static int vlan_dev_get_lock_subclass(struct net_device *dev) +{ + return vlan_dev_priv(dev)->nest_level; +} + static const struct header_ops vlan_header_ops = { .create = vlan_dev_hard_header, .rebuild = vlan_dev_rebuild_header, @@ -597,7 +564,6 @@ static const struct net_device_ops vlan_netdev_ops; static int vlan_dev_init(struct net_device *dev) { struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; - int subclass = 0; netif_carrier_off(dev); @@ -646,8 +612,7 @@ static int vlan_dev_init(struct net_device *dev) SET_NETDEV_DEVTYPE(dev, &vlan_type); - subclass = vlan_calculate_locking_subclass(dev); - vlan_dev_set_lockdep_class(dev, subclass); + vlan_dev_set_lockdep_class(dev, vlan_dev_get_lock_subclass(dev)); vlan_dev_priv(dev)->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats); if (!vlan_dev_priv(dev)->vlan_pcpu_stats) @@ -678,9 +643,9 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev, struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; netdev_features_t old_features = features; - features &= real_dev->vlan_features; + features = netdev_intersect_features(features, real_dev->vlan_features); features |= NETIF_F_RXCSUM; - features &= real_dev->features; + features = netdev_intersect_features(features, real_dev->features); features |= old_features & NETIF_F_SOFT_FEATURES; features |= NETIF_F_LLTX; @@ -817,6 +782,7 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, #endif .ndo_fix_features = vlan_dev_fix_features, + .ndo_get_lock_subclass = vlan_dev_get_lock_subclass, }; void vlan_setup(struct net_device *dev) diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 786ee2f..01a1082 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1669,7 +1669,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr goto out; } - if (sk->sk_no_check == 1) + if (sk->sk_no_check_tx) ddp->deh_sum = 0; else ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); diff --git a/net/atm/svc.c b/net/atm/svc.c index 1281049..d8e5d0c2 100644 --- a/net/atm/svc.c +++ b/net/atm/svc.c @@ -263,17 +263,11 @@ static int svc_connect(struct socket *sock, struct sockaddr *sockaddr, goto out; } } -/* - * Not supported yet - * - * #ifndef CONFIG_SINGLE_SIGITF - */ + vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp); vcc->qos.txtp.pcr = 0; vcc->qos.txtp.min_pcr = 0; -/* - * #endif - */ + error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci); if (!error) sock->state = SS_CONNECTED; diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b3bd4ec..f04224c 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1545,6 +1545,8 @@ out_neigh: if ((orig_neigh_node) && (!is_single_hop_neigh)) batadv_orig_node_free_ref(orig_neigh_node); out: + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); if (router) batadv_neigh_node_free_ref(router); if (router_router) diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index b758881..a12e25e 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -245,6 +245,7 @@ static int batadv_algorithms_open(struct inode *inode, struct file *file) static int batadv_originators_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_orig_seq_print_text, net_dev); } @@ -258,18 +259,21 @@ static int batadv_originators_hardif_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_orig_hardif_seq_print_text, net_dev); } static int batadv_gateways_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_gw_client_seq_print_text, net_dev); } static int batadv_transtable_global_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_tt_global_seq_print_text, net_dev); } @@ -277,6 +281,7 @@ static int batadv_transtable_global_open(struct inode *inode, struct file *file) static int batadv_bla_claim_table_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_bla_claim_table_seq_print_text, net_dev); } @@ -285,6 +290,7 @@ static int batadv_bla_backbone_table_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_bla_backbone_table_seq_print_text, net_dev); } @@ -300,6 +306,7 @@ static int batadv_bla_backbone_table_open(struct inode *inode, static int batadv_dat_cache_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_dat_cache_seq_print_text, net_dev); } #endif @@ -307,6 +314,7 @@ static int batadv_dat_cache_open(struct inode *inode, struct file *file) static int batadv_transtable_local_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_tt_local_seq_print_text, net_dev); } @@ -319,6 +327,7 @@ struct batadv_debuginfo { static int batadv_nc_nodes_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_nc_nodes_seq_print_text, net_dev); } #endif @@ -333,7 +342,7 @@ struct batadv_debuginfo batadv_debuginfo_##_name = { \ .llseek = seq_lseek, \ .release = single_release, \ } \ -}; +} /* the following attributes are general and therefore they will be directly * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs @@ -395,7 +404,7 @@ struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \ .llseek = seq_lseek, \ .release = single_release, \ }, \ -}; +} static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO, batadv_originators_hardif_open); diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index b25fd64..dcd99b2 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -662,6 +662,7 @@ static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv) void batadv_dat_status_update(struct net_device *net_dev) { struct batadv_priv *bat_priv = netdev_priv(net_dev); + batadv_dat_tvlv_container_update(bat_priv); } @@ -940,8 +941,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, * additional DAT answer may trigger kernel warnings about * a packet coming from the wrong port. */ - if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, - BATADV_NO_FLAGS)) { + if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) { ret = true; goto out; } diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index bcc4bea..f14e54a 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -418,12 +418,13 @@ bool batadv_frag_send_packet(struct sk_buff *skb, struct batadv_neigh_node *neigh_node) { struct batadv_priv *bat_priv; - struct batadv_hard_iface *primary_if; + struct batadv_hard_iface *primary_if = NULL; struct batadv_frag_packet frag_header; struct sk_buff *skb_fragment; unsigned mtu = neigh_node->if_incoming->net_dev->mtu; unsigned header_size = sizeof(frag_header); unsigned max_fragment_size, max_packet_size; + bool ret = false; /* To avoid merge and refragmentation at next-hops we never send * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE @@ -483,7 +484,11 @@ bool batadv_frag_send_packet(struct sk_buff *skb, skb->len + ETH_HLEN); batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - return true; + ret = true; + out_err: - return false; + if (primary_if) + batadv_hardif_free_ref(primary_if); + + return ret; } diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index c835e13..90cff58 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -42,8 +42,10 @@ static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node) { - if (atomic_dec_and_test(&gw_node->refcount)) + if (atomic_dec_and_test(&gw_node->refcount)) { + batadv_orig_node_free_ref(gw_node->orig_node); kfree_rcu(gw_node, rcu); + } } static struct batadv_gw_node * @@ -406,9 +408,14 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv, if (gateway->bandwidth_down == 0) return; + if (!atomic_inc_not_zero(&orig_node->refcount)) + return; + gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); - if (!gw_node) + if (!gw_node) { + batadv_orig_node_free_ref(orig_node); return; + } INIT_HLIST_NODE(&gw_node->list); gw_node->orig_node = orig_node; diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index b851cc5..fbda6b5 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -83,7 +83,7 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) return true; /* no more parents..stop recursion */ - if (net_dev->iflink == net_dev->ifindex) + if (net_dev->iflink == 0 || net_dev->iflink == net_dev->ifindex) return false; /* recurse over the parent device */ diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 770dc89..118b990 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -24,7 +24,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2014.2.0" +#define BATADV_SOURCE_VERSION "2014.3.0" #endif /* B.A.T.M.A.N. parameters */ diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index a9546fe..40a2fc4 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -86,6 +86,7 @@ static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv) void batadv_nc_status_update(struct net_device *net_dev) { struct batadv_priv *bat_priv = netdev_priv(net_dev); + batadv_nc_tvlv_container_update(bat_priv); } diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index ffd9dfb..6a48451 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -501,12 +501,17 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node, static void batadv_orig_ifinfo_free_rcu(struct rcu_head *rcu) { struct batadv_orig_ifinfo *orig_ifinfo; + struct batadv_neigh_node *router; orig_ifinfo = container_of(rcu, struct batadv_orig_ifinfo, rcu); if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT) batadv_hardif_free_ref_now(orig_ifinfo->if_outgoing); + /* this is the last reference to this object */ + router = rcu_dereference_protected(orig_ifinfo->router, true); + if (router) + batadv_neigh_node_free_ref_now(router); kfree(orig_ifinfo); } @@ -702,6 +707,47 @@ free_orig_node: } /** + * batadv_purge_neigh_ifinfo - purge obsolete ifinfo entries from neighbor + * @bat_priv: the bat priv with all the soft interface information + * @neigh: orig node which is to be checked + */ +static void +batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv, + struct batadv_neigh_node *neigh) +{ + struct batadv_neigh_ifinfo *neigh_ifinfo; + struct batadv_hard_iface *if_outgoing; + struct hlist_node *node_tmp; + + spin_lock_bh(&neigh->ifinfo_lock); + + /* for all ifinfo objects for this neighinator */ + hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, + &neigh->ifinfo_list, list) { + if_outgoing = neigh_ifinfo->if_outgoing; + + /* always keep the default interface */ + if (if_outgoing == BATADV_IF_DEFAULT) + continue; + + /* don't purge if the interface is not (going) down */ + if ((if_outgoing->if_status != BATADV_IF_INACTIVE) && + (if_outgoing->if_status != BATADV_IF_NOT_IN_USE) && + (if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)) + continue; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "neighbor/ifinfo purge: neighbor %pM, iface: %s\n", + neigh->addr, if_outgoing->net_dev->name); + + hlist_del_rcu(&neigh_ifinfo->list); + batadv_neigh_ifinfo_free_ref(neigh_ifinfo); + } + + spin_unlock_bh(&neigh->ifinfo_lock); +} + +/** * batadv_purge_orig_ifinfo - purge obsolete ifinfo entries from originator * @bat_priv: the bat priv with all the soft interface information * @orig_node: orig node which is to be checked @@ -800,6 +846,11 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, hlist_del_rcu(&neigh_node->list); batadv_neigh_node_free_ref(neigh_node); + } else { + /* only necessary if not the whole neighbor is to be + * deleted, but some interface has been removed. + */ + batadv_purge_neigh_ifinfo(bat_priv, neigh_node); } } @@ -857,7 +908,7 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, { struct batadv_neigh_node *best_neigh_node; struct batadv_hard_iface *hard_iface; - bool changed; + bool changed_ifinfo, changed_neigh; if (batadv_has_timed_out(orig_node->last_seen, 2 * BATADV_PURGE_TIMEOUT)) { @@ -867,10 +918,10 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, jiffies_to_msecs(orig_node->last_seen)); return true; } - changed = batadv_purge_orig_ifinfo(bat_priv, orig_node); - changed = changed || batadv_purge_orig_neighbors(bat_priv, orig_node); + changed_ifinfo = batadv_purge_orig_ifinfo(bat_priv, orig_node); + changed_neigh = batadv_purge_orig_neighbors(bat_priv, orig_node); - if (!changed) + if (!changed_ifinfo && !changed_neigh) return false; /* first for NULL ... */ @@ -1028,7 +1079,8 @@ int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset) bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq, hard_iface); out: - batadv_hardif_free_ref(hard_iface); + if (hard_iface) + batadv_hardif_free_ref(hard_iface); return 0; } diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 1ebb0d9..fc47baa 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -29,12 +29,14 @@ static struct net_device *batadv_kobj_to_netdev(struct kobject *obj) { struct device *dev = container_of(obj->parent, struct device, kobj); + return to_net_dev(dev); } static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj) { struct net_device *net_dev = batadv_kobj_to_netdev(obj); + return netdev_priv(net_dev); } @@ -106,7 +108,7 @@ struct batadv_attribute batadv_attr_vlan_##_name = { \ .mode = _mode }, \ .show = _show, \ .store = _store, \ -}; +} /* Use this, if you have customized show and store functions */ #define BATADV_ATTR(_name, _mode, _show, _store) \ @@ -115,7 +117,7 @@ struct batadv_attribute batadv_attr_##_name = { \ .mode = _mode }, \ .show = _show, \ .store = _store, \ -}; +} #define BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func) \ ssize_t batadv_store_##_name(struct kobject *kobj, \ @@ -124,6 +126,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj, \ { \ struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ struct batadv_priv *bat_priv = netdev_priv(net_dev); \ + \ return __batadv_store_bool_attr(buff, count, _post_func, attr, \ &bat_priv->_name, net_dev); \ } @@ -133,6 +136,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff) \ { \ struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \ + \ return sprintf(buff, "%s\n", \ atomic_read(&bat_priv->_name) == 0 ? \ "disabled" : "enabled"); \ @@ -155,6 +159,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj, \ { \ struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ struct batadv_priv *bat_priv = netdev_priv(net_dev); \ + \ return __batadv_store_uint_attr(buff, count, _min, _max, \ _post_func, attr, \ &bat_priv->_name, net_dev); \ @@ -165,6 +170,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff) \ { \ struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \ + \ return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name)); \ } \ @@ -188,6 +194,7 @@ ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \ size_t res = __batadv_store_bool_attr(buff, count, _post_func, \ attr, &vlan->_name, \ bat_priv->soft_iface); \ + \ batadv_softif_vlan_free_ref(vlan); \ return res; \ } @@ -202,6 +209,7 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \ size_t res = sprintf(buff, "%s\n", \ atomic_read(&vlan->_name) == 0 ? \ "disabled" : "enabled"); \ + \ batadv_softif_vlan_free_ref(vlan); \ return res; \ } @@ -324,12 +332,14 @@ static ssize_t batadv_show_bat_algo(struct kobject *kobj, struct attribute *attr, char *buff) { struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); + return sprintf(buff, "%s\n", bat_priv->bat_algo_ops->name); } static void batadv_post_gw_reselect(struct net_device *net_dev) { struct batadv_priv *bat_priv = netdev_priv(net_dev); + batadv_gw_reselect(bat_priv); } diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 095943c..8671bc7 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -28,6 +28,7 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include <net/bluetooth/l2cap.h> #include "smp.h" #include "a2mp.h" @@ -407,6 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->io_capability = hdev->io_capability; conn->remote_auth = 0xff; conn->key_type = 0xff; + conn->tx_power = HCI_TX_POWER_INVALID; + conn->max_tx_power = HCI_TX_POWER_INVALID; set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d31f144..0a43cce 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -34,6 +34,7 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include <net/bluetooth/l2cap.h> #include "smp.h" @@ -579,6 +580,62 @@ static int sniff_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, sniff_max_interval_set, "%llu\n"); +static int conn_info_min_age_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val > hdev->conn_info_max_age) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->conn_info_min_age = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_info_min_age_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->conn_info_min_age; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get, + conn_info_min_age_set, "%llu\n"); + +static int conn_info_max_age_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val < hdev->conn_info_min_age) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->conn_info_max_age = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_info_max_age_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->conn_info_max_age; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get, + conn_info_max_age_set, "%llu\n"); + static int identity_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1754,6 +1811,11 @@ static int __hci_init(struct hci_dev *hdev) &blacklist_fops); debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); + debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, + &conn_info_min_age_fops); + debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev, + &conn_info_max_age_fops); + if (lmp_bredr_capable(hdev)) { debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev, &inquiry_cache_fops); @@ -3789,6 +3851,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; + hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE; + hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE; mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ca19fd4..3454807 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1245,6 +1245,59 @@ static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, amp_write_rem_assoc_continue(hdev, rp->phy_handle); } +static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_rssi *rp = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) + conn->rssi = rp->rssi; + + hci_dev_unlock(hdev); +} + +static void hci_cc_read_tx_power(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_cp_read_tx_power *sent; + struct hci_rp_read_tx_power *rp = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER); + if (!sent) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (!conn) + goto unlock; + + switch (sent->type) { + case 0x00: + conn->tx_power = rp->tx_power; + break; + case 0x01: + conn->max_tx_power = rp->tx_power; + break; + } + +unlock: + hci_dev_unlock(hdev); +} + static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -2637,6 +2690,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_remote_amp_assoc(hdev, skb); break; + case HCI_OP_READ_RSSI: + hci_cc_read_rssi(hdev, skb); + break; + + case HCI_OP_READ_TX_POWER: + hci_cc_read_tx_power(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a1e5bb7..dc4d301 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7519,9 +7519,9 @@ int __init l2cap_init(void) l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, NULL, &l2cap_debugfs_fops); - debugfs_create_u16("l2cap_le_max_credits", 0466, bt_debugfs, + debugfs_create_u16("l2cap_le_max_credits", 0644, bt_debugfs, &le_max_credits); - debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs, + debugfs_create_u16("l2cap_le_default_mps", 0644, bt_debugfs, &le_default_mps); bt_6lowpan_init(); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 54abbce..5e9c21a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -29,12 +29,13 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include <net/bluetooth/l2cap.h> #include <net/bluetooth/mgmt.h> #include "smp.h" #define MGMT_VERSION 1 -#define MGMT_REVISION 5 +#define MGMT_REVISION 6 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, @@ -83,6 +84,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_DEBUG_KEYS, MGMT_OP_SET_PRIVACY, MGMT_OP_LOAD_IRKS, + MGMT_OP_GET_CONN_INFO, }; static const u16 mgmt_events[] = { @@ -4532,7 +4534,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, for (i = 0; i < key_count; i++) { struct mgmt_ltk_info *key = &cp->keys[i]; - u8 type, addr_type; + u8 type, addr_type, authenticated; if (key->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; @@ -4544,8 +4546,13 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, else type = HCI_SMP_LTK_SLAVE; + if (key->type == MGMT_LTK_UNAUTHENTICATED) + authenticated = 0x00; + else + authenticated = 0x01; + hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type, - key->type, key->val, key->enc_size, key->ediv, + authenticated, key->val, key->enc_size, key->ediv, key->rand); } @@ -4557,6 +4564,218 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, return err; } +struct cmd_conn_lookup { + struct hci_conn *conn; + bool valid_tx_power; + u8 mgmt_status; +}; + +static void get_conn_info_complete(struct pending_cmd *cmd, void *data) +{ + struct cmd_conn_lookup *match = data; + struct mgmt_cp_get_conn_info *cp; + struct mgmt_rp_get_conn_info rp; + struct hci_conn *conn = cmd->user_data; + + if (conn != match->conn) + return; + + cp = (struct mgmt_cp_get_conn_info *) cmd->param; + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + + if (!match->mgmt_status) { + rp.rssi = conn->rssi; + + if (match->valid_tx_power) { + rp.tx_power = conn->tx_power; + rp.max_tx_power = conn->max_tx_power; + } else { + rp.tx_power = HCI_TX_POWER_INVALID; + rp.max_tx_power = HCI_TX_POWER_INVALID; + } + } + + cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, + match->mgmt_status, &rp, sizeof(rp)); + + hci_conn_drop(conn); + + mgmt_pending_remove(cmd); +} + +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_read_rssi *cp; + struct hci_conn *conn; + struct cmd_conn_lookup match; + u16 handle; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + /* TX power data is valid in case request completed successfully, + * otherwise we assume it's not valid. At the moment we assume that + * either both or none of current and max values are valid to keep code + * simple. + */ + match.valid_tx_power = !status; + + /* Commands sent in request are either Read RSSI or Read Transmit Power + * Level so we check which one was last sent to retrieve connection + * handle. Both commands have handle as first parameter so it's safe to + * cast data on the same command struct. + * + * First command sent is always Read RSSI and we fail only if it fails. + * In other case we simply override error to indicate success as we + * already remembered if TX power value is actually valid. + */ + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI); + if (!cp) { + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER); + status = 0; + } + + if (!cp) { + BT_ERR("invalid sent_cmd in response"); + goto unlock; + } + + handle = __le16_to_cpu(cp->handle); + conn = hci_conn_hash_lookup_handle(hdev, handle); + if (!conn) { + BT_ERR("unknown handle (%d) in response", handle); + goto unlock; + } + + match.conn = conn; + match.mgmt_status = mgmt_status(status); + + /* Cache refresh is complete, now reply for mgmt request for given + * connection only. + */ + mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev, + get_conn_info_complete, &match); + +unlock: + hci_dev_unlock(hdev); +} + +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_get_conn_info *cp = data; + struct mgmt_rp_get_conn_info rp; + struct hci_conn *conn; + unsigned long conn_info_age; + int err = 0; + + BT_DBG("%s", hdev->name); + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); + goto unlock; + } + + if (cp->addr.type == BDADDR_BREDR) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->addr.bdaddr); + else + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); + + if (!conn || conn->state != BT_CONNECTED) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp)); + goto unlock; + } + + /* To avoid client trying to guess when to poll again for information we + * calculate conn info age as random value between min/max set in hdev. + */ + conn_info_age = hdev->conn_info_min_age + + prandom_u32_max(hdev->conn_info_max_age - + hdev->conn_info_min_age); + + /* Query controller to refresh cached values if they are too old or were + * never read. + */ + if (time_after(jiffies, conn->conn_info_timestamp + + msecs_to_jiffies(conn_info_age)) || + !conn->conn_info_timestamp) { + struct hci_request req; + struct hci_cp_read_tx_power req_txp_cp; + struct hci_cp_read_rssi req_rssi_cp; + struct pending_cmd *cmd; + + hci_req_init(&req, hdev); + req_rssi_cp.handle = cpu_to_le16(conn->handle); + hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp), + &req_rssi_cp); + + /* For LE links TX power does not change thus we don't need to + * query for it once value is known. + */ + if (!bdaddr_type_is_le(cp->addr.type) || + conn->tx_power == HCI_TX_POWER_INVALID) { + req_txp_cp.handle = cpu_to_le16(conn->handle); + req_txp_cp.type = 0x00; + hci_req_add(&req, HCI_OP_READ_TX_POWER, + sizeof(req_txp_cp), &req_txp_cp); + } + + /* Max TX power needs to be read only once per connection */ + if (conn->max_tx_power == HCI_TX_POWER_INVALID) { + req_txp_cp.handle = cpu_to_le16(conn->handle); + req_txp_cp.type = 0x01; + hci_req_add(&req, HCI_OP_READ_TX_POWER, + sizeof(req_txp_cp), &req_txp_cp); + } + + err = hci_req_run(&req, conn_info_refresh_complete); + if (err < 0) + goto unlock; + + cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev, + data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_conn_hold(conn); + cmd->user_data = conn; + + conn->conn_info_timestamp = jiffies; + } else { + /* Cache is valid, just reply with values cached in hci_conn */ + rp.rssi = conn->rssi; + rp.tx_power = conn->tx_power; + rp.max_tx_power = conn->max_tx_power; + + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); + } + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -4612,6 +4831,7 @@ static const struct mgmt_handler { { set_debug_keys, false, MGMT_SETTING_SIZE }, { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, { load_irks, true, MGMT_LOAD_IRKS_SIZE }, + { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, }; @@ -5007,6 +5227,14 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } +static u8 mgmt_ltk_type(struct smp_ltk *ltk) +{ + if (ltk->authenticated) + return MGMT_LTK_AUTHENTICATED; + + return MGMT_LTK_UNAUTHENTICATED; +} + void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) { struct mgmt_ev_new_long_term_key ev; @@ -5032,7 +5260,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); - ev.key.type = key->authenticated; + ev.key.type = mgmt_ltk_type(key); ev.key.enc_size = key->enc_size; ev.key.ediv = key->ediv; ev.key.rand = key->rand; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index cf620260..754b6fe 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -307,7 +307,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d); skb_queue_head_init(&d->tx_queue); - spin_lock_init(&d->lock); + mutex_init(&d->lock); atomic_set(&d->refcnt, 1); rfcomm_dlc_clear_state(d); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 403ec09..8e385a0 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -70,7 +70,7 @@ struct rfcomm_dev { }; static LIST_HEAD(rfcomm_dev_list); -static DEFINE_SPINLOCK(rfcomm_dev_lock); +static DEFINE_MUTEX(rfcomm_dev_lock); static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); @@ -96,9 +96,9 @@ static void rfcomm_dev_destruct(struct tty_port *port) if (dev->tty_dev) tty_unregister_device(rfcomm_tty_driver, dev->id); - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); list_del(&dev->list); - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); kfree(dev); @@ -161,14 +161,14 @@ static struct rfcomm_dev *rfcomm_dev_get(int id) { struct rfcomm_dev *dev; - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); dev = __rfcomm_dev_lookup(id); if (dev && !tty_port_get(&dev->port)) dev = NULL; - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); return dev; } @@ -224,7 +224,7 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req, if (!dev) return ERR_PTR(-ENOMEM); - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); if (req->dev_id < 0) { dev->id = 0; @@ -305,11 +305,11 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req, holds reference to this module. */ __module_get(THIS_MODULE); - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); return dev; out: - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); kfree(dev); return ERR_PTR(err); } @@ -524,7 +524,7 @@ static int rfcomm_get_dev_list(void __user *arg) di = dl->dev_info; - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); list_for_each_entry(dev, &rfcomm_dev_list, list) { if (!tty_port_get(&dev->port)) @@ -540,7 +540,7 @@ static int rfcomm_get_dev_list(void __user *arg) break; } - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); dl->dev_num = n; size = sizeof(*dl) + n * sizeof(*di); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index dfb4e11..4f9662d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -35,6 +35,33 @@ #define AUTH_REQ_MASK 0x07 +#define SMP_FLAG_TK_VALID 1 +#define SMP_FLAG_CFM_PENDING 2 +#define SMP_FLAG_MITM_AUTH 3 +#define SMP_FLAG_COMPLETE 4 +#define SMP_FLAG_INITIATOR 5 + +struct smp_chan { + struct l2cap_conn *conn; + u8 preq[7]; /* SMP Pairing Request */ + u8 prsp[7]; /* SMP Pairing Response */ + u8 prnd[16]; /* SMP Pairing Random (local) */ + u8 rrnd[16]; /* SMP Pairing Random (remote) */ + u8 pcnf[16]; /* SMP Pairing Confirm */ + u8 tk[16]; /* SMP Temporary Key */ + u8 enc_key_size; + u8 remote_key_dist; + bdaddr_t id_addr; + u8 id_addr_type; + u8 irk[16]; + struct smp_csrk *csrk; + struct smp_csrk *slave_csrk; + struct smp_ltk *ltk; + struct smp_ltk *slave_ltk; + struct smp_irk *remote_irk; + unsigned long flags; +}; + static inline void swap128(const u8 src[16], u8 dst[16]) { int i; @@ -369,7 +396,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, /* Initialize key for JUST WORKS */ memset(smp->tk, 0, sizeof(smp->tk)); - clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); + clear_bit(SMP_FLAG_TK_VALID, &smp->flags); BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); @@ -388,19 +415,18 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, method = JUST_WORKS; /* Don't confirm locally initiated pairing attempts */ - if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, - &smp->smp_flags)) + if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags)) method = JUST_WORKS; /* If Just Works, Continue with Zero TK */ if (method == JUST_WORKS) { - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); + set_bit(SMP_FLAG_TK_VALID, &smp->flags); return 0; } /* Not Just Works/Confirm results in MITM Authentication */ if (method != JUST_CFM) - set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags); + set_bit(SMP_FLAG_MITM_AUTH, &smp->flags); /* If both devices have Keyoard-Display I/O, the master * Confirms and the slave Enters the passkey. @@ -419,7 +445,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, passkey %= 1000000; put_unaligned_le32(passkey, smp->tk); BT_DBG("PassKey: %d", passkey); - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); + set_bit(SMP_FLAG_TK_VALID, &smp->flags); } hci_dev_lock(hcon->hdev); @@ -441,15 +467,13 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, return ret; } -static void confirm_work(struct work_struct *work) +static u8 smp_confirm(struct smp_chan *smp) { - struct smp_chan *smp = container_of(work, struct smp_chan, confirm); struct l2cap_conn *conn = smp->conn; struct hci_dev *hdev = conn->hcon->hdev; struct crypto_blkcipher *tfm = hdev->tfm_aes; struct smp_cmd_pairing_confirm cp; int ret; - u8 reason; BT_DBG("conn %p", conn); @@ -463,35 +487,27 @@ static void confirm_work(struct work_struct *work) hci_dev_unlock(hdev); - if (ret) { - reason = SMP_UNSPECIFIED; - goto error; - } + if (ret) + return SMP_UNSPECIFIED; - clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); + clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags); smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); - return; - -error: - smp_failure(conn, reason); + return 0; } -static void random_work(struct work_struct *work) +static u8 smp_random(struct smp_chan *smp) { - struct smp_chan *smp = container_of(work, struct smp_chan, random); struct l2cap_conn *conn = smp->conn; struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; struct crypto_blkcipher *tfm = hdev->tfm_aes; - u8 reason, confirm[16]; + u8 confirm[16]; int ret; - if (IS_ERR_OR_NULL(tfm)) { - reason = SMP_UNSPECIFIED; - goto error; - } + if (IS_ERR_OR_NULL(tfm)) + return SMP_UNSPECIFIED; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); @@ -504,15 +520,12 @@ static void random_work(struct work_struct *work) hci_dev_unlock(hdev); - if (ret) { - reason = SMP_UNSPECIFIED; - goto error; - } + if (ret) + return SMP_UNSPECIFIED; if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { BT_ERR("Pairing failed (confirmation values mismatch)"); - reason = SMP_CONFIRM_FAILED; - goto error; + return SMP_CONFIRM_FAILED; } if (hcon->out) { @@ -525,10 +538,8 @@ static void random_work(struct work_struct *work) memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { - reason = SMP_UNSPECIFIED; - goto error; - } + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) + return SMP_UNSPECIFIED; hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; @@ -550,10 +561,7 @@ static void random_work(struct work_struct *work) ediv, rand); } - return; - -error: - smp_failure(conn, reason); + return 0; } static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) @@ -564,9 +572,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) if (!smp) return NULL; - INIT_WORK(&smp->confirm, confirm_work); - INIT_WORK(&smp->random, random_work); - smp->conn = conn; conn->smp_chan = smp; conn->hcon->smp_conn = conn; @@ -583,7 +588,7 @@ void smp_chan_destroy(struct l2cap_conn *conn) BUG_ON(!smp); - complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags); + complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags); mgmt_smp_complete(conn->hcon, complete); kfree(smp->csrk); @@ -634,7 +639,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) put_unaligned_le32(value, smp->tk); /* Fall Through */ case MGMT_OP_USER_CONFIRM_REPLY: - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); + set_bit(SMP_FLAG_TK_VALID, &smp->flags); break; case MGMT_OP_USER_PASSKEY_NEG_REPLY: case MGMT_OP_USER_CONFIRM_NEG_REPLY: @@ -646,8 +651,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) } /* If it is our turn to send Pairing Confirm, do so now */ - if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags)) - queue_work(hcon->hdev->workqueue, &smp->confirm); + if (test_bit(SMP_FLAG_CFM_PENDING, &smp->flags)) { + u8 rsp = smp_confirm(smp); + if (rsp) + smp_failure(conn, rsp); + } return 0; } @@ -656,14 +664,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; struct smp_chan *smp; - u8 key_size; - u8 auth = SMP_AUTH_NONE; + u8 key_size, auth; int ret; BT_DBG("conn %p", conn); if (skb->len < sizeof(*req)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; if (conn->hcon->link_mode & HCI_LM_MASTER) return SMP_CMD_NOTSUPP; @@ -681,8 +688,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*req)); /* We didn't start the pairing, so match remote */ - if (req->auth_req & SMP_AUTH_BONDING) - auth = req->auth_req; + auth = req->auth_req; conn->hcon->pending_sec_level = authreq_to_seclevel(auth); @@ -704,7 +710,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (ret) return SMP_UNSPECIFIED; - clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags); + clear_bit(SMP_FLAG_INITIATOR, &smp->flags); return 0; } @@ -713,14 +719,13 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; u8 key_size, auth = SMP_AUTH_NONE; int ret; BT_DBG("conn %p", conn); if (skb->len < sizeof(*rsp)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; if (!(conn->hcon->link_mode & HCI_LM_MASTER)) return SMP_CMD_NOTSUPP; @@ -753,11 +758,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (ret) return SMP_UNSPECIFIED; - set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); + set_bit(SMP_FLAG_CFM_PENDING, &smp->flags); /* Can't compose response until we have been confirmed */ - if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) - queue_work(hdev->workqueue, &smp->confirm); + if (test_bit(SMP_FLAG_TK_VALID, &smp->flags)) + return smp_confirm(smp); return 0; } @@ -765,12 +770,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); if (skb->len < sizeof(smp->pcnf)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); skb_pull(skb, sizeof(smp->pcnf)); @@ -778,10 +782,10 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) if (conn->hcon->out) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); - else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) - queue_work(hdev->workqueue, &smp->confirm); + else if (test_bit(SMP_FLAG_TK_VALID, &smp->flags)) + return smp_confirm(smp); else - set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); + set_bit(SMP_FLAG_CFM_PENDING, &smp->flags); return 0; } @@ -789,19 +793,16 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p", conn); if (skb->len < sizeof(smp->rrnd)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd)); skb_pull(skb, sizeof(smp->rrnd)); - queue_work(hdev->workqueue, &smp->random); - - return 0; + return smp_random(smp); } static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) @@ -836,7 +837,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); if (skb->len < sizeof(*rp)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; if (!(conn->hcon->link_mode & HCI_LM_MASTER)) return SMP_CMD_NOTSUPP; @@ -861,7 +862,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags); + clear_bit(SMP_FLAG_INITIATOR, &smp->flags); return 0; } @@ -928,7 +929,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); } - set_bit(SMP_FLAG_INITIATOR, &smp->smp_flags); + set_bit(SMP_FLAG_INITIATOR, &smp->flags); done: hcon->pending_sec_level = sec_level; @@ -944,7 +945,7 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); if (skb->len < sizeof(*rp)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; /* Ignore this PDU if it wasn't requested */ if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY)) @@ -969,7 +970,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); if (skb->len < sizeof(*rp)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; /* Ignore this PDU if it wasn't requested */ if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY)) @@ -1001,7 +1002,7 @@ static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG(""); if (skb->len < sizeof(*info)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; /* Ignore this PDU if it wasn't requested */ if (!(smp->remote_key_dist & SMP_DIST_ID_KEY)) @@ -1025,7 +1026,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, BT_DBG(""); if (skb->len < sizeof(*info)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; /* Ignore this PDU if it wasn't requested */ if (!(smp->remote_key_dist & SMP_DIST_ID_KEY)) @@ -1075,7 +1076,7 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); if (skb->len < sizeof(*rp)) - return SMP_UNSPECIFIED; + return SMP_INVALID_PARAMS; /* Ignore this PDU if it wasn't requested */ if (!(smp->remote_key_dist & SMP_DIST_SIGN)) @@ -1358,7 +1359,7 @@ int smp_distribute_keys(struct l2cap_conn *conn) clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags); cancel_delayed_work_sync(&conn->security_timer); - set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags); + set_bit(SMP_FLAG_COMPLETE, &smp->flags); smp_notify_keys(conn); smp_chan_destroy(conn); diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 1277147..5a8dc36 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -111,39 +111,11 @@ struct smp_cmd_security_req { #define SMP_CMD_NOTSUPP 0x07 #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 +#define SMP_INVALID_PARAMS 0x0a #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 -#define SMP_FLAG_TK_VALID 1 -#define SMP_FLAG_CFM_PENDING 2 -#define SMP_FLAG_MITM_AUTH 3 -#define SMP_FLAG_COMPLETE 4 -#define SMP_FLAG_INITIATOR 5 - -struct smp_chan { - struct l2cap_conn *conn; - u8 preq[7]; /* SMP Pairing Request */ - u8 prsp[7]; /* SMP Pairing Response */ - u8 prnd[16]; /* SMP Pairing Random (local) */ - u8 rrnd[16]; /* SMP Pairing Random (remote) */ - u8 pcnf[16]; /* SMP Pairing Confirm */ - u8 tk[16]; /* SMP Temporary Key */ - u8 enc_key_size; - u8 remote_key_dist; - bdaddr_t id_addr; - u8 id_addr_type; - u8 irk[16]; - struct smp_csrk *csrk; - struct smp_csrk *slave_csrk; - struct smp_ltk *ltk; - struct smp_ltk *slave_ltk; - struct smp_irk *remote_irk; - unsigned long smp_flags; - struct work_struct confirm; - struct work_struct random; -}; - /* SMP Commands */ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level); int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); diff --git a/net/bridge/Makefile b/net/bridge/Makefile index e85498b2f..8590b94 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_BRIDGE) += bridge.o bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ - br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \ + br_ioctl.o br_stp.o br_stp_bpdu.o \ br_stp_if.o br_stp_timer.o br_netlink.o bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o @@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o -obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ +obj-$(CONFIG_NETFILTER) += netfilter/ diff --git a/net/bridge/br.c b/net/bridge/br.c index 19311aaf..1a755a1 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -22,6 +22,104 @@ #include "br_private.h" +/* + * Handle changes in state of network devices enslaved to a bridge. + * + * Note: don't care about up/down if bridge itself is down, because + * port state is checked when bridge is brought up. + */ +static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct net_bridge_port *p; + struct net_bridge *br; + bool changed_addr; + int err; + + /* register of bridge completed, add sysfs entries */ + if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { + br_sysfs_addbr(dev); + return NOTIFY_DONE; + } + + /* not a port of a bridge */ + p = br_port_get_rtnl(dev); + if (!p) + return NOTIFY_DONE; + + br = p->br; + + switch (event) { + case NETDEV_CHANGEMTU: + dev_set_mtu(br->dev, br_min_mtu(br)); + break; + + case NETDEV_CHANGEADDR: + spin_lock_bh(&br->lock); + br_fdb_changeaddr(p, dev->dev_addr); + changed_addr = br_stp_recalculate_bridge_id(br); + spin_unlock_bh(&br->lock); + + if (changed_addr) + call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); + + break; + + case NETDEV_CHANGE: + br_port_carrier_check(p); + break; + + case NETDEV_FEAT_CHANGE: + netdev_update_features(br->dev); + break; + + case NETDEV_DOWN: + spin_lock_bh(&br->lock); + if (br->dev->flags & IFF_UP) + br_stp_disable_port(p); + spin_unlock_bh(&br->lock); + break; + + case NETDEV_UP: + if (netif_running(br->dev) && netif_oper_up(dev)) { + spin_lock_bh(&br->lock); + br_stp_enable_port(p); + spin_unlock_bh(&br->lock); + } + break; + + case NETDEV_UNREGISTER: + br_del_if(br, dev); + break; + + case NETDEV_CHANGENAME: + err = br_sysfs_renameif(p); + if (err) + return notifier_from_errno(err); + break; + + case NETDEV_PRE_TYPE_CHANGE: + /* Forbid underlaying device to change its type. */ + return NOTIFY_BAD; + + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, br->dev); + break; + } + + /* Events that may cause spanning tree to refresh */ + if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || + event == NETDEV_CHANGE || event == NETDEV_DOWN) + br_ifinfo_notify(RTM_NEWLINK, p); + + return NOTIFY_DONE; +} + +static struct notifier_block br_device_notifier = { + .notifier_call = br_device_event +}; + static void __net_exit br_net_exit(struct net *net) { struct net_device *dev; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 9212015..d77e2f0 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -112,6 +112,12 @@ static void br_dev_set_multicast_list(struct net_device *dev) { } +static void br_dev_change_rx_flags(struct net_device *dev, int change) +{ + if (change & IFF_PROMISC) + br_manage_promisc(netdev_priv(dev)); +} + static int br_dev_stop(struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); @@ -309,6 +315,7 @@ static const struct net_device_ops br_netdev_ops = { .ndo_get_stats64 = br_get_stats64, .ndo_set_mac_address = br_set_mac_address, .ndo_set_rx_mode = br_dev_set_multicast_list, + .ndo_change_rx_flags = br_dev_change_rx_flags, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 9203d5a..2c45c06 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -85,8 +85,58 @@ static void fdb_rcu_free(struct rcu_head *head) kmem_cache_free(br_fdb_cache, ent); } +/* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports + * are then updated with the new information. + * Called under RTNL. + */ +static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr) +{ + int err; + struct net_bridge_port *p, *tmp; + + ASSERT_RTNL(); + + list_for_each_entry(p, &br->port_list, list) { + if (!br_promisc_port(p)) { + err = dev_uc_add(p->dev, addr); + if (err) + goto undo; + } + } + + return; +undo: + list_for_each_entry(tmp, &br->port_list, list) { + if (tmp == p) + break; + if (!br_promisc_port(tmp)) + dev_uc_del(tmp->dev, addr); + } +} + +/* When a static FDB entry is deleted, the HW address from that entry is + * also removed from the bridge private HW address list and updates all + * the ports with needed information. + * Called under RTNL. + */ +static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr) +{ + struct net_bridge_port *p; + + ASSERT_RTNL(); + + list_for_each_entry(p, &br->port_list, list) { + if (!br_promisc_port(p)) + dev_uc_del(p->dev, addr); + } +} + static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { + if (f->is_static) + fdb_del_hw(br, f->addr.addr); + hlist_del_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); call_rcu(&f->rcu, fdb_rcu_free); @@ -466,6 +516,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, return -ENOMEM; fdb->is_local = fdb->is_static = 1; + fdb_add_hw(br, addr); fdb_notify(br, fdb, RTM_NEWNEIGH); return 0; } @@ -565,6 +616,8 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr)) goto nla_put_failure; + if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex)) + goto nla_put_failure; ci.ndm_used = jiffies_to_clock_t(now - fdb->used); ci.ndm_confirmed = 0; ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated); @@ -586,6 +639,7 @@ static inline size_t fdb_nlmsg_size(void) { return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + + nla_total_size(sizeof(u32)) /* NDA_MASTER */ + nla_total_size(sizeof(u16)) /* NDA_VLAN */ + nla_total_size(sizeof(struct nda_cacheinfo)); } @@ -678,13 +732,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, } if (fdb_to_nud(fdb) != state) { - if (state & NUD_PERMANENT) - fdb->is_local = fdb->is_static = 1; - else if (state & NUD_NOARP) { + if (state & NUD_PERMANENT) { + fdb->is_local = 1; + if (!fdb->is_static) { + fdb->is_static = 1; + fdb_add_hw(br, addr); + } + } else if (state & NUD_NOARP) { fdb->is_local = 0; - fdb->is_static = 1; - } else - fdb->is_local = fdb->is_static = 0; + if (!fdb->is_static) { + fdb->is_static = 1; + fdb_add_hw(br, addr); + } + } else { + fdb->is_local = 0; + if (fdb->is_static) { + fdb->is_static = 0; + fdb_del_hw(br, addr); + } + } modified = true; } @@ -874,3 +940,59 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], out: return err; } + +int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p) +{ + struct net_bridge_fdb_entry *fdb, *tmp; + int i; + int err; + + ASSERT_RTNL(); + + for (i = 0; i < BR_HASH_SIZE; i++) { + hlist_for_each_entry(fdb, &br->hash[i], hlist) { + /* We only care for static entries */ + if (!fdb->is_static) + continue; + + err = dev_uc_add(p->dev, fdb->addr.addr); + if (err) + goto rollback; + } + } + return 0; + +rollback: + for (i = 0; i < BR_HASH_SIZE; i++) { + hlist_for_each_entry(tmp, &br->hash[i], hlist) { + /* If we reached the fdb that failed, we can stop */ + if (tmp == fdb) + break; + + /* We only care for static entries */ + if (!tmp->is_static) + continue; + + dev_uc_del(p->dev, tmp->addr.addr); + } + } + return err; +} + +void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) +{ + struct net_bridge_fdb_entry *fdb; + int i; + + ASSERT_RTNL(); + + for (i = 0; i < BR_HASH_SIZE; i++) { + hlist_for_each_entry_rcu(fdb, &br->hash[i], hlist) { + /* We only care for static entries */ + if (!fdb->is_static) + continue; + + dev_uc_del(p->dev, fdb->addr.addr); + } + } +} diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 5262b86..a08d2b8 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -85,6 +85,110 @@ void br_port_carrier_check(struct net_bridge_port *p) spin_unlock_bh(&br->lock); } +static void br_port_set_promisc(struct net_bridge_port *p) +{ + int err = 0; + + if (br_promisc_port(p)) + return; + + err = dev_set_promiscuity(p->dev, 1); + if (err) + return; + + br_fdb_unsync_static(p->br, p); + p->flags |= BR_PROMISC; +} + +static void br_port_clear_promisc(struct net_bridge_port *p) +{ + int err; + + /* Check if the port is already non-promisc or if it doesn't + * support UNICAST filtering. Without unicast filtering support + * we'll end up re-enabling promisc mode anyway, so just check for + * it here. + */ + if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT)) + return; + + /* Since we'll be clearing the promisc mode, program the port + * first so that we don't have interruption in traffic. + */ + err = br_fdb_sync_static(p->br, p); + if (err) + return; + + dev_set_promiscuity(p->dev, -1); + p->flags &= ~BR_PROMISC; +} + +/* When a port is added or removed or when certain port flags + * change, this function is called to automatically manage + * promiscuity setting of all the bridge ports. We are always called + * under RTNL so can skip using rcu primitives. + */ +void br_manage_promisc(struct net_bridge *br) +{ + struct net_bridge_port *p; + bool set_all = false; + + /* If vlan filtering is disabled or bridge interface is placed + * into promiscuous mode, place all ports in promiscuous mode. + */ + if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br)) + set_all = true; + + list_for_each_entry(p, &br->port_list, list) { + if (set_all) { + br_port_set_promisc(p); + } else { + /* If the number of auto-ports is <= 1, then all other + * ports will have their output configuration + * statically specified through fdbs. Since ingress + * on the auto-port becomes forwarding/egress to other + * ports and egress configuration is statically known, + * we can say that ingress configuration of the + * auto-port is also statically known. + * This lets us disable promiscuous mode and write + * this config to hw. + */ + if (br->auto_cnt <= br_auto_port(p)) + br_port_clear_promisc(p); + else + br_port_set_promisc(p); + } + } +} + +static void nbp_update_port_count(struct net_bridge *br) +{ + struct net_bridge_port *p; + u32 cnt = 0; + + list_for_each_entry(p, &br->port_list, list) { + if (br_auto_port(p)) + cnt++; + } + if (br->auto_cnt != cnt) { + br->auto_cnt = cnt; + br_manage_promisc(br); + } +} + +static void nbp_delete_promisc(struct net_bridge_port *p) +{ + /* If port is currently promiscuous, unset promiscuity. + * Otherwise, it is a static port so remove all addresses + * from it. + */ + dev_set_allmulti(p->dev, -1); + if (br_promisc_port(p)) + dev_set_promiscuity(p->dev, -1); + else + br_fdb_unsync_static(p->br, p); +} + static void release_nbp(struct kobject *kobj) { struct net_bridge_port *p @@ -133,7 +237,7 @@ static void del_nbp(struct net_bridge_port *p) sysfs_remove_link(br->ifobj, p->dev->name); - dev_set_promiscuity(dev, -1); + nbp_delete_promisc(p); spin_lock_bh(&br->lock); br_stp_disable_port(p); @@ -141,10 +245,11 @@ static void del_nbp(struct net_bridge_port *p) br_ifinfo_notify(RTM_DELLINK, p); + list_del_rcu(&p->list); + nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); - - list_del_rcu(&p->list); + nbp_update_port_count(br); dev->priv_flags &= ~IFF_BRIDGE_PORT; @@ -353,7 +458,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) call_netdevice_notifiers(NETDEV_JOIN, dev); - err = dev_set_promiscuity(dev, 1); + err = dev_set_allmulti(dev, 1); if (err) goto put_back; @@ -384,6 +489,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) list_add_rcu(&p->list, &br->port_list); + nbp_update_port_count(br); + netdev_update_features(br->dev); if (br->dev->needed_headroom < dev->needed_headroom) @@ -421,7 +528,7 @@ err2: kobject_put(&p->kobj); p = NULL; /* kobject_put frees */ err1: - dev_set_promiscuity(dev, -1); + dev_set_allmulti(dev, -1); put_back: dev_put(dev); kfree(p); @@ -455,3 +562,11 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) return 0; } + +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) +{ + struct net_bridge *br = p->br; + + if (mask & BR_AUTO_MASK) + nbp_update_port_count(br); +} diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e8844d9..26edb51 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) { int err; + unsigned long old_flags = p->flags; br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) if (err) return err; } + + br_port_flags_change(p, old_flags ^ p->flags); return 0; } diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c deleted file mode 100644 index 2998dd1..0000000 --- a/net/bridge/br_notify.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Device event handling - * Linux ethernet bridge - * - * Authors: - * Lennert Buytenhek <buytenh@gnu.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <linux/kernel.h> -#include <linux/rtnetlink.h> -#include <net/net_namespace.h> - -#include "br_private.h" - -static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr); - -struct notifier_block br_device_notifier = { - .notifier_call = br_device_event -}; - -/* - * Handle changes in state of network devices enslaved to a bridge. - * - * Note: don't care about up/down if bridge itself is down, because - * port state is checked when bridge is brought up. - */ -static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct net_bridge_port *p; - struct net_bridge *br; - bool changed_addr; - int err; - - /* register of bridge completed, add sysfs entries */ - if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { - br_sysfs_addbr(dev); - return NOTIFY_DONE; - } - - /* not a port of a bridge */ - p = br_port_get_rtnl(dev); - if (!p) - return NOTIFY_DONE; - - br = p->br; - - switch (event) { - case NETDEV_CHANGEMTU: - dev_set_mtu(br->dev, br_min_mtu(br)); - break; - - case NETDEV_CHANGEADDR: - spin_lock_bh(&br->lock); - br_fdb_changeaddr(p, dev->dev_addr); - changed_addr = br_stp_recalculate_bridge_id(br); - spin_unlock_bh(&br->lock); - - if (changed_addr) - call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); - - break; - - case NETDEV_CHANGE: - br_port_carrier_check(p); - break; - - case NETDEV_FEAT_CHANGE: - netdev_update_features(br->dev); - break; - - case NETDEV_DOWN: - spin_lock_bh(&br->lock); - if (br->dev->flags & IFF_UP) - br_stp_disable_port(p); - spin_unlock_bh(&br->lock); - break; - - case NETDEV_UP: - if (netif_running(br->dev) && netif_oper_up(dev)) { - spin_lock_bh(&br->lock); - br_stp_enable_port(p); - spin_unlock_bh(&br->lock); - } - break; - - case NETDEV_UNREGISTER: - br_del_if(br, dev); - break; - - case NETDEV_CHANGENAME: - err = br_sysfs_renameif(p); - if (err) - return notifier_from_errno(err); - break; - - case NETDEV_PRE_TYPE_CHANGE: - /* Forbid underlaying device to change its type. */ - return NOTIFY_BAD; - - case NETDEV_RESEND_IGMP: - /* Propagate to master device */ - call_netdevice_notifiers(event, br->dev); - break; - } - - /* Events that may cause spanning tree to refresh */ - if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || - event == NETDEV_CHANGE || event == NETDEV_DOWN) - br_ifinfo_notify(RTM_NEWLINK, p); - - return NOTIFY_DONE; -} diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 06811d7..53d6e32 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -174,6 +174,8 @@ struct net_bridge_port #define BR_ADMIN_COST 0x00000010 #define BR_LEARNING 0x00000020 #define BR_FLOOD 0x00000040 +#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING) +#define BR_PROMISC 0x00000080 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING struct bridge_mcast_query ip4_query; @@ -198,6 +200,9 @@ struct net_bridge_port #endif }; +#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) +#define br_promisc_port(p) ((p)->flags & BR_PROMISC) + #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT) static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev) @@ -290,6 +295,7 @@ struct net_bridge struct timer_list topology_change_timer; struct timer_list gc_timer; struct kobject *ifobj; + u32 auto_cnt; #ifdef CONFIG_BRIDGE_VLAN_FILTERING u8 vlan_enabled; struct net_port_vlans __rcu *vlan_info; @@ -327,8 +333,6 @@ struct br_input_skb_cb { #define br_debug(br, format, args...) \ pr_debug("%s: " format, (br)->dev->name, ##args) -extern struct notifier_block br_device_notifier; - /* called under bridge lock */ static inline int br_is_root_bridge(const struct net_bridge *br) { @@ -395,6 +399,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 nlh_flags); int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, int idx); +int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); +void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p); /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); @@ -415,6 +421,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev); int br_min_mtu(const struct net_bridge *br); netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features); +void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); +void br_manage_promisc(struct net_bridge *br); /* br_input.c */ int br_handle_frame_finish(struct sk_buff *skb); @@ -632,6 +640,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) return v->pvid ?: VLAN_N_VID; } +static inline int br_vlan_enabled(struct net_bridge *br) +{ + return br->vlan_enabled; +} #else static inline bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, @@ -712,6 +724,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) { return VLAN_N_VID; /* Returns invalid vid */ } + +static inline int br_vlan_enabled(struct net_bridge *br) +{ + return 0; +} #endif /* br_netfilter.c */ diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index dd595bd..e561cd5 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -41,20 +41,30 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \ } \ static int store_##_name(struct net_bridge_port *p, unsigned long v) \ { \ - unsigned long flags = p->flags; \ - if (v) \ - flags |= _mask; \ - else \ - flags &= ~_mask; \ - if (flags != p->flags) { \ - p->flags = flags; \ - br_ifinfo_notify(RTM_NEWLINK, p); \ - } \ - return 0; \ + return store_flag(p, v, _mask); \ } \ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ show_##_name, store_##_name) +static int store_flag(struct net_bridge_port *p, unsigned long v, + unsigned long mask) +{ + unsigned long flags; + + flags = p->flags; + + if (v) + flags |= mask; + else + flags &= ~mask; + + if (flags != p->flags) { + p->flags = flags; + br_port_flags_change(p, mask); + br_ifinfo_notify(RTM_NEWLINK, p); + } + return 0; +} static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) { diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 4a37161..24c5cc5 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -332,6 +332,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) goto unlock; br->vlan_enabled = val; + br_manage_promisc(br); unlock: rtnl_unlock(); diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 5ca74a0..629dc77 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -2,14 +2,23 @@ # Bridge netfilter configuration # # -config NF_TABLES_BRIDGE - depends on NF_TABLES +menuconfig NF_TABLES_BRIDGE + depends on BRIDGE && NETFILTER && NF_TABLES tristate "Ethernet Bridge nf_tables support" +if NF_TABLES_BRIDGE + +config NFT_BRIDGE_META + tristate "Netfilter nf_table bridge meta support" + depends on NFT_META + help + Add support for bridge dedicated meta key. + +endif # NF_TABLES_BRIDGE + menuconfig BRIDGE_NF_EBTABLES tristate "Ethernet Bridge tables (ebtables) support" - depends on BRIDGE && NETFILTER - select NETFILTER_XTABLES + depends on BRIDGE && NETFILTER && NETFILTER_XTABLES help ebtables is a general, extensible frame/packet identification framework. Say 'Y' or 'M' here if you want to do Ethernet diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index ea7629f..6f2f394 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o +obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c new file mode 100644 index 0000000..4f02109 --- /dev/null +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nft_meta.h> + +#include "../br_private.h" + +static void nft_meta_bridge_get_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + const struct net_device *in = pkt->in, *out = pkt->out; + struct nft_data *dest = &data[priv->dreg]; + const struct net_bridge_port *p; + + switch (priv->key) { + case NFT_META_BRI_IIFNAME: + if (in == NULL || (p = br_port_get_rcu(in)) == NULL) + goto err; + break; + case NFT_META_BRI_OIFNAME: + if (out == NULL || (p = br_port_get_rcu(out)) == NULL) + goto err; + break; + default: + goto out; + } + + strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data)); + return; +out: + return nft_meta_get_eval(expr, data, pkt); +err: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_meta *priv = nft_expr_priv(expr); + int err; + + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (priv->key) { + case NFT_META_BRI_IIFNAME: + case NFT_META_BRI_OIFNAME: + break; + default: + return nft_meta_get_init(ctx, expr, tb); + } + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + return err; + + return 0; +} + +static struct nft_expr_type nft_meta_bridge_type; +static const struct nft_expr_ops nft_meta_bridge_get_ops = { + .type = &nft_meta_bridge_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_bridge_get_eval, + .init = nft_meta_bridge_get_init, + .dump = nft_meta_get_dump, +}; + +static const struct nft_expr_ops nft_meta_bridge_set_ops = { + .type = &nft_meta_bridge_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_set_eval, + .init = nft_meta_set_init, + .dump = nft_meta_set_dump, +}; + +static const struct nft_expr_ops * +nft_meta_bridge_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + if (tb[NFTA_META_KEY] == NULL) + return ERR_PTR(-EINVAL); + + if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) + return ERR_PTR(-EINVAL); + + if (tb[NFTA_META_DREG]) + return &nft_meta_bridge_get_ops; + + if (tb[NFTA_META_SREG]) + return &nft_meta_bridge_set_ops; + + return ERR_PTR(-EINVAL); +} + +static struct nft_expr_type nft_meta_bridge_type __read_mostly = { + .family = NFPROTO_BRIDGE, + .name = "meta", + .select_ops = &nft_meta_bridge_select_ops, + .policy = nft_meta_policy, + .maxattr = NFTA_META_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_meta_bridge_module_init(void) +{ + return nft_register_expr(&nft_meta_bridge_type); +} + +static void __exit nft_meta_bridge_module_exit(void) +{ + nft_unregister_expr(&nft_meta_bridge_type); +} + +module_init(nft_meta_bridge_module_init); +module_exit(nft_meta_bridge_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); +MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta"); diff --git a/net/can/af_can.c b/net/can/af_can.c index a27f8aa..ce82337 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -338,6 +338,29 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev) } /** + * effhash - hash function for 29 bit CAN identifier reduction + * @can_id: 29 bit CAN identifier + * + * Description: + * To reduce the linear traversal in one linked list of _single_ EFF CAN + * frame subscriptions the 29 bit identifier is mapped to 10 bits. + * (see CAN_EFF_RCV_HASH_BITS definition) + * + * Return: + * Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask ) + */ +static unsigned int effhash(canid_t can_id) +{ + unsigned int hash; + + hash = can_id; + hash ^= can_id >> CAN_EFF_RCV_HASH_BITS; + hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS); + + return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1); +} + +/** * find_rcv_list - determine optimal filterlist inside device filter struct * @can_id: pointer to CAN identifier of a given can_filter * @mask: pointer to CAN mask of a given can_filter @@ -400,10 +423,8 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, !(*can_id & CAN_RTR_FLAG)) { if (*can_id & CAN_EFF_FLAG) { - if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) { - /* RFC: a future use-case for hash-tables? */ - return &d->rx[RX_EFF]; - } + if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) + return &d->rx_eff[effhash(*can_id)]; } else { if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS)) return &d->rx_sff[*can_id]; @@ -632,7 +653,7 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb) return matches; if (can_id & CAN_EFF_FLAG) { - hlist_for_each_entry_rcu(r, &d->rx[RX_EFF], list) { + hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) { if (r->can_id == can_id) { deliver(skb, r); matches++; diff --git a/net/can/af_can.h b/net/can/af_can.h index 6de58b4..fca0fe9 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -59,12 +59,17 @@ struct receiver { char *ident; }; -enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX }; +#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS) +#define CAN_EFF_RCV_HASH_BITS 10 +#define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS) + +enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX }; /* per device receive filters linked at dev->ml_priv */ struct dev_rcv_lists { struct hlist_head rx[RX_MAX]; - struct hlist_head rx_sff[0x800]; + struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ]; + struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ]; int remove_on_zero_entries; int entries; }; diff --git a/net/can/proc.c b/net/can/proc.c index b543470..1a19b98 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -80,7 +80,6 @@ static const char rx_list_name[][8] = { [RX_ALL] = "rx_all", [RX_FIL] = "rx_fil", [RX_INV] = "rx_inv", - [RX_EFF] = "rx_eff", }; /* @@ -389,25 +388,26 @@ static const struct file_operations can_rcvlist_proc_fops = { .release = single_release, }; -static inline void can_rcvlist_sff_proc_show_one(struct seq_file *m, - struct net_device *dev, - struct dev_rcv_lists *d) +static inline void can_rcvlist_proc_show_array(struct seq_file *m, + struct net_device *dev, + struct hlist_head *rcv_array, + unsigned int rcv_array_sz) { - int i; + unsigned int i; int all_empty = 1; /* check whether at least one list is non-empty */ - for (i = 0; i < 0x800; i++) - if (!hlist_empty(&d->rx_sff[i])) { + for (i = 0; i < rcv_array_sz; i++) + if (!hlist_empty(&rcv_array[i])) { all_empty = 0; break; } if (!all_empty) { can_print_recv_banner(m); - for (i = 0; i < 0x800; i++) { - if (!hlist_empty(&d->rx_sff[i])) - can_print_rcvlist(m, &d->rx_sff[i], dev); + for (i = 0; i < rcv_array_sz; i++) { + if (!hlist_empty(&rcv_array[i])) + can_print_rcvlist(m, &rcv_array[i], dev); } } else seq_printf(m, " (%s: no entry)\n", DNAME(dev)); @@ -425,12 +425,15 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) /* sff receive list for 'all' CAN devices (dev == NULL) */ d = &can_rx_alldev_list; - can_rcvlist_sff_proc_show_one(m, NULL, d); + can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff)); /* sff receive list for registered CAN devices */ for_each_netdev_rcu(&init_net, dev) { - if (dev->type == ARPHRD_CAN && dev->ml_priv) - can_rcvlist_sff_proc_show_one(m, dev, dev->ml_priv); + if (dev->type == ARPHRD_CAN && dev->ml_priv) { + d = dev->ml_priv; + can_rcvlist_proc_show_array(m, dev, d->rx_sff, + ARRAY_SIZE(d->rx_sff)); + } } rcu_read_unlock(); @@ -452,6 +455,49 @@ static const struct file_operations can_rcvlist_sff_proc_fops = { .release = single_release, }; + +static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) +{ + struct net_device *dev; + struct dev_rcv_lists *d; + + /* RX_EFF */ + seq_puts(m, "\nreceive list 'rx_eff':\n"); + + rcu_read_lock(); + + /* eff receive list for 'all' CAN devices (dev == NULL) */ + d = &can_rx_alldev_list; + can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff)); + + /* eff receive list for registered CAN devices */ + for_each_netdev_rcu(&init_net, dev) { + if (dev->type == ARPHRD_CAN && dev->ml_priv) { + d = dev->ml_priv; + can_rcvlist_proc_show_array(m, dev, d->rx_eff, + ARRAY_SIZE(d->rx_eff)); + } + } + + rcu_read_unlock(); + + seq_putc(m, '\n'); + return 0; +} + +static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, can_rcvlist_eff_proc_show, NULL); +} + +static const struct file_operations can_rcvlist_eff_proc_fops = { + .owner = THIS_MODULE, + .open = can_rcvlist_eff_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * proc utility functions */ @@ -491,8 +537,8 @@ void can_init_proc(void) &can_rcvlist_proc_fops, (void *)RX_FIL); pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir, &can_rcvlist_proc_fops, (void *)RX_INV); - pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir, - &can_rcvlist_proc_fops, (void *)RX_EFF); + pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir, + &can_rcvlist_eff_proc_fops); pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir, &can_rcvlist_sff_proc_fops); } diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index dac7f9b..1948d59 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -557,7 +557,7 @@ static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov, return r; } -static int ceph_tcp_sendpage(struct socket *sock, struct page *page, +static int __ceph_tcp_sendpage(struct socket *sock, struct page *page, int offset, size_t size, bool more) { int flags = MSG_DONTWAIT | MSG_NOSIGNAL | (more ? MSG_MORE : MSG_EOR); @@ -570,6 +570,24 @@ static int ceph_tcp_sendpage(struct socket *sock, struct page *page, return ret; } +static int ceph_tcp_sendpage(struct socket *sock, struct page *page, + int offset, size_t size, bool more) +{ + int ret; + struct kvec iov; + + /* sendpage cannot properly handle pages with page_count == 0, + * we need to fallback to sendmsg if that's the case */ + if (page_count(page) >= 1) + return __ceph_tcp_sendpage(sock, page, offset, size, more); + + iov.iov_base = kmap(page) + offset; + iov.iov_len = size; + ret = ceph_tcp_sendmsg(sock, &iov, 1, size, more); + kunmap(page); + + return ret; +} /* * Shutdown/close the socket for the given connection. diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 8b8a5a2..c547e46 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -329,6 +329,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end) dout("crush decode tunable chooseleaf_descend_once = %d", c->chooseleaf_descend_once); + ceph_decode_need(p, end, sizeof(u8), done); + c->chooseleaf_vary_r = ceph_decode_8(p); + dout("crush decode tunable chooseleaf_vary_r = %d", + c->chooseleaf_vary_r); + done: dout("crush_decode success\n"); return c; diff --git a/net/core/Makefile b/net/core/Makefile index 826b925..71093d9 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ - sock_diag.o dev_ioctl.o + sock_diag.o dev_ioctl.o tso.o obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o diff --git a/net/core/dev.c b/net/core/dev.c index 867adb2..0355ca5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3957,6 +3957,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff } NAPI_GRO_CB(skb)->count = 1; NAPI_GRO_CB(skb)->age = jiffies; + NAPI_GRO_CB(skb)->last = skb; skb_shinfo(skb)->gso_size = skb_gro_len(skb); skb->next = napi->gro_list; napi->gro_list = skb; @@ -4547,6 +4548,32 @@ void *netdev_adjacent_get_private(struct list_head *adj_list) EXPORT_SYMBOL(netdev_adjacent_get_private); /** + * netdev_upper_get_next_dev_rcu - Get the next dev from upper list + * @dev: device + * @iter: list_head ** of the current position + * + * Gets the next device from the dev's upper list, starting from iter + * position. The caller must hold RCU read lock. + */ +struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *upper; + + WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held()); + + upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); + + if (&upper->list == &dev->adj_list.upper) + return NULL; + + *iter = &upper->list; + + return upper->dev; +} +EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu); + +/** * netdev_all_upper_get_next_dev_rcu - Get the next dev from upper list * @dev: device * @iter: list_head ** of the current position @@ -4628,6 +4655,32 @@ void *netdev_lower_get_next_private_rcu(struct net_device *dev, EXPORT_SYMBOL(netdev_lower_get_next_private_rcu); /** + * netdev_lower_get_next - Get the next device from the lower neighbour + * list + * @dev: device + * @iter: list_head ** of the current position + * + * Gets the next netdev_adjacent from the dev's lower neighbour + * list, starting from iter position. The caller must hold RTNL lock or + * its own locking that guarantees that the neighbour lower + * list will remain unchainged. + */ +void *netdev_lower_get_next(struct net_device *dev, struct list_head **iter) +{ + struct netdev_adjacent *lower; + + lower = list_entry((*iter)->next, struct netdev_adjacent, list); + + if (&lower->list == &dev->adj_list.lower) + return NULL; + + *iter = &lower->list; + + return lower->dev; +} +EXPORT_SYMBOL(netdev_lower_get_next); + +/** * netdev_lower_get_first_private_rcu - Get the first ->private from the * lower neighbour list, RCU * variant @@ -5077,6 +5130,30 @@ void *netdev_lower_dev_get_private(struct net_device *dev, } EXPORT_SYMBOL(netdev_lower_dev_get_private); + +int dev_get_nest_level(struct net_device *dev, + bool (*type_check)(struct net_device *dev)) +{ + struct net_device *lower = NULL; + struct list_head *iter; + int max_nest = -1; + int nest; + + ASSERT_RTNL(); + + netdev_for_each_lower_dev(dev, lower, iter) { + nest = dev_get_nest_level(lower, type_check); + if (max_nest < nest) + max_nest = nest; + } + + if (type_check(dev)) + max_nest++; + + return max_nest; +} +EXPORT_SYMBOL(dev_get_nest_level); + static void dev_change_rx_flags(struct net_device *dev, int flags) { const struct net_device_ops *ops = dev->netdev_ops; @@ -5242,7 +5319,6 @@ void __dev_set_rx_mode(struct net_device *dev) if (ops->ndo_set_rx_mode) ops->ndo_set_rx_mode(dev); } -EXPORT_SYMBOL(__dev_set_rx_mode); void dev_set_rx_mode(struct net_device *dev) { @@ -5547,7 +5623,7 @@ static int dev_new_index(struct net *net) /* Delayed registration/unregisteration */ static LIST_HEAD(net_todo_list); -static DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); +DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); static void net_set_todo(struct net_device *dev) { diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 329d579..b6b2306 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -225,6 +225,91 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, } EXPORT_SYMBOL(__hw_addr_unsync); +/** + * __hw_addr_sync_dev - Synchonize device's multicast list + * @list: address list to syncronize + * @dev: device to sync + * @sync: function to call if address should be added + * @unsync: function to call if address should be removed + * + * This funciton is intended to be called from the ndo_set_rx_mode + * function of devices that require explicit address add/remove + * notifications. The unsync function may be NULL in which case + * the addresses requiring removal will simply be removed without + * any notification to the device. + **/ +int __hw_addr_sync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*sync)(struct net_device *, const unsigned char *), + int (*unsync)(struct net_device *, + const unsigned char *)) +{ + struct netdev_hw_addr *ha, *tmp; + int err; + + /* first go through and flush out any stale entries */ + list_for_each_entry_safe(ha, tmp, &list->list, list) { + if (!ha->sync_cnt || ha->refcount != 1) + continue; + + /* if unsync is defined and fails defer unsyncing address */ + if (unsync && unsync(dev, ha->addr)) + continue; + + ha->sync_cnt--; + __hw_addr_del_entry(list, ha, false, false); + } + + /* go through and sync new entries to the list */ + list_for_each_entry_safe(ha, tmp, &list->list, list) { + if (ha->sync_cnt) + continue; + + err = sync(dev, ha->addr); + if (err) + return err; + + ha->sync_cnt++; + ha->refcount++; + } + + return 0; +} +EXPORT_SYMBOL(__hw_addr_sync_dev); + +/** + * __hw_addr_unsync_dev - Remove synchonized addresses from device + * @list: address list to remove syncronized addresses from + * @dev: device to sync + * @unsync: function to call if address should be removed + * + * Remove all addresses that were added to the device by __hw_addr_sync_dev(). + * This function is intended to be called from the ndo_stop or ndo_open + * functions on devices that require explicit address add/remove + * notifications. If the unsync function pointer is NULL then this function + * can be used to just reset the sync_cnt for the addresses in the list. + **/ +void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*unsync)(struct net_device *, + const unsigned char *)) +{ + struct netdev_hw_addr *ha, *tmp; + + list_for_each_entry_safe(ha, tmp, &list->list, list) { + if (!ha->sync_cnt) + continue; + + /* if unsync is defined and fails defer unsyncing address */ + if (unsync && unsync(dev, ha->addr)) + continue; + + ha->sync_cnt--; + __hw_addr_del_entry(list, ha, false, false); + } +} +EXPORT_SYMBOL(__hw_addr_unsync_dev); + static void __hw_addr_flush(struct netdev_hw_addr_list *list) { struct netdev_hw_addr *ha, *tmp; diff --git a/net/core/filter.c b/net/core/filter.c index c442a0d..842f839 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -153,102 +153,107 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) * keep, 0 for none. @ctx is the data we are operating on, @insn is the * array of filter instructions. */ -unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) +static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) { u64 stack[MAX_BPF_STACK / sizeof(u64)]; u64 regs[MAX_BPF_REG], tmp; static const void *jumptable[256] = { [0 ... 255] = &&default_label, /* Now overwrite non-defaults ... */ -#define DL(A, B, C) [BPF_##A|BPF_##B|BPF_##C] = &&A##_##B##_##C - DL(ALU, ADD, X), - DL(ALU, ADD, K), - DL(ALU, SUB, X), - DL(ALU, SUB, K), - DL(ALU, AND, X), - DL(ALU, AND, K), - DL(ALU, OR, X), - DL(ALU, OR, K), - DL(ALU, LSH, X), - DL(ALU, LSH, K), - DL(ALU, RSH, X), - DL(ALU, RSH, K), - DL(ALU, XOR, X), - DL(ALU, XOR, K), - DL(ALU, MUL, X), - DL(ALU, MUL, K), - DL(ALU, MOV, X), - DL(ALU, MOV, K), - DL(ALU, DIV, X), - DL(ALU, DIV, K), - DL(ALU, MOD, X), - DL(ALU, MOD, K), - DL(ALU, NEG, 0), - DL(ALU, END, TO_BE), - DL(ALU, END, TO_LE), - DL(ALU64, ADD, X), - DL(ALU64, ADD, K), - DL(ALU64, SUB, X), - DL(ALU64, SUB, K), - DL(ALU64, AND, X), - DL(ALU64, AND, K), - DL(ALU64, OR, X), - DL(ALU64, OR, K), - DL(ALU64, LSH, X), - DL(ALU64, LSH, K), - DL(ALU64, RSH, X), - DL(ALU64, RSH, K), - DL(ALU64, XOR, X), - DL(ALU64, XOR, K), - DL(ALU64, MUL, X), - DL(ALU64, MUL, K), - DL(ALU64, MOV, X), - DL(ALU64, MOV, K), - DL(ALU64, ARSH, X), - DL(ALU64, ARSH, K), - DL(ALU64, DIV, X), - DL(ALU64, DIV, K), - DL(ALU64, MOD, X), - DL(ALU64, MOD, K), - DL(ALU64, NEG, 0), - DL(JMP, CALL, 0), - DL(JMP, JA, 0), - DL(JMP, JEQ, X), - DL(JMP, JEQ, K), - DL(JMP, JNE, X), - DL(JMP, JNE, K), - DL(JMP, JGT, X), - DL(JMP, JGT, K), - DL(JMP, JGE, X), - DL(JMP, JGE, K), - DL(JMP, JSGT, X), - DL(JMP, JSGT, K), - DL(JMP, JSGE, X), - DL(JMP, JSGE, K), - DL(JMP, JSET, X), - DL(JMP, JSET, K), - DL(JMP, EXIT, 0), - DL(STX, MEM, B), - DL(STX, MEM, H), - DL(STX, MEM, W), - DL(STX, MEM, DW), - DL(STX, XADD, W), - DL(STX, XADD, DW), - DL(ST, MEM, B), - DL(ST, MEM, H), - DL(ST, MEM, W), - DL(ST, MEM, DW), - DL(LDX, MEM, B), - DL(LDX, MEM, H), - DL(LDX, MEM, W), - DL(LDX, MEM, DW), - DL(LD, ABS, W), - DL(LD, ABS, H), - DL(LD, ABS, B), - DL(LD, IND, W), - DL(LD, IND, H), - DL(LD, IND, B), -#undef DL + /* 32 bit ALU operations */ + [BPF_ALU | BPF_ADD | BPF_X] = &&ALU_ADD_X, + [BPF_ALU | BPF_ADD | BPF_K] = &&ALU_ADD_K, + [BPF_ALU | BPF_SUB | BPF_X] = &&ALU_SUB_X, + [BPF_ALU | BPF_SUB | BPF_K] = &&ALU_SUB_K, + [BPF_ALU | BPF_AND | BPF_X] = &&ALU_AND_X, + [BPF_ALU | BPF_AND | BPF_K] = &&ALU_AND_K, + [BPF_ALU | BPF_OR | BPF_X] = &&ALU_OR_X, + [BPF_ALU | BPF_OR | BPF_K] = &&ALU_OR_K, + [BPF_ALU | BPF_LSH | BPF_X] = &&ALU_LSH_X, + [BPF_ALU | BPF_LSH | BPF_K] = &&ALU_LSH_K, + [BPF_ALU | BPF_RSH | BPF_X] = &&ALU_RSH_X, + [BPF_ALU | BPF_RSH | BPF_K] = &&ALU_RSH_K, + [BPF_ALU | BPF_XOR | BPF_X] = &&ALU_XOR_X, + [BPF_ALU | BPF_XOR | BPF_K] = &&ALU_XOR_K, + [BPF_ALU | BPF_MUL | BPF_X] = &&ALU_MUL_X, + [BPF_ALU | BPF_MUL | BPF_K] = &&ALU_MUL_K, + [BPF_ALU | BPF_MOV | BPF_X] = &&ALU_MOV_X, + [BPF_ALU | BPF_MOV | BPF_K] = &&ALU_MOV_K, + [BPF_ALU | BPF_DIV | BPF_X] = &&ALU_DIV_X, + [BPF_ALU | BPF_DIV | BPF_K] = &&ALU_DIV_K, + [BPF_ALU | BPF_MOD | BPF_X] = &&ALU_MOD_X, + [BPF_ALU | BPF_MOD | BPF_K] = &&ALU_MOD_K, + [BPF_ALU | BPF_NEG] = &&ALU_NEG, + [BPF_ALU | BPF_END | BPF_TO_BE] = &&ALU_END_TO_BE, + [BPF_ALU | BPF_END | BPF_TO_LE] = &&ALU_END_TO_LE, + /* 64 bit ALU operations */ + [BPF_ALU64 | BPF_ADD | BPF_X] = &&ALU64_ADD_X, + [BPF_ALU64 | BPF_ADD | BPF_K] = &&ALU64_ADD_K, + [BPF_ALU64 | BPF_SUB | BPF_X] = &&ALU64_SUB_X, + [BPF_ALU64 | BPF_SUB | BPF_K] = &&ALU64_SUB_K, + [BPF_ALU64 | BPF_AND | BPF_X] = &&ALU64_AND_X, + [BPF_ALU64 | BPF_AND | BPF_K] = &&ALU64_AND_K, + [BPF_ALU64 | BPF_OR | BPF_X] = &&ALU64_OR_X, + [BPF_ALU64 | BPF_OR | BPF_K] = &&ALU64_OR_K, + [BPF_ALU64 | BPF_LSH | BPF_X] = &&ALU64_LSH_X, + [BPF_ALU64 | BPF_LSH | BPF_K] = &&ALU64_LSH_K, + [BPF_ALU64 | BPF_RSH | BPF_X] = &&ALU64_RSH_X, + [BPF_ALU64 | BPF_RSH | BPF_K] = &&ALU64_RSH_K, + [BPF_ALU64 | BPF_XOR | BPF_X] = &&ALU64_XOR_X, + [BPF_ALU64 | BPF_XOR | BPF_K] = &&ALU64_XOR_K, + [BPF_ALU64 | BPF_MUL | BPF_X] = &&ALU64_MUL_X, + [BPF_ALU64 | BPF_MUL | BPF_K] = &&ALU64_MUL_K, + [BPF_ALU64 | BPF_MOV | BPF_X] = &&ALU64_MOV_X, + [BPF_ALU64 | BPF_MOV | BPF_K] = &&ALU64_MOV_K, + [BPF_ALU64 | BPF_ARSH | BPF_X] = &&ALU64_ARSH_X, + [BPF_ALU64 | BPF_ARSH | BPF_K] = &&ALU64_ARSH_K, + [BPF_ALU64 | BPF_DIV | BPF_X] = &&ALU64_DIV_X, + [BPF_ALU64 | BPF_DIV | BPF_K] = &&ALU64_DIV_K, + [BPF_ALU64 | BPF_MOD | BPF_X] = &&ALU64_MOD_X, + [BPF_ALU64 | BPF_MOD | BPF_K] = &&ALU64_MOD_K, + [BPF_ALU64 | BPF_NEG] = &&ALU64_NEG, + /* Call instruction */ + [BPF_JMP | BPF_CALL] = &&JMP_CALL, + /* Jumps */ + [BPF_JMP | BPF_JA] = &&JMP_JA, + [BPF_JMP | BPF_JEQ | BPF_X] = &&JMP_JEQ_X, + [BPF_JMP | BPF_JEQ | BPF_K] = &&JMP_JEQ_K, + [BPF_JMP | BPF_JNE | BPF_X] = &&JMP_JNE_X, + [BPF_JMP | BPF_JNE | BPF_K] = &&JMP_JNE_K, + [BPF_JMP | BPF_JGT | BPF_X] = &&JMP_JGT_X, + [BPF_JMP | BPF_JGT | BPF_K] = &&JMP_JGT_K, + [BPF_JMP | BPF_JGE | BPF_X] = &&JMP_JGE_X, + [BPF_JMP | BPF_JGE | BPF_K] = &&JMP_JGE_K, + [BPF_JMP | BPF_JSGT | BPF_X] = &&JMP_JSGT_X, + [BPF_JMP | BPF_JSGT | BPF_K] = &&JMP_JSGT_K, + [BPF_JMP | BPF_JSGE | BPF_X] = &&JMP_JSGE_X, + [BPF_JMP | BPF_JSGE | BPF_K] = &&JMP_JSGE_K, + [BPF_JMP | BPF_JSET | BPF_X] = &&JMP_JSET_X, + [BPF_JMP | BPF_JSET | BPF_K] = &&JMP_JSET_K, + /* Program return */ + [BPF_JMP | BPF_EXIT] = &&JMP_EXIT, + /* Store instructions */ + [BPF_STX | BPF_MEM | BPF_B] = &&STX_MEM_B, + [BPF_STX | BPF_MEM | BPF_H] = &&STX_MEM_H, + [BPF_STX | BPF_MEM | BPF_W] = &&STX_MEM_W, + [BPF_STX | BPF_MEM | BPF_DW] = &&STX_MEM_DW, + [BPF_STX | BPF_XADD | BPF_W] = &&STX_XADD_W, + [BPF_STX | BPF_XADD | BPF_DW] = &&STX_XADD_DW, + [BPF_ST | BPF_MEM | BPF_B] = &&ST_MEM_B, + [BPF_ST | BPF_MEM | BPF_H] = &&ST_MEM_H, + [BPF_ST | BPF_MEM | BPF_W] = &&ST_MEM_W, + [BPF_ST | BPF_MEM | BPF_DW] = &&ST_MEM_DW, + /* Load instructions */ + [BPF_LDX | BPF_MEM | BPF_B] = &&LDX_MEM_B, + [BPF_LDX | BPF_MEM | BPF_H] = &&LDX_MEM_H, + [BPF_LDX | BPF_MEM | BPF_W] = &&LDX_MEM_W, + [BPF_LDX | BPF_MEM | BPF_DW] = &&LDX_MEM_DW, + [BPF_LD | BPF_ABS | BPF_W] = &&LD_ABS_W, + [BPF_LD | BPF_ABS | BPF_H] = &&LD_ABS_H, + [BPF_LD | BPF_ABS | BPF_B] = &&LD_ABS_B, + [BPF_LD | BPF_IND | BPF_W] = &&LD_IND_W, + [BPF_LD | BPF_IND | BPF_H] = &&LD_IND_H, + [BPF_LD | BPF_IND | BPF_B] = &&LD_IND_B, }; void *ptr; int off; @@ -290,10 +295,10 @@ select_insn: ALU(XOR, ^) ALU(MUL, *) #undef ALU - ALU_NEG_0: + ALU_NEG: A = (u32) -A; CONT; - ALU64_NEG_0: + ALU64_NEG: A = -A; CONT; ALU_MOV_X: @@ -382,7 +387,7 @@ select_insn: CONT; /* CALL */ - JMP_CALL_0: + JMP_CALL: /* Function call scratches BPF_R1-BPF_R5 registers, * preserves BPF_R6-BPF_R9, and stores return value * into BPF_R0. @@ -392,7 +397,7 @@ select_insn: CONT; /* JMP */ - JMP_JA_0: + JMP_JA: insn += insn->off; CONT; JMP_JEQ_X: @@ -479,7 +484,7 @@ select_insn: CONT_JMP; } CONT; - JMP_EXIT_0: + JMP_EXIT: return BPF_R0; /* STX and ST and LDX*/ @@ -531,11 +536,13 @@ load_word: * Output: * BPF_R0 - 8/16/32-bit skb data converted to cpu endianness */ + ptr = load_pointer((struct sk_buff *) ctx, off, 4, &tmp); if (likely(ptr != NULL)) { BPF_R0 = get_unaligned_be32(ptr); CONT; } + return 0; LD_ABS_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + K)) */ off = K; @@ -545,6 +552,7 @@ load_half: BPF_R0 = get_unaligned_be16(ptr); CONT; } + return 0; LD_ABS_B: /* BPF_R0 = *(u8 *) (ctx + K) */ off = K; @@ -554,6 +562,7 @@ load_byte: BPF_R0 = *(u8 *)ptr; CONT; } + return 0; LD_IND_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + X + K)) */ off = K + X; @@ -571,15 +580,6 @@ load_byte: return 0; } -u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx, - const struct sock_filter_int *insni) - __attribute__ ((alias ("__sk_run_filter"))); - -u32 sk_run_filter_int_skb(const struct sk_buff *ctx, - const struct sock_filter_int *insni) - __attribute__ ((alias ("__sk_run_filter"))); -EXPORT_SYMBOL_GPL(sk_run_filter_int_skb); - /* Helper to find the offset of pkt_type in sk_buff structure. We want * to make sure its still a 3bit field starting at a byte boundary; * taken from arch/x86/net/bpf_jit_comp.c. @@ -672,14 +672,10 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); /* A = *(u16 *) (ctx + offsetof(protocol)) */ - *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, - offsetof(struct sk_buff, protocol)); - insn++; - + *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, protocol)); /* A = ntohs(A) [emitting a nop or swap16] */ - insn->code = BPF_ALU | BPF_END | BPF_FROM_BE; - insn->a_reg = BPF_REG_A; - insn->imm = 16; + *insn = BPF_ENDIAN(BPF_FROM_BE, BPF_REG_A, 16); break; case SKF_AD_OFF + SKF_AD_PKTTYPE: @@ -688,37 +684,27 @@ static bool convert_bpf_extensions(struct sock_filter *fp, if (insn->off < 0) return false; insn++; - *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, PKT_TYPE_MAX); break; case SKF_AD_OFF + SKF_AD_IFINDEX: case SKF_AD_OFF + SKF_AD_HATYPE: - *insn = BPF_LDX_MEM(size_to_bpf(FIELD_SIZEOF(struct sk_buff, dev)), - BPF_REG_TMP, BPF_REG_CTX, - offsetof(struct sk_buff, dev)); - insn++; - - /* if (tmp != 0) goto pc+1 */ - *insn = BPF_JMP_IMM(BPF_JNE, BPF_REG_TMP, 0, 1); - insn++; - - *insn = BPF_EXIT_INSN(); - insn++; - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2); - - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_TMP; - - if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX) { - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->off = offsetof(struct net_device, ifindex); - } else { - insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->off = offsetof(struct net_device, type); - } + BUILD_BUG_ON(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)) < 0); + + *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)), + BPF_REG_TMP, BPF_REG_CTX, + offsetof(struct sk_buff, dev)); + /* if (tmp != 0) goto pc + 1 */ + *insn++ = BPF_JMP_IMM(BPF_JNE, BPF_REG_TMP, 0, 1); + *insn++ = BPF_EXIT_INSN(); + if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX) + *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_TMP, + offsetof(struct net_device, ifindex)); + else + *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_TMP, + offsetof(struct net_device, type)); break; case SKF_AD_OFF + SKF_AD_MARK: @@ -745,22 +731,17 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_VLAN_TAG: case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); - - /* A = *(u16 *) (ctx + offsetof(vlan_tci)) */ - *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, - offsetof(struct sk_buff, vlan_tci)); - insn++; - BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); + /* A = *(u16 *) (ctx + offsetof(vlan_tci)) */ + *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, vlan_tci)); if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) { *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, ~VLAN_TAG_PRESENT); } else { /* A >>= 12 */ - *insn = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12); - insn++; - + *insn++ = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12); /* A &= 1 */ *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 1); } @@ -772,34 +753,27 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_CPU: case SKF_AD_OFF + SKF_AD_RANDOM: /* arg1 = ctx */ - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_ARG1, BPF_REG_CTX); - insn++; - + *insn++ = BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX); /* arg2 = A */ - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_ARG2, BPF_REG_A); - insn++; - + *insn++ = BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_A); /* arg3 = X */ - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_ARG3, BPF_REG_X); - insn++; - + *insn++ = BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_X); /* Emit call(ctx, arg2=A, arg3=X) */ - insn->code = BPF_JMP | BPF_CALL; switch (fp->k) { case SKF_AD_OFF + SKF_AD_PAY_OFFSET: - insn->imm = __skb_get_pay_offset - __bpf_call_base; + *insn = BPF_EMIT_CALL(__skb_get_pay_offset); break; case SKF_AD_OFF + SKF_AD_NLATTR: - insn->imm = __skb_get_nlattr - __bpf_call_base; + *insn = BPF_EMIT_CALL(__skb_get_nlattr); break; case SKF_AD_OFF + SKF_AD_NLATTR_NEST: - insn->imm = __skb_get_nlattr_nest - __bpf_call_base; + *insn = BPF_EMIT_CALL(__skb_get_nlattr_nest); break; case SKF_AD_OFF + SKF_AD_CPU: - insn->imm = __get_raw_cpu_id - __bpf_call_base; + *insn = BPF_EMIT_CALL(__get_raw_cpu_id); break; case SKF_AD_OFF + SKF_AD_RANDOM: - insn->imm = __get_random_u32 - __bpf_call_base; + *insn = BPF_EMIT_CALL(__get_random_u32); break; } break; @@ -871,9 +845,8 @@ do_pass: new_insn = new_prog; fp = prog; - if (new_insn) { - *new_insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_CTX, BPF_REG_ARG1); - } + if (new_insn) + *new_insn = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1); new_insn++; for (i = 0; i < len; fp++, i++) { @@ -921,17 +894,16 @@ do_pass: convert_bpf_extensions(fp, &insn)) break; - insn->code = fp->code; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_X; - insn->imm = fp->k; + *insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k); break; - /* Jump opcodes map as-is, but offsets need adjustment. */ - case BPF_JMP | BPF_JA: - target = i + fp->k + 1; - insn->code = fp->code; -#define EMIT_JMP \ + /* Jump transformation cannot use BPF block macros + * everywhere as offset calculation and target updates + * require a bit more work than the rest, i.e. jump + * opcodes map as-is, but offsets need adjustment. + */ + +#define BPF_EMIT_JMP \ do { \ if (target >= len || target < 0) \ goto err; \ @@ -940,7 +912,10 @@ do_pass: insn->off -= insn - tmp_insns; \ } while (0) - EMIT_JMP; + case BPF_JMP | BPF_JA: + target = i + fp->k + 1; + insn->code = fp->code; + BPF_EMIT_JMP; break; case BPF_JMP | BPF_JEQ | BPF_K: @@ -956,10 +931,7 @@ do_pass: * immediate into tmp register and use it * in compare insn. */ - insn->code = BPF_ALU | BPF_MOV | BPF_K; - insn->a_reg = BPF_REG_TMP; - insn->imm = fp->k; - insn++; + *insn++ = BPF_MOV32_IMM(BPF_REG_TMP, fp->k); insn->a_reg = BPF_REG_A; insn->x_reg = BPF_REG_TMP; @@ -975,7 +947,7 @@ do_pass: if (fp->jf == 0) { insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src; target = i + fp->jt + 1; - EMIT_JMP; + BPF_EMIT_JMP; break; } @@ -983,116 +955,94 @@ do_pass: if (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) { insn->code = BPF_JMP | BPF_JNE | bpf_src; target = i + fp->jf + 1; - EMIT_JMP; + BPF_EMIT_JMP; break; } /* Other jumps are mapped into two insns: Jxx and JA. */ target = i + fp->jt + 1; insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src; - EMIT_JMP; + BPF_EMIT_JMP; insn++; insn->code = BPF_JMP | BPF_JA; target = i + fp->jf + 1; - EMIT_JMP; + BPF_EMIT_JMP; break; /* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */ case BPF_LDX | BPF_MSH | BPF_B: /* tmp = A */ - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_TMP, BPF_REG_A); - insn++; - + *insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_A); /* A = BPF_R0 = *(u8 *) (skb->data + K) */ - *insn = BPF_LD_ABS(BPF_B, fp->k); - insn++; - + *insn++ = BPF_LD_ABS(BPF_B, fp->k); /* A &= 0xf */ - *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 0xf); - insn++; - + *insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 0xf); /* A <<= 2 */ - *insn = BPF_ALU32_IMM(BPF_LSH, BPF_REG_A, 2); - insn++; - + *insn++ = BPF_ALU32_IMM(BPF_LSH, BPF_REG_A, 2); /* X = A */ - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_X, BPF_REG_A); - insn++; - + *insn++ = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A); /* A = tmp */ - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_A, BPF_REG_TMP); + *insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_TMP); break; /* RET_K, RET_A are remaped into 2 insns. */ case BPF_RET | BPF_A: case BPF_RET | BPF_K: - insn->code = BPF_ALU | BPF_MOV | - (BPF_RVAL(fp->code) == BPF_K ? - BPF_K : BPF_X); - insn->a_reg = 0; - insn->x_reg = BPF_REG_A; - insn->imm = fp->k; - insn++; - + *insn++ = BPF_MOV32_RAW(BPF_RVAL(fp->code) == BPF_K ? + BPF_K : BPF_X, BPF_REG_0, + BPF_REG_A, fp->k); *insn = BPF_EXIT_INSN(); break; /* Store to stack. */ case BPF_ST: case BPF_STX: - insn->code = BPF_STX | BPF_MEM | BPF_W; - insn->a_reg = BPF_REG_FP; - insn->x_reg = fp->code == BPF_ST ? - BPF_REG_A : BPF_REG_X; - insn->off = -(BPF_MEMWORDS - fp->k) * 4; + *insn = BPF_STX_MEM(BPF_W, BPF_REG_FP, BPF_CLASS(fp->code) == + BPF_ST ? BPF_REG_A : BPF_REG_X, + -(BPF_MEMWORDS - fp->k) * 4); break; /* Load from stack. */ case BPF_LD | BPF_MEM: case BPF_LDX | BPF_MEM: - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? - BPF_REG_A : BPF_REG_X; - insn->x_reg = BPF_REG_FP; - insn->off = -(BPF_MEMWORDS - fp->k) * 4; + *insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD ? + BPF_REG_A : BPF_REG_X, BPF_REG_FP, + -(BPF_MEMWORDS - fp->k) * 4); break; /* A = K or X = K */ case BPF_LD | BPF_IMM: case BPF_LDX | BPF_IMM: - insn->code = BPF_ALU | BPF_MOV | BPF_K; - insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? - BPF_REG_A : BPF_REG_X; - insn->imm = fp->k; + *insn = BPF_MOV32_IMM(BPF_CLASS(fp->code) == BPF_LD ? + BPF_REG_A : BPF_REG_X, fp->k); break; /* X = A */ case BPF_MISC | BPF_TAX: - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_X, BPF_REG_A); + *insn = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A); break; /* A = X */ case BPF_MISC | BPF_TXA: - *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_A, BPF_REG_X); + *insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_X); break; /* A = skb->len or X = skb->len */ case BPF_LD | BPF_W | BPF_LEN: case BPF_LDX | BPF_W | BPF_LEN: - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? - BPF_REG_A : BPF_REG_X; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, len); + *insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD ? + BPF_REG_A : BPF_REG_X, BPF_REG_CTX, + offsetof(struct sk_buff, len)); break; - /* access seccomp_data fields */ + /* Access seccomp_data fields. */ case BPF_LDX | BPF_ABS | BPF_W: /* A = *(u32 *) (ctx + K) */ *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, fp->k); break; + /* Unkown instruction. */ default: goto err; } @@ -1101,7 +1051,6 @@ do_pass: if (new_prog) memcpy(new_insn, tmp_insns, sizeof(*insn) * (insn - tmp_insns)); - new_insn += insn - tmp_insns; } @@ -1116,7 +1065,6 @@ do_pass: new_flen = new_insn - new_prog; if (pass > 2) goto err; - goto do_pass; } @@ -1140,44 +1088,46 @@ err: */ static int check_load_and_stores(struct sock_filter *filter, int flen) { - u16 *masks, memvalid = 0; /* one bit per cell, 16 cells */ + u16 *masks, memvalid = 0; /* One bit per cell, 16 cells */ int pc, ret = 0; BUILD_BUG_ON(BPF_MEMWORDS > 16); + masks = kmalloc(flen * sizeof(*masks), GFP_KERNEL); if (!masks) return -ENOMEM; + memset(masks, 0xff, flen * sizeof(*masks)); for (pc = 0; pc < flen; pc++) { memvalid &= masks[pc]; switch (filter[pc].code) { - case BPF_S_ST: - case BPF_S_STX: + case BPF_ST: + case BPF_STX: memvalid |= (1 << filter[pc].k); break; - case BPF_S_LD_MEM: - case BPF_S_LDX_MEM: + case BPF_LD | BPF_MEM: + case BPF_LDX | BPF_MEM: if (!(memvalid & (1 << filter[pc].k))) { ret = -EINVAL; goto error; } break; - case BPF_S_JMP_JA: - /* a jump must set masks on target */ + case BPF_JMP | BPF_JA: + /* A jump must set masks on target */ masks[pc + 1 + filter[pc].k] &= memvalid; memvalid = ~0; break; - case BPF_S_JMP_JEQ_K: - case BPF_S_JMP_JEQ_X: - case BPF_S_JMP_JGE_K: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JSET_X: - case BPF_S_JMP_JSET_K: - /* a jump must set masks on targets */ + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JSET | BPF_K: + case BPF_JMP | BPF_JSET | BPF_X: + /* A jump must set masks on targets */ masks[pc + 1 + filter[pc].jt] &= memvalid; masks[pc + 1 + filter[pc].jf] &= memvalid; memvalid = ~0; @@ -1189,6 +1139,72 @@ error: return ret; } +static bool chk_code_allowed(u16 code_to_probe) +{ + static const bool codes[] = { + /* 32 bit ALU operations */ + [BPF_ALU | BPF_ADD | BPF_K] = true, + [BPF_ALU | BPF_ADD | BPF_X] = true, + [BPF_ALU | BPF_SUB | BPF_K] = true, + [BPF_ALU | BPF_SUB | BPF_X] = true, + [BPF_ALU | BPF_MUL | BPF_K] = true, + [BPF_ALU | BPF_MUL | BPF_X] = true, + [BPF_ALU | BPF_DIV | BPF_K] = true, + [BPF_ALU | BPF_DIV | BPF_X] = true, + [BPF_ALU | BPF_MOD | BPF_K] = true, + [BPF_ALU | BPF_MOD | BPF_X] = true, + [BPF_ALU | BPF_AND | BPF_K] = true, + [BPF_ALU | BPF_AND | BPF_X] = true, + [BPF_ALU | BPF_OR | BPF_K] = true, + [BPF_ALU | BPF_OR | BPF_X] = true, + [BPF_ALU | BPF_XOR | BPF_K] = true, + [BPF_ALU | BPF_XOR | BPF_X] = true, + [BPF_ALU | BPF_LSH | BPF_K] = true, + [BPF_ALU | BPF_LSH | BPF_X] = true, + [BPF_ALU | BPF_RSH | BPF_K] = true, + [BPF_ALU | BPF_RSH | BPF_X] = true, + [BPF_ALU | BPF_NEG] = true, + /* Load instructions */ + [BPF_LD | BPF_W | BPF_ABS] = true, + [BPF_LD | BPF_H | BPF_ABS] = true, + [BPF_LD | BPF_B | BPF_ABS] = true, + [BPF_LD | BPF_W | BPF_LEN] = true, + [BPF_LD | BPF_W | BPF_IND] = true, + [BPF_LD | BPF_H | BPF_IND] = true, + [BPF_LD | BPF_B | BPF_IND] = true, + [BPF_LD | BPF_IMM] = true, + [BPF_LD | BPF_MEM] = true, + [BPF_LDX | BPF_W | BPF_LEN] = true, + [BPF_LDX | BPF_B | BPF_MSH] = true, + [BPF_LDX | BPF_IMM] = true, + [BPF_LDX | BPF_MEM] = true, + /* Store instructions */ + [BPF_ST] = true, + [BPF_STX] = true, + /* Misc instructions */ + [BPF_MISC | BPF_TAX] = true, + [BPF_MISC | BPF_TXA] = true, + /* Return instructions */ + [BPF_RET | BPF_K] = true, + [BPF_RET | BPF_A] = true, + /* Jump instructions */ + [BPF_JMP | BPF_JA] = true, + [BPF_JMP | BPF_JEQ | BPF_K] = true, + [BPF_JMP | BPF_JEQ | BPF_X] = true, + [BPF_JMP | BPF_JGE | BPF_K] = true, + [BPF_JMP | BPF_JGE | BPF_X] = true, + [BPF_JMP | BPF_JGT | BPF_K] = true, + [BPF_JMP | BPF_JGT | BPF_X] = true, + [BPF_JMP | BPF_JSET | BPF_K] = true, + [BPF_JMP | BPF_JSET | BPF_X] = true, + }; + + if (code_to_probe >= ARRAY_SIZE(codes)) + return false; + + return codes[code_to_probe]; +} + /** * sk_chk_filter - verify socket filter code * @filter: filter to verify @@ -1205,154 +1221,76 @@ error: */ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) { - /* - * Valid instructions are initialized to non-0. - * Invalid instructions are initialized to 0. - */ - static const u8 codes[] = { - [BPF_ALU|BPF_ADD|BPF_K] = BPF_S_ALU_ADD_K, - [BPF_ALU|BPF_ADD|BPF_X] = BPF_S_ALU_ADD_X, - [BPF_ALU|BPF_SUB|BPF_K] = BPF_S_ALU_SUB_K, - [BPF_ALU|BPF_SUB|BPF_X] = BPF_S_ALU_SUB_X, - [BPF_ALU|BPF_MUL|BPF_K] = BPF_S_ALU_MUL_K, - [BPF_ALU|BPF_MUL|BPF_X] = BPF_S_ALU_MUL_X, - [BPF_ALU|BPF_DIV|BPF_X] = BPF_S_ALU_DIV_X, - [BPF_ALU|BPF_MOD|BPF_K] = BPF_S_ALU_MOD_K, - [BPF_ALU|BPF_MOD|BPF_X] = BPF_S_ALU_MOD_X, - [BPF_ALU|BPF_AND|BPF_K] = BPF_S_ALU_AND_K, - [BPF_ALU|BPF_AND|BPF_X] = BPF_S_ALU_AND_X, - [BPF_ALU|BPF_OR|BPF_K] = BPF_S_ALU_OR_K, - [BPF_ALU|BPF_OR|BPF_X] = BPF_S_ALU_OR_X, - [BPF_ALU|BPF_XOR|BPF_K] = BPF_S_ALU_XOR_K, - [BPF_ALU|BPF_XOR|BPF_X] = BPF_S_ALU_XOR_X, - [BPF_ALU|BPF_LSH|BPF_K] = BPF_S_ALU_LSH_K, - [BPF_ALU|BPF_LSH|BPF_X] = BPF_S_ALU_LSH_X, - [BPF_ALU|BPF_RSH|BPF_K] = BPF_S_ALU_RSH_K, - [BPF_ALU|BPF_RSH|BPF_X] = BPF_S_ALU_RSH_X, - [BPF_ALU|BPF_NEG] = BPF_S_ALU_NEG, - [BPF_LD|BPF_W|BPF_ABS] = BPF_S_LD_W_ABS, - [BPF_LD|BPF_H|BPF_ABS] = BPF_S_LD_H_ABS, - [BPF_LD|BPF_B|BPF_ABS] = BPF_S_LD_B_ABS, - [BPF_LD|BPF_W|BPF_LEN] = BPF_S_LD_W_LEN, - [BPF_LD|BPF_W|BPF_IND] = BPF_S_LD_W_IND, - [BPF_LD|BPF_H|BPF_IND] = BPF_S_LD_H_IND, - [BPF_LD|BPF_B|BPF_IND] = BPF_S_LD_B_IND, - [BPF_LD|BPF_IMM] = BPF_S_LD_IMM, - [BPF_LDX|BPF_W|BPF_LEN] = BPF_S_LDX_W_LEN, - [BPF_LDX|BPF_B|BPF_MSH] = BPF_S_LDX_B_MSH, - [BPF_LDX|BPF_IMM] = BPF_S_LDX_IMM, - [BPF_MISC|BPF_TAX] = BPF_S_MISC_TAX, - [BPF_MISC|BPF_TXA] = BPF_S_MISC_TXA, - [BPF_RET|BPF_K] = BPF_S_RET_K, - [BPF_RET|BPF_A] = BPF_S_RET_A, - [BPF_ALU|BPF_DIV|BPF_K] = BPF_S_ALU_DIV_K, - [BPF_LD|BPF_MEM] = BPF_S_LD_MEM, - [BPF_LDX|BPF_MEM] = BPF_S_LDX_MEM, - [BPF_ST] = BPF_S_ST, - [BPF_STX] = BPF_S_STX, - [BPF_JMP|BPF_JA] = BPF_S_JMP_JA, - [BPF_JMP|BPF_JEQ|BPF_K] = BPF_S_JMP_JEQ_K, - [BPF_JMP|BPF_JEQ|BPF_X] = BPF_S_JMP_JEQ_X, - [BPF_JMP|BPF_JGE|BPF_K] = BPF_S_JMP_JGE_K, - [BPF_JMP|BPF_JGE|BPF_X] = BPF_S_JMP_JGE_X, - [BPF_JMP|BPF_JGT|BPF_K] = BPF_S_JMP_JGT_K, - [BPF_JMP|BPF_JGT|BPF_X] = BPF_S_JMP_JGT_X, - [BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K, - [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X, - }; - int pc; bool anc_found; + int pc; if (flen == 0 || flen > BPF_MAXINSNS) return -EINVAL; - /* check the filter code now */ + /* Check the filter code now */ for (pc = 0; pc < flen; pc++) { struct sock_filter *ftest = &filter[pc]; - u16 code = ftest->code; - if (code >= ARRAY_SIZE(codes)) - return -EINVAL; - code = codes[code]; - if (!code) + /* May we actually operate on this code? */ + if (!chk_code_allowed(ftest->code)) return -EINVAL; + /* Some instructions need special checks */ - switch (code) { - case BPF_S_ALU_DIV_K: - case BPF_S_ALU_MOD_K: - /* check for division by zero */ + switch (ftest->code) { + case BPF_ALU | BPF_DIV | BPF_K: + case BPF_ALU | BPF_MOD | BPF_K: + /* Check for division by zero */ if (ftest->k == 0) return -EINVAL; break; - case BPF_S_LD_MEM: - case BPF_S_LDX_MEM: - case BPF_S_ST: - case BPF_S_STX: - /* check for invalid memory addresses */ + case BPF_LD | BPF_MEM: + case BPF_LDX | BPF_MEM: + case BPF_ST: + case BPF_STX: + /* Check for invalid memory addresses */ if (ftest->k >= BPF_MEMWORDS) return -EINVAL; break; - case BPF_S_JMP_JA: - /* - * Note, the large ftest->k might cause loops. + case BPF_JMP | BPF_JA: + /* Note, the large ftest->k might cause loops. * Compare this with conditional jumps below, * where offsets are limited. --ANK (981016) */ - if (ftest->k >= (unsigned int)(flen-pc-1)) + if (ftest->k >= (unsigned int)(flen - pc - 1)) return -EINVAL; break; - case BPF_S_JMP_JEQ_K: - case BPF_S_JMP_JEQ_X: - case BPF_S_JMP_JGE_K: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JSET_X: - case BPF_S_JMP_JSET_K: - /* for conditionals both must be safe */ + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JSET | BPF_K: + case BPF_JMP | BPF_JSET | BPF_X: + /* Both conditionals must be safe */ if (pc + ftest->jt + 1 >= flen || pc + ftest->jf + 1 >= flen) return -EINVAL; break; - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: + case BPF_LD | BPF_W | BPF_ABS: + case BPF_LD | BPF_H | BPF_ABS: + case BPF_LD | BPF_B | BPF_ABS: anc_found = false; -#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE: \ - code = BPF_S_ANC_##CODE; \ - anc_found = true; \ - break - switch (ftest->k) { - ANCILLARY(PROTOCOL); - ANCILLARY(PKTTYPE); - ANCILLARY(IFINDEX); - ANCILLARY(NLATTR); - ANCILLARY(NLATTR_NEST); - ANCILLARY(MARK); - ANCILLARY(QUEUE); - ANCILLARY(HATYPE); - ANCILLARY(RXHASH); - ANCILLARY(CPU); - ANCILLARY(ALU_XOR_X); - ANCILLARY(VLAN_TAG); - ANCILLARY(VLAN_TAG_PRESENT); - ANCILLARY(PAY_OFFSET); - ANCILLARY(RANDOM); - } - - /* ancillary operation unknown or unsupported */ + if (bpf_anc_helper(ftest) & BPF_ANC) + anc_found = true; + /* Ancillary operation unknown or unsupported */ if (anc_found == false && ftest->k >= SKF_AD_OFF) return -EINVAL; } - ftest->code = code; } - /* last instruction must be a RET code */ + /* Last instruction must be a RET code */ switch (filter[flen - 1].code) { - case BPF_S_RET_K: - case BPF_S_RET_A: + case BPF_RET | BPF_K: + case BPF_RET | BPF_A: return check_load_and_stores(filter, flen); } + return -EINVAL; } EXPORT_SYMBOL(sk_chk_filter); @@ -1397,7 +1335,7 @@ static void sk_filter_release_rcu(struct rcu_head *rcu) struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); sk_release_orig_filter(fp); - bpf_jit_free(fp); + sk_filter_free(fp); } /** @@ -1452,7 +1390,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, { struct sock_filter *old_prog; struct sk_filter *old_fp; - int i, err, new_len, old_len = fp->len; + int err, new_len, old_len = fp->len; /* We are free to overwrite insns et al right here as it * won't be used at this point in time anymore internally @@ -1462,13 +1400,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, BUILD_BUG_ON(sizeof(struct sock_filter) != sizeof(struct sock_filter_int)); - /* For now, we need to unfiddle BPF_S_* identifiers in place. - * This can sooner or later on be subject to removal, e.g. when - * JITs have been converted. - */ - for (i = 0; i < fp->len; i++) - sk_decode_filter(&fp->insns[i], &fp->insns[i]); - /* Conversion cannot happen on overlapping memory areas, * so we need to keep the user BPF around until the 2nd * pass. At this time, the user BPF is stored in fp->insns. @@ -1497,7 +1428,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, goto out_err_free; } - fp->bpf_func = sk_run_filter_int_skb; fp->len = new_len; /* 2nd pass: remap sock_filter insns into sock_filter_int insns. */ @@ -1510,6 +1440,8 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, */ goto out_err_free; + sk_filter_select_runtime(fp); + kfree(old_prog); return fp; @@ -1524,6 +1456,33 @@ out_err: return ERR_PTR(err); } +void __weak bpf_int_jit_compile(struct sk_filter *prog) +{ +} + +/** + * sk_filter_select_runtime - select execution runtime for BPF program + * @fp: sk_filter populated with internal BPF program + * + * try to JIT internal BPF program, if JIT is not available select interpreter + * BPF program will be executed via SK_RUN_FILTER() macro + */ +void sk_filter_select_runtime(struct sk_filter *fp) +{ + fp->bpf_func = (void *) __sk_run_filter; + + /* Probe if internal BPF can be JITed */ + bpf_int_jit_compile(fp); +} +EXPORT_SYMBOL_GPL(sk_filter_select_runtime); + +/* free internal BPF program */ +void sk_filter_free(struct sk_filter *fp) +{ + bpf_jit_free(fp); +} +EXPORT_SYMBOL_GPL(sk_filter_free); + static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, struct sock *sk) { @@ -1561,7 +1520,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, * a negative errno code is returned. On success the return is zero. */ int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog *fprog) + struct sock_fprog_kern *fprog) { unsigned int fsize = sk_filter_proglen(fprog); struct sk_filter *fp; @@ -1682,84 +1641,6 @@ int sk_detach_filter(struct sock *sk) } EXPORT_SYMBOL_GPL(sk_detach_filter); -void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) -{ - static const u16 decodes[] = { - [BPF_S_ALU_ADD_K] = BPF_ALU|BPF_ADD|BPF_K, - [BPF_S_ALU_ADD_X] = BPF_ALU|BPF_ADD|BPF_X, - [BPF_S_ALU_SUB_K] = BPF_ALU|BPF_SUB|BPF_K, - [BPF_S_ALU_SUB_X] = BPF_ALU|BPF_SUB|BPF_X, - [BPF_S_ALU_MUL_K] = BPF_ALU|BPF_MUL|BPF_K, - [BPF_S_ALU_MUL_X] = BPF_ALU|BPF_MUL|BPF_X, - [BPF_S_ALU_DIV_X] = BPF_ALU|BPF_DIV|BPF_X, - [BPF_S_ALU_MOD_K] = BPF_ALU|BPF_MOD|BPF_K, - [BPF_S_ALU_MOD_X] = BPF_ALU|BPF_MOD|BPF_X, - [BPF_S_ALU_AND_K] = BPF_ALU|BPF_AND|BPF_K, - [BPF_S_ALU_AND_X] = BPF_ALU|BPF_AND|BPF_X, - [BPF_S_ALU_OR_K] = BPF_ALU|BPF_OR|BPF_K, - [BPF_S_ALU_OR_X] = BPF_ALU|BPF_OR|BPF_X, - [BPF_S_ALU_XOR_K] = BPF_ALU|BPF_XOR|BPF_K, - [BPF_S_ALU_XOR_X] = BPF_ALU|BPF_XOR|BPF_X, - [BPF_S_ALU_LSH_K] = BPF_ALU|BPF_LSH|BPF_K, - [BPF_S_ALU_LSH_X] = BPF_ALU|BPF_LSH|BPF_X, - [BPF_S_ALU_RSH_K] = BPF_ALU|BPF_RSH|BPF_K, - [BPF_S_ALU_RSH_X] = BPF_ALU|BPF_RSH|BPF_X, - [BPF_S_ALU_NEG] = BPF_ALU|BPF_NEG, - [BPF_S_LD_W_ABS] = BPF_LD|BPF_W|BPF_ABS, - [BPF_S_LD_H_ABS] = BPF_LD|BPF_H|BPF_ABS, - [BPF_S_LD_B_ABS] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_PROTOCOL] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_PKTTYPE] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_IFINDEX] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_NLATTR] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_NLATTR_NEST] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_MARK] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_QUEUE] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_HATYPE] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_RXHASH] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_CPU] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_ALU_XOR_X] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_VLAN_TAG] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_PAY_OFFSET] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_ANC_RANDOM] = BPF_LD|BPF_B|BPF_ABS, - [BPF_S_LD_W_LEN] = BPF_LD|BPF_W|BPF_LEN, - [BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND, - [BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND, - [BPF_S_LD_B_IND] = BPF_LD|BPF_B|BPF_IND, - [BPF_S_LD_IMM] = BPF_LD|BPF_IMM, - [BPF_S_LDX_W_LEN] = BPF_LDX|BPF_W|BPF_LEN, - [BPF_S_LDX_B_MSH] = BPF_LDX|BPF_B|BPF_MSH, - [BPF_S_LDX_IMM] = BPF_LDX|BPF_IMM, - [BPF_S_MISC_TAX] = BPF_MISC|BPF_TAX, - [BPF_S_MISC_TXA] = BPF_MISC|BPF_TXA, - [BPF_S_RET_K] = BPF_RET|BPF_K, - [BPF_S_RET_A] = BPF_RET|BPF_A, - [BPF_S_ALU_DIV_K] = BPF_ALU|BPF_DIV|BPF_K, - [BPF_S_LD_MEM] = BPF_LD|BPF_MEM, - [BPF_S_LDX_MEM] = BPF_LDX|BPF_MEM, - [BPF_S_ST] = BPF_ST, - [BPF_S_STX] = BPF_STX, - [BPF_S_JMP_JA] = BPF_JMP|BPF_JA, - [BPF_S_JMP_JEQ_K] = BPF_JMP|BPF_JEQ|BPF_K, - [BPF_S_JMP_JEQ_X] = BPF_JMP|BPF_JEQ|BPF_X, - [BPF_S_JMP_JGE_K] = BPF_JMP|BPF_JGE|BPF_K, - [BPF_S_JMP_JGE_X] = BPF_JMP|BPF_JGE|BPF_X, - [BPF_S_JMP_JGT_K] = BPF_JMP|BPF_JGT|BPF_K, - [BPF_S_JMP_JGT_X] = BPF_JMP|BPF_JGT|BPF_X, - [BPF_S_JMP_JSET_K] = BPF_JMP|BPF_JSET|BPF_K, - [BPF_S_JMP_JSET_X] = BPF_JMP|BPF_JSET|BPF_X, - }; - u16 code; - - code = filt->code; - - to->code = decodes[code]; - to->jt = filt->jt; - to->jf = filt->jf; - to->k = filt->k; -} - int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len) { diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8f8a96e..32d872e 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1248,8 +1248,8 @@ void __neigh_set_probe_once(struct neighbour *neigh) neigh->updated = jiffies; if (!(neigh->nud_state & NUD_FAILED)) return; - neigh->nud_state = NUD_PROBE; - atomic_set(&neigh->probes, NEIGH_VAR(neigh->parms, UCAST_PROBES)); + neigh->nud_state = NUD_INCOMPLETE; + atomic_set(&neigh->probes, neigh_max_probes(neigh)); neigh_add_timer(neigh, jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME)); } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 05e949d..85b6269 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -24,7 +24,7 @@ static LIST_HEAD(pernet_list); static struct list_head *first_device = &pernet_list; -static DEFINE_MUTEX(net_mutex); +DEFINE_MUTEX(net_mutex); LIST_HEAD(net_namespace_list); EXPORT_SYMBOL_GPL(net_namespace_list); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 0304f98..fc17a9d 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -573,7 +573,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) is_zero_ether_addr(pkt_dev->src_mac) ? pkt_dev->odev->dev_addr : pkt_dev->src_mac); - seq_printf(seq, "dst_mac: "); + seq_puts(seq, "dst_mac: "); seq_printf(seq, "%pM\n", pkt_dev->dst_mac); seq_printf(seq, @@ -588,7 +588,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) if (pkt_dev->nr_labels) { unsigned int i; - seq_printf(seq, " mpls: "); + seq_puts(seq, " mpls: "); for (i = 0; i < pkt_dev->nr_labels; i++) seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]), i == pkt_dev->nr_labels-1 ? "\n" : ", "); @@ -613,67 +613,67 @@ static int pktgen_if_show(struct seq_file *seq, void *v) if (pkt_dev->node >= 0) seq_printf(seq, " node: %d\n", pkt_dev->node); - seq_printf(seq, " Flags: "); + seq_puts(seq, " Flags: "); if (pkt_dev->flags & F_IPV6) - seq_printf(seq, "IPV6 "); + seq_puts(seq, "IPV6 "); if (pkt_dev->flags & F_IPSRC_RND) - seq_printf(seq, "IPSRC_RND "); + seq_puts(seq, "IPSRC_RND "); if (pkt_dev->flags & F_IPDST_RND) - seq_printf(seq, "IPDST_RND "); + seq_puts(seq, "IPDST_RND "); if (pkt_dev->flags & F_TXSIZE_RND) - seq_printf(seq, "TXSIZE_RND "); + seq_puts(seq, "TXSIZE_RND "); if (pkt_dev->flags & F_UDPSRC_RND) - seq_printf(seq, "UDPSRC_RND "); + seq_puts(seq, "UDPSRC_RND "); if (pkt_dev->flags & F_UDPDST_RND) - seq_printf(seq, "UDPDST_RND "); + seq_puts(seq, "UDPDST_RND "); if (pkt_dev->flags & F_UDPCSUM) - seq_printf(seq, "UDPCSUM "); + seq_puts(seq, "UDPCSUM "); if (pkt_dev->flags & F_MPLS_RND) - seq_printf(seq, "MPLS_RND "); + seq_puts(seq, "MPLS_RND "); if (pkt_dev->flags & F_QUEUE_MAP_RND) - seq_printf(seq, "QUEUE_MAP_RND "); + seq_puts(seq, "QUEUE_MAP_RND "); if (pkt_dev->flags & F_QUEUE_MAP_CPU) - seq_printf(seq, "QUEUE_MAP_CPU "); + seq_puts(seq, "QUEUE_MAP_CPU "); if (pkt_dev->cflows) { if (pkt_dev->flags & F_FLOW_SEQ) - seq_printf(seq, "FLOW_SEQ "); /*in sequence flows*/ + seq_puts(seq, "FLOW_SEQ "); /*in sequence flows*/ else - seq_printf(seq, "FLOW_RND "); + seq_puts(seq, "FLOW_RND "); } #ifdef CONFIG_XFRM if (pkt_dev->flags & F_IPSEC_ON) { - seq_printf(seq, "IPSEC "); + seq_puts(seq, "IPSEC "); if (pkt_dev->spi) seq_printf(seq, "spi:%u", pkt_dev->spi); } #endif if (pkt_dev->flags & F_MACSRC_RND) - seq_printf(seq, "MACSRC_RND "); + seq_puts(seq, "MACSRC_RND "); if (pkt_dev->flags & F_MACDST_RND) - seq_printf(seq, "MACDST_RND "); + seq_puts(seq, "MACDST_RND "); if (pkt_dev->flags & F_VID_RND) - seq_printf(seq, "VID_RND "); + seq_puts(seq, "VID_RND "); if (pkt_dev->flags & F_SVID_RND) - seq_printf(seq, "SVID_RND "); + seq_puts(seq, "SVID_RND "); if (pkt_dev->flags & F_NODE) - seq_printf(seq, "NODE_ALLOC "); + seq_puts(seq, "NODE_ALLOC "); seq_puts(seq, "\n"); @@ -716,7 +716,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) if (pkt_dev->result[0]) seq_printf(seq, "Result: %s\n", pkt_dev->result); else - seq_printf(seq, "Result: Idle\n"); + seq_puts(seq, "Result: Idle\n"); return 0; } @@ -1735,14 +1735,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) BUG_ON(!t); - seq_printf(seq, "Running: "); + seq_puts(seq, "Running: "); if_lock(t); list_for_each_entry(pkt_dev, &t->if_list, list) if (pkt_dev->running) seq_printf(seq, "%s ", pkt_dev->odevname); - seq_printf(seq, "\nStopped: "); + seq_puts(seq, "\nStopped: "); list_for_each_entry(pkt_dev, &t->if_list, list) if (!pkt_dev->running) @@ -1751,7 +1751,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v) if (t->result[0]) seq_printf(seq, "\nResult: %s\n", t->result); else - seq_printf(seq, "\nResult: NA\n"); + seq_puts(seq, "\nResult: NA\n"); if_unlock(t); diff --git a/net/core/ptp_classifier.c b/net/core/ptp_classifier.c index 37d8615..d3027a7 100644 --- a/net/core/ptp_classifier.c +++ b/net/core/ptp_classifier.c @@ -133,7 +133,7 @@ void __init ptp_classifier_init(void) { 0x16, 0, 0, 0x00000000 }, { 0x06, 0, 0, 0x00000000 }, }; - struct sock_fprog ptp_prog = { + struct sock_fprog_kern ptp_prog = { .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter, }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9837beb..f31268d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -353,15 +353,46 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops) } EXPORT_SYMBOL_GPL(__rtnl_link_unregister); +/* Return with the rtnl_lock held when there are no network + * devices unregistering in any network namespace. + */ +static void rtnl_lock_unregistering_all(void) +{ + struct net *net; + bool unregistering; + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&netdev_unregistering_wq, &wait, + TASK_UNINTERRUPTIBLE); + unregistering = false; + rtnl_lock(); + for_each_net(net) { + if (net->dev_unreg_count > 0) { + unregistering = true; + break; + } + } + if (!unregistering) + break; + __rtnl_unlock(); + schedule(); + } + finish_wait(&netdev_unregistering_wq, &wait); +} + /** * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. * @ops: struct rtnl_link_ops * to unregister */ void rtnl_link_unregister(struct rtnl_link_ops *ops) { - rtnl_lock(); + /* Close the race with cleanup_net() */ + mutex_lock(&net_mutex); + rtnl_lock_unregistering_all(); __rtnl_link_unregister(ops); rtnl_unlock(); + mutex_unlock(&net_mutex); } EXPORT_SYMBOL_GPL(rtnl_link_unregister); @@ -767,8 +798,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev, size += num_vfs * (nla_total_size(sizeof(struct ifla_vf_mac)) + nla_total_size(sizeof(struct ifla_vf_vlan)) + - nla_total_size(sizeof(struct ifla_vf_tx_rate)) + - nla_total_size(sizeof(struct ifla_vf_spoofchk))); + nla_total_size(sizeof(struct ifla_vf_spoofchk)) + + nla_total_size(sizeof(struct ifla_vf_rate))); return size; } else return 0; @@ -1034,6 +1065,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, struct ifla_vf_info ivi; struct ifla_vf_mac vf_mac; struct ifla_vf_vlan vf_vlan; + struct ifla_vf_rate vf_rate; struct ifla_vf_tx_rate vf_tx_rate; struct ifla_vf_spoofchk vf_spoofchk; struct ifla_vf_link_state vf_linkstate; @@ -1054,6 +1086,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, break; vf_mac.vf = vf_vlan.vf = + vf_rate.vf = vf_tx_rate.vf = vf_spoofchk.vf = vf_linkstate.vf = ivi.vf; @@ -1061,7 +1094,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac)); vf_vlan.vlan = ivi.vlan; vf_vlan.qos = ivi.qos; - vf_tx_rate.rate = ivi.tx_rate; + vf_tx_rate.rate = ivi.max_tx_rate; + vf_rate.min_tx_rate = ivi.min_tx_rate; + vf_rate.max_tx_rate = ivi.max_tx_rate; vf_spoofchk.setting = ivi.spoofchk; vf_linkstate.link_state = ivi.linkstate; vf = nla_nest_start(skb, IFLA_VF_INFO); @@ -1071,6 +1106,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, } if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) || nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) || + nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate), + &vf_rate) || nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate), &vf_tx_rate) || nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk), @@ -1177,6 +1214,8 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = { .len = sizeof(struct ifla_vf_tx_rate) }, [IFLA_VF_SPOOFCHK] = { .type = NLA_BINARY, .len = sizeof(struct ifla_vf_spoofchk) }, + [IFLA_VF_RATE] = { .type = NLA_BINARY, + .len = sizeof(struct ifla_vf_rate) }, }; static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { @@ -1336,11 +1375,29 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr) } case IFLA_VF_TX_RATE: { struct ifla_vf_tx_rate *ivt; + struct ifla_vf_info ivf; + ivt = nla_data(vf); + err = -EOPNOTSUPP; + if (ops->ndo_get_vf_config) + err = ops->ndo_get_vf_config(dev, ivt->vf, + &ivf); + if (err) + break; + err = -EOPNOTSUPP; + if (ops->ndo_set_vf_rate) + err = ops->ndo_set_vf_rate(dev, ivt->vf, + ivf.min_tx_rate, + ivt->rate); + break; + } + case IFLA_VF_RATE: { + struct ifla_vf_rate *ivt; ivt = nla_data(vf); err = -EOPNOTSUPP; - if (ops->ndo_set_vf_tx_rate) - err = ops->ndo_set_vf_tx_rate(dev, ivt->vf, - ivt->rate); + if (ops->ndo_set_vf_rate) + err = ops->ndo_set_vf_rate(dev, ivt->vf, + ivt->min_tx_rate, + ivt->max_tx_rate); break; } case IFLA_VF_SPOOFCHK: { diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 897da56..ba71212 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -85,31 +85,6 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral); #endif #ifdef CONFIG_INET -__u32 secure_ip_id(__be32 daddr) -{ - u32 hash[MD5_DIGEST_WORDS]; - - net_secret_init(); - hash[0] = (__force __u32) daddr; - hash[1] = net_secret[13]; - hash[2] = net_secret[14]; - hash[3] = net_secret[15]; - - md5_transform(hash, net_secret); - - return hash[0]; -} - -__u32 secure_ipv6_id(const __be32 daddr[4]) -{ - __u32 hash[4]; - - net_secret_init(); - memcpy(hash, daddr, 16); - md5_transform(hash, net_secret); - - return hash[0]; -} __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 3d74530..3f6c7e8 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3076,7 +3076,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) if (unlikely(p->len + len >= 65536)) return -E2BIG; - lp = NAPI_GRO_CB(p)->last ?: p; + lp = NAPI_GRO_CB(p)->last; pinfo = skb_shinfo(lp); if (headlen <= offset) { @@ -3192,7 +3192,7 @@ merge: __skb_pull(skb, offset); - if (!NAPI_GRO_CB(p)->last) + if (NAPI_GRO_CB(p)->last == p) skb_shinfo(p)->frag_list = skb; else NAPI_GRO_CB(p)->last->next = skb; diff --git a/net/core/sock.c b/net/core/sock.c index 664ee42..026e01f 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -784,7 +784,7 @@ set_rcvbuf: break; case SO_NO_CHECK: - sk->sk_no_check = valbool; + sk->sk_no_check_tx = valbool; break; case SO_PRIORITY: @@ -1064,7 +1064,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname, break; case SO_NO_CHECK: - v.val = sk->sk_no_check; + v.val = sk->sk_no_check_tx; break; case SO_PRIORITY: diff --git a/net/core/tso.c b/net/core/tso.c new file mode 100644 index 0000000..8c3203c --- /dev/null +++ b/net/core/tso.c @@ -0,0 +1,77 @@ +#include <linux/export.h> +#include <net/ip.h> +#include <net/tso.h> + +/* Calculate expected number of TX descriptors */ +int tso_count_descs(struct sk_buff *skb) +{ + /* The Marvell Way */ + return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags; +} +EXPORT_SYMBOL(tso_count_descs); + +void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, + int size, bool is_last) +{ + struct iphdr *iph; + struct tcphdr *tcph; + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int mac_hdr_len = skb_network_offset(skb); + + memcpy(hdr, skb->data, hdr_len); + iph = (struct iphdr *)(hdr + mac_hdr_len); + iph->id = htons(tso->ip_id); + iph->tot_len = htons(size + hdr_len - mac_hdr_len); + tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb)); + tcph->seq = htonl(tso->tcp_seq); + tso->ip_id++; + + if (!is_last) { + /* Clear all special flags for not last packet */ + tcph->psh = 0; + tcph->fin = 0; + tcph->rst = 0; + } +} +EXPORT_SYMBOL(tso_build_hdr); + +void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size) +{ + tso->tcp_seq += size; + tso->size -= size; + tso->data += size; + + if ((tso->size == 0) && + (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx]; + + /* Move to next segment */ + tso->size = frag->size; + tso->data = page_address(frag->page.p) + frag->page_offset; + tso->next_frag_idx++; + } +} +EXPORT_SYMBOL(tso_build_data); + +void tso_start(struct sk_buff *skb, struct tso_t *tso) +{ + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + + tso->ip_id = ntohs(ip_hdr(skb)->id); + tso->tcp_seq = ntohl(tcp_hdr(skb)->seq); + tso->next_frag_idx = 0; + + /* Build first data */ + tso->size = skb_headlen(skb) - hdr_len; + tso->data = skb->data + hdr_len; + if ((tso->size == 0) && + (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx]; + + /* Move to next segment */ + tso->size = frag->size; + tso->data = page_address(frag->page.p) + frag->page_offset; + tso->next_frag_idx++; + } +} +EXPORT_SYMBOL(tso_start); diff --git a/net/core/utils.c b/net/core/utils.c index 2f737bf..eed3433 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -348,8 +348,8 @@ static void __net_random_once_deferred(struct work_struct *w) { struct __net_random_once_work *work = container_of(w, struct __net_random_once_work, work); - if (!static_key_enabled(work->key)) - static_key_slow_inc(work->key); + BUG_ON(!static_key_enabled(work->key)); + static_key_slow_dec(work->key); kfree(work); } @@ -367,7 +367,7 @@ static void __net_random_once_disable_jump(struct static_key *key) } bool __net_get_random_once(void *buf, int nbytes, bool *done, - struct static_key *done_key) + struct static_key *once_key) { static DEFINE_SPINLOCK(lock); unsigned long flags; @@ -382,7 +382,7 @@ bool __net_get_random_once(void *buf, int nbytes, bool *done, *done = true; spin_unlock_irqrestore(&lock, flags); - __net_random_once_disable_jump(done_key); + __net_random_once_disable_jump(once_key); return true; } diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 22b5d81..6ca645c 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -1024,7 +1024,6 @@ static struct inet_protosw dccp_v4_protosw = { .protocol = IPPROTO_DCCP, .prot = &dccp_v4_prot, .ops = &inet_dccp_ops, - .no_check = 0, .flags = INET_PROTOSW_ICSK, }; diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 16f0b22..1cd46a3 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -280,7 +280,7 @@ static ktime_t dccp_timestamp_seed; */ u32 dccp_timestamp(void) { - s64 delta = ktime_us_delta(ktime_get_real(), dccp_timestamp_seed); + u64 delta = (u64)ktime_us_delta(ktime_get_real(), dccp_timestamp_seed); do_div(delta, 10); return delta; diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 4c04848..ae011b4 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -481,7 +481,7 @@ static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gf sk->sk_backlog_rcv = dn_nsp_backlog_rcv; sk->sk_destruct = dn_destruct; - sk->sk_no_check = 1; + sk->sk_no_check_tx = 1; sk->sk_family = PF_DECnet; sk->sk_protocol = 0; sk->sk_allocation = gfp; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 0eb5d5e..5db37ce 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -406,8 +406,9 @@ static int dsa_of_probe(struct platform_device *pdev) goto out_free; } - chip_index = 0; + chip_index = -1; for_each_available_child_of_node(np, child) { + chip_index++; cd = &pd->chip[chip_index]; cd->mii_bus = &mdio_bus->dev; diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 0f5a69e..fe6bd7a 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -92,6 +92,7 @@ static int lowpan_header_create(struct sk_buff *skb, const u8 *saddr = _saddr; const u8 *daddr = _daddr; struct ieee802154_addr sa, da; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); /* TODO: * if this package isn't ipv6 one, where should it be routed? @@ -115,8 +116,7 @@ static int lowpan_header_create(struct sk_buff *skb, * from MAC subif of the 'dev' and 'real_dev' network devices, but * this isn't implemented in mainline yet, so currently we assign 0xff */ - mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; - mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); + cb->type = IEEE802154_FC_TYPE_DATA; /* prepare wpan address data */ sa.mode = IEEE802154_ADDR_LONG; @@ -135,11 +135,10 @@ static int lowpan_header_create(struct sk_buff *skb, } else { da.mode = IEEE802154_ADDR_LONG; da.extended_addr = ieee802154_devaddr_from_raw(daddr); - - /* request acknowledgment */ - mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; } + cb->ackreq = !lowpan_is_addr_broadcast(daddr); + return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, type, (void *)&da, (void *)&sa, 0); } @@ -221,139 +220,149 @@ static int lowpan_set_address(struct net_device *dev, void *p) return 0; } -static int -lowpan_fragment_xmit(struct sk_buff *skb, u8 *head, - int mlen, int plen, int offset, int type) +static struct sk_buff* +lowpan_alloc_frag(struct sk_buff *skb, int size, + const struct ieee802154_hdr *master_hdr) { + struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; struct sk_buff *frag; - int hlen; - - hlen = (type == LOWPAN_DISPATCH_FRAG1) ? - LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE; - - raw_dump_inline(__func__, "6lowpan fragment header", head, hlen); + int rc; + + frag = alloc_skb(real_dev->hard_header_len + + real_dev->needed_tailroom + size, + GFP_ATOMIC); + + if (likely(frag)) { + frag->dev = real_dev; + frag->priority = skb->priority; + skb_reserve(frag, real_dev->hard_header_len); + skb_reset_network_header(frag); + *mac_cb(frag) = *mac_cb(skb); + + rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, + &master_hdr->source, size); + if (rc < 0) { + kfree_skb(frag); + return ERR_PTR(-rc); + } + } else { + frag = ERR_PTR(ENOMEM); + } - frag = netdev_alloc_skb(skb->dev, - hlen + mlen + plen + IEEE802154_MFR_SIZE); - if (!frag) - return -ENOMEM; + return frag; +} - frag->priority = skb->priority; +static int +lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, + u8 *frag_hdr, int frag_hdrlen, + int offset, int len) +{ + struct sk_buff *frag; - /* copy header, MFR and payload */ - skb_put(frag, mlen); - skb_copy_to_linear_data(frag, skb_mac_header(skb), mlen); + raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); - skb_put(frag, hlen); - skb_copy_to_linear_data_offset(frag, mlen, head, hlen); + frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); + if (IS_ERR(frag)) + return -PTR_ERR(frag); - skb_put(frag, plen); - skb_copy_to_linear_data_offset(frag, mlen + hlen, - skb_network_header(skb) + offset, plen); + memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); + memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); - raw_dump_table(__func__, " raw fragment dump", frag->data, frag->len); + raw_dump_table(__func__, " fragment dump", frag->data, frag->len); return dev_queue_xmit(frag); } static int -lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev) +lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, + const struct ieee802154_hdr *wpan_hdr) { - int err; - u16 dgram_offset, dgram_size, payload_length, header_length, - lowpan_size, frag_plen, offset; - __be16 tag; - u8 head[5]; - - header_length = skb->mac_len; - payload_length = skb->len - header_length; - tag = lowpan_dev_info(dev)->fragment_tag++; - lowpan_size = skb_network_header_len(skb); + u16 dgram_size, dgram_offset; + __be16 frag_tag; + u8 frag_hdr[5]; + int frag_cap, frag_len, payload_cap, rc; + int skb_unprocessed, skb_offset; + dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - - header_length; + skb->mac_len; + frag_tag = lowpan_dev_info(dev)->fragment_tag++; - /* first fragment header */ - head[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x7); - head[1] = dgram_size & 0xff; - memcpy(head + 2, &tag, sizeof(tag)); + frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); + frag_hdr[1] = dgram_size & 0xff; + memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); - /* calc the nearest payload length(divided to 8) for first fragment - * which fits into a IEEE802154_MTU - */ - frag_plen = round_down(IEEE802154_MTU - header_length - - LOWPAN_FRAG1_HEAD_SIZE - lowpan_size - - IEEE802154_MFR_SIZE, 8); - - err = lowpan_fragment_xmit(skb, head, header_length, - frag_plen + lowpan_size, 0, - LOWPAN_DISPATCH_FRAG1); - if (err) { - pr_debug("%s unable to send FRAG1 packet (tag: %d)", - __func__, tag); - goto exit; - } + payload_cap = ieee802154_max_payload(wpan_hdr); - offset = lowpan_size + frag_plen; - dgram_offset += frag_plen; + frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - + skb_network_header_len(skb), 8); - /* next fragment header */ - head[0] &= ~LOWPAN_DISPATCH_FRAG1; - head[0] |= LOWPAN_DISPATCH_FRAGN; + skb_offset = skb_network_header_len(skb); + skb_unprocessed = skb->len - skb->mac_len - skb_offset; - frag_plen = round_down(IEEE802154_MTU - header_length - - LOWPAN_FRAGN_HEAD_SIZE - IEEE802154_MFR_SIZE, 8); + rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, + LOWPAN_FRAG1_HEAD_SIZE, 0, + frag_len + skb_network_header_len(skb)); + if (rc) { + pr_debug("%s unable to send FRAG1 packet (tag: %d)", + __func__, frag_tag); + goto err; + } - while (payload_length - offset > 0) { - int len = frag_plen; + frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; + frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; + frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); - head[4] = dgram_offset >> 3; + do { + dgram_offset += frag_len; + skb_offset += frag_len; + skb_unprocessed -= frag_len; + frag_len = min(frag_cap, skb_unprocessed); - if (payload_length - offset < len) - len = payload_length - offset; + frag_hdr[4] = dgram_offset >> 3; - err = lowpan_fragment_xmit(skb, head, header_length, len, - offset, LOWPAN_DISPATCH_FRAGN); - if (err) { + rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, + LOWPAN_FRAGN_HEAD_SIZE, skb_offset, + frag_len); + if (rc) { pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", - __func__, tag, offset); - goto exit; + __func__, frag_tag, skb_offset); + goto err; } + } while (skb_unprocessed > frag_cap); - offset += len; - dgram_offset += len; - } + consume_skb(skb); + return NET_XMIT_SUCCESS; -exit: - return err; +err: + kfree_skb(skb); + return rc; } static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) { - int err = -1; + struct ieee802154_hdr wpan_hdr; + int max_single; pr_debug("package xmit\n"); - skb->dev = lowpan_dev_info(dev)->real_dev; - if (skb->dev == NULL) { - pr_debug("ERROR: no real wpan device found\n"); - goto error; + if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { + kfree_skb(skb); + return NET_XMIT_DROP; } - /* Send directly if less than the MTU minus the 2 checksum bytes. */ - if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) { - err = dev_queue_xmit(skb); - goto out; - } + max_single = ieee802154_max_payload(&wpan_hdr); - pr_debug("frame is too big, fragmentation is needed\n"); - err = lowpan_skb_fragmentation(skb, dev); -error: - dev_kfree_skb(skb); -out: - if (err) - pr_debug("ERROR: xmit failed\n"); + if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { + skb->dev = lowpan_dev_info(dev)->real_dev; + return dev_queue_xmit(skb); + } else { + netdev_tx_t rc; + + pr_debug("frame is too big, fragmentation is needed\n"); + rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); - return (err < 0) ? NET_XMIT_DROP : err; + return rc < 0 ? NET_XMIT_DROP : rc; + } } static struct wpan_phy *lowpan_get_phy(const struct net_device *dev) diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 786437b..4f0ed87 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -21,6 +21,7 @@ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> */ +#include <linux/capability.h> #include <linux/net.h> #include <linux/module.h> #include <linux/if_arp.h> @@ -45,7 +46,12 @@ struct dgram_sock { struct ieee802154_addr dst_addr; unsigned int bound:1; + unsigned int connected:1; unsigned int want_ack:1; + unsigned int secen:1; + unsigned int secen_override:1; + unsigned int seclevel:3; + unsigned int seclevel_override:1; }; static inline struct dgram_sock *dgram_sk(const struct sock *sk) @@ -73,10 +79,7 @@ static int dgram_init(struct sock *sk) { struct dgram_sock *ro = dgram_sk(sk); - ro->dst_addr.mode = IEEE802154_ADDR_LONG; - ro->dst_addr.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); ro->want_ack = 1; - memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN); return 0; } @@ -183,6 +186,7 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, } ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); + ro->connected = 1; out: release_sock(sk); @@ -194,10 +198,7 @@ static int dgram_disconnect(struct sock *sk, int flags) struct dgram_sock *ro = dgram_sk(sk); lock_sock(sk); - - ro->dst_addr.mode = IEEE802154_ADDR_LONG; - memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN); - + ro->connected = 0; release_sock(sk); return 0; @@ -209,7 +210,9 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, struct net_device *dev; unsigned int mtu; struct sk_buff *skb; + struct ieee802154_mac_cb *cb; struct dgram_sock *ro = dgram_sk(sk); + struct ieee802154_addr dst_addr; int hlen, tlen; int err; @@ -218,6 +221,11 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, return -EOPNOTSUPP; } + if (!ro->connected && !msg->msg_name) + return -EDESTADDRREQ; + else if (ro->connected && msg->msg_name) + return -EISCONN; + if (!ro->bound) dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154); else @@ -249,18 +257,28 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, skb_reset_network_header(skb); - mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; - if (ro->want_ack) - mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + cb = mac_cb_init(skb); + cb->type = IEEE802154_FC_TYPE_DATA; + cb->ackreq = ro->want_ack; + + if (msg->msg_name) { + DECLARE_SOCKADDR(struct sockaddr_ieee802154*, daddr, msg->msg_name); - mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); - err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr, - ro->bound ? &ro->src_addr : NULL, size); + ieee802154_addr_from_sa(&dst_addr, &daddr->addr); + } else { + dst_addr = ro->dst_addr; + } + + cb->secen = ro->secen; + cb->secen_override = ro->secen_override; + cb->seclevel = ro->seclevel; + cb->seclevel_override = ro->seclevel_override; + + err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, + ro->bound ? &ro->src_addr : NULL, size); if (err < 0) goto out_skb; - skb_reset_mac_header(skb); - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err < 0) goto out_skb; @@ -419,6 +437,20 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname, case WPAN_WANTACK: val = ro->want_ack; break; + case WPAN_SECURITY: + if (!ro->secen_override) + val = WPAN_SECURITY_DEFAULT; + else if (ro->secen) + val = WPAN_SECURITY_ON; + else + val = WPAN_SECURITY_OFF; + break; + case WPAN_SECURITY_LEVEL: + if (!ro->seclevel_override) + val = WPAN_SECURITY_LEVEL_DEFAULT; + else + val = ro->seclevel; + break; default: return -ENOPROTOOPT; } @@ -434,6 +466,7 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { struct dgram_sock *ro = dgram_sk(sk); + struct net *net = sock_net(sk); int val; int err = 0; @@ -449,6 +482,47 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname, case WPAN_WANTACK: ro->want_ack = !!val; break; + case WPAN_SECURITY: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + switch (val) { + case WPAN_SECURITY_DEFAULT: + ro->secen_override = 0; + break; + case WPAN_SECURITY_ON: + ro->secen_override = 1; + ro->secen = 1; + break; + case WPAN_SECURITY_OFF: + ro->secen_override = 1; + ro->secen = 0; + break; + default: + err = -EINVAL; + break; + } + break; + case WPAN_SECURITY_LEVEL: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + if (val < WPAN_SECURITY_LEVEL_DEFAULT || + val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { + err = -EINVAL; + } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { + ro->seclevel_override = 0; + } else { + ro->seclevel_override = 1; + ro->seclevel = val; + } + break; default: err = -ENOPROTOOPT; break; diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index bed42a4..c09294e 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -195,15 +195,16 @@ ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr) return pos; } +static int ieee802154_sechdr_lengths[4] = { + [IEEE802154_SCF_KEY_IMPLICIT] = 5, + [IEEE802154_SCF_KEY_INDEX] = 6, + [IEEE802154_SCF_KEY_SHORT_INDEX] = 10, + [IEEE802154_SCF_KEY_HW_INDEX] = 14, +}; + static int ieee802154_hdr_sechdr_len(u8 sc) { - switch (IEEE802154_SCF_KEY_ID_MODE(sc)) { - case IEEE802154_SCF_KEY_IMPLICIT: return 5; - case IEEE802154_SCF_KEY_INDEX: return 6; - case IEEE802154_SCF_KEY_SHORT_INDEX: return 10; - case IEEE802154_SCF_KEY_HW_INDEX: return 14; - default: return -EINVAL; - } + return ieee802154_sechdr_lengths[IEEE802154_SCF_KEY_ID_MODE(sc)]; } static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr) @@ -285,3 +286,40 @@ ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr) return pos; } EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs); + +int +ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ + const u8 *buf = skb_mac_header(skb); + int pos; + + pos = ieee802154_hdr_peek_addrs(skb, hdr); + if (pos < 0) + return -EINVAL; + + if (hdr->fc.security_enabled) { + u8 key_id_mode = IEEE802154_SCF_KEY_ID_MODE(*(buf + pos)); + int want = pos + ieee802154_sechdr_lengths[key_id_mode]; + + if (buf + want > skb_tail_pointer(skb)) + return -EINVAL; + + pos += ieee802154_hdr_get_sechdr(buf + pos, &hdr->sec); + } + + return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_peek); + +int ieee802154_max_payload(const struct ieee802154_hdr *hdr) +{ + int hlen = ieee802154_hdr_minlen(hdr); + + if (hdr->fc.security_enabled) { + hlen += ieee802154_sechdr_lengths[hdr->sec.key_id_mode] - 1; + hlen += ieee802154_sechdr_authtag_len(&hdr->sec); + } + + return IEEE802154_MTU - hlen - IEEE802154_MFR_SIZE; +} +EXPORT_SYMBOL_GPL(ieee802154_max_payload); diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index 6693a5c..8b83a23 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -68,4 +68,23 @@ int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info); int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb); int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_keys(struct sk_buff *skb, + struct netlink_callback *cb); +int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_devs(struct sk_buff *skb, + struct netlink_callback *cb); +int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, + struct netlink_callback *cb); +int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_seclevels(struct sk_buff *skb, + struct netlink_callback *cb); + #endif diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 04b2058..26efcf4 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -124,6 +124,26 @@ static const struct genl_ops ieee8021154_ops[] = { IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, ieee802154_dump_iface), IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), + IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams), + IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL, + ieee802154_llsec_dump_keys), + IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), + IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL, + ieee802154_llsec_dump_devs), + IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), + IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL, + ieee802154_llsec_dump_devkeys), + IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), + IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, + ieee802154_llsec_dump_seclevels), + IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, + ieee802154_llsec_add_seclevel), + IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, + ieee802154_llsec_del_seclevel), }; static const struct genl_multicast_group ieee802154_mcgrps[] = { diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 5d28549..a3281b8 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -715,3 +715,812 @@ out: dev_put(dev); return rc; } + + + +static int +ieee802154_llsec_parse_key_id(struct genl_info *info, + struct ieee802154_llsec_key_id *desc) +{ + memset(desc, 0, sizeof(*desc)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) + return -EINVAL; + + desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]); + + if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) { + if (!info->attrs[IEEE802154_ATTR_PAN_ID] && + !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] || + info->attrs[IEEE802154_ATTR_HW_ADDR])) + return -EINVAL; + + desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]); + + if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) { + desc->device_addr.mode = IEEE802154_ADDR_SHORT; + desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]); + } else { + desc->device_addr.mode = IEEE802154_ADDR_LONG; + desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + } + } + + if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]) + return -EINVAL; + + if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]) + return -EINVAL; + + if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]) + return -EINVAL; + + if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT) + desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]); + + switch (desc->mode) { + case IEEE802154_SCF_KEY_SHORT_INDEX: + { + u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]); + desc->short_source = cpu_to_le32(source); + break; + } + case IEEE802154_SCF_KEY_HW_INDEX: + desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]); + break; + } + + return 0; +} + +static int +ieee802154_llsec_fill_key_id(struct sk_buff *msg, + const struct ieee802154_llsec_key_id *desc) +{ + if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode)) + return -EMSGSIZE; + + if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) { + if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, + desc->device_addr.pan_id)) + return -EMSGSIZE; + + if (desc->device_addr.mode == IEEE802154_ADDR_SHORT && + nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, + desc->device_addr.short_addr)) + return -EMSGSIZE; + + if (desc->device_addr.mode == IEEE802154_ADDR_LONG && + nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, + desc->device_addr.extended_addr)) + return -EMSGSIZE; + } + + if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT && + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id)) + return -EMSGSIZE; + + if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX && + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT, + le32_to_cpu(desc->short_source))) + return -EMSGSIZE; + + if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX && + nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED, + desc->extended_source)) + return -EMSGSIZE; + + return 0; +} + +int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct net_device *dev = NULL; + int rc = -ENOBUFS; + struct ieee802154_mlme_ops *ops; + void *hdr; + struct ieee802154_llsec_params params; + + pr_debug("%s\n", __func__); + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + ops = ieee802154_mlme_ops(dev); + if (!ops->llsec) { + rc = -EOPNOTSUPP; + goto out_dev; + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + goto out_dev; + + hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0, + IEEE802154_LLSEC_GETPARAMS); + if (!hdr) + goto out_free; + + rc = ops->llsec->get_params(dev, ¶ms); + if (rc < 0) + goto out_free; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) || + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + be32_to_cpu(params.frame_counter)) || + ieee802154_llsec_fill_key_id(msg, ¶ms.out_key)) + goto out_free; + + dev_put(dev); + + return ieee802154_nl_reply(msg, info); +out_free: + nlmsg_free(msg); +out_dev: + dev_put(dev); + return rc; +} + +int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device *dev = NULL; + int rc = -EINVAL; + struct ieee802154_mlme_ops *ops; + struct ieee802154_llsec_params params; + int changed = 0; + + pr_debug("%s\n", __func__); + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] && + !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) + goto out; + + ops = ieee802154_mlme_ops(dev); + if (!ops->llsec) { + rc = -EOPNOTSUPP; + goto out; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] && + nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7) + goto out; + + if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) { + params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]); + changed |= IEEE802154_LLSEC_PARAM_ENABLED; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) { + if (ieee802154_llsec_parse_key_id(info, ¶ms.out_key)) + goto out; + + changed |= IEEE802154_LLSEC_PARAM_OUT_KEY; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) { + params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]); + changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) { + u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + + params.frame_counter = cpu_to_be32(fc); + changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER; + } + + rc = ops->llsec->set_params(dev, ¶ms, changed); + + dev_put(dev); + + return rc; +out: + dev_put(dev); + return rc; +} + + + +struct llsec_dump_data { + struct sk_buff *skb; + int s_idx, s_idx2; + int portid; + int nlmsg_seq; + struct net_device *dev; + struct ieee802154_mlme_ops *ops; + struct ieee802154_llsec_table *table; +}; + +static int +ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb, + int (*step)(struct llsec_dump_data*)) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct llsec_dump_data data; + int idx = 0; + int first_dev = cb->args[0]; + int rc; + + for_each_netdev(net, dev) { + if (idx < first_dev || dev->type != ARPHRD_IEEE802154) + goto skip; + + data.ops = ieee802154_mlme_ops(dev); + if (!data.ops->llsec) + goto skip; + + data.skb = skb; + data.s_idx = cb->args[1]; + data.s_idx2 = cb->args[2]; + data.dev = dev; + data.portid = NETLINK_CB(cb->skb).portid; + data.nlmsg_seq = cb->nlh->nlmsg_seq; + + data.ops->llsec->lock_table(dev); + data.ops->llsec->get_table(data.dev, &data.table); + rc = step(&data); + data.ops->llsec->unlock_table(dev); + + if (rc < 0) + break; + +skip: + idx++; + } + cb->args[0] = idx; + + return skb->len; +} + +static int +ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info, + int (*fn)(struct net_device*, struct genl_info*)) +{ + struct net_device *dev = NULL; + int rc = -EINVAL; + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + if (!ieee802154_mlme_ops(dev)->llsec) + rc = -EOPNOTSUPP; + else + rc = fn(dev, info); + + dev_put(dev); + return rc; +} + + + +static int +ieee802154_llsec_parse_key(struct genl_info *info, + struct ieee802154_llsec_key *key) +{ + u8 frames; + u32 commands[256 / 32]; + + memset(key, 0, sizeof(*key)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] || + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES]) + return -EINVAL; + + frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]); + if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) + return -EINVAL; + + if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) { + nla_memcpy(commands, + info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS], + 256 / 8); + + if (commands[0] || commands[1] || commands[2] || commands[3] || + commands[4] || commands[5] || commands[6] || + commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1)) + return -EINVAL; + + key->cmd_frame_ids = commands[7]; + } + + key->frame_types = frames; + + nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES], + IEEE802154_LLSEC_KEY_SIZE); + + return 0; +} + +static int llsec_add_key(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_key key; + struct ieee802154_llsec_key_id id; + + if (ieee802154_llsec_parse_key(info, &key) || + ieee802154_llsec_parse_key_id(info, &id)) + return -EINVAL; + + return ops->llsec->add_key(dev, &id, &key); +} + +int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_key); +} + +static int llsec_remove_key(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_key_id id; + + if (ieee802154_llsec_parse_key_id(info, &id)) + return -EINVAL; + + return ops->llsec->del_key(dev, &id); +} + +int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_remove_key); +} + +static int +ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq, + const struct ieee802154_llsec_key_entry *key, + const struct net_device *dev) +{ + void *hdr; + u32 commands[256 / 32]; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_KEY); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + ieee802154_llsec_fill_key_id(msg, &key->id) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES, + key->key->frame_types)) + goto nla_put_failure; + + if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) { + memset(commands, 0, sizeof(commands)); + commands[7] = key->key->cmd_frame_ids; + if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, + sizeof(commands), commands)) + goto nla_put_failure; + } + + if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES, + IEEE802154_LLSEC_KEY_SIZE, key->key->key)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_keys(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_key_entry *pos; + int rc = 0, idx = 0; + + list_for_each_entry(pos, &data->table->keys, list) { + if (idx++ < data->s_idx) + continue; + + if (ieee802154_nl_fill_key(data->skb, data->portid, + data->nlmsg_seq, pos, data->dev)) { + rc = -EMSGSIZE; + break; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys); +} + + + +static int +llsec_parse_dev(struct genl_info *info, + struct ieee802154_llsec_device *dev) +{ + memset(dev, 0, sizeof(*dev)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] || + !info->attrs[IEEE802154_ATTR_HW_ADDR] || + !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] || + !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] || + (!!info->attrs[IEEE802154_ATTR_PAN_ID] != + !!info->attrs[IEEE802154_ATTR_SHORT_ADDR])) + return -EINVAL; + + if (info->attrs[IEEE802154_ATTR_PAN_ID]) { + dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]); + dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]); + } else { + dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF); + } + + dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]); + dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]); + + if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX) + return -EINVAL; + + return 0; +} + +static int llsec_add_dev(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_device desc; + + if (llsec_parse_dev(info, &desc)) + return -EINVAL; + + return ops->llsec->add_dev(dev, &desc); +} + +int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_dev); +} + +static int llsec_del_dev(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + __le64 devaddr; + + if (!info->attrs[IEEE802154_ATTR_HW_ADDR]) + return -EINVAL; + + devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + + return ops->llsec->del_dev(dev, devaddr); +} + +int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_del_dev); +} + +static int +ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq, + const struct ieee802154_llsec_device *desc, + const struct net_device *dev) +{ + void *hdr; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_DEV); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) || + nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, + desc->short_addr) || + nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) || + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + desc->frame_counter) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, + desc->seclevel_exempt) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_devs(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_device *pos; + int rc = 0, idx = 0; + + list_for_each_entry(pos, &data->table->devices, list) { + if (idx++ < data->s_idx) + continue; + + if (ieee802154_nl_fill_dev(data->skb, data->portid, + data->nlmsg_seq, pos, data->dev)) { + rc = -EMSGSIZE; + break; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs); +} + + + +static int llsec_add_devkey(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_device_key key; + __le64 devaddr; + + if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] || + !info->attrs[IEEE802154_ATTR_HW_ADDR] || + ieee802154_llsec_parse_key_id(info, &key.key_id)) + return -EINVAL; + + devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + + return ops->llsec->add_devkey(dev, devaddr, &key); +} + +int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey); +} + +static int llsec_del_devkey(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_device_key key; + __le64 devaddr; + + if (!info->attrs[IEEE802154_ATTR_HW_ADDR] || + ieee802154_llsec_parse_key_id(info, &key.key_id)) + return -EINVAL; + + devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + + return ops->llsec->del_devkey(dev, devaddr, &key); +} + +int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey); +} + +static int +ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq, + __le64 devaddr, + const struct ieee802154_llsec_device_key *devkey, + const struct net_device *dev) +{ + void *hdr; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_DEVKEY); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) || + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + devkey->frame_counter) || + ieee802154_llsec_fill_key_id(msg, &devkey->key_id)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_devkeys(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_device *dpos; + struct ieee802154_llsec_device_key *kpos; + int rc = 0, idx = 0, idx2; + + list_for_each_entry(dpos, &data->table->devices, list) { + if (idx++ < data->s_idx) + continue; + + idx2 = 0; + + list_for_each_entry(kpos, &dpos->keys, list) { + if (idx2++ < data->s_idx2) + continue; + + if (ieee802154_nl_fill_devkey(data->skb, data->portid, + data->nlmsg_seq, + dpos->hwaddr, kpos, + data->dev)) { + return rc = -EMSGSIZE; + } + + data->s_idx2++; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, + struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys); +} + + + +static int +llsec_parse_seclevel(struct genl_info *info, + struct ieee802154_llsec_seclevel *sl) +{ + memset(sl, 0, sizeof(*sl)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] || + !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] || + !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]) + return -EINVAL; + + sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]); + if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) { + if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]) + return -EINVAL; + + sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]); + } + + sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]); + sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]); + + return 0; +} + +static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_seclevel sl; + + if (llsec_parse_seclevel(info, &sl)) + return -EINVAL; + + return ops->llsec->add_seclevel(dev, &sl); +} + +int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel); +} + +static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_seclevel sl; + + if (llsec_parse_seclevel(info, &sl)) + return -EINVAL; + + return ops->llsec->del_seclevel(dev, &sl); +} + +int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel); +} + +static int +ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq, + const struct ieee802154_llsec_seclevel *sl, + const struct net_device *dev) +{ + void *hdr; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_SECLEVEL); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, + sl->device_override)) + goto nla_put_failure; + + if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD && + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID, + sl->cmd_frame_id)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_seclevels(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_seclevel *pos; + int rc = 0, idx = 0; + + list_for_each_entry(pos, &data->table->security_levels, list) { + if (idx++ < data->s_idx) + continue; + + if (ieee802154_nl_fill_seclevel(data->skb, data->portid, + data->nlmsg_seq, pos, + data->dev)) { + rc = -EMSGSIZE; + break; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_seclevels(struct sk_buff *skb, + struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels); +} diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index fd7be5e..3a703ab8 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -62,5 +62,21 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, }, [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, }, + + [IEEE802154_ATTR_LLSEC_ENABLED] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_SECLEVEL] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_KEY_MODE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT] = { .type = NLA_U32, }, + [IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED] = { .type = NLA_HW_ADDR, }, + [IEEE802154_ATTR_LLSEC_KEY_ID] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_FRAME_COUNTER] = { .type = NLA_U32 }, + [IEEE802154_ATTR_LLSEC_KEY_BYTES] = { .len = 16, }, + [IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS] = { .len = 258 / 8 }, + [IEEE802154_ATTR_LLSEC_FRAME_TYPE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_CMD_FRAME_ID] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_SECLEVELS] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] = { .type = NLA_U8, }, }; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 279132b..0e9bb08 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -254,7 +254,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, struct inet_sock *inet; struct proto *answer_prot; unsigned char answer_flags; - char answer_no_check; int try_loading_module = 0; int err; @@ -312,7 +311,6 @@ lookup_protocol: sock->ops = answer->ops; answer_prot = answer->prot; - answer_no_check = answer->no_check; answer_flags = answer->flags; rcu_read_unlock(); @@ -324,7 +322,6 @@ lookup_protocol: goto out; err = 0; - sk->sk_no_check = answer_no_check; if (INET_PROTOSW_REUSE & answer_flags) sk->sk_reuse = SK_CAN_REUSE; @@ -1002,7 +999,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_TCP, .prot = &tcp_prot, .ops = &inet_stream_ops, - .no_check = 0, .flags = INET_PROTOSW_PERMANENT | INET_PROTOSW_ICSK, }, @@ -1012,7 +1008,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_UDP, .prot = &udp_prot, .ops = &inet_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_PERMANENT, }, @@ -1021,7 +1016,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_ICMP, .prot = &ping_prot, .ops = &inet_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, }, @@ -1030,7 +1024,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_IP, /* wild card */ .prot = &raw_prot, .ops = &inet_sockraw_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, } }; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 78692e4..e944937 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -827,7 +827,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) ifa_existing = find_matching_ifa(ifa); if (!ifa_existing) { /* It would be best to check for !NLM_F_CREATE here but - * userspace alreay relies on not having to provide this. + * userspace already relies on not having to provide this. */ set_ifa_lifetime(ifa, valid_lft, prefered_lft); return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 5afeb5a..243c7f4 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2166,7 +2166,7 @@ static void fib_table_print(struct seq_file *seq, struct fib_table *tb) static int fib_triestat_seq_show(struct seq_file *seq, void *v) { - struct net *net = (struct net *)seq->private; + struct net *net = seq_file_net(seq); unsigned int h; seq_printf(seq, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 17d34e3..6748d42 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -369,7 +369,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) pip->saddr = fl4.saddr; pip->protocol = IPPROTO_IGMP; pip->tot_len = 0; /* filled in later */ - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(skb, NULL); ((u8 *)&pip[1])[0] = IPOPT_RA; ((u8 *)&pip[1])[1] = 4; ((u8 *)&pip[1])[2] = 0; @@ -714,7 +714,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, iph->daddr = dst; iph->saddr = fl4.saddr; iph->protocol = IPPROTO_IGMP; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(skb, NULL); ((u8 *)&iph[1])[0] = IPOPT_RA; ((u8 *)&iph[1])[1] = 4; ((u8 *)&iph[1])[2] = 0; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index c98cf14..4ced1b9 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -26,20 +26,7 @@ * Theory of operations. * We keep one entry for each peer IP address. The nodes contains long-living * information about the peer which doesn't depend on routes. - * At this moment this information consists only of ID field for the next - * outgoing IP packet. This field is incremented with each packet as encoded - * in inet_getid() function (include/net/inetpeer.h). - * At the moment of writing this notes identifier of IP packets is generated - * to be unpredictable using this code only for packets subjected - * (actually or potentially) to defragmentation. I.e. DF packets less than - * PMTU in size when local fragmentation is disabled use a constant ID and do - * not use this code (see ip_select_ident() in include/net/ip.h). * - * Route cache entries hold references to our nodes. - * New cache entries get references via lookup by destination IP address in - * the avl tree. The reference is grabbed only when it's needed i.e. only - * when we try to output IP packet which needs an unpredictable ID (see - * __ip_select_ident() in net/ipv4/route.c). * Nodes are removed only when reference counter goes to 0. * When it's happened the node may be removed when a sufficient amount of * time has been passed since its last use. The less-recently-used entry can @@ -62,7 +49,6 @@ * refcnt: atomically against modifications on other CPU; * usually under some other lock to prevent node disappearing * daddr: unchangeable - * ip_id_count: atomic value (no lock needed) */ static struct kmem_cache *peer_cachep __read_mostly; @@ -497,10 +483,6 @@ relookup: p->daddr = *daddr; atomic_set(&p->refcnt, 1); atomic_set(&p->rid, 0); - atomic_set(&p->ip_id_count, - (daddr->family == AF_INET) ? - secure_ip_id(daddr->addr.a4) : - secure_ipv6_id(daddr->addr.a6)); p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; p->rate_tokens = 0; /* 60*HZ is arbitrary, but chosen enough high so that the first diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 6e231ab..8d3b6b0 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr); iph->saddr = saddr; iph->protocol = sk->sk_protocol; - ip_select_ident(skb, &rt->dst, sk); + ip_select_ident(skb, sk); if (opt && opt->opt.optlen) { iph->ihl += opt->opt.optlen>>2; @@ -430,8 +430,7 @@ packet_routed: ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); } - ip_select_ident_more(skb, &rt->dst, sk, - (skb_shinfo(skb)->gso_segs ?: 1) - 1); + ip_select_ident_segs(skb, sk, skb_shinfo(skb)->gso_segs ?: 1); /* TODO : should we use skb->sk here instead of sk ? */ skb->priority = sk->sk_priority; @@ -1379,7 +1378,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph->ttl = ttl; iph->protocol = sk->sk_protocol; ip_copy_addrs(iph, fl4); - ip_select_ident(skb, &rt->dst, sk); + ip_select_ident(skb, sk); if (opt) { iph->ihl += opt->optlen>>2; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index b3f8597..86a00bd 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -395,11 +395,10 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net, struct ip_tunnel_net *itn, struct ip_tunnel_parm *parms) { - struct ip_tunnel *nt, *fbt; + struct ip_tunnel *nt; struct net_device *dev; BUG_ON(!itn->fb_tunnel_dev); - fbt = netdev_priv(itn->fb_tunnel_dev); dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms); if (IS_ERR(dev)) return ERR_CAST(dev); @@ -540,9 +539,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, unsigned int max_headroom; /* The extra header space needed */ __be32 dst; int err; - bool connected = true; + bool connected; inner_iph = (const struct iphdr *)skb_inner_network_header(skb); + connected = (tunnel->parms.iph.daddr != 0); dst = tnl_params->daddr; if (dst == 0) { @@ -755,10 +755,8 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd) if (!t && (cmd == SIOCADDTUNNEL)) { t = ip_tunnel_create(net, itn, p); - if (IS_ERR(t)) { - err = PTR_ERR(t); - break; - } + err = PTR_ERR_OR_ZERO(t); + break; } if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) { if (t != NULL) { @@ -882,6 +880,7 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, */ if (!IS_ERR(itn->fb_tunnel_dev)) { itn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; + itn->fb_tunnel_dev->mtu = ip_tunnel_bind_dev(itn->fb_tunnel_dev); ip_tunnel_add(itn, netdev_priv(itn->fb_tunnel_dev)); } rtnl_unlock(); diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index bcf206c..847e69c 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -74,7 +74,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, iph->daddr = dst; iph->saddr = src; iph->ttl = ttl; - __ip_select_ident(iph, &rt->dst, (skb_shinfo(skb)->gso_segs ?: 1) - 1); + __ip_select_ident(iph, skb_shinfo(skb)->gso_segs ?: 1); err = ip_local_out_sk(sk, skb); if (unlikely(net_xmit_eval(err))) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index afcee51..13ef00f 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -239,6 +239,7 @@ static netdev_tx_t vti_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) static int vti4_err(struct sk_buff *skb, u32 info) { __be32 spi; + __u32 mark; struct xfrm_state *x; struct ip_tunnel *tunnel; struct ip_esp_hdr *esph; @@ -254,6 +255,8 @@ static int vti4_err(struct sk_buff *skb, u32 info) if (!tunnel) return -1; + mark = be32_to_cpu(tunnel->parms.o_key); + switch (protocol) { case IPPROTO_ESP: esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); @@ -281,7 +284,7 @@ static int vti4_err(struct sk_buff *skb, u32 info) return 0; } - x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + x = xfrm_state_lookup(net, mark, (const xfrm_address_t *)&iph->daddr, spi, protocol, AF_INET); if (!x) return 0; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 812b183..4bc508f 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -486,4 +486,5 @@ static void __exit ipip_fini(void) module_init(ipip_init); module_exit(ipip_fini); MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("ipip"); MODULE_ALIAS_NETDEV("tunl0"); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index d84dc8d..65bcaa7 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -484,7 +484,7 @@ static void reg_vif_setup(struct net_device *dev) dev->type = ARPHRD_PIMREG; dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; dev->flags = IFF_NOARP; - dev->netdev_ops = ®_vif_netdev_ops, + dev->netdev_ops = ®_vif_netdev_ops; dev->destructor = free_netdev; dev->features |= NETIF_F_NETNS_LOCAL; } @@ -1663,7 +1663,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) iph->protocol = IPPROTO_IPIP; iph->ihl = 5; iph->tot_len = htons(skb->len); - ip_select_ident(skb, skb_dst(skb), NULL); + ip_select_ident(skb, NULL); ip_send_check(iph); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index ee28861..f1787c0 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -91,17 +91,9 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, if (nf_ct_is_untracked(ct)) return NF_ACCEPT; - nat = nfct_nat(ct); - if (!nat) { - /* NAT module was loaded late. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index b5b256d..3964157 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -48,15 +48,9 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET))); - nat = nfct_nat(ct); - if (nat == NULL) { - /* Conntrack module was loaded late, can't add extension. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) - return NF_ACCEPT; - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index a9dbe58..2c65160 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -389,7 +389,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, iph->check = 0; iph->tot_len = htons(length); if (!iph->id) - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(skb, NULL); iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 50e1e0f..082239f 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -89,6 +89,7 @@ #include <linux/rcupdate.h> #include <linux/times.h> #include <linux/slab.h> +#include <linux/jhash.h> #include <net/dst.h> #include <net/net_namespace.h> #include <net/protocol.h> @@ -456,39 +457,19 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, return neigh_create(&arp_tbl, pkey, dev); } -/* - * Peer allocation may fail only in serious out-of-memory conditions. However - * we still can generate some output. - * Random ID selection looks a bit dangerous because we have no chances to - * select ID being unique in a reasonable period of time. - * But broken packet identifier may be better than no packet at all. - */ -static void ip_select_fb_ident(struct iphdr *iph) -{ - static DEFINE_SPINLOCK(ip_fb_id_lock); - static u32 ip_fallback_id; - u32 salt; +atomic_t *ip_idents __read_mostly; +EXPORT_SYMBOL(ip_idents); - spin_lock_bh(&ip_fb_id_lock); - salt = secure_ip_id((__force __be32)ip_fallback_id ^ iph->daddr); - iph->id = htons(salt & 0xFFFF); - ip_fallback_id = salt; - spin_unlock_bh(&ip_fb_id_lock); -} - -void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more) +void __ip_select_ident(struct iphdr *iph, int segs) { - struct net *net = dev_net(dst->dev); - struct inet_peer *peer; + static u32 ip_idents_hashrnd __read_mostly; + u32 hash, id; - peer = inet_getpeer_v4(net->ipv4.peers, iph->daddr, 1); - if (peer) { - iph->id = htons(inet_getid(peer, more)); - inet_putpeer(peer); - return; - } + net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); - ip_select_fb_ident(iph); + hash = jhash_1word((__force u32)iph->daddr, ip_idents_hashrnd); + id = ip_idents_reserve(hash, segs); + iph->id = htons(id); } EXPORT_SYMBOL(__ip_select_ident); @@ -1526,7 +1507,7 @@ static int __mkroute_input(struct sk_buff *skb, struct in_device *out_dev; unsigned int flags = 0; bool do_cache; - u32 itag; + u32 itag = 0; /* get a working reference to the output device */ out_dev = __in_dev_get_rcu(FIB_RES_DEV(*res)); @@ -2711,6 +2692,12 @@ int __init ip_rt_init(void) { int rc = 0; + ip_idents = kmalloc(IP_IDENTS_SZ * sizeof(*ip_idents), GFP_KERNEL); + if (!ip_idents) + panic("IP: failed to allocate ip_idents\n"); + + prandom_bytes(ip_idents, IP_IDENTS_SZ * sizeof(*ip_idents)); + #ifdef CONFIG_IP_ROUTE_CLASSID ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct)); if (!ip_rt_acct) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3d61c52..d463c35 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1402,11 +1402,19 @@ static void tcp_cwnd_application_limited(struct sock *sk) tp->snd_cwnd_stamp = tcp_time_stamp; } -static void tcp_cwnd_validate(struct sock *sk, u32 unsent_segs) +static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) { struct tcp_sock *tp = tcp_sk(sk); - tp->lsnd_pending = tp->packets_out + unsent_segs; + /* Track the maximum number of outstanding packets in each + * window, and remember whether we were cwnd-limited then. + */ + if (!before(tp->snd_una, tp->max_packets_seq) || + tp->packets_out > tp->max_packets_out) { + tp->max_packets_out = tp->packets_out; + tp->max_packets_seq = tp->snd_nxt; + tp->is_cwnd_limited = is_cwnd_limited; + } if (tcp_is_cwnd_limited(sk)) { /* Network is feed fully. */ @@ -1660,7 +1668,8 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, * * This algorithm is from John Heffner. */ -static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) +static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, + bool *is_cwnd_limited) { struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); @@ -1724,6 +1733,9 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) if (!tp->tso_deferred) tp->tso_deferred = 1 | (jiffies << 1); + if (cong_win < send_win && cong_win < skb->len) + *is_cwnd_limited = true; + return true; send_now: @@ -1881,9 +1893,10 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - unsigned int tso_segs, sent_pkts, unsent_segs = 0; + unsigned int tso_segs, sent_pkts; int cwnd_quota; int result; + bool is_cwnd_limited = false; sent_pkts = 0; @@ -1908,6 +1921,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, cwnd_quota = tcp_cwnd_test(tp, skb); if (!cwnd_quota) { + is_cwnd_limited = true; if (push_one == 2) /* Force out a loss probe pkt. */ cwnd_quota = 1; @@ -1924,8 +1938,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, nonagle : TCP_NAGLE_PUSH)))) break; } else { - if (!push_one && tcp_tso_should_defer(sk, skb)) - goto compute_unsent_segs; + if (!push_one && + tcp_tso_should_defer(sk, skb, &is_cwnd_limited)) + break; } /* TCP Small Queues : @@ -1950,14 +1965,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, * there is no smp_mb__after_set_bit() yet */ smp_mb__after_clear_bit(); - if (atomic_read(&sk->sk_wmem_alloc) > limit) { - u32 unsent_bytes; - -compute_unsent_segs: - unsent_bytes = tp->write_seq - tp->snd_nxt; - unsent_segs = DIV_ROUND_UP(unsent_bytes, mss_now); + if (atomic_read(&sk->sk_wmem_alloc) > limit) break; - } } limit = mss_now; @@ -1997,7 +2006,7 @@ repair: /* Send one loss probe per tail loss episode. */ if (push_one != 2) tcp_schedule_loss_probe(sk); - tcp_cwnd_validate(sk, unsent_segs); + tcp_cwnd_validate(sk, is_cwnd_limited); return false; } return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk)); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 590532a..e07d52b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -785,7 +785,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) if (is_udplite) /* UDP-Lite */ csum = udplite_csum(skb); - else if (sk->sk_no_check == UDP_CSUM_NOXMIT) { /* UDP csum disabled */ + else if (sk->sk_no_check_tx) { /* UDP csum disabled */ skb->ip_summed = CHECKSUM_NONE; goto send; @@ -1968,7 +1968,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, int (*push_pending_frames)(struct sock *)) { struct udp_sock *up = udp_sk(sk); - int val; + int val, valbool; int err = 0; int is_udplite = IS_UDPLITE(sk); @@ -1978,6 +1978,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, if (get_user(val, (int __user *)optval)) return -EFAULT; + valbool = val ? 1 : 0; + switch (optname) { case UDP_CORK: if (val != 0) { @@ -2007,6 +2009,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, } break; + case UDP_NO_CHECK6_TX: + up->no_check6_tx = valbool; + break; + + case UDP_NO_CHECK6_RX: + up->no_check6_rx = valbool; + break; + /* * UDP-Lite's partial checksum coverage (RFC 3828). */ @@ -2089,6 +2099,14 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, val = up->encap_type; break; + case UDP_NO_CHECK6_TX: + val = up->no_check6_tx; + break; + + case UDP_NO_CHECK6_RX: + val = up->no_check6_rx; + break; + /* The following two cannot be changed on UDP sockets, the return is * always 0 (which corresponds to the full checksum coverage of UDP). */ case UDPLITE_SEND_CSCOV: diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 2c46acd..3b3efbd 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -70,7 +70,6 @@ static struct inet_protosw udplite4_protosw = { .protocol = IPPROTO_UDPLITE, .prot = &udplite_prot, .ops = &inet_dgram_ops, - .no_check = 0, /* must checksum (RFC 3828) */ .flags = INET_PROTOSW_PERMANENT, }; diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index 05f2b48..91771a7 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -58,12 +58,12 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? 0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF)); - ip_select_ident(skb, dst->child, NULL); top_iph->ttl = ip4_dst_hoplimit(dst->child); top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; + ip_select_ident(skb, NULL); return 0; } diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 8e8c018..d5f6bd9 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -62,10 +62,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) if (err) return err; - memset(IPCB(skb), 0, sizeof(*IPCB(skb))); - IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED; - - skb->protocol = htons(ETH_P_IP); + IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; return x->outer_mode->output2(x, skb); } @@ -73,27 +70,34 @@ EXPORT_SYMBOL(xfrm4_prepare_output); int xfrm4_output_finish(struct sk_buff *skb) { + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + skb->protocol = htons(ETH_P_IP); + +#ifdef CONFIG_NETFILTER + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; +#endif + + return xfrm_output(skb); +} + +static int __xfrm4_output(struct sk_buff *skb) +{ + struct xfrm_state *x = skb_dst(skb)->xfrm; + #ifdef CONFIG_NETFILTER - if (!skb_dst(skb)->xfrm) { + if (!x) { IPCB(skb)->flags |= IPSKB_REROUTED; return dst_output(skb); } - - IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; #endif - skb->protocol = htons(ETH_P_IP); - return xfrm_output(skb); + return x->outer_mode->afinfo->output_finish(skb); } int xfrm4_output(struct sock *sk, struct sk_buff *skb) { - struct dst_entry *dst = skb_dst(skb); - struct xfrm_state *x = dst->xfrm; - return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, - NULL, dst->dev, - x->outer_mode->afinfo->output_finish, + NULL, skb_dst(skb)->dev, __xfrm4_output, !(IPCB(skb)->flags & IPSKB_REROUTED)); } diff --git a/net/ipv4/xfrm4_protocol.c b/net/ipv4/xfrm4_protocol.c index 7f7b243..a2ce010 100644 --- a/net/ipv4/xfrm4_protocol.c +++ b/net/ipv4/xfrm4_protocol.c @@ -50,8 +50,12 @@ int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err) { int ret; struct xfrm4_protocol *handler; + struct xfrm4_protocol __rcu **head = proto_handlers(protocol); - for_each_protocol_rcu(*proto_handlers(protocol), handler) + if (!head) + return 0; + + for_each_protocol_rcu(*head, handler) if ((ret = handler->cb_handler(skb, err)) <= 0) return ret; @@ -64,15 +68,20 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, { int ret; struct xfrm4_protocol *handler; + struct xfrm4_protocol __rcu **head = proto_handlers(nexthdr); XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; XFRM_SPI_SKB_CB(skb)->family = AF_INET; XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); - for_each_protocol_rcu(*proto_handlers(nexthdr), handler) + if (!head) + goto out; + + for_each_protocol_rcu(*head, handler) if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL) return ret; +out: icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); kfree_skb(skb); @@ -208,6 +217,9 @@ int xfrm4_protocol_register(struct xfrm4_protocol *handler, int ret = -EEXIST; int priority = handler->priority; + if (!proto_handlers(protocol) || !netproto(protocol)) + return -EINVAL; + mutex_lock(&xfrm4_protocol_mutex); if (!rcu_dereference_protected(*proto_handlers(protocol), @@ -250,6 +262,9 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, struct xfrm4_protocol *t; int ret = -ENOENT; + if (!proto_handlers(protocol) || !netproto(protocol)) + return -EINVAL; + mutex_lock(&xfrm4_protocol_mutex); for (pprev = proto_handlers(protocol); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index dc47cc7..7cb4392 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -106,7 +106,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, struct inet_protosw *answer; struct proto *answer_prot; unsigned char answer_flags; - char answer_no_check; int try_loading_module = 0; int err; @@ -162,7 +161,6 @@ lookup_protocol: sock->ops = answer->ops; answer_prot = answer->prot; - answer_no_check = answer->no_check; answer_flags = answer->flags; rcu_read_unlock(); @@ -176,7 +174,6 @@ lookup_protocol: sock_init_data(sock, sk); err = 0; - sk->sk_no_check = answer_no_check; if (INET_PROTOSW_REUSE & answer_flags) sk->sk_reuse = SK_CAN_REUSE; diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 59f95af..b2f0915 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -196,7 +196,6 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, unsigned int off; u16 flush = 1; int proto; - __wsum csum; off = skb_gro_offset(skb); hlen = off + sizeof(*iph); @@ -264,13 +263,10 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, NAPI_GRO_CB(skb)->flush |= flush; - csum = skb->csum; - skb_postpull_rcsum(skb, iph, skb_network_header_len(skb)); + skb_gro_postpull_rcsum(skb, iph, nlen); pp = ops->callbacks.gro_receive(head, skb); - skb->csum = csum; - out_unlock: rcu_read_unlock(); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index ab0cc57..cb9df0e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -537,6 +537,18 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) skb_copy_secmark(to, from); } +static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) +{ + static u32 ip6_idents_hashrnd __read_mostly; + u32 hash, id; + + net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); + + hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd); + id = ip_idents_reserve(hash, 1); + fhdr->identification = htonl(id); +} + int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) { struct sk_buff *frag; @@ -1229,7 +1241,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, unsigned int maxnonfragsize, headersize; headersize = sizeof(struct ipv6hdr) + - (opt ? opt->tot_len : 0) + + (opt ? opt->opt_flen + opt->opt_nflen : 0) + (dst_allfrag(&rt->dst) ? sizeof(struct frag_hdr) : 0) + rt->rt6i_nfheader_len; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index b05b609..afa0824 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -61,6 +61,7 @@ MODULE_AUTHOR("Ville Nuorvala"); MODULE_DESCRIPTION("IPv6 tunneling device"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("ip6tnl"); MODULE_ALIAS_NETDEV("ip6tnl0"); #ifdef IP6_TNL_DEBUG @@ -1557,7 +1558,7 @@ static int ip6_tnl_validate(struct nlattr *tb[], struct nlattr *data[]) { u8 proto; - if (!data) + if (!data || !data[IFLA_IPTUN_PROTO]) return 0; proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index b7c0f82..9aaa6bb 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -511,6 +511,7 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { __be32 spi; + __u32 mark; struct xfrm_state *x; struct ip6_tnl *t; struct ip_esp_hdr *esph; @@ -524,6 +525,8 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!t) return -1; + mark = be32_to_cpu(t->parms.o_key); + switch (protocol) { case IPPROTO_ESP: esph = (struct ip_esp_hdr *)(skb->data + offset); @@ -545,7 +548,7 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, type != NDISC_REDIRECT) return 0; - x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + x = xfrm_state_lookup(net, mark, (const xfrm_address_t *)&iph->daddr, spi, protocol, AF_INET6); if (!x) return 0; @@ -792,15 +795,12 @@ static const struct net_device_ops vti6_netdev_ops = { **/ static void vti6_dev_setup(struct net_device *dev) { - struct ip6_tnl *t; - dev->netdev_ops = &vti6_netdev_ops; dev->destructor = vti6_dev_free; dev->type = ARPHRD_TUNNEL6; dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr); dev->mtu = ETH_DATA_LEN; - t = netdev_priv(dev); dev->flags |= IFF_NOARP; dev->addr_len = sizeof(struct in6_addr); dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; @@ -1097,7 +1097,6 @@ static int __init vti6_tunnel_init(void) err = xfrm6_protocol_register(&vti_esp6_protocol, IPPROTO_ESP); if (err < 0) { - unregister_pernet_device(&vti6_net_ops); pr_err("%s: can't register vti6 protocol\n", __func__); goto out; @@ -1106,7 +1105,6 @@ static int __init vti6_tunnel_init(void) err = xfrm6_protocol_register(&vti_ah6_protocol, IPPROTO_AH); if (err < 0) { xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP); - unregister_pernet_device(&vti6_net_ops); pr_err("%s: can't register vti6 protocol\n", __func__); goto out; @@ -1116,7 +1114,6 @@ static int __init vti6_tunnel_init(void) if (err < 0) { xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH); xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP); - unregister_pernet_device(&vti6_net_ops); pr_err("%s: can't register vti6 protocol\n", __func__); goto out; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 09a22f4..ca8d4ea 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -851,7 +851,7 @@ out: static void ndisc_recv_na(struct sk_buff *skb) { struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); - const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + @@ -944,10 +944,7 @@ static void ndisc_recv_na(struct sk_buff *skb) /* * Change: router to host */ - struct rt6_info *rt; - rt = rt6_get_dflt_router(saddr, dev); - if (rt) - ip6_del_rt(rt); + rt6_clean_tohost(dev_net(dev), saddr); } out: diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 84c7f33..387d8b8 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -90,17 +90,9 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, if (nf_ct_is_untracked(ct)) return NF_ACCEPT; - nat = nfct_nat(ct); - if (!nat) { - /* NAT module was loaded late. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index 9c3297a..d189fcb 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -47,15 +47,9 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, if (ct == NULL || nf_ct_is_untracked(ct)) return NF_ACCEPT; - nat = nfct_nat(ct); - if (nat == NULL) { - /* Conntrack module was loaded late, can't add extension. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) - return NF_ACCEPT; - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 6313abd..6179ac1 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -8,36 +8,6 @@ #include <net/addrconf.h> #include <net/secure_seq.h> -void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) -{ - static atomic_t ipv6_fragmentation_id; - struct in6_addr addr; - int old, new; - -#if IS_ENABLED(CONFIG_IPV6) - struct inet_peer *peer; - struct net *net; - - net = dev_net(rt->dst.dev); - peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1); - if (peer) { - fhdr->identification = htonl(inet_getid(peer, 0)); - inet_putpeer(peer); - return; - } -#endif - do { - old = atomic_read(&ipv6_fragmentation_id); - new = old + 1; - if (!new) - new = 1; - } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old); - - addr = rt->rt6i_dst.addr; - addr.s6_addr32[0] ^= (__force __be32)new; - fhdr->identification = htonl(secure_ipv6_id(addr.s6_addr32)); -} -EXPORT_SYMBOL(ipv6_select_ident); int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) { diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index a2a1d80..5b7a1ed 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -51,7 +51,6 @@ static struct inet_protosw pingv6_protosw = { .protocol = IPPROTO_ICMPV6, .prot = &pingv6_prot, .ops = &inet6_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, }; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index dddfb5f..b2dc60b 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1322,7 +1322,6 @@ static struct inet_protosw rawv6_protosw = { .protocol = IPPROTO_IP, /* wild card */ .prot = &rawv6_prot, .ops = &inet6_sockraw_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, }; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index f0a8ff9..f23fbd2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1455,7 +1455,7 @@ static int ip6_dst_gc(struct dst_ops *ops) goto out; net->ipv6.ip6_rt_gc_expire++; - fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, entries > rt_max_size); + fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); entries = dst_entries_get_slow(ops); if (entries < ops->gc_thresh) net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; @@ -2234,6 +2234,27 @@ void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) fib6_clean_all(net, fib6_remove_prefsrc, &adni); } +#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) +#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) + +/* Remove routers and update dst entries when gateway turn into host. */ +static int fib6_clean_tohost(struct rt6_info *rt, void *arg) +{ + struct in6_addr *gateway = (struct in6_addr *)arg; + + if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || + ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && + ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { + return -1; + } + return 0; +} + +void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) +{ + fib6_clean_all(net, fib6_clean_tohost, gateway); +} + struct arg_dev_net { struct net_device *dev; struct net *net; @@ -2709,6 +2730,9 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh) if (tb[RTA_OIF]) oif = nla_get_u32(tb[RTA_OIF]); + if (tb[RTA_MARK]) + fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); + if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index e5a453c..f438004 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1828,4 +1828,5 @@ xfrm_tunnel_failed: module_init(sit_init); module_exit(sit_cleanup); MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("sit"); MODULE_ALIAS_NETDEV("sit0"); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f07b2ab..229239a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1992,7 +1992,6 @@ static struct inet_protosw tcpv6_protosw = { .protocol = IPPROTO_TCP, .prot = &tcpv6_prot, .ops = &inet6_stream_ops, - .no_check = 0, .flags = INET_PROTOSW_PERMANENT | INET_PROTOSW_ICSK, }; diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c index 0d78132..8517d3c 100644 --- a/net/ipv6/tcpv6_offload.c +++ b/net/ipv6/tcpv6_offload.c @@ -42,7 +42,7 @@ static struct sk_buff **tcp6_gro_receive(struct sk_buff **head, if (NAPI_GRO_CB(skb)->flush) goto skip_csum; - wsum = skb->csum; + wsum = NAPI_GRO_CB(skb)->csum; switch (skb->ip_summed) { case CHECKSUM_NONE: diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 7edf096..6032523 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -794,10 +794,10 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, dif = inet6_iif(skb); sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif); while (sk) { - /* If zero checksum and sk_no_check is not on for + /* If zero checksum and no_check is not on for * the socket then skip it. */ - if (uh->check || sk->sk_no_check) + if (uh->check || udp_sk(sk)->no_check6_rx) stack[count++] = sk; sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr, @@ -887,7 +887,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (sk != NULL) { int ret; - if (!uh->check && !sk->sk_no_check) { + if (!uh->check && !udp_sk(sk)->no_check6_rx) { sock_put(sk); udp6_csum_zero_error(skb); goto csum_error; @@ -1037,7 +1037,7 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (is_udplite) csum = udplite_csum_outgoing(sk, skb); - else if (sk->sk_no_check == UDP_CSUM_NOXMIT) { /* UDP csum disabled */ + else if (up->no_check6_tx) { /* UDP csum disabled */ skb->ip_summed = CHECKSUM_NONE; goto send; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ @@ -1507,7 +1507,6 @@ static struct inet_protosw udpv6_protosw = { .protocol = IPPROTO_UDP, .prot = &udpv6_prot, .ops = &inet6_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_PERMANENT, }; diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index dfcc4be..9cf097e 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -64,7 +64,6 @@ static struct inet_protosw udplite6_protosw = { .protocol = IPPROTO_UDPLITE, .prot = &udplitev6_prot, .ops = &inet6_dgram_ops, - .no_check = 0, .flags = INET_PROTOSW_PERMANENT, }; diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index f47c8b1..433672d 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -114,12 +114,6 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) if (err) return err; - memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); -#ifdef CONFIG_NETFILTER - IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; -#endif - - skb->protocol = htons(ETH_P_IPV6); skb->ignore_df = 1; return x->outer_mode->output2(x, skb); @@ -128,11 +122,13 @@ EXPORT_SYMBOL(xfrm6_prepare_output); int xfrm6_output_finish(struct sk_buff *skb) { + memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); + skb->protocol = htons(ETH_P_IPV6); + #ifdef CONFIG_NETFILTER IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; #endif - skb->protocol = htons(ETH_P_IPV6); return xfrm_output(skb); } @@ -142,6 +138,13 @@ static int __xfrm6_output(struct sk_buff *skb) struct xfrm_state *x = dst->xfrm; int mtu; +#ifdef CONFIG_NETFILTER + if (!x) { + IP6CB(skb)->flags |= IP6SKB_REROUTED; + return dst_output(skb); + } +#endif + if (skb->protocol == htons(ETH_P_IPV6)) mtu = ip6_skb_dst_mtu(skb); else @@ -165,6 +168,7 @@ static int __xfrm6_output(struct sk_buff *skb) int xfrm6_output(struct sock *sk, struct sk_buff *skb) { - return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, - skb_dst(skb)->dev, __xfrm6_output); + return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, + NULL, skb_dst(skb)->dev, __xfrm6_output, + !(IP6CB(skb)->flags & IP6SKB_REROUTED)); } diff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index 6ab989c..54d13f8 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -50,6 +50,10 @@ int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) { int ret; struct xfrm6_protocol *handler; + struct xfrm6_protocol __rcu **head = proto_handlers(protocol); + + if (!head) + return 0; for_each_protocol_rcu(*proto_handlers(protocol), handler) if ((ret = handler->cb_handler(skb, err)) <= 0) @@ -184,10 +188,12 @@ int xfrm6_protocol_register(struct xfrm6_protocol *handler, struct xfrm6_protocol __rcu **pprev; struct xfrm6_protocol *t; bool add_netproto = false; - int ret = -EEXIST; int priority = handler->priority; + if (!proto_handlers(protocol) || !netproto(protocol)) + return -EINVAL; + mutex_lock(&xfrm6_protocol_mutex); if (!rcu_dereference_protected(*proto_handlers(protocol), @@ -230,6 +236,9 @@ int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, struct xfrm6_protocol *t; int ret = -ENOENT; + if (!proto_handlers(protocol) || !netproto(protocol)) + return -EINVAL; + mutex_lock(&xfrm6_protocol_mutex); for (pprev = proto_handlers(protocol); diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 41e4e93..91729b8 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1353,7 +1353,7 @@ static int ipx_create(struct net *net, struct socket *sock, int protocol, sk_refcnt_debug_inc(sk); sock_init_data(sock, sk); - sk->sk_no_check = 1; /* Checksum off by default */ + sk->sk_no_check_tx = 1; /* Checksum off by default */ sock->ops = &ipx_dgram_ops; rc = 0; out: diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c index c1f0318..67e7ad3 100644 --- a/net/ipx/ipx_route.c +++ b/net/ipx/ipx_route.c @@ -236,7 +236,8 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, } /* Apply checksum. Not allowed on 802.3 links. */ - if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023)) + if (sk->sk_no_check_tx || + intrfc->if_dlink_type == htons(IPX_FRAME_8023)) ipx->ipx_checksum = htons(0xFFFF); else ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr)); diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 01e77b0..7a95fa4 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -682,6 +682,18 @@ struct sock *iucv_accept_dequeue(struct sock *parent, struct socket *newsock) return NULL; } +static void __iucv_auto_name(struct iucv_sock *iucv) +{ + char name[12]; + + sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name)); + while (__iucv_get_sock_by_name(name)) { + sprintf(name, "%08x", + atomic_inc_return(&iucv_sk_list.autobind_name)); + } + memcpy(iucv->src_name, name, 8); +} + /* Bind an unbound socket */ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) @@ -724,8 +736,12 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, rcu_read_lock(); for_each_netdev_rcu(&init_net, dev) { if (!memcmp(dev->perm_addr, uid, 8)) { - memcpy(iucv->src_name, sa->siucv_name, 8); memcpy(iucv->src_user_id, sa->siucv_user_id, 8); + /* Check for unitialized siucv_name */ + if (strncmp(sa->siucv_name, " ", 8) == 0) + __iucv_auto_name(iucv); + else + memcpy(iucv->src_name, sa->siucv_name, 8); sk->sk_bound_dev_if = dev->ifindex; iucv->hs_dev = dev; dev_hold(dev); @@ -763,7 +779,6 @@ done: static int iucv_sock_autobind(struct sock *sk) { struct iucv_sock *iucv = iucv_sk(sk); - char name[12]; int err = 0; if (unlikely(!pr_iucv)) @@ -772,17 +787,9 @@ static int iucv_sock_autobind(struct sock *sk) memcpy(iucv->src_user_id, iucv_userid, 8); write_lock_bh(&iucv_sk_list.lock); - - sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name)); - while (__iucv_get_sock_by_name(name)) { - sprintf(name, "%08x", - atomic_inc_return(&iucv_sk_list.autobind_name)); - } - + __iucv_auto_name(iucv); write_unlock_bh(&iucv_sk_list.lock); - memcpy(&iucv->src_name, name, 8); - if (!iucv->msglimit) iucv->msglimit = IUCV_QUEUELEN_DEFAULT; @@ -1830,7 +1837,7 @@ static void iucv_callback_txdone(struct iucv_path *path, spin_lock_irqsave(&list->lock, flags); while (list_skb != (struct sk_buff *)list) { - if (msg->tag != IUCV_SKB_CB(list_skb)->tag) { + if (msg->tag == IUCV_SKB_CB(list_skb)->tag) { this = list_skb; break; } @@ -1936,11 +1943,10 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb) sk_acceptq_is_full(sk) || !nsk) { /* error on server socket - connection refused */ - if (nsk) - sk_free(nsk); afiucv_swap_src_dest(skb); trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN; err = dev_queue_xmit(skb); + iucv_sock_kill(nsk); bh_unlock_sock(sk); goto out; } diff --git a/net/key/af_key.c b/net/key/af_key.c index f3c8307..ba2a2f9 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1476,9 +1476,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg else err = xfrm_state_update(x); - xfrm_audit_state_add(x, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current), 0); + xfrm_audit_state_add(x, err ? 0 : 1, true); if (err < 0) { x->km.state = XFRM_STATE_DEAD; @@ -1532,9 +1530,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_ c.event = XFRM_MSG_DELSA; km_state_notify(x, &c); out: - xfrm_audit_state_delete(x, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current), 0); + xfrm_audit_state_delete(x, err ? 0 : 1, true); xfrm_state_put(x); return err; @@ -1726,17 +1722,13 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m struct net *net = sock_net(sk); unsigned int proto; struct km_event c; - struct xfrm_audit audit_info; int err, err2; proto = pfkey_satype2proto(hdr->sadb_msg_satype); if (proto == 0) return -EINVAL; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - audit_info.secid = 0; - err = xfrm_state_flush(net, proto, &audit_info); + err = xfrm_state_flush(net, proto, true); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { if (err == -ESRCH) /* empty table - go quietly */ @@ -2288,9 +2280,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_ err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp, hdr->sadb_msg_type != SADB_X_SPDUPDATE); - xfrm_audit_policy_add(xp, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current), 0); + xfrm_audit_policy_add(xp, err ? 0 : 1, true); if (err) goto out; @@ -2372,9 +2362,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa if (xp == NULL) return -ENOENT; - xfrm_audit_policy_delete(xp, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current), 0); + xfrm_audit_policy_delete(xp, err ? 0 : 1, true); if (err) goto out; @@ -2553,7 +2541,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, sel.sport_mask = htons(0xffff); /* set destination address info of selector */ - sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1], + sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1]; pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr); sel.prefixlen_d = sa->sadb_address_prefixlen; sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto); @@ -2622,9 +2610,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_ return -ENOENT; if (delete) { - xfrm_audit_policy_delete(xp, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current), 0); + xfrm_audit_policy_delete(xp, err ? 0 : 1, true); if (err) goto out; @@ -2733,13 +2719,9 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad { struct net *net = sock_net(sk); struct km_event c; - struct xfrm_audit audit_info; int err, err2; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - audit_info.secid = 0; - err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); + err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { if (err == -ESRCH) /* empty table - old silent behavior */ diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index ed0716a..3795580 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1102,7 +1102,9 @@ static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, struct ipv6_pinfo *np = inet6_sk(sk); struct udphdr *uh = udp_hdr(skb); - if (!skb_dst(skb) || !skb_dst(skb)->dev || + if (udp_get_no_check6_tx(sk)) + skb->ip_summed = CHECKSUM_NONE; + else if (!skb_dst(skb) || !skb_dst(skb)->dev || !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { __wsum csum = skb_checksum(skb, 0, udp_len, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1188,7 +1190,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len l2tp_xmit_ipv6_csum(sk, skb, udp_len); else #endif - if (sk->sk_no_check == UDP_CSUM_NOXMIT) + if (sk->sk_no_check_tx) skb->ip_summed = CHECKSUM_NONE; else if ((skb_dst(skb) && skb_dst(skb)->dev) && (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { @@ -1435,6 +1437,11 @@ static int l2tp_tunnel_sock_create(struct net *net, sizeof(udp6_addr), 0); if (err < 0) goto out; + + if (cfg->udp6_zero_tx_checksums) + udp_set_no_check6_tx(sock->sk, true); + if (cfg->udp6_zero_rx_checksums) + udp_set_no_check6_rx(sock->sk, true); } else #endif { @@ -1463,7 +1470,7 @@ static int l2tp_tunnel_sock_create(struct net *net, } if (!cfg->use_udp_checksums) - sock->sk->sk_no_check = UDP_CSUM_NOXMIT; + sock->sk->sk_no_check_tx = 1; break; diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 3f93ccd..68aa9ff 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -162,7 +162,9 @@ struct l2tp_tunnel_cfg { #endif u16 local_udp_port; u16 peer_udp_port; - unsigned int use_udp_checksums:1; + unsigned int use_udp_checksums:1, + udp6_zero_tx_checksums:1, + udp6_zero_rx_checksums:1; }; struct l2tp_tunnel { diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 3397fe6..369a982 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -606,7 +606,6 @@ static struct inet_protosw l2tp_ip_protosw = { .protocol = IPPROTO_L2TP, .prot = &l2tp_ip_prot, .ops = &l2tp_ip_ops, - .no_check = 0, }; static struct net_protocol l2tp_ip_protocol __read_mostly = { diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index e472d44..f3f98a1 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -755,7 +755,6 @@ static struct inet_protosw l2tp_ip6_protosw = { .protocol = IPPROTO_L2TP, .prot = &l2tp_ip6_prot, .ops = &l2tp_ip6_ops, - .no_check = 0, }; static struct inet6_protocol l2tp_ip6_protocol __read_mostly = { diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index bd7387a..0ac907a 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -161,6 +161,13 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); if (info->attrs[L2TP_ATTR_UDP_CSUM]) cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); + +#if IS_ENABLED(CONFIG_IPV6) + if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]) + cfg.udp6_zero_tx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); + if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]) + cfg.udp6_zero_rx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]); +#endif } if (info->attrs[L2TP_ATTR_DEBUG]) @@ -297,8 +304,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla case L2TP_ENCAPTYPE_UDP: if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) || - nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, - (sk->sk_no_check != UDP_CSUM_NOXMIT))) + nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx)) goto nla_put_failure; /* NOBREAK */ case L2TP_ENCAPTYPE_IP: diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 9d7d840..1e46ffa 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -25,7 +25,8 @@ mac80211-y := \ wme.o \ event.o \ chan.o \ - trace.o mlme.o + trace.o mlme.o \ + tdls.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7b8d3cf..d7513a5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -472,8 +472,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + struct rate_control_ref *ref = local->rate_ctrl; struct timespec uptime; u64 packets = 0; + u32 thr = 0; int i, ac; sinfo->generation = sdata->local->sta_generation; @@ -587,6 +589,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + /* check if the driver has a SW RC implementation */ + if (ref && ref->ops->get_expected_throughput) + thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); + else + thr = drv_get_expected_throughput(local, &sta->sta); + + if (thr != 0) { + sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; + sinfo->expected_throughput = thr; + } } static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { @@ -777,7 +790,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy, } static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo) + int idx, u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -807,7 +820,7 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1084,6 +1097,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + + lockdep_assert_held(&local->mtx); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + + if (!sdata->vif.csa_active) + continue; + + if (!sdata->csa_block_tx) + continue; + + rcu_read_unlock(); + return true; + } + rcu_read_unlock(); + + return false; +} + static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1101,7 +1139,14 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata); /* abort any running channel switch */ + mutex_lock(&local->mtx); sdata->vif.csa_active = false; + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); + kfree(sdata->u.ap.next_beacon); sdata->u.ap.next_beacon = NULL; @@ -1425,7 +1470,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, } static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params) + const u8 *mac, + struct station_parameters *params) { struct ieee80211_local *local = wiphy_priv(wiphy); struct sta_info *sta; @@ -1459,6 +1505,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) { sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + } else { + sta->sta.tdls = true; } err = sta_apply_parameters(local, sta, params); @@ -1492,7 +1540,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct ieee80211_sub_if_data *sdata; @@ -1506,7 +1554,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_change_station(struct wiphy *wiphy, - struct net_device *dev, u8 *mac, + struct net_device *dev, const u8 *mac, struct station_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1631,7 +1679,7 @@ out_err: #ifdef CONFIG_MAC80211_MESH static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop) + const u8 *dst, const u8 *next_hop) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; @@ -1659,7 +1707,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, - u8 *dst) + const u8 *dst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1670,9 +1718,8 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ieee80211_change_mpath(struct wiphy *wiphy, - struct net_device *dev, - u8 *dst, u8 *next_hop) +static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev, + const u8 *dst, const u8 *next_hop) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; @@ -1764,8 +1811,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *dst, u8 *next_hop, - struct mpath_info *pinfo) + int idx, u8 *dst, u8 *next_hop, + struct mpath_info *pinfo) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; @@ -3019,26 +3066,11 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_csa_finish); -static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, + u32 *changed) { - struct ieee80211_local *local = sdata->local; - int err, changed = 0; - - sdata_assert_lock(sdata); - - mutex_lock(&local->mtx); - sdata->radar_required = sdata->csa_radar_required; - err = ieee80211_vif_change_channel(sdata, &changed); - mutex_unlock(&local->mtx); - if (WARN_ON(err < 0)) - return; - - if (!local->use_chanctx) { - local->_oper_chandef = sdata->csa_chandef; - ieee80211_hw_config(local, 0); - } + int err; - sdata->vif.csa_active = false; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); @@ -3046,35 +3078,74 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) sdata->u.ap.next_beacon = NULL; if (err < 0) - return; - changed |= err; + return err; + *changed |= err; break; case NL80211_IFTYPE_ADHOC: err = ieee80211_ibss_finish_csa(sdata); if (err < 0) - return; - changed |= err; + return err; + *changed |= err; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: err = ieee80211_mesh_finish_csa(sdata); if (err < 0) - return; - changed |= err; + return err; + *changed |= err; break; #endif default: WARN_ON(1); - return; + return -EINVAL; + } + + return 0; +} + +static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u32 changed = 0; + int err; + + sdata_assert_lock(sdata); + lockdep_assert_held(&local->mtx); + + sdata->radar_required = sdata->csa_radar_required; + err = ieee80211_vif_change_channel(sdata, &changed); + if (err < 0) + return err; + + if (!local->use_chanctx) { + local->_oper_chandef = sdata->csa_chandef; + ieee80211_hw_config(local, 0); } + sdata->vif.csa_active = false; + + err = ieee80211_set_after_csa_beacon(sdata, &changed); + if (err) + return err; + ieee80211_bss_info_change_notify(sdata, changed); + cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); - ieee80211_wake_queues_by_reason(&sdata->local->hw, + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); - cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + return 0; +} + +static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ + if (__ieee80211_csa_finalize(sdata)) { + sdata_info(sdata, "failed to finalize CSA, disconnecting\n"); + cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev, + GFP_KERNEL); + } } void ieee80211_csa_finalize_work(struct work_struct *work) @@ -3082,8 +3153,11 @@ void ieee80211_csa_finalize_work(struct work_struct *work) struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, csa_finalize_work); + struct ieee80211_local *local = sdata->local; sdata_lock(sdata); + mutex_lock(&local->mtx); + /* AP might have been stopped while waiting for the lock. */ if (!sdata->vif.csa_active) goto unlock; @@ -3094,6 +3168,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ieee80211_csa_finalize(sdata); unlock: + mutex_unlock(&local->mtx); sdata_unlock(sdata); } @@ -3129,9 +3204,25 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, if (params->count <= 1) break; - sdata->csa_counter_offset_beacon = - params->counter_offset_beacon; - sdata->csa_counter_offset_presp = params->counter_offset_presp; + if ((params->n_counter_offsets_beacon > + IEEE80211_MAX_CSA_COUNTERS_NUM) || + (params->n_counter_offsets_presp > + IEEE80211_MAX_CSA_COUNTERS_NUM)) + return -EINVAL; + + /* make sure we don't have garbage in other counters */ + memset(sdata->csa_counter_offset_beacon, 0, + sizeof(sdata->csa_counter_offset_beacon)); + memset(sdata->csa_counter_offset_presp, 0, + sizeof(sdata->csa_counter_offset_presp)); + + memcpy(sdata->csa_counter_offset_beacon, + params->counter_offsets_beacon, + params->n_counter_offsets_beacon * sizeof(u16)); + memcpy(sdata->csa_counter_offset_presp, + params->counter_offsets_presp, + params->n_counter_offsets_presp * sizeof(u16)); + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); if (err < 0) { kfree(sdata->u.ap.next_beacon); @@ -3220,8 +3311,9 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, return 0; } -int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_csa_settings *params) +static int +__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -3230,6 +3322,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, int err, num_chanctx, changed = 0; sdata_assert_lock(sdata); + lockdep_assert_held(&local->mtx); if (!list_empty(&local->roc_list) || local->scanning) return -EBUSY; @@ -3272,15 +3365,16 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, return err; sdata->csa_radar_required = params->radar_required; - - if (params->block_tx) - ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); - sdata->csa_chandef = params->chandef; + sdata->csa_block_tx = params->block_tx; + sdata->csa_current_counter = params->count; sdata->vif.csa_active = true; + if (sdata->csa_block_tx) + ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + if (changed) { ieee80211_bss_info_change_notify(sdata, changed); drv_channel_switch_beacon(sdata, ¶ms->chandef); @@ -3292,6 +3386,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, return 0; } +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + int err; + + mutex_lock(&local->mtx); + err = __ieee80211_channel_switch(wiphy, dev, params); + mutex_unlock(&local->mtx); + + return err; +} + static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie) @@ -3304,6 +3412,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, bool need_offchan = false; u32 flags; int ret; + u8 *data; if (params->dont_wait_for_ack) flags = IEEE80211_TX_CTL_NO_ACK; @@ -3397,7 +3506,20 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } skb_reserve(skb, local->hw.extra_tx_headroom); - memcpy(skb_put(skb, params->len), params->buf, params->len); + data = skb_put(skb, params->len); + memcpy(data, params->buf, params->len); + + /* Update CSA counters */ + if (sdata->vif.csa_active && + (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_ADHOC) && + params->n_csa_offsets) { + int i; + u8 c = sdata->csa_current_counter; + + for (i = 0; i < params->n_csa_offsets; i++) + data[params->csa_offsets[i]] = c; + } IEEE80211_SKB_CB(skb)->flags = flags; @@ -3506,320 +3628,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, return 0; } -static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) -{ - u8 *pos = (void *)skb_put(skb, 7); - - *pos++ = WLAN_EID_EXT_CAPABILITY; - *pos++ = 5; /* len */ - *pos++ = 0x0; - *pos++ = 0x0; - *pos++ = 0x0; - *pos++ = 0x0; - *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; -} - -static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - u16 capab; - - capab = 0; - if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) - return capab; - - if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) - capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) - capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; - - return capab; -} - -static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, - u8 *peer, u8 *bssid) -{ - struct ieee80211_tdls_lnkie *lnkid; - - lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); - - lnkid->ie_type = WLAN_EID_LINK_ID; - lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; - - memcpy(lnkid->bssid, bssid, ETH_ALEN); - memcpy(lnkid->init_sta, src_addr, ETH_ALEN); - memcpy(lnkid->resp_sta, peer, ETH_ALEN); -} - -static int -ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, struct sk_buff *skb) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - enum ieee80211_band band = ieee80211_get_sdata_band(sdata); - struct ieee80211_tdls_data *tf; - - tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); - - memcpy(tf->da, peer, ETH_ALEN); - memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); - tf->ether_type = cpu_to_be16(ETH_P_TDLS); - tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_REQUEST; - - skb_put(skb, sizeof(tf->u.setup_req)); - tf->u.setup_req.dialog_token = dialog_token; - tf->u.setup_req.capability = - cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); - break; - case WLAN_TDLS_SETUP_RESPONSE: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_RESPONSE; - - skb_put(skb, sizeof(tf->u.setup_resp)); - tf->u.setup_resp.status_code = cpu_to_le16(status_code); - tf->u.setup_resp.dialog_token = dialog_token; - tf->u.setup_resp.capability = - cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); - break; - case WLAN_TDLS_SETUP_CONFIRM: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_CONFIRM; - - skb_put(skb, sizeof(tf->u.setup_cfm)); - tf->u.setup_cfm.status_code = cpu_to_le16(status_code); - tf->u.setup_cfm.dialog_token = dialog_token; - break; - case WLAN_TDLS_TEARDOWN: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_TEARDOWN; - - skb_put(skb, sizeof(tf->u.teardown)); - tf->u.teardown.reason_code = cpu_to_le16(status_code); - break; - case WLAN_TDLS_DISCOVERY_REQUEST: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; - - skb_put(skb, sizeof(tf->u.discover_req)); - tf->u.discover_req.dialog_token = dialog_token; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int -ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, struct sk_buff *skb) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - enum ieee80211_band band = ieee80211_get_sdata_band(sdata); - struct ieee80211_mgmt *mgmt; - - mgmt = (void *)skb_put(skb, 24); - memset(mgmt, 0, 24); - memcpy(mgmt->da, peer, ETH_ALEN); - memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); - - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - - switch (action_code) { - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); - mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; - mgmt->u.action.u.tdls_discover_resp.action_code = - WLAN_PUB_ACTION_TDLS_DISCOVER_RES; - mgmt->u.action.u.tdls_discover_resp.dialog_token = - dialog_token; - mgmt->u.action.u.tdls_discover_resp.capability = - cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = NULL; - bool send_direct; - int ret; - - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -ENOTSUPP; - - /* make sure we are in managed mode, and associated */ - if (sdata->vif.type != NL80211_IFTYPE_STATION || - !sdata->u.mgd.associated) - return -EINVAL; - - tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", - action_code, peer); - - skb = dev_alloc_skb(local->hw.extra_tx_headroom + - max(sizeof(struct ieee80211_mgmt), - sizeof(struct ieee80211_tdls_data)) + - 50 + /* supported rates */ - 7 + /* ext capab */ - extra_ies_len + - sizeof(struct ieee80211_tdls_lnkie)); - if (!skb) - return -ENOMEM; - - skb_reserve(skb, local->hw.extra_tx_headroom); - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_RESPONSE: - case WLAN_TDLS_SETUP_CONFIRM: - case WLAN_TDLS_TEARDOWN: - case WLAN_TDLS_DISCOVERY_REQUEST: - ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, - action_code, dialog_token, - status_code, skb); - send_direct = false; - break; - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, - dialog_token, status_code, - skb); - send_direct = true; - break; - default: - ret = -ENOTSUPP; - break; - } - - if (ret < 0) - goto fail; - - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - - /* the TDLS link IE is always added last */ - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_CONFIRM: - case WLAN_TDLS_TEARDOWN: - case WLAN_TDLS_DISCOVERY_REQUEST: - /* we are the initiator */ - ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, - sdata->u.mgd.bssid); - break; - case WLAN_TDLS_SETUP_RESPONSE: - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - /* we are the responder */ - ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, - sdata->u.mgd.bssid); - break; - default: - ret = -ENOTSUPP; - goto fail; - } - - if (send_direct) { - ieee80211_tx_skb(sdata, skb); - return 0; - } - - /* - * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise - * we should default to AC_VI. - */ - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_RESPONSE: - skb_set_queue_mapping(skb, IEEE80211_AC_BK); - skb->priority = 2; - break; - default: - skb_set_queue_mapping(skb, IEEE80211_AC_VI); - skb->priority = 5; - break; - } - - /* disable bottom halves when entering the Tx path */ - local_bh_disable(); - ret = ieee80211_subif_start_xmit(skb, dev); - local_bh_enable(); - - return ret; - -fail: - dev_kfree_skb(skb); - return ret; -} - -static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation oper) -{ - struct sta_info *sta; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -ENOTSUPP; - - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EINVAL; - - tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); - - switch (oper) { - case NL80211_TDLS_ENABLE_LINK: - rcu_read_lock(); - sta = sta_info_get(sdata, peer); - if (!sta) { - rcu_read_unlock(); - return -ENOLINK; - } - - set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); - rcu_read_unlock(); - break; - case NL80211_TDLS_DISABLE_LINK: - return sta_info_destroy_addr(sdata, peer); - case NL80211_TDLS_TEARDOWN: - case NL80211_TDLS_SETUP: - case NL80211_TDLS_DISCOVERY_REQ: - /* We don't support in-driver setup/teardown/discovery */ - return -ENOTSUPP; - default: - return -ENOTSUPP; - } - - return 0; -} - static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie) { diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 48e6d6f..a310e33 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -855,7 +855,7 @@ static void __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, bool clear) { - struct ieee80211_local *local = sdata->local; + struct ieee80211_local *local __maybe_unused = sdata->local; struct ieee80211_sub_if_data *vlan; struct ieee80211_chanctx_conf *conf; @@ -871,7 +871,7 @@ __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, * to a channel context that has already been freed. */ conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->chanctx_mtx)); WARN_ON(!conf); if (clear) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 40a6489..e205eba 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -34,8 +34,7 @@ static ssize_t ieee80211_if_read( ssize_t ret = -EINVAL; read_lock(&dev_base_lock); - if (sdata->dev->reg_state == NETREG_REGISTERED) - ret = (*format)(sdata, buf, sizeof(buf)); + ret = (*format)(sdata, buf, sizeof(buf)); read_unlock(&dev_base_lock); if (ret >= 0) @@ -62,8 +61,7 @@ static ssize_t ieee80211_if_write( ret = -ENODEV; rtnl_lock(); - if (sdata->dev->reg_state == NETREG_REGISTERED) - ret = (*write)(sdata, buf, count); + ret = (*write)(sdata, buf, count); rtnl_unlock(); return ret; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5331582..bd782dc 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,11 +5,11 @@ #include "ieee80211_i.h" #include "trace.h" -static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) +static inline bool check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) { - WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), - "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", - sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); + return !WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), + "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", + sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); } static inline struct ieee80211_sub_if_data * @@ -168,7 +168,8 @@ static inline int drv_change_interface(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_change_interface(local, sdata, type, p2p); ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); @@ -181,7 +182,8 @@ static inline void drv_remove_interface(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_remove_interface(local, sdata); local->ops->remove_interface(&local->hw, &sdata->vif); @@ -219,7 +221,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, sdata->vif.type == NL80211_IFTYPE_MONITOR)) return; - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_bss_info_changed(local, sdata, info, changed); if (local->ops->bss_info_changed) @@ -278,7 +281,8 @@ static inline int drv_set_key(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_set_key(local, cmd, sdata, sta, key); ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); @@ -298,7 +302,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, ista = &sta->sta; sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_update_tkip_key(local, sdata, conf, ista, iv32); if (local->ops->update_tkip_key) @@ -315,7 +320,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); @@ -328,7 +334,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_cancel_hw_scan(local, sdata); local->ops->cancel_hw_scan(&local->hw, &sdata->vif); @@ -345,7 +352,8 @@ drv_sched_scan_start(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sched_scan_start(local, sdata); ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, @@ -361,7 +369,8 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sched_scan_stop(local, sdata); ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif); @@ -462,7 +471,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local, struct ieee80211_sta *sta) { sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_sta_notify(local, sdata, cmd, sta); if (local->ops->sta_notify) @@ -479,7 +489,8 @@ static inline int drv_sta_add(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sta_add(local, sdata, sta); if (local->ops->sta_add) @@ -497,7 +508,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_sta_remove(local, sdata, sta); if (local->ops->sta_remove) @@ -515,7 +527,8 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; if (local->ops->sta_add_debugfs) local->ops->sta_add_debugfs(&local->hw, &sdata->vif, @@ -545,7 +558,8 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta); if (local->ops->sta_pre_rcu_remove) @@ -566,7 +580,8 @@ int drv_sta_state(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); if (local->ops->sta_state) { @@ -590,7 +605,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, struct ieee80211_sta *sta, u32 changed) { sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && (sdata->vif.type != NL80211_IFTYPE_ADHOC && @@ -612,7 +628,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_conf_tx(local, sdata, ac, params); if (local->ops->conf_tx) @@ -629,7 +646,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return ret; trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) @@ -644,7 +662,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) @@ -657,7 +676,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) @@ -689,7 +709,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); @@ -733,8 +754,8 @@ static inline void drv_flush(struct ieee80211_local *local, might_sleep(); - if (sdata) - check_sdata_in_driver(sdata); + if (sdata && !check_sdata_in_driver(sdata)) + return; trace_drv_flush(local, queues, drop); if (local->ops->flush) @@ -854,7 +875,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_set_bitrate_mask(local, sdata, mask); if (local->ops->set_bitrate_mask) @@ -869,7 +891,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_gtk_rekey_data *data) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_set_rekey_data(local, sdata, data); if (local->ops->set_rekey_data) @@ -937,7 +960,8 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); trace_drv_mgd_prepare_tx(local, sdata); @@ -964,6 +988,9 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, static inline void drv_remove_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { + if (WARN_ON(!ctx->driver_present)) + return; + trace_drv_remove_chanctx(local, ctx); if (local->ops->remove_chanctx) local->ops->remove_chanctx(&local->hw, &ctx->conf); @@ -989,7 +1016,8 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local, { int ret = 0; - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_assign_vif_chanctx(local, sdata, ctx); if (local->ops->assign_vif_chanctx) { @@ -1007,7 +1035,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx *ctx) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_unassign_vif_chanctx(local, sdata, ctx); if (local->ops->unassign_vif_chanctx) { @@ -1019,12 +1048,66 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline int +drv_switch_vif_chanctx(struct ieee80211_local *local, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + int ret = 0; + int i; + + if (!local->ops->switch_vif_chanctx) + return -EOPNOTSUPP; + + for (i = 0; i < n_vifs; i++) { + struct ieee80211_chanctx *new_ctx = + container_of(vifs[i].new_ctx, + struct ieee80211_chanctx, + conf); + struct ieee80211_chanctx *old_ctx = + container_of(vifs[i].old_ctx, + struct ieee80211_chanctx, + conf); + + WARN_ON_ONCE(!old_ctx->driver_present); + WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && + new_ctx->driver_present) || + (mode == CHANCTX_SWMODE_REASSIGN_VIF && + !new_ctx->driver_present)); + } + + trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); + ret = local->ops->switch_vif_chanctx(&local->hw, + vifs, n_vifs, mode); + trace_drv_return_int(local, ret); + + if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { + for (i = 0; i < n_vifs; i++) { + struct ieee80211_chanctx *new_ctx = + container_of(vifs[i].new_ctx, + struct ieee80211_chanctx, + conf); + struct ieee80211_chanctx *old_ctx = + container_of(vifs[i].old_ctx, + struct ieee80211_chanctx, + conf); + + new_ctx->driver_present = true; + old_ctx->driver_present = false; + } + } + + return ret; +} + static inline int drv_start_ap(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { int ret = 0; - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf); if (local->ops->start_ap) @@ -1036,7 +1119,8 @@ static inline int drv_start_ap(struct ieee80211_local *local, static inline void drv_stop_ap(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_stop_ap(local, sdata); if (local->ops->stop_ap) @@ -1059,7 +1143,8 @@ drv_set_default_unicast_key(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int key_idx) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; WARN_ON_ONCE(key_idx < -1 || key_idx > 3); @@ -1101,7 +1186,8 @@ static inline int drv_join_ibss(struct ieee80211_local *local, int ret = 0; might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf); if (local->ops->join_ibss) @@ -1114,7 +1200,8 @@ static inline void drv_leave_ibss(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_leave_ibss(local, sdata); if (local->ops->leave_ibss) @@ -1122,4 +1209,17 @@ static inline void drv_leave_ibss(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline u32 drv_get_expected_throughput(struct ieee80211_local *local, + struct ieee80211_sta *sta) +{ + u32 ret = 0; + + trace_drv_get_expected_throughput(sta); + if (local->ops->get_expected_throughput) + ret = local->ops->get_expected_throughput(sta); + trace_drv_return_u32(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index ff4d415..18ee0a2 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, *pos++ = csa_settings->block_tx ? 1 : 0; *pos++ = ieee80211_frequency_to_channel( csa_settings->chandef.chan->center_freq); - sdata->csa_counter_offset_beacon = (pos - presp->head); + sdata->csa_counter_offset_beacon[0] = (pos - presp->head); *pos++ = csa_settings->count; } @@ -1677,6 +1677,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.control_port = params->control_port; sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs; sdata->u.ibss.basic_rates = params->basic_rates; + sdata->u.ibss.last_scan_completed = jiffies; /* fix basic_rates if channel does not support these rates */ rate_flags = ieee80211_chandef_rate_flags(¶ms->chandef); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b455f62..ac9836e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -317,6 +317,7 @@ struct ieee80211_roc_work { bool started, abort, hw_begun, notified; bool to_be_freed; + bool on_channel; unsigned long hw_start_time; @@ -753,9 +754,10 @@ struct ieee80211_sub_if_data { struct mac80211_qos_map __rcu *qos_map; struct work_struct csa_finalize_work; - int csa_counter_offset_beacon; - int csa_counter_offset_presp; + u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM]; + u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM]; bool csa_radar_required; + bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ struct cfg80211_chan_def csa_chandef; struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */ @@ -765,6 +767,7 @@ struct ieee80211_sub_if_data { struct ieee80211_chanctx *reserved_chanctx; struct cfg80211_chan_def reserved_chandef; bool reserved_radar_required; + u8 csa_current_counter; /* used to reconfigure hardware SM PS */ struct work_struct recalc_smps; @@ -1458,6 +1461,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_sched_scan_end(struct ieee80211_local *local); void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ @@ -1472,6 +1476,7 @@ void ieee80211_sw_roc_work(struct work_struct *work); void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); /* channel switch handling */ +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local); void ieee80211_csa_finalize_work(struct work_struct *work); int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); @@ -1833,6 +1838,15 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, u8 radar_detect); int ieee80211_max_num_channels(struct ieee80211_local *local); +/* TDLS */ +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len); +int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, enum nl80211_tdls_operation oper); + + #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7fff3dc..81a8e2a 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -399,6 +399,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) sdata->vif.type = NL80211_IFTYPE_MONITOR; snprintf(sdata->name, IFNAMSIZ, "%s-monitor", wiphy_name(local->hw.wiphy)); + sdata->wdev.iftype = NL80211_IFTYPE_MONITOR; sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; @@ -838,8 +839,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->recalc_smps); sdata_lock(sdata); + mutex_lock(&local->mtx); sdata->vif.csa_active = false; + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); sdata_unlock(sdata); + cancel_work_sync(&sdata->csa_finalize_work); cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); @@ -1278,6 +1286,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE); sdata->control_port_no_encrypt = false; sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; + sdata->vif.bss_conf.idle = true; sdata->noack_map = 0; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 6ff65a1..16d97f0 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -325,7 +325,8 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, struct ieee80211_key *key; int i, j, err; - BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); + if (WARN_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)) + return ERR_PTR(-EINVAL); key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); if (!key) @@ -481,8 +482,8 @@ int ieee80211_key_link(struct ieee80211_key *key, int idx, ret; bool pairwise; - BUG_ON(!sdata); - BUG_ON(!key); + if (WARN_ON(!sdata || !key)) + return -EINVAL; pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; idx = key->conf.keyidx; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 27b9364..d17c26d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -956,6 +956,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index b06ddc9..6495a3f 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -679,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) *pos++ = 0x0; *pos++ = ieee80211_frequency_to_channel( csa->settings.chandef.chan->center_freq); - sdata->csa_counter_offset_beacon = hdr_len + 6; + sdata->csa_counter_offset_beacon[0] = hdr_len + 6; *pos++ = csa->settings.count; *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; *pos++ = 6; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 7d050ed..cf032a8 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -287,8 +287,10 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, struct sk_buff_head failq; unsigned long flags; - BUG_ON(gate_mpath == from_mpath); - BUG_ON(!gate_mpath->next_hop); + if (WARN_ON(gate_mpath == from_mpath)) + return; + if (WARN_ON(!gate_mpath->next_hop)) + return; __skb_queue_head_init(&failq); diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 2bc5dc2..09625d6 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -171,7 +171,7 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, u8 cap; WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); - BUG_ON(!rcu_read_lock_held()); + WARN_ON(!rcu_read_lock_held()); cap = beacon->meshconf->meshconf_cap; spin_lock_bh(&ifmsh->sync_offset_lock); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 488826f..3345401 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -975,16 +975,23 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->csa_chandef.chan; + ieee80211_bss_info_change_notify(sdata, changed); + + mutex_lock(&local->mtx); + sdata->vif.csa_active = false; /* XXX: wait for a beacon first? */ - ieee80211_wake_queues_by_reason(&local->hw, + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); - ieee80211_bss_info_change_notify(sdata, changed); - - out: - sdata->vif.csa_active = false; ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + + ieee80211_sta_reset_beacon_monitor(sdata); + ieee80211_sta_reset_conn_monitor(sdata); + +out: sdata_unlock(sdata); } @@ -1100,12 +1107,16 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->chanctx_mtx); sdata->csa_chandef = csa_ie.chandef; + + mutex_lock(&local->mtx); sdata->vif.csa_active = true; + sdata->csa_block_tx = csa_ie.mode; - if (csa_ie.mode) + if (sdata->csa_block_tx) ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); if (local->ops->channel_switch) { /* use driver's channel switch callback */ @@ -1817,6 +1828,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags = 0; mutex_lock(&local->mtx); ieee80211_vif_release_channel(sdata); + + sdata->vif.csa_active = false; + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); mutex_unlock(&local->mtx); sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; @@ -2045,6 +2062,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get); static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { + struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -2058,10 +2076,14 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, true, frame_buf); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + + mutex_lock(&local->mtx); sdata->vif.csa_active = false; - ieee80211_wake_queues_by_reason(&sdata->local->hw, + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); @@ -3546,6 +3568,9 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; + if (sdata->vif.csa_active) + return; + sdata->u.mgd.connection_loss = false; ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_connection_loss_work); @@ -3561,6 +3586,9 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data) if (local->quiescing) return; + if (sdata->vif.csa_active) + return; + ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); } @@ -3598,18 +3626,24 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) sdata_lock(sdata); - if (ifmgd->auth_data) { + if (ifmgd->auth_data || ifmgd->assoc_data) { + const u8 *bssid = ifmgd->auth_data ? + ifmgd->auth_data->bss->bssid : + ifmgd->assoc_data->bss->bssid; + /* - * If we are trying to authenticate while suspending, cfg80211 - * won't know and won't actually abort those attempts, thus we - * need to do that ourselves. + * If we are trying to authenticate / associate while suspending, + * cfg80211 won't know and won't actually abort those attempts, + * thus we need to do that ourselves. */ - ieee80211_send_deauth_disassoc(sdata, - ifmgd->auth_data->bss->bssid, + ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, false, frame_buf); - ieee80211_destroy_auth_data(sdata, false); + if (ifmgd->assoc_data) + ieee80211_destroy_assoc_data(sdata, false); + if (ifmgd->auth_data) + ieee80211_destroy_auth_data(sdata, false); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); } diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 6fb3855..7a17dec 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -333,7 +333,7 @@ void ieee80211_sw_roc_work(struct work_struct *work) container_of(work, struct ieee80211_roc_work, work.work); struct ieee80211_sub_if_data *sdata = roc->sdata; struct ieee80211_local *local = sdata->local; - bool started; + bool started, on_channel; mutex_lock(&local->mtx); @@ -354,14 +354,26 @@ void ieee80211_sw_roc_work(struct work_struct *work) if (!roc->started) { struct ieee80211_roc_work *dep; - /* start this ROC */ - ieee80211_offchannel_stop_vifs(local); + WARN_ON(local->use_chanctx); + + /* If actually operating on the desired channel (with at least + * 20 MHz channel width) don't stop all the operations but still + * treat it as though the ROC operation started properly, so + * other ROC operations won't interfere with this one. + */ + roc->on_channel = roc->chan == local->_oper_chandef.chan && + local->_oper_chandef.width != NL80211_CHAN_WIDTH_5 && + local->_oper_chandef.width != NL80211_CHAN_WIDTH_10; - /* switch channel etc */ + /* start this ROC */ ieee80211_recalc_idle(local); - local->tmp_channel = roc->chan; - ieee80211_hw_config(local, 0); + if (!roc->on_channel) { + ieee80211_offchannel_stop_vifs(local); + + local->tmp_channel = roc->chan; + ieee80211_hw_config(local, 0); + } /* tell userspace or send frame */ ieee80211_handle_roc_started(roc); @@ -380,9 +392,10 @@ void ieee80211_sw_roc_work(struct work_struct *work) finish: list_del(&roc->list); started = roc->started; + on_channel = roc->on_channel; ieee80211_roc_notify_destroy(roc, !roc->abort); - if (started) { + if (started && !on_channel) { ieee80211_flush_queues(local, NULL); local->tmp_channel = NULL; diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 26fd94f..1c1469c 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -657,6 +657,17 @@ minstrel_free(void *priv) kfree(priv); } +static u32 minstrel_get_expected_throughput(void *priv_sta) +{ + struct minstrel_sta_info *mi = priv_sta; + int idx = mi->max_tp_rate[0]; + + /* convert pkt per sec in kbps (1200 is the average pkt size used for + * computing cur_tp + */ + return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024; +} + const struct rate_control_ops mac80211_minstrel = { .name = "minstrel", .tx_status = minstrel_tx_status, @@ -670,6 +681,7 @@ const struct rate_control_ops mac80211_minstrel = { .add_sta_debugfs = minstrel_add_sta_debugfs, .remove_sta_debugfs = minstrel_remove_sta_debugfs, #endif + .get_expected_throughput = minstrel_get_expected_throughput, }; int __init diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index bccaf85..85c1e74 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -22,7 +22,7 @@ #define MCS_NBITS (AVG_PKT_SIZE << 3) /* Number of symbols for a packet with (bps) bits per symbol */ -#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) +#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps)) /* Transmission time (nanoseconds) for a packet containing (syms) symbols */ #define MCS_SYMBOL_TIME(sgi, syms) \ @@ -226,8 +226,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); nsecs += minstrel_mcs_groups[group].duration[rate]; - tp = 1000000 * ((prob * 1000) / nsecs); + /* prob is scaled - see MINSTREL_FRAC above */ + tp = 1000000 * ((prob * 1000) / nsecs); mr->cur_tp = MINSTREL_TRUNC(tp); } @@ -1031,6 +1032,22 @@ minstrel_ht_free(void *priv) mac80211_minstrel.free(priv); } +static u32 minstrel_ht_get_expected_throughput(void *priv_sta) +{ + struct minstrel_ht_sta_priv *msp = priv_sta; + struct minstrel_ht_sta *mi = &msp->ht; + int i, j; + + if (!msp->is_ht) + return mac80211_minstrel.get_expected_throughput(priv_sta); + + i = mi->max_tp_rate / MCS_GROUP_RATES; + j = mi->max_tp_rate % MCS_GROUP_RATES; + + /* convert cur_tp from pkt per second in kbps */ + return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; +} + static const struct rate_control_ops mac80211_minstrel_ht = { .name = "minstrel_ht", .tx_status = minstrel_ht_tx_status, @@ -1045,6 +1062,7 @@ static const struct rate_control_ops mac80211_minstrel_ht = { .add_sta_debugfs = minstrel_ht_add_sta_debugfs, .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs, #endif + .get_expected_throughput = minstrel_ht_get_expected_throughput, }; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 28185c8..f40661e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1076,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_sched_scan_results); -void ieee80211_sched_scan_stopped_work(struct work_struct *work) +void ieee80211_sched_scan_end(struct ieee80211_local *local) { - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, - sched_scan_stopped_work); - mutex_lock(&local->mtx); if (!rcu_access_pointer(local->sched_scan_sdata)) { @@ -1099,6 +1095,15 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) cfg80211_sched_scan_stopped(local->hw.wiphy); } +void ieee80211_sched_scan_stopped_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, + sched_scan_stopped_work); + + ieee80211_sched_scan_end(local); +} + void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 632d372..a9b46d8 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -240,6 +240,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); + kfree(rcu_dereference_raw(sta->sta.rates)); kfree(sta); } diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 60cb7a6..ba29ebc 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -541,6 +541,23 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, */ #define STA_LOST_PKT_THRESHOLD 50 +static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD) + return; + + cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, + sta->lost_packets, GFP_ATOMIC); + sta->lost_packets = 0; +} + void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) { struct sk_buff *skb2; @@ -680,12 +697,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (info->flags & IEEE80211_TX_STAT_ACK) { if (sta->lost_packets) sta->lost_packets = 0; - } else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) { - cfg80211_cqm_pktloss_notify(sta->sdata->dev, - sta->sta.addr, - sta->lost_packets, - GFP_ATOMIC); - sta->lost_packets = 0; + } else { + ieee80211_lost_packet(sta, skb); } } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c new file mode 100644 index 0000000..652813b --- /dev/null +++ b/net/mac80211/tdls.c @@ -0,0 +1,325 @@ +/* + * mac80211 TDLS handling code + * + * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2014, Intel Corporation + * + * This file is GPLv2 as found in COPYING. + */ + +#include <linux/ieee80211.h> +#include "ieee80211_i.h" + +static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 7); + + *pos++ = WLAN_EID_EXT_CAPABILITY; + *pos++ = 5; /* len */ + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u16 capab; + + capab = 0; + if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) + return capab; + + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + return capab; +} + +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +static int +ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_tdls_data *tf; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + break; + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_mgmt *mgmt; + + mgmt = (void *)skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); + ieee80211_tdls_add_ext_capab(skb); + break; + default: + return -EINVAL; + } + + return 0; +} + +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct sk_buff *skb = NULL; + bool send_direct; + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in managed mode, and associated */ + if (sdata->vif.type != NL80211_IFTYPE_STATION || + !sdata->u.mgd.associated) + return -EINVAL; + + tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", + action_code, peer); + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + 50 + /* supported rates */ + 7 + /* ext capab */ + extra_ies_len + + sizeof(struct ieee80211_tdls_lnkie)); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, + action_code, dialog_token, + status_code, skb); + send_direct = false; + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, + dialog_token, status_code, + skb); + send_direct = true; + break; + default: + ret = -ENOTSUPP; + break; + } + + if (ret < 0) + goto fail; + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + /* we are the initiator */ + ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, + sdata->u.mgd.bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + /* we are the responder */ + ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, + sdata->u.mgd.bssid); + break; + default: + ret = -ENOTSUPP; + goto fail; + } + + if (send_direct) { + ieee80211_tx_skb(sdata, skb); + return 0; + } + + /* + * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise + * we should default to AC_VI. + */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb_set_queue_mapping(skb, IEEE80211_AC_BK); + skb->priority = 2; + break; + default: + skb_set_queue_mapping(skb, IEEE80211_AC_VI); + skb->priority = 5; + break; + } + + /* disable bottom halves when entering the Tx path */ + local_bh_disable(); + ret = ieee80211_subif_start_xmit(skb, dev); + local_bh_enable(); + + return ret; + +fail: + dev_kfree_skb(skb); + return ret; +} + +int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, enum nl80211_tdls_operation oper) +{ + struct sta_info *sta; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EINVAL; + + tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); + + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + if (!sta) { + rcu_read_unlock(); + return -ENOLINK; + } + + set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); + rcu_read_unlock(); + break; + case NL80211_TDLS_DISABLE_LINK: + return sta_info_destroy_addr(sdata, peer); + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + case NL80211_TDLS_DISCOVERY_REQ: + /* We don't support in-driver setup/teardown/discovery */ + return -ENOTSUPP; + default: + return -ENOTSUPP; + } + + return 0; +} diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a0b0aea..cfe1a06 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -21,10 +21,10 @@ #define VIF_ENTRY __field(enum nl80211_iftype, vif_type) __field(void *, sdata) \ __field(bool, p2p) \ - __string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>") + __string(vif_name, sdata->name) #define VIF_ASSIGN __entry->vif_type = sdata->vif.type; __entry->sdata = sdata; \ __entry->p2p = sdata->vif.p2p; \ - __assign_str(vif_name, sdata->dev ? sdata->dev->name : sdata->name) + __assign_str(vif_name, sdata->name) #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" @@ -184,6 +184,20 @@ TRACE_EVENT(drv_return_bool, "true" : "false") ); +TRACE_EVENT(drv_return_u32, + TP_PROTO(struct ieee80211_local *local, u32 ret), + TP_ARGS(local, ret), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, ret) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret) +); + TRACE_EVENT(drv_return_u64, TP_PROTO(struct ieee80211_local *local, u64 ret), TP_ARGS(local, ret), @@ -1375,6 +1389,91 @@ TRACE_EVENT(drv_change_chanctx, ) ); +#if !defined(__TRACE_VIF_ENTRY) +#define __TRACE_VIF_ENTRY +struct trace_vif_entry { + enum nl80211_iftype vif_type; + bool p2p; + char vif_name[IFNAMSIZ]; +} __packed; + +struct trace_chandef_entry { + u32 control_freq; + u32 chan_width; + u32 center_freq1; + u32 center_freq2; +} __packed; + +struct trace_switch_entry { + struct trace_vif_entry vif; + struct trace_chandef_entry old_chandef; + struct trace_chandef_entry new_chandef; +} __packed; + +#define SWITCH_ENTRY_ASSIGN(to, from) local_vifs[i].to = vifs[i].from +#endif + +TRACE_EVENT(drv_switch_vif_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, enum ieee80211_chanctx_switch_mode mode), + TP_ARGS(local, vifs, n_vifs, mode), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, n_vifs) + __field(u32, mode) + __dynamic_array(u8, vifs, + sizeof(struct trace_switch_entry) * n_vifs) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->n_vifs = n_vifs; + __entry->mode = mode; + { + struct trace_switch_entry *local_vifs = + __get_dynamic_array(vifs); + int i; + + for (i = 0; i < n_vifs; i++) { + struct ieee80211_sub_if_data *sdata; + + sdata = container_of(vifs[i].vif, + struct ieee80211_sub_if_data, + vif); + + SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type); + SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p); + strncpy(local_vifs[i].vif.vif_name, + sdata->name, + sizeof(local_vifs[i].vif.vif_name)); + SWITCH_ENTRY_ASSIGN(old_chandef.control_freq, + old_ctx->def.chan->center_freq); + SWITCH_ENTRY_ASSIGN(old_chandef.chan_width, + old_ctx->def.width); + SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1, + old_ctx->def.center_freq1); + SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2, + old_ctx->def.center_freq2); + SWITCH_ENTRY_ASSIGN(new_chandef.control_freq, + new_ctx->def.chan->center_freq); + SWITCH_ENTRY_ASSIGN(new_chandef.chan_width, + new_ctx->def.width); + SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1, + new_ctx->def.center_freq1); + SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2, + new_ctx->def.center_freq2); + } + } + ), + + TP_printk( + LOCAL_PR_FMT " n_vifs:%d mode:%d", + LOCAL_PR_ARG, __entry->n_vifs, __entry->mode + ) +); + DECLARE_EVENT_CLASS(local_sdata_chanctx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -1499,6 +1598,24 @@ DEFINE_EVENT(local_sdata_evt, drv_leave_ibss, TP_ARGS(local, sdata) ); +TRACE_EVENT(drv_get_expected_throughput, + TP_PROTO(struct ieee80211_sta *sta), + + TP_ARGS(sta), + + TP_STRUCT__entry( + STA_ENTRY + ), + + TP_fast_assign( + STA_ASSIGN; + ), + + TP_printk( + STA_PR_FMT, STA_PR_ARG + ) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 19d36d4..5214686 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2328,7 +2328,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ps_data *ps, struct sk_buff *skb) + struct ps_data *ps, struct sk_buff *skb, + bool is_template) { u8 *pos, *tim; int aid0 = 0; @@ -2341,11 +2342,12 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, * checking byte-for-byte */ have_bits = !bitmap_empty((unsigned long *)ps->tim, IEEE80211_MAX_AID+1); - - if (ps->dtim_count == 0) - ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; - else - ps->dtim_count--; + if (!is_template) { + if (ps->dtim_count == 0) + ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; + else + ps->dtim_count--; + } tim = pos = (u8 *) skb_put(skb, 6); *pos++ = WLAN_EID_TIM; @@ -2391,7 +2393,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, } static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ps_data *ps, struct sk_buff *skb) + struct ps_data *ps, struct sk_buff *skb, + bool is_template) { struct ieee80211_local *local = sdata->local; @@ -2403,24 +2406,24 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { - __ieee80211_beacon_add_tim(sdata, ps, skb); + __ieee80211_beacon_add_tim(sdata, ps, skb, is_template); } else { spin_lock_bh(&local->tim_lock); - __ieee80211_beacon_add_tim(sdata, ps, skb); + __ieee80211_beacon_add_tim(sdata, ps, skb, is_template); spin_unlock_bh(&local->tim_lock); } return 0; } -static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, - struct beacon_data *beacon) +static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, + struct beacon_data *beacon) { struct probe_resp *resp; - int counter_offset_beacon = sdata->csa_counter_offset_beacon; - int counter_offset_presp = sdata->csa_counter_offset_presp; u8 *beacon_data; size_t beacon_data_len; + int i; + u8 count = sdata->csa_current_counter; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: @@ -2438,40 +2441,57 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, default: return; } - if (WARN_ON(counter_offset_beacon >= beacon_data_len)) - return; - /* Warn if the driver did not check for/react to csa - * completeness. A beacon with CSA counter set to 0 should - * never occur, because a counter of 1 means switch just - * before the next beacon. - */ - if (WARN_ON(beacon_data[counter_offset_beacon] == 1)) - return; + for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) { + u16 counter_offset_beacon = + sdata->csa_counter_offset_beacon[i]; + u16 counter_offset_presp = sdata->csa_counter_offset_presp[i]; - beacon_data[counter_offset_beacon]--; + if (counter_offset_beacon) { + if (WARN_ON(counter_offset_beacon >= beacon_data_len)) + return; - if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) { - rcu_read_lock(); - resp = rcu_dereference(sdata->u.ap.probe_resp); + beacon_data[counter_offset_beacon] = count; + } + + if (sdata->vif.type == NL80211_IFTYPE_AP && + counter_offset_presp) { + rcu_read_lock(); + resp = rcu_dereference(sdata->u.ap.probe_resp); - /* if nl80211 accepted the offset, this should not happen. */ - if (WARN_ON(!resp)) { + /* If nl80211 accepted the offset, this should + * not happen. + */ + if (WARN_ON(!resp)) { + rcu_read_unlock(); + return; + } + resp->data[counter_offset_presp] = count; rcu_read_unlock(); - return; } - resp->data[counter_offset_presp]--; - rcu_read_unlock(); } } +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + sdata->csa_current_counter--; + + /* the counter should never reach 0 */ + WARN_ON(!sdata->csa_current_counter); + + return sdata->csa_current_counter; +} +EXPORT_SYMBOL(ieee80211_csa_update_counter); + bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct beacon_data *beacon = NULL; u8 *beacon_data; size_t beacon_data_len; - int counter_beacon = sdata->csa_counter_offset_beacon; + int counter_beacon = sdata->csa_counter_offset_beacon[0]; int ret = false; if (!ieee80211_sdata_running(sdata)) @@ -2521,9 +2541,11 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_csa_is_complete); -struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u16 *tim_offset, u16 *tim_length) +static struct sk_buff * +__ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_mutable_offsets *offs, + bool is_template) { struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; @@ -2532,6 +2554,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, enum ieee80211_band band; struct ieee80211_tx_rate_control txrc; struct ieee80211_chanctx_conf *chanctx_conf; + int csa_off_base = 0; rcu_read_lock(); @@ -2541,18 +2564,20 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!ieee80211_sdata_running(sdata) || !chanctx_conf) goto out; - if (tim_offset) - *tim_offset = 0; - if (tim_length) - *tim_length = 0; + if (offs) + memset(offs, 0, sizeof(*offs)); if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_if_ap *ap = &sdata->u.ap; struct beacon_data *beacon = rcu_dereference(ap->beacon); if (beacon) { - if (sdata->vif.csa_active) - ieee80211_update_csa(sdata, beacon); + if (sdata->vif.csa_active) { + if (!is_template) + ieee80211_csa_update_counter(vif); + + ieee80211_set_csa(sdata, beacon); + } /* * headroom, head length, @@ -2569,12 +2594,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memcpy(skb_put(skb, beacon->head_len), beacon->head, beacon->head_len); - ieee80211_beacon_add_tim(sdata, &ap->ps, skb); + ieee80211_beacon_add_tim(sdata, &ap->ps, skb, + is_template); - if (tim_offset) - *tim_offset = beacon->head_len; - if (tim_length) - *tim_length = skb->len - beacon->head_len; + if (offs) { + offs->tim_offset = beacon->head_len; + offs->tim_length = skb->len - beacon->head_len; + + /* for AP the csa offsets are from tail */ + csa_off_base = skb->len; + } if (beacon->tail) memcpy(skb_put(skb, beacon->tail_len), @@ -2589,9 +2618,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!presp) goto out; - if (sdata->vif.csa_active) - ieee80211_update_csa(sdata, presp); + if (sdata->vif.csa_active) { + if (!is_template) + ieee80211_csa_update_counter(vif); + ieee80211_set_csa(sdata, presp); + } skb = dev_alloc_skb(local->tx_headroom + presp->head_len + local->hw.extra_beacon_tailroom); @@ -2611,8 +2643,17 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!bcn) goto out; - if (sdata->vif.csa_active) - ieee80211_update_csa(sdata, bcn); + if (sdata->vif.csa_active) { + if (!is_template) + /* TODO: For mesh csa_counter is in TU, so + * decrementing it by one isn't correct, but + * for now we leave it consistent with overall + * mac80211's behavior. + */ + ieee80211_csa_update_counter(vif); + + ieee80211_set_csa(sdata, bcn); + } if (ifmsh->sync_ops) ifmsh->sync_ops->adjust_tbtt(sdata, bcn); @@ -2626,13 +2667,33 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, goto out; skb_reserve(skb, local->tx_headroom); memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); - ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb); + ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template); + + if (offs) { + offs->tim_offset = bcn->head_len; + offs->tim_length = skb->len - bcn->head_len; + } + memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len); } else { WARN_ON(1); goto out; } + /* CSA offsets */ + if (offs) { + int i; + + for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) { + u16 csa_off = sdata->csa_counter_offset_beacon[i]; + + if (!csa_off) + continue; + + offs->csa_counter_offs[i] = csa_off_base + csa_off; + } + } + band = chanctx_conf->def.chan->band; info = IEEE80211_SKB_CB(skb); @@ -2663,6 +2724,32 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, out: rcu_read_unlock(); return skb; + +} + +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_mutable_offsets *offs) +{ + return __ieee80211_beacon_get(hw, vif, offs, true); +} +EXPORT_SYMBOL(ieee80211_beacon_get_template); + +struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 *tim_offset, u16 *tim_length) +{ + struct ieee80211_mutable_offsets offs = {}; + struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false); + + if (tim_offset) + *tim_offset = offs.tim_offset; + + if (tim_length) + *tim_length = offs.tim_length; + + return bcn; } EXPORT_SYMBOL(ieee80211_beacon_get_tim); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c08bd4a..6886601 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1457,6 +1457,44 @@ void ieee80211_stop_device(struct ieee80211_local *local) drv_stop(local); } +static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_chanctx *ctx; + + /* + * We get here if during resume the device can't be restarted properly. + * We might also get here if this happens during HW reset, which is a + * slightly different situation and we need to drop all connections in + * the latter case. + * + * Ask cfg80211 to turn off all interfaces, this will result in more + * warnings but at least we'll then get into a clean stopped state. + */ + + local->resuming = false; + local->suspended = false; + local->started = false; + + /* scheduled scan clearly can't be running any more, but tell + * cfg80211 and clear local state + */ + ieee80211_sched_scan_end(local); + + list_for_each_entry(sdata, &local->interfaces, list) + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; + + /* Mark channel contexts as not being in the driver any more to avoid + * removing them from the driver during the shutdown process... + */ + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) + ctx->driver_present = false; + mutex_unlock(&local->chanctx_mtx); + + cfg80211_shutdown_all_interfaces(local->hw.wiphy); +} + static void ieee80211_assign_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { @@ -1520,9 +1558,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) */ res = drv_start(local); if (res) { - WARN(local->suspended, "Hardware became unavailable " - "upon resume. This could be a software issue " - "prior to suspend or a hardware issue.\n"); + if (local->suspended) + WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); + else + WARN(1, "Hardware became unavailable during restart.\n"); + ieee80211_handle_reconfig_failure(local); return res; } diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index e9e36a2..9265adf 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -129,9 +129,12 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (!vht_cap_ie || !sband->vht_cap.vht_supported) return; - /* A VHT STA must support 40 MHz */ - if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - return; + /* + * A VHT STA must support 40 MHz, but if we verify that here + * then we break a few things - some APs (e.g. Netgear R6300v2 + * and others based on the BCM4360 chipset) will unset this + * capability bit when operating in 20 MHz. + */ vht_cap->vht_supported = true; diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig index b33dd76..1818a99 100644 --- a/net/mac802154/Kconfig +++ b/net/mac802154/Kconfig @@ -2,6 +2,10 @@ config MAC802154 tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)" depends on IEEE802154 select CRC_CCITT + select CRYPTO_AUTHENC + select CRYPTO_CCM + select CRYPTO_CTR + select CRYPTO_AES ---help--- This option enables the hardware independent IEEE 802.15.4 networking stack for SoftMAC devices (the ones implementing diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index 15d62df5..9723d6f 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_MAC802154) += mac802154.o -mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o wpan.o +mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \ + monitor.o wpan.o llsec.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c new file mode 100644 index 0000000..e4a2558 --- /dev/null +++ b/net/mac802154/llsec.c @@ -0,0 +1,1069 @@ +/* + * Copyright (C) 2014 Fraunhofer ITWM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Written by: + * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> + */ + +#include <linux/err.h> +#include <linux/bug.h> +#include <linux/completion.h> +#include <net/ieee802154.h> +#include <crypto/algapi.h> + +#include "mac802154.h" +#include "llsec.h" + +static void llsec_key_put(struct mac802154_llsec_key *key); +static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a, + const struct ieee802154_llsec_key_id *b); + +static void llsec_dev_free(struct mac802154_llsec_device *dev); + +void mac802154_llsec_init(struct mac802154_llsec *sec) +{ + memset(sec, 0, sizeof(*sec)); + + memset(&sec->params.default_key_source, 0xFF, IEEE802154_ADDR_LEN); + + INIT_LIST_HEAD(&sec->table.security_levels); + INIT_LIST_HEAD(&sec->table.devices); + INIT_LIST_HEAD(&sec->table.keys); + hash_init(sec->devices_short); + hash_init(sec->devices_hw); + rwlock_init(&sec->lock); +} + +void mac802154_llsec_destroy(struct mac802154_llsec *sec) +{ + struct ieee802154_llsec_seclevel *sl, *sn; + struct ieee802154_llsec_device *dev, *dn; + struct ieee802154_llsec_key_entry *key, *kn; + + list_for_each_entry_safe(sl, sn, &sec->table.security_levels, list) { + struct mac802154_llsec_seclevel *msl; + + msl = container_of(sl, struct mac802154_llsec_seclevel, level); + list_del(&sl->list); + kfree(msl); + } + + list_for_each_entry_safe(dev, dn, &sec->table.devices, list) { + struct mac802154_llsec_device *mdev; + + mdev = container_of(dev, struct mac802154_llsec_device, dev); + list_del(&dev->list); + llsec_dev_free(mdev); + } + + list_for_each_entry_safe(key, kn, &sec->table.keys, list) { + struct mac802154_llsec_key *mkey; + + mkey = container_of(key->key, struct mac802154_llsec_key, key); + list_del(&key->list); + llsec_key_put(mkey); + kfree(key); + } +} + + + +int mac802154_llsec_get_params(struct mac802154_llsec *sec, + struct ieee802154_llsec_params *params) +{ + read_lock_bh(&sec->lock); + *params = sec->params; + read_unlock_bh(&sec->lock); + + return 0; +} + +int mac802154_llsec_set_params(struct mac802154_llsec *sec, + const struct ieee802154_llsec_params *params, + int changed) +{ + write_lock_bh(&sec->lock); + + if (changed & IEEE802154_LLSEC_PARAM_ENABLED) + sec->params.enabled = params->enabled; + if (changed & IEEE802154_LLSEC_PARAM_FRAME_COUNTER) + sec->params.frame_counter = params->frame_counter; + if (changed & IEEE802154_LLSEC_PARAM_OUT_LEVEL) + sec->params.out_level = params->out_level; + if (changed & IEEE802154_LLSEC_PARAM_OUT_KEY) + sec->params.out_key = params->out_key; + if (changed & IEEE802154_LLSEC_PARAM_KEY_SOURCE) + sec->params.default_key_source = params->default_key_source; + if (changed & IEEE802154_LLSEC_PARAM_PAN_ID) + sec->params.pan_id = params->pan_id; + if (changed & IEEE802154_LLSEC_PARAM_HWADDR) + sec->params.hwaddr = params->hwaddr; + if (changed & IEEE802154_LLSEC_PARAM_COORD_HWADDR) + sec->params.coord_hwaddr = params->coord_hwaddr; + if (changed & IEEE802154_LLSEC_PARAM_COORD_SHORTADDR) + sec->params.coord_shortaddr = params->coord_shortaddr; + + write_unlock_bh(&sec->lock); + + return 0; +} + + + +static struct mac802154_llsec_key* +llsec_key_alloc(const struct ieee802154_llsec_key *template) +{ + const int authsizes[3] = { 4, 8, 16 }; + struct mac802154_llsec_key *key; + int i; + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return NULL; + + kref_init(&key->ref); + key->key = *template; + + BUILD_BUG_ON(ARRAY_SIZE(authsizes) != ARRAY_SIZE(key->tfm)); + + for (i = 0; i < ARRAY_SIZE(key->tfm); i++) { + key->tfm[i] = crypto_alloc_aead("ccm(aes)", 0, + CRYPTO_ALG_ASYNC); + if (!key->tfm[i]) + goto err_tfm; + if (crypto_aead_setkey(key->tfm[i], template->key, + IEEE802154_LLSEC_KEY_SIZE)) + goto err_tfm; + if (crypto_aead_setauthsize(key->tfm[i], authsizes[i])) + goto err_tfm; + } + + key->tfm0 = crypto_alloc_blkcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); + if (!key->tfm0) + goto err_tfm; + + if (crypto_blkcipher_setkey(key->tfm0, template->key, + IEEE802154_LLSEC_KEY_SIZE)) + goto err_tfm0; + + return key; + +err_tfm0: + crypto_free_blkcipher(key->tfm0); +err_tfm: + for (i = 0; i < ARRAY_SIZE(key->tfm); i++) + if (key->tfm[i]) + crypto_free_aead(key->tfm[i]); + + kfree(key); + return NULL; +} + +static void llsec_key_release(struct kref *ref) +{ + struct mac802154_llsec_key *key; + int i; + + key = container_of(ref, struct mac802154_llsec_key, ref); + + for (i = 0; i < ARRAY_SIZE(key->tfm); i++) + crypto_free_aead(key->tfm[i]); + + crypto_free_blkcipher(key->tfm0); + kfree(key); +} + +static struct mac802154_llsec_key* +llsec_key_get(struct mac802154_llsec_key *key) +{ + kref_get(&key->ref); + return key; +} + +static void llsec_key_put(struct mac802154_llsec_key *key) +{ + kref_put(&key->ref, llsec_key_release); +} + +static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a, + const struct ieee802154_llsec_key_id *b) +{ + if (a->mode != b->mode) + return false; + + if (a->mode == IEEE802154_SCF_KEY_IMPLICIT) + return ieee802154_addr_equal(&a->device_addr, &b->device_addr); + + if (a->id != b->id) + return false; + + switch (a->mode) { + case IEEE802154_SCF_KEY_INDEX: + return true; + case IEEE802154_SCF_KEY_SHORT_INDEX: + return a->short_source == b->short_source; + case IEEE802154_SCF_KEY_HW_INDEX: + return a->extended_source == b->extended_source; + } + + return false; +} + +int mac802154_llsec_key_add(struct mac802154_llsec *sec, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key) +{ + struct mac802154_llsec_key *mkey = NULL; + struct ieee802154_llsec_key_entry *pos, *new; + + if (!(key->frame_types & (1 << IEEE802154_FC_TYPE_MAC_CMD)) && + key->cmd_frame_ids) + return -EINVAL; + + list_for_each_entry(pos, &sec->table.keys, list) { + if (llsec_key_id_equal(&pos->id, id)) + return -EEXIST; + + if (memcmp(pos->key->key, key->key, + IEEE802154_LLSEC_KEY_SIZE)) + continue; + + mkey = container_of(pos->key, struct mac802154_llsec_key, key); + + /* Don't allow multiple instances of the same AES key to have + * different allowed frame types/command frame ids, as this is + * not possible in the 802.15.4 PIB. + */ + if (pos->key->frame_types != key->frame_types || + pos->key->cmd_frame_ids != key->cmd_frame_ids) + return -EEXIST; + + break; + } + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return -ENOMEM; + + if (!mkey) + mkey = llsec_key_alloc(key); + else + mkey = llsec_key_get(mkey); + + if (!mkey) + goto fail; + + new->id = *id; + new->key = &mkey->key; + + list_add_rcu(&new->list, &sec->table.keys); + + return 0; + +fail: + kfree(new); + return -ENOMEM; +} + +int mac802154_llsec_key_del(struct mac802154_llsec *sec, + const struct ieee802154_llsec_key_id *key) +{ + struct ieee802154_llsec_key_entry *pos; + + list_for_each_entry(pos, &sec->table.keys, list) { + struct mac802154_llsec_key *mkey; + + mkey = container_of(pos->key, struct mac802154_llsec_key, key); + + if (llsec_key_id_equal(&pos->id, key)) { + llsec_key_put(mkey); + return 0; + } + } + + return -ENOENT; +} + + + +static bool llsec_dev_use_shortaddr(__le16 short_addr) +{ + return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) && + short_addr != cpu_to_le16(0xffff); +} + +static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id) +{ + return ((__force u16) short_addr) << 16 | (__force u16) pan_id; +} + +static u64 llsec_dev_hash_long(__le64 hwaddr) +{ + return (__force u64) hwaddr; +} + +static struct mac802154_llsec_device* +llsec_dev_find_short(struct mac802154_llsec *sec, __le16 short_addr, + __le16 pan_id) +{ + struct mac802154_llsec_device *dev; + u32 key = llsec_dev_hash_short(short_addr, pan_id); + + hash_for_each_possible_rcu(sec->devices_short, dev, bucket_s, key) { + if (dev->dev.short_addr == short_addr && + dev->dev.pan_id == pan_id) + return dev; + } + + return NULL; +} + +static struct mac802154_llsec_device* +llsec_dev_find_long(struct mac802154_llsec *sec, __le64 hwaddr) +{ + struct mac802154_llsec_device *dev; + u64 key = llsec_dev_hash_long(hwaddr); + + hash_for_each_possible_rcu(sec->devices_hw, dev, bucket_hw, key) { + if (dev->dev.hwaddr == hwaddr) + return dev; + } + + return NULL; +} + +static void llsec_dev_free(struct mac802154_llsec_device *dev) +{ + struct ieee802154_llsec_device_key *pos, *pn; + struct mac802154_llsec_device_key *devkey; + + list_for_each_entry_safe(pos, pn, &dev->dev.keys, list) { + devkey = container_of(pos, struct mac802154_llsec_device_key, + devkey); + + list_del(&pos->list); + kfree(devkey); + } + + kfree(dev); +} + +int mac802154_llsec_dev_add(struct mac802154_llsec *sec, + const struct ieee802154_llsec_device *dev) +{ + struct mac802154_llsec_device *entry; + u32 skey = llsec_dev_hash_short(dev->short_addr, dev->pan_id); + u64 hwkey = llsec_dev_hash_long(dev->hwaddr); + + BUILD_BUG_ON(sizeof(hwkey) != IEEE802154_ADDR_LEN); + + if ((llsec_dev_use_shortaddr(dev->short_addr) && + llsec_dev_find_short(sec, dev->short_addr, dev->pan_id)) || + llsec_dev_find_long(sec, dev->hwaddr)) + return -EEXIST; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->dev = *dev; + spin_lock_init(&entry->lock); + INIT_LIST_HEAD(&entry->dev.keys); + + if (llsec_dev_use_shortaddr(dev->short_addr)) + hash_add_rcu(sec->devices_short, &entry->bucket_s, skey); + else + INIT_HLIST_NODE(&entry->bucket_s); + + hash_add_rcu(sec->devices_hw, &entry->bucket_hw, hwkey); + list_add_tail_rcu(&entry->dev.list, &sec->table.devices); + + return 0; +} + +static void llsec_dev_free_rcu(struct rcu_head *rcu) +{ + llsec_dev_free(container_of(rcu, struct mac802154_llsec_device, rcu)); +} + +int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr) +{ + struct mac802154_llsec_device *pos; + + pos = llsec_dev_find_long(sec, device_addr); + if (!pos) + return -ENOENT; + + hash_del_rcu(&pos->bucket_s); + hash_del_rcu(&pos->bucket_hw); + call_rcu(&pos->rcu, llsec_dev_free_rcu); + + return 0; +} + + + +static struct mac802154_llsec_device_key* +llsec_devkey_find(struct mac802154_llsec_device *dev, + const struct ieee802154_llsec_key_id *key) +{ + struct ieee802154_llsec_device_key *devkey; + + list_for_each_entry_rcu(devkey, &dev->dev.keys, list) { + if (!llsec_key_id_equal(key, &devkey->key_id)) + continue; + + return container_of(devkey, struct mac802154_llsec_device_key, + devkey); + } + + return NULL; +} + +int mac802154_llsec_devkey_add(struct mac802154_llsec *sec, + __le64 dev_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct mac802154_llsec_device *dev; + struct mac802154_llsec_device_key *devkey; + + dev = llsec_dev_find_long(sec, dev_addr); + + if (!dev) + return -ENOENT; + + if (llsec_devkey_find(dev, &key->key_id)) + return -EEXIST; + + devkey = kmalloc(sizeof(*devkey), GFP_KERNEL); + if (!devkey) + return -ENOMEM; + + devkey->devkey = *key; + list_add_tail_rcu(&devkey->devkey.list, &dev->dev.keys); + return 0; +} + +int mac802154_llsec_devkey_del(struct mac802154_llsec *sec, + __le64 dev_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct mac802154_llsec_device *dev; + struct mac802154_llsec_device_key *devkey; + + dev = llsec_dev_find_long(sec, dev_addr); + + if (!dev) + return -ENOENT; + + devkey = llsec_devkey_find(dev, &key->key_id); + if (!devkey) + return -ENOENT; + + list_del_rcu(&devkey->devkey.list); + kfree_rcu(devkey, rcu); + return 0; +} + + + +static struct mac802154_llsec_seclevel* +llsec_find_seclevel(const struct mac802154_llsec *sec, + const struct ieee802154_llsec_seclevel *sl) +{ + struct ieee802154_llsec_seclevel *pos; + + list_for_each_entry(pos, &sec->table.security_levels, list) { + if (pos->frame_type != sl->frame_type || + (pos->frame_type == IEEE802154_FC_TYPE_MAC_CMD && + pos->cmd_frame_id != sl->cmd_frame_id) || + pos->device_override != sl->device_override || + pos->sec_levels != sl->sec_levels) + continue; + + return container_of(pos, struct mac802154_llsec_seclevel, + level); + } + + return NULL; +} + +int mac802154_llsec_seclevel_add(struct mac802154_llsec *sec, + const struct ieee802154_llsec_seclevel *sl) +{ + struct mac802154_llsec_seclevel *entry; + + if (llsec_find_seclevel(sec, sl)) + return -EEXIST; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->level = *sl; + + list_add_tail_rcu(&entry->level.list, &sec->table.security_levels); + + return 0; +} + +int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, + const struct ieee802154_llsec_seclevel *sl) +{ + struct mac802154_llsec_seclevel *pos; + + pos = llsec_find_seclevel(sec, sl); + if (!pos) + return -ENOENT; + + list_del_rcu(&pos->level.list); + kfree_rcu(pos, rcu); + + return 0; +} + + + +static int llsec_recover_addr(struct mac802154_llsec *sec, + struct ieee802154_addr *addr) +{ + __le16 caddr = sec->params.coord_shortaddr; + addr->pan_id = sec->params.pan_id; + + if (caddr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) { + return -EINVAL; + } else if (caddr == cpu_to_le16(IEEE802154_ADDR_UNDEF)) { + addr->extended_addr = sec->params.coord_hwaddr; + addr->mode = IEEE802154_ADDR_LONG; + } else { + addr->short_addr = sec->params.coord_shortaddr; + addr->mode = IEEE802154_ADDR_SHORT; + } + + return 0; +} + +static struct mac802154_llsec_key* +llsec_lookup_key(struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + const struct ieee802154_addr *addr, + struct ieee802154_llsec_key_id *key_id) +{ + struct ieee802154_addr devaddr = *addr; + u8 key_id_mode = hdr->sec.key_id_mode; + struct ieee802154_llsec_key_entry *key_entry; + struct mac802154_llsec_key *key; + + if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT && + devaddr.mode == IEEE802154_ADDR_NONE) { + if (hdr->fc.type == IEEE802154_FC_TYPE_BEACON) { + devaddr.extended_addr = sec->params.coord_hwaddr; + devaddr.mode = IEEE802154_ADDR_LONG; + } else if (llsec_recover_addr(sec, &devaddr) < 0) { + return NULL; + } + } + + list_for_each_entry_rcu(key_entry, &sec->table.keys, list) { + const struct ieee802154_llsec_key_id *id = &key_entry->id; + + if (!(key_entry->key->frame_types & BIT(hdr->fc.type))) + continue; + + if (id->mode != key_id_mode) + continue; + + if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT) { + if (ieee802154_addr_equal(&devaddr, &id->device_addr)) + goto found; + } else { + if (id->id != hdr->sec.key_id) + continue; + + if ((key_id_mode == IEEE802154_SCF_KEY_INDEX) || + (key_id_mode == IEEE802154_SCF_KEY_SHORT_INDEX && + id->short_source == hdr->sec.short_src) || + (key_id_mode == IEEE802154_SCF_KEY_HW_INDEX && + id->extended_source == hdr->sec.extended_src)) + goto found; + } + } + + return NULL; + +found: + key = container_of(key_entry->key, struct mac802154_llsec_key, key); + if (key_id) + *key_id = key_entry->id; + return llsec_key_get(key); +} + + +static void llsec_geniv(u8 iv[16], __le64 addr, + const struct ieee802154_sechdr *sec) +{ + __be64 addr_bytes = (__force __be64) swab64((__force u64) addr); + __be32 frame_counter = (__force __be32) swab32((__force u32) sec->frame_counter); + + iv[0] = 1; /* L' = L - 1 = 1 */ + memcpy(iv + 1, &addr_bytes, sizeof(addr_bytes)); + memcpy(iv + 9, &frame_counter, sizeof(frame_counter)); + iv[13] = sec->level; + iv[14] = 0; + iv[15] = 1; +} + +static int +llsec_do_encrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + struct mac802154_llsec_key *key) +{ + u8 iv[16]; + struct scatterlist src; + struct blkcipher_desc req = { + .tfm = key->tfm0, + .info = iv, + .flags = 0, + }; + + llsec_geniv(iv, sec->params.hwaddr, &hdr->sec); + sg_init_one(&src, skb->data, skb->len); + return crypto_blkcipher_encrypt_iv(&req, &src, &src, skb->len); +} + +static struct crypto_aead* +llsec_tfm_by_len(struct mac802154_llsec_key *key, int authlen) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(key->tfm); i++) + if (crypto_aead_authsize(key->tfm[i]) == authlen) + return key->tfm[i]; + + BUG(); +} + +static int +llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + struct mac802154_llsec_key *key) +{ + u8 iv[16]; + unsigned char *data; + int authlen, assoclen, datalen, rc; + struct scatterlist src, assoc[2], dst[2]; + struct aead_request *req; + + authlen = ieee802154_sechdr_authtag_len(&hdr->sec); + llsec_geniv(iv, sec->params.hwaddr, &hdr->sec); + + req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC); + if (!req) + return -ENOMEM; + + sg_init_table(assoc, 2); + sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len); + assoclen = skb->mac_len; + + data = skb_mac_header(skb) + skb->mac_len; + datalen = skb_tail_pointer(skb) - data; + + if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) { + sg_set_buf(&assoc[1], data, 0); + } else { + sg_set_buf(&assoc[1], data, datalen); + assoclen += datalen; + datalen = 0; + } + + sg_init_one(&src, data, datalen); + + sg_init_table(dst, 2); + sg_set_buf(&dst[0], data, datalen); + sg_set_buf(&dst[1], skb_put(skb, authlen), authlen); + + aead_request_set_callback(req, 0, NULL, NULL); + aead_request_set_assoc(req, assoc, assoclen); + aead_request_set_crypt(req, &src, dst, datalen, iv); + + rc = crypto_aead_encrypt(req); + + kfree(req); + + return rc; +} + +static int llsec_do_encrypt(struct sk_buff *skb, + const struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + struct mac802154_llsec_key *key) +{ + if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) + return llsec_do_encrypt_unauth(skb, sec, hdr, key); + else + return llsec_do_encrypt_auth(skb, sec, hdr, key); +} + +int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) +{ + struct ieee802154_hdr hdr; + int rc, authlen, hlen; + struct mac802154_llsec_key *key; + u32 frame_ctr; + + hlen = ieee802154_hdr_pull(skb, &hdr); + + if (hlen < 0 || hdr.fc.type != IEEE802154_FC_TYPE_DATA) + return -EINVAL; + + if (!hdr.fc.security_enabled || hdr.sec.level == 0) { + skb_push(skb, hlen); + return 0; + } + + authlen = ieee802154_sechdr_authtag_len(&hdr.sec); + + if (skb->len + hlen + authlen + IEEE802154_MFR_SIZE > IEEE802154_MTU) + return -EMSGSIZE; + + rcu_read_lock(); + + read_lock_bh(&sec->lock); + + if (!sec->params.enabled) { + rc = -EINVAL; + goto fail_read; + } + + key = llsec_lookup_key(sec, &hdr, &hdr.dest, NULL); + if (!key) { + rc = -ENOKEY; + goto fail_read; + } + + read_unlock_bh(&sec->lock); + + write_lock_bh(&sec->lock); + + frame_ctr = be32_to_cpu(sec->params.frame_counter); + hdr.sec.frame_counter = cpu_to_le32(frame_ctr); + if (frame_ctr == 0xFFFFFFFF) { + write_unlock_bh(&sec->lock); + llsec_key_put(key); + rc = -EOVERFLOW; + goto fail; + } + + sec->params.frame_counter = cpu_to_be32(frame_ctr + 1); + + write_unlock_bh(&sec->lock); + + rcu_read_unlock(); + + skb->mac_len = ieee802154_hdr_push(skb, &hdr); + skb_reset_mac_header(skb); + + rc = llsec_do_encrypt(skb, sec, &hdr, key); + llsec_key_put(key); + + return rc; + +fail_read: + read_unlock_bh(&sec->lock); +fail: + rcu_read_unlock(); + return rc; +} + + + +static struct mac802154_llsec_device* +llsec_lookup_dev(struct mac802154_llsec *sec, + const struct ieee802154_addr *addr) +{ + struct ieee802154_addr devaddr = *addr; + struct mac802154_llsec_device *dev = NULL; + + if (devaddr.mode == IEEE802154_ADDR_NONE && + llsec_recover_addr(sec, &devaddr) < 0) + return NULL; + + if (devaddr.mode == IEEE802154_ADDR_SHORT) { + u32 key = llsec_dev_hash_short(devaddr.short_addr, + devaddr.pan_id); + + hash_for_each_possible_rcu(sec->devices_short, dev, + bucket_s, key) { + if (dev->dev.pan_id == devaddr.pan_id && + dev->dev.short_addr == devaddr.short_addr) + return dev; + } + } else { + u64 key = llsec_dev_hash_long(devaddr.extended_addr); + + hash_for_each_possible_rcu(sec->devices_hw, dev, + bucket_hw, key) { + if (dev->dev.hwaddr == devaddr.extended_addr) + return dev; + } + } + + return NULL; +} + +static int +llsec_lookup_seclevel(const struct mac802154_llsec *sec, + u8 frame_type, u8 cmd_frame_id, + struct ieee802154_llsec_seclevel *rlevel) +{ + struct ieee802154_llsec_seclevel *level; + + list_for_each_entry_rcu(level, &sec->table.security_levels, list) { + if (level->frame_type == frame_type && + (frame_type != IEEE802154_FC_TYPE_MAC_CMD || + level->cmd_frame_id == cmd_frame_id)) { + *rlevel = *level; + return 0; + } + } + + return -EINVAL; +} + +static int +llsec_do_decrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + struct mac802154_llsec_key *key, __le64 dev_addr) +{ + u8 iv[16]; + unsigned char *data; + int datalen; + struct scatterlist src; + struct blkcipher_desc req = { + .tfm = key->tfm0, + .info = iv, + .flags = 0, + }; + + llsec_geniv(iv, dev_addr, &hdr->sec); + data = skb_mac_header(skb) + skb->mac_len; + datalen = skb_tail_pointer(skb) - data; + + sg_init_one(&src, data, datalen); + + return crypto_blkcipher_decrypt_iv(&req, &src, &src, datalen); +} + +static int +llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + struct mac802154_llsec_key *key, __le64 dev_addr) +{ + u8 iv[16]; + unsigned char *data; + int authlen, datalen, assoclen, rc; + struct scatterlist src, assoc[2]; + struct aead_request *req; + + authlen = ieee802154_sechdr_authtag_len(&hdr->sec); + llsec_geniv(iv, dev_addr, &hdr->sec); + + req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC); + if (!req) + return -ENOMEM; + + sg_init_table(assoc, 2); + sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len); + assoclen = skb->mac_len; + + data = skb_mac_header(skb) + skb->mac_len; + datalen = skb_tail_pointer(skb) - data; + + if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) { + sg_set_buf(&assoc[1], data, 0); + } else { + sg_set_buf(&assoc[1], data, datalen - authlen); + assoclen += datalen - authlen; + data += datalen - authlen; + datalen = authlen; + } + + sg_init_one(&src, data, datalen); + + aead_request_set_callback(req, 0, NULL, NULL); + aead_request_set_assoc(req, assoc, assoclen); + aead_request_set_crypt(req, &src, &src, datalen, iv); + + rc = crypto_aead_decrypt(req); + + kfree(req); + skb_trim(skb, skb->len - authlen); + + return rc; +} + +static int +llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, + const struct ieee802154_hdr *hdr, + struct mac802154_llsec_key *key, __le64 dev_addr) +{ + if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) + return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr); + else + return llsec_do_decrypt_auth(skb, sec, hdr, key, dev_addr); +} + +static int +llsec_update_devkey_record(struct mac802154_llsec_device *dev, + const struct ieee802154_llsec_key_id *in_key) +{ + struct mac802154_llsec_device_key *devkey; + + devkey = llsec_devkey_find(dev, in_key); + + if (!devkey) { + struct mac802154_llsec_device_key *next; + + next = kzalloc(sizeof(*devkey), GFP_ATOMIC); + if (!next) + return -ENOMEM; + + next->devkey.key_id = *in_key; + + spin_lock_bh(&dev->lock); + + devkey = llsec_devkey_find(dev, in_key); + if (!devkey) + list_add_rcu(&next->devkey.list, &dev->dev.keys); + else + kfree(next); + + spin_unlock_bh(&dev->lock); + } + + return 0; +} + +static int +llsec_update_devkey_info(struct mac802154_llsec_device *dev, + const struct ieee802154_llsec_key_id *in_key, + u32 frame_counter) +{ + struct mac802154_llsec_device_key *devkey = NULL; + + if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RESTRICT) { + devkey = llsec_devkey_find(dev, in_key); + if (!devkey) + return -ENOENT; + } + + if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RECORD) { + int rc = llsec_update_devkey_record(dev, in_key); + + if (rc < 0) + return rc; + } + + spin_lock_bh(&dev->lock); + + if ((!devkey && frame_counter < dev->dev.frame_counter) || + (devkey && frame_counter < devkey->devkey.frame_counter)) { + spin_unlock_bh(&dev->lock); + return -EINVAL; + } + + if (devkey) + devkey->devkey.frame_counter = frame_counter + 1; + else + dev->dev.frame_counter = frame_counter + 1; + + spin_unlock_bh(&dev->lock); + + return 0; +} + +int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb) +{ + struct ieee802154_hdr hdr; + struct mac802154_llsec_key *key; + struct ieee802154_llsec_key_id key_id; + struct mac802154_llsec_device *dev; + struct ieee802154_llsec_seclevel seclevel; + int err; + __le64 dev_addr; + u32 frame_ctr; + + if (ieee802154_hdr_peek(skb, &hdr) < 0) + return -EINVAL; + if (!hdr.fc.security_enabled) + return 0; + if (hdr.fc.version == 0) + return -EINVAL; + + read_lock_bh(&sec->lock); + if (!sec->params.enabled) { + read_unlock_bh(&sec->lock); + return -EINVAL; + } + read_unlock_bh(&sec->lock); + + rcu_read_lock(); + + key = llsec_lookup_key(sec, &hdr, &hdr.source, &key_id); + if (!key) { + err = -ENOKEY; + goto fail; + } + + dev = llsec_lookup_dev(sec, &hdr.source); + if (!dev) { + err = -EINVAL; + goto fail_dev; + } + + if (llsec_lookup_seclevel(sec, hdr.fc.type, 0, &seclevel) < 0) { + err = -EINVAL; + goto fail_dev; + } + + if (!(seclevel.sec_levels & BIT(hdr.sec.level)) && + (hdr.sec.level == 0 && seclevel.device_override && + !dev->dev.seclevel_exempt)) { + err = -EINVAL; + goto fail_dev; + } + + frame_ctr = le32_to_cpu(hdr.sec.frame_counter); + + if (frame_ctr == 0xffffffff) { + err = -EOVERFLOW; + goto fail_dev; + } + + err = llsec_update_devkey_info(dev, &key_id, frame_ctr); + if (err) + goto fail_dev; + + dev_addr = dev->dev.hwaddr; + + rcu_read_unlock(); + + err = llsec_do_decrypt(skb, sec, &hdr, key, dev_addr); + llsec_key_put(key); + return err; + +fail_dev: + llsec_key_put(key); +fail: + rcu_read_unlock(); + return err; +} diff --git a/net/mac802154/llsec.h b/net/mac802154/llsec.h new file mode 100644 index 0000000..950578e --- /dev/null +++ b/net/mac802154/llsec.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014 Fraunhofer ITWM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Written by: + * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> + */ + +#ifndef MAC802154_LLSEC_H +#define MAC802154_LLSEC_H + +#include <linux/slab.h> +#include <linux/hashtable.h> +#include <linux/crypto.h> +#include <linux/kref.h> +#include <linux/spinlock.h> +#include <net/af_ieee802154.h> +#include <net/ieee802154_netdev.h> + +struct mac802154_llsec_key { + struct ieee802154_llsec_key key; + + /* one tfm for each authsize (4/8/16) */ + struct crypto_aead *tfm[3]; + struct crypto_blkcipher *tfm0; + + struct kref ref; +}; + +struct mac802154_llsec_device_key { + struct ieee802154_llsec_device_key devkey; + + struct rcu_head rcu; +}; + +struct mac802154_llsec_device { + struct ieee802154_llsec_device dev; + + struct hlist_node bucket_s; + struct hlist_node bucket_hw; + + /* protects dev.frame_counter and the elements of dev.keys */ + spinlock_t lock; + + struct rcu_head rcu; +}; + +struct mac802154_llsec_seclevel { + struct ieee802154_llsec_seclevel level; + + struct rcu_head rcu; +}; + +struct mac802154_llsec { + struct ieee802154_llsec_params params; + struct ieee802154_llsec_table table; + + DECLARE_HASHTABLE(devices_short, 6); + DECLARE_HASHTABLE(devices_hw, 6); + + /* protects params, all other fields are fine with RCU */ + rwlock_t lock; +}; + +void mac802154_llsec_init(struct mac802154_llsec *sec); +void mac802154_llsec_destroy(struct mac802154_llsec *sec); + +int mac802154_llsec_get_params(struct mac802154_llsec *sec, + struct ieee802154_llsec_params *params); +int mac802154_llsec_set_params(struct mac802154_llsec *sec, + const struct ieee802154_llsec_params *params, + int changed); + +int mac802154_llsec_key_add(struct mac802154_llsec *sec, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key); +int mac802154_llsec_key_del(struct mac802154_llsec *sec, + const struct ieee802154_llsec_key_id *key); + +int mac802154_llsec_dev_add(struct mac802154_llsec *sec, + const struct ieee802154_llsec_device *dev); +int mac802154_llsec_dev_del(struct mac802154_llsec *sec, + __le64 device_addr); + +int mac802154_llsec_devkey_add(struct mac802154_llsec *sec, + __le64 dev_addr, + const struct ieee802154_llsec_device_key *key); +int mac802154_llsec_devkey_del(struct mac802154_llsec *sec, + __le64 dev_addr, + const struct ieee802154_llsec_device_key *key); + +int mac802154_llsec_seclevel_add(struct mac802154_llsec *sec, + const struct ieee802154_llsec_seclevel *sl); +int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, + const struct ieee802154_llsec_seclevel *sl); + +int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb); +int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb); + +#endif /* MAC802154_LLSEC_H */ diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index 28ef59c..762a6f8 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -23,8 +23,12 @@ #ifndef MAC802154_H #define MAC802154_H +#include <linux/mutex.h> +#include <net/mac802154.h> #include <net/ieee802154_netdev.h> +#include "llsec.h" + /* mac802154 device private data */ struct mac802154_priv { struct ieee802154_dev hw; @@ -90,6 +94,13 @@ struct mac802154_sub_if_data { u8 bsn; /* MAC DSN field */ u8 dsn; + + /* protects sec from concurrent access by netlink. access by + * encrypt/decrypt/header_create safe without additional protection. + */ + struct mutex sec_mtx; + + struct mac802154_llsec sec; }; #define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw) @@ -125,4 +136,37 @@ int mac802154_set_mac_params(struct net_device *dev, void mac802154_get_mac_params(struct net_device *dev, struct ieee802154_mac_params *params); +int mac802154_get_params(struct net_device *dev, + struct ieee802154_llsec_params *params); +int mac802154_set_params(struct net_device *dev, + const struct ieee802154_llsec_params *params, + int changed); + +int mac802154_add_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key); +int mac802154_del_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id); + +int mac802154_add_dev(struct net_device *dev, + const struct ieee802154_llsec_device *llsec_dev); +int mac802154_del_dev(struct net_device *dev, __le64 dev_addr); + +int mac802154_add_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); +int mac802154_del_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); + +int mac802154_add_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); +int mac802154_del_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); + +void mac802154_lock_table(struct net_device *dev); +void mac802154_get_table(struct net_device *dev, + struct ieee802154_llsec_table **t); +void mac802154_unlock_table(struct net_device *dev); + #endif /* MAC802154_H */ diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index d40c092..bf80913 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -40,6 +40,9 @@ static int mac802154_mlme_start_req(struct net_device *dev, u8 pan_coord, u8 blx, u8 coord_realign) { + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + int rc = 0; + BUG_ON(addr->mode != IEEE802154_ADDR_SHORT); mac802154_dev_set_pan_id(dev, addr->pan_id); @@ -47,12 +50,31 @@ static int mac802154_mlme_start_req(struct net_device *dev, mac802154_dev_set_ieee_addr(dev); mac802154_dev_set_page_channel(dev, page, channel); + if (ops->llsec) { + struct ieee802154_llsec_params params; + int changed = 0; + + params.coord_shortaddr = addr->short_addr; + changed |= IEEE802154_LLSEC_PARAM_COORD_SHORTADDR; + + params.pan_id = addr->pan_id; + changed |= IEEE802154_LLSEC_PARAM_PAN_ID; + + params.hwaddr = ieee802154_devaddr_from_raw(dev->dev_addr); + changed |= IEEE802154_LLSEC_PARAM_HWADDR; + + params.coord_hwaddr = params.hwaddr; + changed |= IEEE802154_LLSEC_PARAM_COORD_HWADDR; + + rc = ops->llsec->set_params(dev, ¶ms, changed); + } + /* FIXME: add validation for unused parameters to be sane * for SoftMAC */ ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS); - return 0; + return rc; } static struct wpan_phy *mac802154_get_phy(const struct net_device *dev) @@ -64,6 +86,22 @@ static struct wpan_phy *mac802154_get_phy(const struct net_device *dev) return to_phy(get_device(&priv->hw->phy->dev)); } +static struct ieee802154_llsec_ops mac802154_llsec_ops = { + .get_params = mac802154_get_params, + .set_params = mac802154_set_params, + .add_key = mac802154_add_key, + .del_key = mac802154_del_key, + .add_dev = mac802154_add_dev, + .del_dev = mac802154_del_dev, + .add_devkey = mac802154_add_devkey, + .del_devkey = mac802154_del_devkey, + .add_seclevel = mac802154_add_seclevel, + .del_seclevel = mac802154_del_seclevel, + .lock_table = mac802154_lock_table, + .get_table = mac802154_get_table, + .unlock_table = mac802154_unlock_table, +}; + struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = { .get_phy = mac802154_get_phy, }; @@ -75,6 +113,8 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = { .get_short_addr = mac802154_dev_get_short_addr, .get_dsn = mac802154_dev_get_dsn, + .llsec = &mac802154_llsec_ops, + .set_mac_params = mac802154_set_mac_params, .get_mac_params = mac802154_get_mac_params, }; diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index f0991f2..15aa2f2 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -213,3 +213,190 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) } else mutex_unlock(&priv->hw->phy->pib_lock); } + + +int mac802154_get_params(struct net_device *dev, + struct ieee802154_llsec_params *params) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_get_params(&priv->sec, params); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_set_params(struct net_device *dev, + const struct ieee802154_llsec_params *params, + int changed) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_set_params(&priv->sec, params, changed); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_key_add(&priv->sec, id, key); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_key_del(&priv->sec, id); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_dev(struct net_device *dev, + const struct ieee802154_llsec_device *llsec_dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_dev_add(&priv->sec, llsec_dev); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_dev(struct net_device *dev, __le64 dev_addr) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_dev_del(&priv->sec, dev_addr); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_seclevel_add(&priv->sec, sl); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_seclevel_del(&priv->sec, sl); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +void mac802154_lock_table(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); +} + +void mac802154_get_table(struct net_device *dev, + struct ieee802154_llsec_table **t) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + *t = &priv->sec.table; +} + +void mac802154_unlock_table(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_unlock(&priv->sec_mtx); +} diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 03855b0..0597b96 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -59,8 +59,6 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) skb->protocol = htons(ETH_P_IEEE802154); skb_reset_mac_header(skb); - BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb)); - if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { u16 crc; diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 1df7a6a..23bc91c 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -35,6 +35,28 @@ #include "mac802154.h" +static int mac802154_wpan_update_llsec(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + int rc = 0; + + if (ops->llsec) { + struct ieee802154_llsec_params params; + int changed = 0; + + params.pan_id = priv->pan_id; + changed |= IEEE802154_LLSEC_PARAM_PAN_ID; + + params.hwaddr = priv->extended_addr; + changed |= IEEE802154_LLSEC_PARAM_HWADDR; + + rc = ops->llsec->set_params(dev, ¶ms, changed); + } + + return rc; +} + static int mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -81,7 +103,7 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) priv->pan_id = cpu_to_le16(sa->addr.pan_id); priv->short_addr = cpu_to_le16(sa->addr.short_addr); - err = 0; + err = mac802154_wpan_update_llsec(dev); break; } @@ -99,7 +121,7 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) /* FIXME: validate addr */ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); mac802154_dev_set_ieee_addr(dev); - return 0; + return mac802154_wpan_update_llsec(dev); } int mac802154_set_mac_params(struct net_device *dev, @@ -124,7 +146,7 @@ void mac802154_get_mac_params(struct net_device *dev, mutex_unlock(&priv->hw->slaves_mtx); } -int mac802154_wpan_open(struct net_device *dev) +static int mac802154_wpan_open(struct net_device *dev) { int rc; struct mac802154_sub_if_data *priv = netdev_priv(dev); @@ -183,6 +205,38 @@ out: return rc; } +static int mac802154_set_header_security(struct mac802154_sub_if_data *priv, + struct ieee802154_hdr *hdr, + const struct ieee802154_mac_cb *cb) +{ + struct ieee802154_llsec_params params; + u8 level; + + mac802154_llsec_get_params(&priv->sec, ¶ms); + + if (!params.enabled && cb->secen_override && cb->secen) + return -EINVAL; + if (!params.enabled || + (cb->secen_override && !cb->secen) || + !params.out_level) + return 0; + if (cb->seclevel_override && !cb->seclevel) + return -EINVAL; + + level = cb->seclevel_override ? cb->seclevel : params.out_level; + + hdr->fc.security_enabled = 1; + hdr->sec.level = level; + hdr->sec.key_id_mode = params.out_key.mode; + if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX) + hdr->sec.short_src = params.out_key.short_source; + else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX) + hdr->sec.extended_src = params.out_key.extended_source; + hdr->sec.key_id = params.out_key.id; + + return 0; +} + static int mac802154_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, @@ -192,15 +246,20 @@ static int mac802154_header_create(struct sk_buff *skb, { struct ieee802154_hdr hdr; struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_mac_cb *cb = mac_cb(skb); int hlen; if (!daddr) return -EINVAL; memset(&hdr.fc, 0, sizeof(hdr.fc)); - hdr.fc.type = mac_cb_type(skb); - hdr.fc.security_enabled = mac_cb_is_secen(skb); - hdr.fc.ack_request = mac_cb_is_ackreq(skb); + hdr.fc.type = cb->type; + hdr.fc.security_enabled = cb->secen; + hdr.fc.ack_request = cb->ackreq; + hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); + + if (mac802154_set_header_security(priv, &hdr, cb) < 0) + return -EINVAL; if (!saddr) { spin_lock_bh(&priv->mib_lock); @@ -231,7 +290,7 @@ static int mac802154_header_create(struct sk_buff *skb, skb_reset_mac_header(skb); skb->mac_len = hlen; - if (hlen + len + 2 > dev->mtu) + if (len > ieee802154_max_payload(&hdr)) return -EMSGSIZE; return hlen; @@ -257,6 +316,7 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) { struct mac802154_sub_if_data *priv; u8 chan, page; + int rc; priv = netdev_priv(dev); @@ -272,6 +332,13 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } + rc = mac802154_llsec_encrypt(&priv->sec, skb); + if (rc) { + pr_warn("encryption failed: %i\n", rc); + kfree_skb(skb); + return NETDEV_TX_OK; + } + skb->skb_iif = dev->ifindex; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; @@ -292,6 +359,15 @@ static const struct net_device_ops mac802154_wpan_ops = { .ndo_set_mac_address = mac802154_wpan_mac_addr, }; +static void mac802154_wpan_free(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + mac802154_llsec_destroy(&priv->sec); + + free_netdev(dev); +} + void mac802154_wpan_setup(struct net_device *dev) { struct mac802154_sub_if_data *priv; @@ -301,14 +377,14 @@ void mac802154_wpan_setup(struct net_device *dev) dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; dev->header_ops = &mac802154_header_ops; - dev->needed_tailroom = 2; /* FCS */ + dev->needed_tailroom = 2 + 16; /* FCS + MIC */ dev->mtu = IEEE802154_MTU; dev->tx_queue_len = 300; dev->type = ARPHRD_IEEE802154; dev->flags = IFF_NOARP | IFF_BROADCAST; dev->watchdog_timeo = 0; - dev->destructor = free_netdev; + dev->destructor = mac802154_wpan_free; dev->netdev_ops = &mac802154_wpan_ops; dev->ml_priv = &mac802154_mlme_wpan; @@ -319,6 +395,7 @@ void mac802154_wpan_setup(struct net_device *dev) priv->page = 0; spin_lock_init(&priv->mib_lock); + mutex_init(&priv->sec_mtx); get_random_bytes(&priv->bsn, 1); get_random_bytes(&priv->dsn, 1); @@ -331,6 +408,8 @@ void mac802154_wpan_setup(struct net_device *dev) priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + + mac802154_llsec_init(&priv->sec); } static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) @@ -339,9 +418,11 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) } static int -mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) +mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb, + const struct ieee802154_hdr *hdr) { __le16 span, sshort; + int rc; pr_debug("getting packet via slave interface %s\n", sdata->dev->name); @@ -388,15 +469,21 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) skb->dev = sdata->dev; + rc = mac802154_llsec_decrypt(&sdata->sec, skb); + if (rc) { + pr_debug("decryption failed: %i\n", rc); + return NET_RX_DROP; + } + sdata->dev->stats.rx_packets++; sdata->dev->stats.rx_bytes += skb->len; - switch (mac_cb_type(skb)) { + switch (mac_cb(skb)->type) { case IEEE802154_FC_TYPE_DATA: return mac802154_process_data(sdata->dev, skb); default: pr_warn("ieee802154: bad frame received (type = %d)\n", - mac_cb_type(skb)); + mac_cb(skb)->type); kfree_skb(skb); return NET_RX_DROP; } @@ -419,62 +506,58 @@ static void mac802154_print_addr(const char *name, } } -static int mac802154_parse_frame_start(struct sk_buff *skb) +static int mac802154_parse_frame_start(struct sk_buff *skb, + struct ieee802154_hdr *hdr) { int hlen; - struct ieee802154_hdr hdr; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); - hlen = ieee802154_hdr_pull(skb, &hdr); + hlen = ieee802154_hdr_pull(skb, hdr); if (hlen < 0) return -EINVAL; skb->mac_len = hlen; - pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc), - hdr.seq); - - mac_cb(skb)->flags = hdr.fc.type; + pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc), + hdr->seq); - if (hdr.fc.ack_request) - mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; - if (hdr.fc.security_enabled) - mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN; + cb->type = hdr->fc.type; + cb->ackreq = hdr->fc.ack_request; + cb->secen = hdr->fc.security_enabled; - mac802154_print_addr("destination", &hdr.dest); - mac802154_print_addr("source", &hdr.source); + mac802154_print_addr("destination", &hdr->dest); + mac802154_print_addr("source", &hdr->source); - mac_cb(skb)->source = hdr.source; - mac_cb(skb)->dest = hdr.dest; + cb->source = hdr->source; + cb->dest = hdr->dest; - if (hdr.fc.security_enabled) { + if (hdr->fc.security_enabled) { u64 key; - pr_debug("seclevel %i\n", hdr.sec.level); + pr_debug("seclevel %i\n", hdr->sec.level); - switch (hdr.sec.key_id_mode) { + switch (hdr->sec.key_id_mode) { case IEEE802154_SCF_KEY_IMPLICIT: pr_debug("implicit key\n"); break; case IEEE802154_SCF_KEY_INDEX: - pr_debug("key %02x\n", hdr.sec.key_id); + pr_debug("key %02x\n", hdr->sec.key_id); break; case IEEE802154_SCF_KEY_SHORT_INDEX: pr_debug("key %04x:%04x %02x\n", - le32_to_cpu(hdr.sec.short_src) >> 16, - le32_to_cpu(hdr.sec.short_src) & 0xffff, - hdr.sec.key_id); + le32_to_cpu(hdr->sec.short_src) >> 16, + le32_to_cpu(hdr->sec.short_src) & 0xffff, + hdr->sec.key_id); break; case IEEE802154_SCF_KEY_HW_INDEX: - key = swab64((__force u64) hdr.sec.extended_src); + key = swab64((__force u64) hdr->sec.extended_src); pr_debug("key source %8phC %02x\n", &key, - hdr.sec.key_id); + hdr->sec.key_id); break; } - - return -EINVAL; } return 0; @@ -485,8 +568,9 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) int ret; struct sk_buff *sskb; struct mac802154_sub_if_data *sdata; + struct ieee802154_hdr hdr; - ret = mac802154_parse_frame_start(skb); + ret = mac802154_parse_frame_start(skb, &hdr); if (ret) { pr_debug("got invalid frame\n"); return; @@ -499,7 +583,7 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) sskb = skb_clone(skb, GFP_ATOMIC); if (sskb) - mac802154_subif_frame(sdata, sskb); + mac802154_subif_frame(sdata, sskb, &hdr); } rcu_read_unlock(); } diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 4f26ee4..d9da8c4 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -97,7 +97,7 @@ const char *ip_vs_proto_name(unsigned int proto) return "ICMPv6"; #endif default: - sprintf(buf, "IP_%d", proto); + sprintf(buf, "IP_%u", proto); return buf; } } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 487b55e..73ba1cc 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -883,7 +883,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, iph->daddr = cp->daddr.ip; iph->saddr = saddr; iph->ttl = old_iph->ttl; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(skb, NULL); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 52ca952..09096a6 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -358,6 +358,19 @@ out: rcu_read_unlock(); } +struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct) +{ + struct nf_conn_nat *nat = nfct_nat(ct); + if (nat) + return nat; + + if (!nf_ct_is_confirmed(ct)) + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + + return nat; +} +EXPORT_SYMBOL_GPL(nf_ct_nat_ext_add); + unsigned int nf_nat_setup_info(struct nf_conn *ct, const struct nf_nat_range *range, @@ -368,14 +381,9 @@ nf_nat_setup_info(struct nf_conn *ct, struct nf_conn_nat *nat; /* nat helper or nfctnetlink also setup binding */ - nat = nfct_nat(ct); - if (!nat) { - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || maniptype == NF_NAT_MANIP_DST); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3fd159d..0478847 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -88,6 +88,45 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload) return ERR_PTR(-EAFNOSUPPORT); } +static void nft_ctx_init(struct nft_ctx *ctx, + const struct sk_buff *skb, + const struct nlmsghdr *nlh, + struct nft_af_info *afi, + struct nft_table *table, + struct nft_chain *chain, + const struct nlattr * const *nla) +{ + ctx->net = sock_net(skb->sk); + ctx->afi = afi; + ctx->table = table; + ctx->chain = chain; + ctx->nla = nla; + ctx->portid = NETLINK_CB(skb).portid; + ctx->report = nlmsg_report(nlh); + ctx->seq = nlh->nlmsg_seq; +} + +static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type, + u32 size) +{ + struct nft_trans *trans; + + trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL); + if (trans == NULL) + return NULL; + + trans->msg_type = msg_type; + trans->ctx = *ctx; + + return trans; +} + +static void nft_trans_destroy(struct nft_trans *trans) +{ + list_del(&trans->list); + kfree(trans); +} + /* * Tables */ @@ -197,20 +236,13 @@ nla_put_failure: return -1; } -static int nf_tables_table_notify(const struct sk_buff *oskb, - const struct nlmsghdr *nlh, - const struct nft_table *table, - int event, int family) +static int nf_tables_table_notify(const struct nft_ctx *ctx, int event) { struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - u32 seq = nlh ? nlh->nlmsg_seq : 0; - struct net *net = oskb ? sock_net(oskb->sk) : &init_net; - bool report; int err; - report = nlh ? nlmsg_report(nlh) : false; - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -218,18 +250,20 @@ static int nf_tables_table_notify(const struct sk_buff *oskb, if (skb == NULL) goto err; - err = nf_tables_fill_table_info(skb, portid, seq, event, 0, - family, table); + err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0, + ctx->afi->family, ctx->table); if (err < 0) { kfree_skb(skb); goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } return err; } @@ -269,6 +303,9 @@ done: return skb->len; } +/* Internal table flags */ +#define NFT_TABLE_INACTIVE (1 << 15) + static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -295,6 +332,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb2) @@ -343,7 +382,7 @@ err: return err; } -static int nf_tables_table_disable(const struct nft_af_info *afi, +static void nf_tables_table_disable(const struct nft_af_info *afi, struct nft_table *table) { struct nft_chain *chain; @@ -353,45 +392,63 @@ static int nf_tables_table_disable(const struct nft_af_info *afi, nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops); } - - return 0; } -static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[], - struct nft_af_info *afi, struct nft_table *table) +static int nf_tables_updtable(struct nft_ctx *ctx) { - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - int family = nfmsg->nfgen_family, ret = 0; + struct nft_trans *trans; + u32 flags; + int ret = 0; - if (nla[NFTA_TABLE_FLAGS]) { - u32 flags; + if (!ctx->nla[NFTA_TABLE_FLAGS]) + return 0; - flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); - if (flags & ~NFT_TABLE_F_DORMANT) - return -EINVAL; + flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS])); + if (flags & ~NFT_TABLE_F_DORMANT) + return -EINVAL; + + trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE, + sizeof(struct nft_trans_table)); + if (trans == NULL) + return -ENOMEM; - if ((flags & NFT_TABLE_F_DORMANT) && - !(table->flags & NFT_TABLE_F_DORMANT)) { - ret = nf_tables_table_disable(afi, table); - if (ret >= 0) - table->flags |= NFT_TABLE_F_DORMANT; - } else if (!(flags & NFT_TABLE_F_DORMANT) && - table->flags & NFT_TABLE_F_DORMANT) { - ret = nf_tables_table_enable(afi, table); - if (ret >= 0) - table->flags &= ~NFT_TABLE_F_DORMANT; + if ((flags & NFT_TABLE_F_DORMANT) && + !(ctx->table->flags & NFT_TABLE_F_DORMANT)) { + nft_trans_table_enable(trans) = false; + } else if (!(flags & NFT_TABLE_F_DORMANT) && + ctx->table->flags & NFT_TABLE_F_DORMANT) { + ret = nf_tables_table_enable(ctx->afi, ctx->table); + if (ret >= 0) { + ctx->table->flags &= ~NFT_TABLE_F_DORMANT; + nft_trans_table_enable(trans) = true; } - if (ret < 0) - goto err; } + if (ret < 0) + goto err; - nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); + nft_trans_table_update(trans) = true; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + return 0; err: + nft_trans_destroy(trans); return ret; } +static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWTABLE) + ctx->table->flags |= NFT_TABLE_INACTIVE; + + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + return 0; +} + static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -403,6 +460,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; u32 flags = 0; + struct nft_ctx ctx; + int err; afi = nf_tables_afinfo_lookup(net, family, true); if (IS_ERR(afi)) @@ -417,11 +476,15 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, } if (table != NULL) { + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; if (nlh->nlmsg_flags & NLM_F_EXCL) return -EEXIST; if (nlh->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; - return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table); + + nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); + return nf_tables_updtable(&ctx); } if (nla[NFTA_TABLE_FLAGS]) { @@ -444,8 +507,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, INIT_LIST_HEAD(&table->sets); table->flags = flags; + nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); + err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE); + if (err < 0) { + kfree(table); + module_put(afi->owner); + return err; + } list_add_tail(&table->list, &afi->tables); - nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); return 0; } @@ -457,7 +526,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, struct nft_af_info *afi; struct nft_table *table; struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; + int family = nfmsg->nfgen_family, err; + struct nft_ctx ctx; afi = nf_tables_afinfo_lookup(net, family, false); if (IS_ERR(afi)) @@ -466,17 +536,27 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; if (!list_empty(&table->chains) || !list_empty(&table->sets)) return -EBUSY; + nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); + err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE); + if (err < 0) + return err; + list_del(&table->list); - nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family); - kfree(table); - module_put(afi->owner); return 0; } +static void nf_tables_table_destroy(struct nft_ctx *ctx) +{ + kfree(ctx->table); + module_put(ctx->afi->owner); +} + int nft_register_chain_type(const struct nf_chain_type *ctype) { int err = 0; @@ -541,7 +621,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { .len = NFT_CHAIN_MAXNAMELEN - 1 }, [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, [NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, - [NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING }, + [NFTA_CHAIN_TYPE] = { .type = NLA_STRING }, [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, }; @@ -637,21 +717,13 @@ nla_put_failure: return -1; } -static int nf_tables_chain_notify(const struct sk_buff *oskb, - const struct nlmsghdr *nlh, - const struct nft_table *table, - const struct nft_chain *chain, - int event, int family) +static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event) { struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - struct net *net = oskb ? sock_net(oskb->sk) : &init_net; - u32 seq = nlh ? nlh->nlmsg_seq : 0; - bool report; int err; - report = nlh ? nlmsg_report(nlh) : false; - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -659,18 +731,21 @@ static int nf_tables_chain_notify(const struct sk_buff *oskb, if (skb == NULL) goto err; - err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family, - table, chain); + err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0, + ctx->afi->family, ctx->table, + ctx->chain); if (err < 0) { kfree_skb(skb); goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } return err; } @@ -740,10 +815,14 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); if (IS_ERR(chain)) return PTR_ERR(chain); + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb2) @@ -767,8 +846,7 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, }; -static int -nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) +static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr) { struct nlattr *tb[NFTA_COUNTER_MAX+1]; struct nft_stats __percpu *newstats; @@ -777,14 +855,14 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy); if (err < 0) - return err; + return ERR_PTR(err); if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS]) - return -EINVAL; + return ERR_PTR(-EINVAL); newstats = alloc_percpu(struct nft_stats); if (newstats == NULL) - return -ENOMEM; + return ERR_PTR(-ENOMEM); /* Restore old counters on this cpu, no problem. Per-cpu statistics * are not exposed to userspace. @@ -793,6 +871,12 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); + return newstats; +} + +static void nft_chain_stats_replace(struct nft_base_chain *chain, + struct nft_stats __percpu *newstats) +{ if (chain->stats) { struct nft_stats __percpu *oldstats = nft_dereference(chain->stats); @@ -802,17 +886,43 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) free_percpu(oldstats); } else rcu_assign_pointer(chain->stats, newstats); +} + +static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWCHAIN) + ctx->chain->flags |= NFT_CHAIN_INACTIVE; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); return 0; } +static void nf_tables_chain_destroy(struct nft_chain *chain) +{ + BUG_ON(chain->use > 0); + + if (chain->flags & NFT_BASE_CHAIN) { + module_put(nft_base_chain(chain)->type->owner); + free_percpu(nft_base_chain(chain)->stats); + kfree(nft_base_chain(chain)); + } else { + kfree(chain); + } +} + static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nlattr * uninitialized_var(name); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; struct nft_base_chain *basechain = NULL; @@ -822,8 +932,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, u8 policy = NF_ACCEPT; u64 handle = 0; unsigned int i; + struct nft_stats __percpu *stats; int err; bool create; + struct nft_ctx ctx; create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; @@ -869,6 +981,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, } if (chain != NULL) { + struct nft_stats *stats = NULL; + struct nft_trans *trans; + + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; if (nlh->nlmsg_flags & NLM_F_EXCL) return -EEXIST; if (nlh->nlmsg_flags & NLM_F_REPLACE) @@ -882,19 +999,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, if (!(chain->flags & NFT_BASE_CHAIN)) return -EOPNOTSUPP; - err = nf_tables_counters(nft_base_chain(chain), - nla[NFTA_CHAIN_COUNTERS]); - if (err < 0) - return err; + stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); + if (IS_ERR(stats)) + return PTR_ERR(stats); } - if (nla[NFTA_CHAIN_POLICY]) - nft_base_chain(chain)->policy = policy; + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN, + sizeof(struct nft_trans_chain)); + if (trans == NULL) + return -ENOMEM; - if (nla[NFTA_CHAIN_HANDLE] && name) - nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); + nft_trans_chain_stats(trans) = stats; + nft_trans_chain_update(trans) = true; - goto notify; + if (nla[NFTA_CHAIN_POLICY]) + nft_trans_chain_policy(trans) = policy; + else + nft_trans_chain_policy(trans) = -1; + + if (nla[NFTA_CHAIN_HANDLE] && name) { + nla_strlcpy(nft_trans_chain_name(trans), name, + NFT_CHAIN_MAXNAMELEN); + } + list_add_tail(&trans->list, &net->nft.commit_list); + return 0; } if (table->use == UINT_MAX) @@ -939,23 +1068,21 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, return -ENOMEM; if (nla[NFTA_CHAIN_COUNTERS]) { - err = nf_tables_counters(basechain, - nla[NFTA_CHAIN_COUNTERS]); - if (err < 0) { + stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); + if (IS_ERR(stats)) { module_put(type->owner); kfree(basechain); - return err; + return PTR_ERR(stats); } + basechain->stats = stats; } else { - struct nft_stats __percpu *newstats; - - newstats = alloc_percpu(struct nft_stats); - if (newstats == NULL) { + stats = alloc_percpu(struct nft_stats); + if (IS_ERR(stats)) { module_put(type->owner); kfree(basechain); - return -ENOMEM; + return PTR_ERR(stats); } - rcu_assign_pointer(basechain->stats, newstats); + rcu_assign_pointer(basechain->stats, stats); } basechain->type = type; @@ -992,31 +1119,26 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, if (!(table->flags & NFT_TABLE_F_DORMANT) && chain->flags & NFT_BASE_CHAIN) { err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); - if (err < 0) { - module_put(basechain->type->owner); - free_percpu(basechain->stats); - kfree(basechain); - return err; - } + if (err < 0) + goto err1; } - list_add_tail(&chain->list, &table->chains); - table->use++; -notify: - nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, - family); - return 0; -} -static void nf_tables_chain_destroy(struct nft_chain *chain) -{ - BUG_ON(chain->use > 0); + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN); + if (err < 0) + goto err2; - if (chain->flags & NFT_BASE_CHAIN) { - module_put(nft_base_chain(chain)->type->owner); - free_percpu(nft_base_chain(chain)->stats); - kfree(nft_base_chain(chain)); - } else - kfree(chain); + list_add_tail(&chain->list, &table->chains); + return 0; +err2: + if (!(table->flags & NFT_TABLE_F_DORMANT) && + chain->flags & NFT_BASE_CHAIN) { + nf_unregister_hooks(nft_base_chain(chain)->ops, + afi->nops); + } +err1: + nf_tables_chain_destroy(chain); + return err; } static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, @@ -1024,11 +1146,13 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; + struct nft_ctx ctx; + int err; afi = nf_tables_afinfo_lookup(net, family, false); if (IS_ERR(afi)) @@ -1037,48 +1161,26 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); if (IS_ERR(chain)) return PTR_ERR(chain); - + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; if (!list_empty(&chain->rules) || chain->use > 0) return -EBUSY; - list_del(&chain->list); - table->use--; - - if (!(table->flags & NFT_TABLE_F_DORMANT) && - chain->flags & NFT_BASE_CHAIN) - nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops); - - nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, - family); - - /* Make sure all rule references are gone before this is released */ - synchronize_rcu(); + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN); + if (err < 0) + return err; - nf_tables_chain_destroy(chain); + list_del(&chain->list); return 0; } -static void nft_ctx_init(struct nft_ctx *ctx, - const struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nft_af_info *afi, - const struct nft_table *table, - const struct nft_chain *chain, - const struct nlattr * const *nla) -{ - ctx->net = sock_net(skb->sk); - ctx->skb = skb; - ctx->nlh = nlh; - ctx->afi = afi; - ctx->table = table; - ctx->chain = chain; - ctx->nla = nla; -} - /* * Expressions */ @@ -1093,7 +1195,10 @@ static void nft_ctx_init(struct nft_ctx *ctx, int nft_register_expr(struct nft_expr_type *type) { nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_add_tail(&type->list, &nf_tables_expressions); + if (type->family == NFPROTO_UNSPEC) + list_add_tail(&type->list, &nf_tables_expressions); + else + list_add(&type->list, &nf_tables_expressions); nfnl_unlock(NFNL_SUBSYS_NFTABLES); return 0; } @@ -1361,22 +1466,15 @@ nla_put_failure: return -1; } -static int nf_tables_rule_notify(const struct sk_buff *oskb, - const struct nlmsghdr *nlh, - const struct nft_table *table, - const struct nft_chain *chain, +static int nf_tables_rule_notify(const struct nft_ctx *ctx, const struct nft_rule *rule, - int event, u32 flags, int family) + int event) { struct sk_buff *skb; - u32 portid = NETLINK_CB(oskb).portid; - struct net *net = oskb ? sock_net(oskb->sk) : &init_net; - u32 seq = nlh->nlmsg_seq; - bool report; int err; - report = nlmsg_report(nlh); - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -1384,18 +1482,21 @@ static int nf_tables_rule_notify(const struct sk_buff *oskb, if (skb == NULL) goto err; - err = nf_tables_fill_rule_info(skb, portid, seq, event, flags, - family, table, chain, rule); + err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0, + ctx->afi->family, ctx->table, + ctx->chain, rule); if (err < 0) { kfree_skb(skb); goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } return err; } @@ -1511,10 +1612,14 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); if (IS_ERR(chain)) return PTR_ERR(chain); + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); if (IS_ERR(rule)) @@ -1554,37 +1659,36 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, kfree(rule); } -#define NFT_RULE_MAXEXPRS 128 - -static struct nft_expr_info *info; - -static struct nft_rule_trans * -nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule) +static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type, + struct nft_rule *rule) { - struct nft_rule_trans *rupd; + struct nft_trans *trans; - rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL); - if (rupd == NULL) - return NULL; + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule)); + if (trans == NULL) + return NULL; - rupd->ctx = *ctx; - rupd->rule = rule; - list_add_tail(&rupd->list, &ctx->net->nft.commit_list); + nft_trans_rule(trans) = rule; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); - return rupd; + return trans; } +#define NFT_RULE_MAXEXPRS 128 + +static struct nft_expr_info *info; + static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct net *net = sock_net(skb->sk); struct nft_table *table; struct nft_chain *chain; struct nft_rule *rule, *old_rule = NULL; - struct nft_rule_trans *repl = NULL; + struct nft_trans *trans = NULL; struct nft_expr *expr; struct nft_ctx ctx; struct nlattr *tmp; @@ -1682,8 +1786,9 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_REPLACE) { if (nft_rule_is_active_next(net, old_rule)) { - repl = nf_tables_trans_add(&ctx, old_rule); - if (repl == NULL) { + trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, + old_rule); + if (trans == NULL) { err = -ENOMEM; goto err2; } @@ -1705,7 +1810,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, list_add_rcu(&rule->list, &chain->rules); } - if (nf_tables_trans_add(&ctx, rule) == NULL) { + if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) { err = -ENOMEM; goto err3; } @@ -1713,11 +1818,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, err3: list_del_rcu(&rule->list); - if (repl) { - list_del_rcu(&repl->rule->list); - list_del(&repl->list); - nft_rule_clear(net, repl->rule); - kfree(repl); + if (trans) { + list_del_rcu(&nft_trans_rule(trans)->list); + nft_rule_clear(net, nft_trans_rule(trans)); + nft_trans_destroy(trans); } err2: nf_tables_rule_destroy(&ctx, rule); @@ -1734,7 +1838,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule) { /* You cannot delete the same rule twice */ if (nft_rule_is_active_next(ctx->net, rule)) { - if (nf_tables_trans_add(ctx, rule) == NULL) + if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL) return -ENOMEM; nft_rule_disactivate_next(ctx->net, rule); return 0; @@ -1760,9 +1864,9 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct net *net = sock_net(skb->sk); - const struct nft_table *table; + struct nft_table *table; struct nft_chain *chain = NULL; struct nft_rule *rule; int family = nfmsg->nfgen_family, err = 0; @@ -1775,6 +1879,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; if (nla[NFTA_RULE_CHAIN]) { chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); @@ -1807,88 +1913,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, return err; } -static int nf_tables_commit(struct sk_buff *skb) -{ - struct net *net = sock_net(skb->sk); - struct nft_rule_trans *rupd, *tmp; - - /* Bump generation counter, invalidate any dump in progress */ - net->nft.genctr++; - - /* A new generation has just started */ - net->nft.gencursor = gencursor_next(net); - - /* Make sure all packets have left the previous generation before - * purging old rules. - */ - synchronize_rcu(); - - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - /* This rule was inactive in the past and just became active. - * Clear the next bit of the genmask since its meaning has - * changed, now it is the future. - */ - if (nft_rule_is_active(net, rupd->rule)) { - nft_rule_clear(net, rupd->rule); - nf_tables_rule_notify(skb, rupd->ctx.nlh, - rupd->ctx.table, rupd->ctx.chain, - rupd->rule, NFT_MSG_NEWRULE, 0, - rupd->ctx.afi->family); - list_del(&rupd->list); - kfree(rupd); - continue; - } - - /* This rule is in the past, get rid of it */ - list_del_rcu(&rupd->rule->list); - nf_tables_rule_notify(skb, rupd->ctx.nlh, - rupd->ctx.table, rupd->ctx.chain, - rupd->rule, NFT_MSG_DELRULE, 0, - rupd->ctx.afi->family); - } - - /* Make sure we don't see any packet traversing old rules */ - synchronize_rcu(); - - /* Now we can safely release unused old rules */ - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - nf_tables_rule_destroy(&rupd->ctx, rupd->rule); - list_del(&rupd->list); - kfree(rupd); - } - - return 0; -} - -static int nf_tables_abort(struct sk_buff *skb) -{ - struct net *net = sock_net(skb->sk); - struct nft_rule_trans *rupd, *tmp; - - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - if (!nft_rule_is_active_next(net, rupd->rule)) { - nft_rule_clear(net, rupd->rule); - list_del(&rupd->list); - kfree(rupd); - continue; - } - - /* This rule is inactive, get rid of it */ - list_del_rcu(&rupd->rule->list); - } - - /* Make sure we don't see any packet accessing aborted rules */ - synchronize_rcu(); - - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - nf_tables_rule_destroy(&rupd->ctx, rupd->rule); - list_del(&rupd->list); - kfree(rupd); - } - - return 0; -} - /* * Sets */ @@ -1912,9 +1936,18 @@ void nft_unregister_set(struct nft_set_ops *ops) } EXPORT_SYMBOL_GPL(nft_unregister_set); -static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[]) +/* + * Select a set implementation based on the data characteristics and the + * given policy. The total memory use might not be known if no size is + * given, in that case the amount of memory per element is used. + */ +static const struct nft_set_ops * +nft_select_set_ops(const struct nlattr * const nla[], + const struct nft_set_desc *desc, + enum nft_set_policies policy) { - const struct nft_set_ops *ops; + const struct nft_set_ops *ops, *bops; + struct nft_set_estimate est, best; u32 features; #ifdef CONFIG_MODULES @@ -1932,15 +1965,45 @@ static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const features &= NFT_SET_INTERVAL | NFT_SET_MAP; } - // FIXME: implement selection properly + bops = NULL; + best.size = ~0; + best.class = ~0; + list_for_each_entry(ops, &nf_tables_set_ops, list) { if ((ops->features & features) != features) continue; + if (!ops->estimate(desc, features, &est)) + continue; + + switch (policy) { + case NFT_SET_POL_PERFORMANCE: + if (est.class < best.class) + break; + if (est.class == best.class && est.size < best.size) + break; + continue; + case NFT_SET_POL_MEMORY: + if (est.size < best.size) + break; + if (est.size == best.size && est.class < best.class) + break; + continue; + default: + break; + } + if (!try_module_get(ops->owner)) continue; - return ops; + if (bops != NULL) + module_put(bops->owner); + + bops = ops; + best = est; } + if (bops != NULL) + return bops; + return ERR_PTR(-EOPNOTSUPP); } @@ -1953,6 +2016,13 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_KEY_LEN] = { .type = NLA_U32 }, [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 }, [NFTA_SET_DATA_LEN] = { .type = NLA_U32 }, + [NFTA_SET_POLICY] = { .type = NLA_U32 }, + [NFTA_SET_DESC] = { .type = NLA_NESTED }, + [NFTA_SET_ID] = { .type = NLA_U32 }, +}; + +static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { + [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 }, }; static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, @@ -1962,8 +2032,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, { struct net *net = sock_net(skb->sk); const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi = NULL; - const struct nft_table *table = NULL; + struct nft_af_info *afi = NULL; + struct nft_table *table = NULL; if (nfmsg->nfgen_family != NFPROTO_UNSPEC) { afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); @@ -1978,6 +2048,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; } nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); @@ -1999,13 +2071,27 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table, return ERR_PTR(-ENOENT); } +struct nft_set *nf_tables_set_lookup_byid(const struct net *net, + const struct nlattr *nla) +{ + struct nft_trans *trans; + u32 id = ntohl(nla_get_be32(nla)); + + list_for_each_entry(trans, &net->nft.commit_list, list) { + if (trans->msg_type == NFT_MSG_NEWSET && + id == nft_trans_set_id(trans)) + return nft_trans_set(trans); + } + return ERR_PTR(-ENOENT); +} + static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, const char *name) { const struct nft_set *i; const char *p; unsigned long *inuse; - unsigned int n = 0; + unsigned int n = 0, min = 0; p = strnchr(name, IFNAMSIZ, '%'); if (p != NULL) { @@ -2015,23 +2101,28 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL); if (inuse == NULL) return -ENOMEM; - +cont: list_for_each_entry(i, &ctx->table->sets, list) { int tmp; if (!sscanf(i->name, name, &tmp)) continue; - if (tmp < 0 || tmp >= BITS_PER_BYTE * PAGE_SIZE) + if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE) continue; - set_bit(tmp, inuse); + set_bit(tmp - min, inuse); } n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE); + if (n >= BITS_PER_BYTE * PAGE_SIZE) { + min += BITS_PER_BYTE * PAGE_SIZE; + memset(inuse, 0, PAGE_SIZE); + goto cont; + } free_page((unsigned long)inuse); } - snprintf(set->name, sizeof(set->name), name, n); + snprintf(set->name, sizeof(set->name), name, min + n); list_for_each_entry(i, &ctx->table->sets, list) { if (!strcmp(set->name, i->name)) return -ENFILE; @@ -2044,8 +2135,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, { struct nfgenmsg *nfmsg; struct nlmsghdr *nlh; - u32 portid = NETLINK_CB(ctx->skb).portid; - u32 seq = ctx->nlh->nlmsg_seq; + struct nlattr *desc; + u32 portid = ctx->portid; + u32 seq = ctx->seq; event |= NFNL_SUBSYS_NFTABLES << 8; nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), @@ -2077,6 +2169,14 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, goto nla_put_failure; } + desc = nla_nest_start(skb, NFTA_SET_DESC); + if (desc == NULL) + goto nla_put_failure; + if (set->size && + nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size))) + goto nla_put_failure; + nla_nest_end(skb, desc); + return nlmsg_end(skb, nlh); nla_put_failure: @@ -2089,12 +2189,11 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx, int event) { struct sk_buff *skb; - u32 portid = NETLINK_CB(ctx->skb).portid; - bool report; + u32 portid = ctx->portid; int err; - report = nlmsg_report(ctx->nlh); - if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -2108,8 +2207,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx, goto err; } - err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: if (err < 0) nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err); @@ -2183,7 +2282,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, { const struct nft_set *set; unsigned int idx, s_idx = cb->args[0]; - const struct nft_af_info *afi; + struct nft_af_info *afi; struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; struct net *net = sock_net(skb->sk); int cur_family = cb->args[3]; @@ -2260,6 +2359,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb) return ret; } +#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */ + static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2289,6 +2390,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (skb2 == NULL) @@ -2305,13 +2408,50 @@ err: return err; } +static int nf_tables_set_desc_parse(const struct nft_ctx *ctx, + struct nft_set_desc *desc, + const struct nlattr *nla) +{ + struct nlattr *da[NFTA_SET_DESC_MAX + 1]; + int err; + + err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy); + if (err < 0) + return err; + + if (da[NFTA_SET_DESC_SIZE] != NULL) + desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE])); + + return 0; +} + +static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type, + struct nft_set *set) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) { + nft_trans_set_id(trans) = + ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID])); + set->flags |= NFT_SET_INACTIVE; + } + nft_trans_set(trans) = set; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + + return 0; +} + static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nft_set_ops *ops; - const struct nft_af_info *afi; + struct nft_af_info *afi; struct net *net = sock_net(skb->sk); struct nft_table *table; struct nft_set *set; @@ -2319,14 +2459,18 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, char name[IFNAMSIZ]; unsigned int size; bool create; - u32 ktype, klen, dlen, dtype, flags; + u32 ktype, dtype, flags, policy; + struct nft_set_desc desc; int err; if (nla[NFTA_SET_TABLE] == NULL || nla[NFTA_SET_NAME] == NULL || - nla[NFTA_SET_KEY_LEN] == NULL) + nla[NFTA_SET_KEY_LEN] == NULL || + nla[NFTA_SET_ID] == NULL) return -EINVAL; + memset(&desc, 0, sizeof(desc)); + ktype = NFT_DATA_VALUE; if (nla[NFTA_SET_KEY_TYPE] != NULL) { ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE])); @@ -2334,8 +2478,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, return -EINVAL; } - klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); - if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data)) + desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); + if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data)) return -EINVAL; flags = 0; @@ -2347,7 +2491,6 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, } dtype = 0; - dlen = 0; if (nla[NFTA_SET_DATA_TYPE] != NULL) { if (!(flags & NFT_SET_MAP)) return -EINVAL; @@ -2360,15 +2503,25 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (dtype != NFT_DATA_VERDICT) { if (nla[NFTA_SET_DATA_LEN] == NULL) return -EINVAL; - dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); - if (dlen == 0 || - dlen > FIELD_SIZEOF(struct nft_data, data)) + desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); + if (desc.dlen == 0 || + desc.dlen > FIELD_SIZEOF(struct nft_data, data)) return -EINVAL; } else - dlen = sizeof(struct nft_data); + desc.dlen = sizeof(struct nft_data); } else if (flags & NFT_SET_MAP) return -EINVAL; + policy = NFT_SET_POL_PERFORMANCE; + if (nla[NFTA_SET_POLICY] != NULL) + policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY])); + + if (nla[NFTA_SET_DESC] != NULL) { + err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]); + if (err < 0) + return err; + } + create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); @@ -2399,7 +2552,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (!(nlh->nlmsg_flags & NLM_F_CREATE)) return -ENOENT; - ops = nft_select_set_ops(nla); + ops = nft_select_set_ops(nla, &desc, policy); if (IS_ERR(ops)) return PTR_ERR(ops); @@ -2420,17 +2573,21 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, INIT_LIST_HEAD(&set->bindings); set->ops = ops; set->ktype = ktype; - set->klen = klen; + set->klen = desc.klen; set->dtype = dtype; - set->dlen = dlen; + set->dlen = desc.dlen; set->flags = flags; + set->size = desc.size; - err = ops->init(set, nla); + err = ops->init(set, &desc, nla); + if (err < 0) + goto err2; + + err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set); if (err < 0) goto err2; list_add_tail(&set->list, &table->sets); - nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET); return 0; err2: @@ -2440,16 +2597,20 @@ err1: return err; } -static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) +static void nft_set_destroy(struct nft_set *set) { - list_del(&set->list); - nf_tables_set_notify(ctx, set, NFT_MSG_DELSET); - set->ops->destroy(set); module_put(set->ops->owner); kfree(set); } +static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) +{ + list_del(&set->list); + nf_tables_set_notify(ctx, set, NFT_MSG_DELSET); + nft_set_destroy(set); +} + static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2471,10 +2632,16 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; if (!list_empty(&set->bindings)) return -EBUSY; - nf_tables_set_destroy(&ctx, set); + err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set); + if (err < 0) + return err; + + list_del(&set->list); return 0; } @@ -2534,7 +2701,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, { list_del(&binding->list); - if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) + if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS && + !(set->flags & NFT_SET_INACTIVE)) nf_tables_set_destroy(ctx, set); } @@ -2552,16 +2720,18 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING }, [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING }, [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 }, }; static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, const struct sk_buff *skb, const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) + const struct nlattr * const nla[], + bool trans) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table; + struct nft_af_info *afi; + struct nft_table *table; struct net *net = sock_net(skb->sk); afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); @@ -2571,6 +2741,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (!trans && (table->flags & NFT_TABLE_INACTIVE)) + return -ENOENT; nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); return 0; @@ -2644,13 +2816,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) if (err < 0) return err; - err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla); + err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla, + false); if (err < 0) return err; set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; event = NFT_MSG_NEWSETELEM; event |= NFNL_SUBSYS_NFTABLES << 8; @@ -2707,13 +2882,15 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, struct nft_ctx ctx; int err; - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false); if (err < 0) return err; set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; if (nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { @@ -2724,7 +2901,98 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, return -EOPNOTSUPP; } -static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, +static int nf_tables_fill_setelem_info(struct sk_buff *skb, + const struct nft_ctx *ctx, u32 seq, + u32 portid, int event, u16 flags, + const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + struct nlattr *nest; + int err; + + event |= NFNL_SUBSYS_NFTABLES << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), + flags); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = ctx->afi->family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_SET_NAME, set->name)) + goto nla_put_failure; + + nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS); + if (nest == NULL) + goto nla_put_failure; + + err = nf_tables_fill_setelem(skb, set, elem); + if (err < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest); + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_trim(skb, nlh); + return -1; +} + +static int nf_tables_setelem_notify(const struct nft_ctx *ctx, + const struct nft_set *set, + const struct nft_set_elem *elem, + int event, u16 flags) +{ + struct net *net = ctx->net; + u32 portid = ctx->portid; + struct sk_buff *skb; + int err; + + if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + return 0; + + err = -ENOBUFS; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err; + + err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags, + set, elem); + if (err < 0) { + kfree_skb(skb); + goto err; + } + + err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report, + GFP_KERNEL); +err: + if (err < 0) + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + return err; +} + +static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx, + int msg_type, + struct nft_set *set) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem)); + if (trans == NULL) + return NULL; + + nft_trans_elem_set(trans) = set; + return trans; +} + +static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr) { struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; @@ -2732,8 +3000,12 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_elem elem; struct nft_set_binding *binding; enum nft_registers dreg; + struct nft_trans *trans; int err; + if (set->size && set->nelems == set->size) + return -ENFILE; + err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, nft_set_elem_policy); if (err < 0) @@ -2786,7 +3058,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, struct nft_ctx bind_ctx = { .afi = ctx->afi, .table = ctx->table, - .chain = binding->chain, + .chain = (struct nft_chain *)binding->chain, }; err = nft_validate_data_load(&bind_ctx, dreg, @@ -2796,12 +3068,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, } } + trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); + if (trans == NULL) + goto err3; + err = set->ops->insert(set, &elem); if (err < 0) - goto err3; + goto err4; + nft_trans_elem(trans) = elem; + list_add(&trans->list, &ctx->net->nft.commit_list); return 0; +err4: + kfree(trans); err3: if (nla[NFTA_SET_ELEM_DATA] != NULL) nft_data_uninit(&elem.data, d2.type); @@ -2815,35 +3095,44 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) { + struct net *net = sock_net(skb->sk); const struct nlattr *attr; struct nft_set *set; struct nft_ctx ctx; - int rem, err; + int rem, err = 0; - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true); if (err < 0) return err; set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); + if (IS_ERR(set)) { + if (nla[NFTA_SET_ELEM_LIST_SET_ID]) { + set = nf_tables_set_lookup_byid(net, + nla[NFTA_SET_ELEM_LIST_SET_ID]); + } + if (IS_ERR(set)) + return PTR_ERR(set); + } + if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) return -EBUSY; nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { err = nft_add_set_elem(&ctx, set, attr); if (err < 0) - return err; + break; } - return 0; + return err; } -static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, +static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr) { struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nft_data_desc desc; struct nft_set_elem elem; + struct nft_trans *trans; int err; err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, @@ -2867,7 +3156,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, if (err < 0) goto err2; - set->ops->remove(set, &elem); + trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set); + if (trans == NULL) + goto err2; + + nft_trans_elem(trans) = elem; + list_add(&trans->list, &ctx->net->nft.commit_list); nft_data_uninit(&elem.key, NFT_DATA_VALUE); if (set->flags & NFT_SET_MAP) @@ -2886,9 +3180,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, const struct nlattr *attr; struct nft_set *set; struct nft_ctx ctx; - int rem, err; + int rem, err = 0; - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false); if (err < 0) return err; @@ -2901,14 +3195,14 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { err = nft_del_setelem(&ctx, set, attr); if (err < 0) - return err; + break; } - return 0; + return err; } static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { [NFT_MSG_NEWTABLE] = { - .call = nf_tables_newtable, + .call_batch = nf_tables_newtable, .attr_count = NFTA_TABLE_MAX, .policy = nft_table_policy, }, @@ -2918,12 +3212,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_table_policy, }, [NFT_MSG_DELTABLE] = { - .call = nf_tables_deltable, + .call_batch = nf_tables_deltable, .attr_count = NFTA_TABLE_MAX, .policy = nft_table_policy, }, [NFT_MSG_NEWCHAIN] = { - .call = nf_tables_newchain, + .call_batch = nf_tables_newchain, .attr_count = NFTA_CHAIN_MAX, .policy = nft_chain_policy, }, @@ -2933,7 +3227,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_chain_policy, }, [NFT_MSG_DELCHAIN] = { - .call = nf_tables_delchain, + .call_batch = nf_tables_delchain, .attr_count = NFTA_CHAIN_MAX, .policy = nft_chain_policy, }, @@ -2953,7 +3247,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_rule_policy, }, [NFT_MSG_NEWSET] = { - .call = nf_tables_newset, + .call_batch = nf_tables_newset, .attr_count = NFTA_SET_MAX, .policy = nft_set_policy, }, @@ -2963,12 +3257,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_set_policy, }, [NFT_MSG_DELSET] = { - .call = nf_tables_delset, + .call_batch = nf_tables_delset, .attr_count = NFTA_SET_MAX, .policy = nft_set_policy, }, [NFT_MSG_NEWSETELEM] = { - .call = nf_tables_newsetelem, + .call_batch = nf_tables_newsetelem, .attr_count = NFTA_SET_ELEM_LIST_MAX, .policy = nft_set_elem_list_policy, }, @@ -2978,12 +3272,270 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_set_elem_list_policy, }, [NFT_MSG_DELSETELEM] = { - .call = nf_tables_delsetelem, + .call_batch = nf_tables_delsetelem, .attr_count = NFTA_SET_ELEM_LIST_MAX, .policy = nft_set_elem_list_policy, }, }; +static void nft_chain_commit_update(struct nft_trans *trans) +{ + struct nft_base_chain *basechain; + + if (nft_trans_chain_name(trans)[0]) + strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans)); + + if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN)) + return; + + basechain = nft_base_chain(trans->ctx.chain); + nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans)); + + switch (nft_trans_chain_policy(trans)) { + case NF_DROP: + case NF_ACCEPT: + basechain->policy = nft_trans_chain_policy(trans); + break; + } +} + +/* Schedule objects for release via rcu to make sure no packets are accesing + * removed rules. + */ +static void nf_tables_commit_release_rcu(struct rcu_head *rt) +{ + struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); + + switch (trans->msg_type) { + case NFT_MSG_DELTABLE: + nf_tables_table_destroy(&trans->ctx); + break; + case NFT_MSG_DELCHAIN: + nf_tables_chain_destroy(trans->ctx.chain); + break; + case NFT_MSG_DELRULE: + nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); + break; + case NFT_MSG_DELSET: + nft_set_destroy(nft_trans_set(trans)); + break; + } + kfree(trans); +} + +static int nf_tables_commit(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + struct nft_trans *trans, *next; + struct nft_set *set; + + /* Bump generation counter, invalidate any dump in progress */ + net->nft.genctr++; + + /* A new generation has just started */ + net->nft.gencursor = gencursor_next(net); + + /* Make sure all packets have left the previous generation before + * purging old rules. + */ + synchronize_rcu(); + + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + if (nft_trans_table_update(trans)) { + if (!nft_trans_table_enable(trans)) { + nf_tables_table_disable(trans->ctx.afi, + trans->ctx.table); + trans->ctx.table->flags |= NFT_TABLE_F_DORMANT; + } + } else { + trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE; + } + nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELTABLE: + nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE); + break; + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain_update(trans)) + nft_chain_commit_update(trans); + else { + trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE; + trans->ctx.table->use++; + } + nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELCHAIN: + trans->ctx.table->use--; + nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN); + if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) && + trans->ctx.chain->flags & NFT_BASE_CHAIN) { + nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops, + trans->ctx.afi->nops); + } + break; + case NFT_MSG_NEWRULE: + nft_rule_clear(trans->ctx.net, nft_trans_rule(trans)); + nf_tables_rule_notify(&trans->ctx, + nft_trans_rule(trans), + NFT_MSG_NEWRULE); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELRULE: + list_del_rcu(&nft_trans_rule(trans)->list); + nf_tables_rule_notify(&trans->ctx, + nft_trans_rule(trans), + NFT_MSG_DELRULE); + break; + case NFT_MSG_NEWSET: + nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE; + nf_tables_set_notify(&trans->ctx, nft_trans_set(trans), + NFT_MSG_NEWSET); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELSET: + nf_tables_set_notify(&trans->ctx, nft_trans_set(trans), + NFT_MSG_DELSET); + break; + case NFT_MSG_NEWSETELEM: + nft_trans_elem_set(trans)->nelems++; + nf_tables_setelem_notify(&trans->ctx, + nft_trans_elem_set(trans), + &nft_trans_elem(trans), + NFT_MSG_NEWSETELEM, 0); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELSETELEM: + nft_trans_elem_set(trans)->nelems--; + nf_tables_setelem_notify(&trans->ctx, + nft_trans_elem_set(trans), + &nft_trans_elem(trans), + NFT_MSG_DELSETELEM, 0); + set = nft_trans_elem_set(trans); + set->ops->get(set, &nft_trans_elem(trans)); + set->ops->remove(set, &nft_trans_elem(trans)); + nft_trans_destroy(trans); + break; + } + } + + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + list_del(&trans->list); + trans->ctx.nla = NULL; + call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu); + } + + return 0; +} + +/* Schedule objects for release via rcu to make sure no packets are accesing + * aborted rules. + */ +static void nf_tables_abort_release_rcu(struct rcu_head *rt) +{ + struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); + + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + nf_tables_table_destroy(&trans->ctx); + break; + case NFT_MSG_NEWCHAIN: + nf_tables_chain_destroy(trans->ctx.chain); + break; + case NFT_MSG_NEWRULE: + nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); + break; + case NFT_MSG_NEWSET: + nft_set_destroy(nft_trans_set(trans)); + break; + } + kfree(trans); +} + +static int nf_tables_abort(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + struct nft_trans *trans, *next; + struct nft_set *set; + + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + if (nft_trans_table_update(trans)) { + if (nft_trans_table_enable(trans)) { + nf_tables_table_disable(trans->ctx.afi, + trans->ctx.table); + trans->ctx.table->flags |= NFT_TABLE_F_DORMANT; + } + nft_trans_destroy(trans); + } else { + list_del(&trans->ctx.table->list); + } + break; + case NFT_MSG_DELTABLE: + list_add_tail(&trans->ctx.table->list, + &trans->ctx.afi->tables); + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain_update(trans)) { + if (nft_trans_chain_stats(trans)) + free_percpu(nft_trans_chain_stats(trans)); + + nft_trans_destroy(trans); + } else { + list_del(&trans->ctx.chain->list); + if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) && + trans->ctx.chain->flags & NFT_BASE_CHAIN) { + nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops, + trans->ctx.afi->nops); + } + } + break; + case NFT_MSG_DELCHAIN: + list_add_tail(&trans->ctx.chain->list, + &trans->ctx.table->chains); + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWRULE: + list_del_rcu(&nft_trans_rule(trans)->list); + break; + case NFT_MSG_DELRULE: + nft_rule_clear(trans->ctx.net, nft_trans_rule(trans)); + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWSET: + list_del(&nft_trans_set(trans)->list); + break; + case NFT_MSG_DELSET: + list_add_tail(&nft_trans_set(trans)->list, + &trans->ctx.table->sets); + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWSETELEM: + set = nft_trans_elem_set(trans); + set->ops->get(set, &nft_trans_elem(trans)); + set->ops->remove(set, &nft_trans_elem(trans)); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELSETELEM: + nft_trans_destroy(trans); + break; + } + } + + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + list_del(&trans->list); + trans->ctx.nla = NULL; + call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu); + } + + return 0; +} + static const struct nfnetlink_subsystem nf_tables_subsys = { .name = "nf_tables", .subsys_id = NFNL_SUBSYS_NFTABLES, diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 8041053..345acfb 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -66,20 +66,6 @@ struct nft_jumpstack { int rulenum; }; -static inline void -nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt, - struct nft_jumpstack *jumpstack, unsigned int stackptr) -{ - struct nft_stats __percpu *stats; - const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this; - - rcu_read_lock_bh(); - stats = rcu_dereference(nft_base_chain(chain)->stats); - __this_cpu_inc(stats->pkts); - __this_cpu_add(stats->bytes, pkt->skb->len); - rcu_read_unlock_bh(); -} - enum nft_trace { NFT_TRACE_RULE, NFT_TRACE_RETURN, @@ -117,13 +103,14 @@ static void nft_trace_packet(const struct nft_pktinfo *pkt, unsigned int nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) { - const struct nft_chain *chain = ops->priv; + const struct nft_chain *chain = ops->priv, *basechain = chain; const struct nft_rule *rule; const struct nft_expr *expr, *last; struct nft_data data[NFT_REG_MAX + 1]; unsigned int stackptr = 0; struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; - int rulenum = 0; + struct nft_stats __percpu *stats; + int rulenum; /* * Cache cursor to avoid problems in case that the cursor is updated * while traversing the ruleset. @@ -131,6 +118,7 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); do_chain: + rulenum = 0; rule = list_entry(&chain->rules, struct nft_rule, list); next_rule: data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; @@ -156,8 +144,10 @@ next_rule: switch (data[NFT_REG_VERDICT].verdict) { case NFT_BREAK: data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; - /* fall through */ + continue; case NFT_CONTINUE: + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); continue; } break; @@ -183,37 +173,44 @@ next_rule: jumpstack[stackptr].rule = rule; jumpstack[stackptr].rulenum = rulenum; stackptr++; - /* fall through */ + chain = data[NFT_REG_VERDICT].chain; + goto do_chain; case NFT_GOTO: + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); + chain = data[NFT_REG_VERDICT].chain; goto do_chain; case NFT_RETURN: if (unlikely(pkt->skb->nf_trace)) nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); - - /* fall through */ + break; case NFT_CONTINUE: + if (unlikely(pkt->skb->nf_trace && !(chain->flags & NFT_BASE_CHAIN))) + nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); break; default: WARN_ON(1); } if (stackptr > 0) { - if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); - stackptr--; chain = jumpstack[stackptr].chain; rule = jumpstack[stackptr].rule; rulenum = jumpstack[stackptr].rulenum; goto next_rule; } - nft_chain_stats(chain, pkt, jumpstack, stackptr); if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY); + nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY); + + rcu_read_lock_bh(); + stats = rcu_dereference(nft_base_chain(basechain)->stats); + __this_cpu_inc(stats->pkts); + __this_cpu_add(stats->bytes, pkt->skb->len); + rcu_read_unlock_bh(); - return nft_base_chain(chain)->policy; + return nft_base_chain(basechain)->policy; } EXPORT_SYMBOL_GPL(nft_do_chain); diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index c7b6d46..70e86bb 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -32,18 +32,24 @@ static LIST_HEAD(nfnl_acct_list); struct nf_acct { atomic64_t pkts; atomic64_t bytes; + unsigned long flags; struct list_head head; atomic_t refcnt; char name[NFACCT_NAME_MAX]; struct rcu_head rcu_head; + char data[0]; }; +#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) + static int nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const tb[]) { struct nf_acct *nfacct, *matching = NULL; char *acct_name; + unsigned int size = 0; + u32 flags = 0; if (!tb[NFACCT_NAME]) return -EINVAL; @@ -68,15 +74,39 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, /* reset counters if you request a replacement. */ atomic64_set(&matching->pkts, 0); atomic64_set(&matching->bytes, 0); + smp_mb__before_clear_bit(); + /* reset overquota flag if quota is enabled. */ + if ((matching->flags & NFACCT_F_QUOTA)) + clear_bit(NFACCT_F_OVERQUOTA, &matching->flags); return 0; } return -EBUSY; } nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL); + if (tb[NFACCT_FLAGS]) { + flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); + if (flags & ~NFACCT_F_QUOTA) + return -EOPNOTSUPP; + if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) + return -EINVAL; + if (flags & NFACCT_F_OVERQUOTA) + return -EINVAL; + + size += sizeof(u64); + } + + nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); if (nfacct == NULL) return -ENOMEM; + if (flags & NFACCT_F_QUOTA) { + u64 *quota = (u64 *)nfacct->data; + + *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); + nfacct->flags = flags; + } + strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); if (tb[NFACCT_BYTES]) { @@ -117,6 +147,9 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (type == NFNL_MSG_ACCT_GET_CTRZERO) { pkts = atomic64_xchg(&acct->pkts, 0); bytes = atomic64_xchg(&acct->bytes, 0); + smp_mb__before_clear_bit(); + if (acct->flags & NFACCT_F_QUOTA) + clear_bit(NFACCT_F_OVERQUOTA, &acct->flags); } else { pkts = atomic64_read(&acct->pkts); bytes = atomic64_read(&acct->bytes); @@ -125,7 +158,13 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) goto nla_put_failure; + if (acct->flags & NFACCT_F_QUOTA) { + u64 *quota = (u64 *)acct->data; + if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) || + nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) + goto nla_put_failure; + } nlmsg_end(skb, nlh); return skb->len; @@ -270,6 +309,8 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, [NFACCT_BYTES] = { .type = NLA_U64 }, [NFACCT_PKTS] = { .type = NLA_U64 }, + [NFACCT_FLAGS] = { .type = NLA_U32 }, + [NFACCT_QUOTA] = { .type = NLA_U64 }, }; static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { @@ -336,6 +377,50 @@ void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) } EXPORT_SYMBOL_GPL(nfnl_acct_update); +static void nfnl_overquota_report(struct nf_acct *nfacct) +{ + int ret; + struct sk_buff *skb; + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (skb == NULL) + return; + + ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, + nfacct); + if (ret <= 0) { + kfree_skb(skb); + return; + } + netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, + GFP_ATOMIC); +} + +int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) +{ + u64 now; + u64 *quota; + int ret = NFACCT_UNDERQUOTA; + + /* no place here if we don't have a quota */ + if (!(nfacct->flags & NFACCT_F_QUOTA)) + return NFACCT_NO_QUOTA; + + quota = (u64 *)nfacct->data; + now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? + atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); + + ret = now > *quota; + + if (now >= *quota && + !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) { + nfnl_overquota_report(nfacct); + } + + return ret; +} +EXPORT_SYMBOL_GPL(nfnl_acct_overquota); + static int __init nfnl_acct_init(void) { int ret; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index bd0d41e..cc56030 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -215,22 +215,14 @@ static void nft_ct_l3proto_module_put(uint8_t family) nf_ct_l3proto_module_put(family); } -static int nft_ct_init_validate_get(const struct nft_expr *expr, - const struct nlattr * const tb[]) +static int nft_ct_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_ct *priv = nft_expr_priv(expr); + int err; - if (tb[NFTA_CT_DIRECTION] != NULL) { - priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); - switch (priv->dir) { - case IP_CT_DIR_ORIGINAL: - case IP_CT_DIR_REPLY: - break; - default: - return -EINVAL; - } - } - + priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); switch (priv->key) { case NFT_CT_STATE: case NFT_CT_DIRECTION: @@ -262,55 +254,55 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr, return -EOPNOTSUPP; } - return 0; -} - -static int nft_ct_init_validate_set(uint32_t key) -{ - switch (key) { - case NFT_CT_MARK: - break; - default: - return -EOPNOTSUPP; + if (tb[NFTA_CT_DIRECTION] != NULL) { + priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); + switch (priv->dir) { + case IP_CT_DIR_ORIGINAL: + case IP_CT_DIR_REPLY: + break; + default: + return -EINVAL; + } } + priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + return err; + + err = nft_ct_l3proto_try_module_get(ctx->afi->family); + if (err < 0) + return err; + return 0; } -static int nft_ct_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +static int nft_ct_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_ct *priv = nft_expr_priv(expr); int err; priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); - - if (tb[NFTA_CT_DREG]) { - err = nft_ct_init_validate_get(expr, tb); - if (err < 0) - return err; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - - err = nft_validate_data_load(ctx, priv->dreg, NULL, - NFT_DATA_VALUE); - if (err < 0) - return err; - } else { - err = nft_ct_init_validate_set(priv->key); - if (err < 0) - return err; - - priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG])); - err = nft_validate_input_register(priv->sreg); - if (err < 0) - return err; + switch (priv->key) { +#ifdef CONFIG_NF_CONNTRACK_MARK + case NFT_CT_MARK: + break; +#endif + default: + return -EOPNOTSUPP; } + priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG])); + err = nft_validate_input_register(priv->sreg); + if (err < 0) + return err; + err = nft_ct_l3proto_try_module_get(ctx->afi->family); if (err < 0) return err; @@ -370,7 +362,7 @@ static const struct nft_expr_ops nft_ct_get_ops = { .type = &nft_ct_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), .eval = nft_ct_get_eval, - .init = nft_ct_init, + .init = nft_ct_get_init, .destroy = nft_ct_destroy, .dump = nft_ct_get_dump, }; @@ -379,7 +371,7 @@ static const struct nft_expr_ops nft_ct_set_ops = { .type = &nft_ct_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), .eval = nft_ct_set_eval, - .init = nft_ct_init, + .init = nft_ct_set_init, .destroy = nft_ct_destroy, .dump = nft_ct_set_dump, }; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 3b1ad87..1dfeb67 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/list.h> +#include <linux/log2.h> #include <linux/jhash.h> #include <linux/netlink.h> #include <linux/vmalloc.h> @@ -19,7 +20,7 @@ #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h> -#define NFT_HASH_MIN_SIZE 4 +#define NFT_HASH_MIN_SIZE 4UL struct nft_hash { struct nft_hash_table __rcu *tbl; @@ -27,7 +28,6 @@ struct nft_hash { struct nft_hash_table { unsigned int size; - unsigned int elements; struct nft_hash_elem __rcu *buckets[]; }; @@ -82,6 +82,11 @@ static void nft_hash_tbl_free(const struct nft_hash_table *tbl) kfree(tbl); } +static unsigned int nft_hash_tbl_size(unsigned int nelem) +{ + return max(roundup_pow_of_two(nelem * 4 / 3), NFT_HASH_MIN_SIZE); +} + static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets) { struct nft_hash_table *tbl; @@ -161,7 +166,6 @@ static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv) break; } } - ntbl->elements = tbl->elements; /* Publish new table */ rcu_assign_pointer(priv->tbl, ntbl); @@ -201,7 +205,6 @@ static int nft_hash_tbl_shrink(const struct nft_set *set, struct nft_hash *priv) ; RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]); } - ntbl->elements = tbl->elements; /* Publish new table */ rcu_assign_pointer(priv->tbl, ntbl); @@ -237,10 +240,9 @@ static int nft_hash_insert(const struct nft_set *set, h = nft_hash_data(&he->key, tbl->size, set->klen); RCU_INIT_POINTER(he->next, tbl->buckets[h]); rcu_assign_pointer(tbl->buckets[h], he); - tbl->elements++; /* Expand table when exceeding 75% load */ - if (tbl->elements > tbl->size / 4 * 3) + if (set->nelems + 1 > tbl->size / 4 * 3) nft_hash_tbl_expand(set, priv); return 0; @@ -268,10 +270,9 @@ static void nft_hash_remove(const struct nft_set *set, RCU_INIT_POINTER(*pprev, he->next); synchronize_rcu(); kfree(he); - tbl->elements--; /* Shrink table beneath 30% load */ - if (tbl->elements < tbl->size * 3 / 10 && + if (set->nelems - 1 < tbl->size * 3 / 10 && tbl->size > NFT_HASH_MIN_SIZE) nft_hash_tbl_shrink(set, priv); } @@ -335,17 +336,23 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) } static int nft_hash_init(const struct nft_set *set, + const struct nft_set_desc *desc, const struct nlattr * const tb[]) { struct nft_hash *priv = nft_set_priv(set); struct nft_hash_table *tbl; + unsigned int size; if (unlikely(!nft_hash_rnd_initted)) { get_random_bytes(&nft_hash_rnd, 4); nft_hash_rnd_initted = true; } - tbl = nft_hash_tbl_alloc(NFT_HASH_MIN_SIZE); + size = NFT_HASH_MIN_SIZE; + if (desc->size) + size = nft_hash_tbl_size(desc->size); + + tbl = nft_hash_tbl_alloc(size); if (tbl == NULL) return -ENOMEM; RCU_INIT_POINTER(priv->tbl, tbl); @@ -369,8 +376,37 @@ static void nft_hash_destroy(const struct nft_set *set) kfree(tbl); } +static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, + struct nft_set_estimate *est) +{ + unsigned int esize; + + esize = sizeof(struct nft_hash_elem); + if (features & NFT_SET_MAP) + esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]); + + if (desc->size) { + est->size = sizeof(struct nft_hash) + + nft_hash_tbl_size(desc->size) * + sizeof(struct nft_hash_elem *) + + desc->size * esize; + } else { + /* Resizing happens when the load drops below 30% or goes + * above 75%. The average of 52.5% load (approximated by 50%) + * is used for the size estimation of the hash buckets, + * meaning we calculate two buckets per element. + */ + est->size = esize + 2 * sizeof(struct nft_hash_elem *); + } + + est->class = NFT_SET_CLASS_O_1; + + return true; +} + static struct nft_set_ops nft_hash_ops __read_mostly = { .privsize = nft_hash_privsize, + .estimate = nft_hash_estimate, .init = nft_hash_init, .destroy = nft_hash_destroy, .get = nft_hash_get, diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 7fd2bea..6404a72 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx, return -EINVAL; set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); + if (IS_ERR(set)) { + if (tb[NFTA_LOOKUP_SET_ID]) { + set = nf_tables_set_lookup_byid(ctx->net, + tb[NFTA_LOOKUP_SET_ID]); + } + if (IS_ERR(set)) + return PTR_ERR(set); + } priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG])); err = nft_validate_input_register(priv->sreg); diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 425cf39..852b178 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -18,18 +18,11 @@ #include <net/sock.h> #include <net/tcp_states.h> /* for TCP_TIME_WAIT */ #include <net/netfilter/nf_tables.h> +#include <net/netfilter/nft_meta.h> -struct nft_meta { - enum nft_meta_keys key:8; - union { - enum nft_registers dreg:8; - enum nft_registers sreg:8; - }; -}; - -static void nft_meta_get_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +void nft_meta_get_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { const struct nft_meta *priv = nft_expr_priv(expr); const struct sk_buff *skb = pkt->skb; @@ -140,10 +133,11 @@ static void nft_meta_get_eval(const struct nft_expr *expr, err: data[NFT_REG_VERDICT].verdict = NFT_BREAK; } +EXPORT_SYMBOL_GPL(nft_meta_get_eval); -static void nft_meta_set_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +void nft_meta_set_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { const struct nft_meta *meta = nft_expr_priv(expr); struct sk_buff *skb = pkt->skb; @@ -163,28 +157,24 @@ static void nft_meta_set_eval(const struct nft_expr *expr, WARN_ON(1); } } +EXPORT_SYMBOL_GPL(nft_meta_set_eval); -static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { +const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { [NFTA_META_DREG] = { .type = NLA_U32 }, [NFTA_META_KEY] = { .type = NLA_U32 }, [NFTA_META_SREG] = { .type = NLA_U32 }, }; +EXPORT_SYMBOL_GPL(nft_meta_policy); -static int nft_meta_init_validate_set(uint32_t key) +int nft_meta_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { - switch (key) { - case NFT_META_MARK: - case NFT_META_PRIORITY: - case NFT_META_NFTRACE: - return 0; - default: - return -EOPNOTSUPP; - } -} + struct nft_meta *priv = nft_expr_priv(expr); + int err; -static int nft_meta_init_validate_get(uint32_t key) -{ - switch (key) { + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (priv->key) { case NFT_META_LEN: case NFT_META_PROTOCOL: case NFT_META_NFPROTO: @@ -205,39 +195,41 @@ static int nft_meta_init_validate_get(uint32_t key) #ifdef CONFIG_NETWORK_SECMARK case NFT_META_SECMARK: #endif - return 0; + break; default: return -EOPNOTSUPP; } + priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + return err; + + return 0; } +EXPORT_SYMBOL_GPL(nft_meta_get_init); -static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr, - const struct nlattr * const tb[]) +int nft_meta_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_meta *priv = nft_expr_priv(expr); int err; priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); - - if (tb[NFTA_META_DREG]) { - err = nft_meta_init_validate_get(priv->key); - if (err < 0) - return err; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - - return nft_validate_data_load(ctx, priv->dreg, NULL, - NFT_DATA_VALUE); + switch (priv->key) { + case NFT_META_MARK: + case NFT_META_PRIORITY: + case NFT_META_NFTRACE: + break; + default: + return -EOPNOTSUPP; } - err = nft_meta_init_validate_set(priv->key); - if (err < 0) - return err; - priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG])); err = nft_validate_input_register(priv->sreg); if (err < 0) @@ -245,9 +237,10 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_set_init); -static int nft_meta_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) +int nft_meta_get_dump(struct sk_buff *skb, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -260,9 +253,10 @@ static int nft_meta_get_dump(struct sk_buff *skb, nla_put_failure: return -1; } +EXPORT_SYMBOL_GPL(nft_meta_get_dump); -static int nft_meta_set_dump(struct sk_buff *skb, - const struct nft_expr *expr) +int nft_meta_set_dump(struct sk_buff *skb, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -276,13 +270,14 @@ static int nft_meta_set_dump(struct sk_buff *skb, nla_put_failure: return -1; } +EXPORT_SYMBOL_GPL(nft_meta_set_dump); static struct nft_expr_type nft_meta_type; static const struct nft_expr_ops nft_meta_get_ops = { .type = &nft_meta_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .eval = nft_meta_get_eval, - .init = nft_meta_init, + .init = nft_meta_get_init, .dump = nft_meta_get_dump, }; @@ -290,7 +285,7 @@ static const struct nft_expr_ops nft_meta_set_ops = { .type = &nft_meta_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .eval = nft_meta_set_eval, - .init = nft_meta_init, + .init = nft_meta_set_init, .dump = nft_meta_set_dump, }; diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c index e21d69d..072e611 100644 --- a/net/netfilter/nft_rbtree.c +++ b/net/netfilter/nft_rbtree.c @@ -201,6 +201,7 @@ static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[]) } static int nft_rbtree_init(const struct nft_set *set, + const struct nft_set_desc *desc, const struct nlattr * const nla[]) { struct nft_rbtree *priv = nft_set_priv(set); @@ -222,8 +223,28 @@ static void nft_rbtree_destroy(const struct nft_set *set) } } +static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, + struct nft_set_estimate *est) +{ + unsigned int nsize; + + nsize = sizeof(struct nft_rbtree_elem); + if (features & NFT_SET_MAP) + nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]); + + if (desc->size) + est->size = sizeof(struct nft_rbtree) + desc->size * nsize; + else + est->size = nsize; + + est->class = NFT_SET_CLASS_O_LOG_N; + + return true; +} + static struct nft_set_ops nft_rbtree_ops __read_mostly = { .privsize = nft_rbtree_privsize, + .estimate = nft_rbtree_estimate, .init = nft_rbtree_init, .destroy = nft_rbtree_destroy, .insert = nft_rbtree_insert, diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c index 12d4da8..bbffdbd 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -23,10 +23,11 @@ MODULE_ALIAS("ip6t_bpf"); static int bpf_mt_check(const struct xt_mtchk_param *par) { struct xt_bpf_info *info = par->matchinfo; - struct sock_fprog program; + struct sock_fprog_kern program; program.len = info->bpf_program_num_elem; - program.filter = (struct sock_filter __user *) info->bpf_program; + program.filter = info->bpf_program; + if (sk_unattached_filter_create(&info->filter, &program)) { pr_info("bpf: check failed: parse error\n"); return -EINVAL; diff --git a/net/netfilter/xt_nfacct.c b/net/netfilter/xt_nfacct.c index b3be0ef..8c646ed 100644 --- a/net/netfilter/xt_nfacct.c +++ b/net/netfilter/xt_nfacct.c @@ -21,11 +21,14 @@ MODULE_ALIAS("ip6t_nfacct"); static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par) { + int overquota; const struct xt_nfacct_match_info *info = par->targinfo; nfnl_acct_update(skb, info->nfacct); - return true; + overquota = nfnl_acct_overquota(skb, info->nfacct); + + return overquota == NFACCT_UNDERQUOTA ? false : true; } static int diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index a3ba3ca..76393f2 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -317,7 +317,7 @@ static void genl_unregister_mc_groups(struct genl_family *family) } } -static int genl_validate_ops(struct genl_family *family) +static int genl_validate_ops(const struct genl_family *family) { const struct genl_ops *ops = family->ops; unsigned int n_ops = family->n_ops; @@ -337,10 +337,6 @@ static int genl_validate_ops(struct genl_family *family) return -EINVAL; } - /* family is not registered yet, so no locking needed */ - family->ops = ops; - family->n_ops = n_ops; - return 0; } diff --git a/net/nfc/digital.h b/net/nfc/digital.h index 3759add..71ad7ee 100644 --- a/net/nfc/digital.h +++ b/net/nfc/digital.h @@ -71,6 +71,7 @@ static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev, void digital_poll_next_tech(struct nfc_digital_dev *ddev); int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech); int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech); int digital_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech); diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index e01e15d..a6ce3c6 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -22,6 +22,8 @@ #define DIGITAL_PROTO_NFCA_RF_TECH \ (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK) +#define DIGITAL_PROTO_NFCB_RF_TECH NFC_PROTO_ISO14443_B_MASK + #define DIGITAL_PROTO_NFCF_RF_TECH \ (NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK) @@ -345,6 +347,12 @@ int digital_target_found(struct nfc_digital_dev *ddev, add_crc = digital_skb_add_crc_a; break; + case NFC_PROTO_ISO14443_B: + framing = NFC_DIGITAL_FRAMING_NFCB_T4T; + check_crc = digital_skb_check_crc_b; + add_crc = digital_skb_add_crc_b; + break; + default: pr_err("Invalid protocol %d\n", protocol); return -EINVAL; @@ -378,6 +386,8 @@ int digital_target_found(struct nfc_digital_dev *ddev, void digital_poll_next_tech(struct nfc_digital_dev *ddev) { + u8 rand_mod; + digital_switch_rf(ddev, 0); mutex_lock(&ddev->poll_lock); @@ -387,8 +397,8 @@ void digital_poll_next_tech(struct nfc_digital_dev *ddev) return; } - ddev->poll_tech_index = (ddev->poll_tech_index + 1) % - ddev->poll_tech_count; + get_random_bytes(&rand_mod, sizeof(rand_mod)); + ddev->poll_tech_index = rand_mod % ddev->poll_tech_count; mutex_unlock(&ddev->poll_lock); @@ -475,6 +485,10 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A, digital_in_send_sens_req); + if (matching_im_protocols & DIGITAL_PROTO_NFCB_RF_TECH) + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106B, + digital_in_send_sensb_req); + if (matching_im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) { digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F, digital_in_send_sensf_req); @@ -635,7 +649,8 @@ static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg, goto done; } - if (ddev->curr_protocol == NFC_PROTO_ISO14443) { + if ((ddev->curr_protocol == NFC_PROTO_ISO14443) || + (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) { rc = digital_in_iso_dep_pull_sod(ddev, resp); if (rc) goto done; @@ -676,7 +691,8 @@ static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target, goto exit; } - if (ddev->curr_protocol == NFC_PROTO_ISO14443) { + if ((ddev->curr_protocol == NFC_PROTO_ISO14443) || + (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) { rc = digital_in_iso_dep_push_sod(ddev, skb); if (rc) goto exit; @@ -747,6 +763,8 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, ddev->protocols |= NFC_PROTO_ISO15693_MASK; if (supported_protocols & NFC_PROTO_ISO14443_MASK) ddev->protocols |= NFC_PROTO_ISO14443_MASK; + if (supported_protocols & NFC_PROTO_ISO14443_B_MASK) + ddev->protocols |= NFC_PROTO_ISO14443_B_MASK; ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN; ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN; diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index d4ed25f..171cb99 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -224,9 +224,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target); - - return 0; + return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, + target); } static int digital_in_send_rtox(struct nfc_digital_dev *ddev, diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c index 278c3fe..c2c1c01 100644 --- a/net/nfc/digital_technology.c +++ b/net/nfc/digital_technology.c @@ -41,6 +41,24 @@ #define DIGITAL_MIFARE_READ_RES_LEN 16 #define DIGITAL_MIFARE_ACK_RES 0x0A +#define DIGITAL_CMD_SENSB_REQ 0x05 +#define DIGITAL_SENSB_ADVANCED BIT(5) +#define DIGITAL_SENSB_EXTENDED BIT(4) +#define DIGITAL_SENSB_ALLB_REQ BIT(3) +#define DIGITAL_SENSB_N(n) ((n) & 0x7) + +#define DIGITAL_CMD_SENSB_RES 0x50 + +#define DIGITAL_CMD_ATTRIB_REQ 0x1D +#define DIGITAL_ATTRIB_P1_TR0_DEFAULT (0x0 << 6) +#define DIGITAL_ATTRIB_P1_TR1_DEFAULT (0x0 << 4) +#define DIGITAL_ATTRIB_P1_SUPRESS_EOS BIT(3) +#define DIGITAL_ATTRIB_P1_SUPRESS_SOS BIT(2) +#define DIGITAL_ATTRIB_P2_LISTEN_POLL_1 (0x0 << 6) +#define DIGITAL_ATTRIB_P2_POLL_LISTEN_1 (0x0 << 4) +#define DIGITAL_ATTRIB_P2_MAX_FRAME_256 0x8 +#define DIGITAL_ATTRIB_P4_DID(n) ((n) & 0xf) + #define DIGITAL_CMD_SENSF_REQ 0x00 #define DIGITAL_CMD_SENSF_RES 0x01 @@ -75,6 +93,7 @@ static const u8 digital_ats_fsc[] = { }; #define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F) +#define DIGITAL_SENSB_FSCI(pi2) (((pi2) & 0xF0) >> 4) #define DIGITAL_ATS_MAX_FSC 256 #define DIGITAL_RATS_BYTE1 0xE0 @@ -92,6 +111,32 @@ struct digital_sel_req { u8 bcc; } __packed; +struct digital_sensb_req { + u8 cmd; + u8 afi; + u8 param; +} __packed; + +struct digital_sensb_res { + u8 cmd; + u8 nfcid0[4]; + u8 app_data[4]; + u8 proto_info[3]; +} __packed; + +struct digital_attrib_req { + u8 cmd; + u8 nfcid0[4]; + u8 param1; + u8 param2; + u8 param3; + u8 param4; +} __packed; + +struct digital_attrib_res { + u8 mbli_did; +} __packed; + struct digital_sensf_req { u8 cmd; u8 sc1; @@ -531,6 +576,175 @@ int digital_in_recv_mifare_res(struct sk_buff *resp) return -EIO; } +static void digital_in_recv_attrib_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = arg; + struct digital_attrib_res *attrib_res; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (resp->len < sizeof(*attrib_res)) { + PROTOCOL_ERR("12.6.2"); + rc = -EIO; + goto exit; + } + + attrib_res = (struct digital_attrib_res *)resp->data; + + if (attrib_res->mbli_did & 0x0f) { + PROTOCOL_ERR("12.6.2.1"); + rc = -EIO; + goto exit; + } + + rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443_B); + +exit: + dev_kfree_skb(resp); + kfree(target); + + if (rc) + digital_poll_next_tech(ddev); +} + +static int digital_in_send_attrib_req(struct nfc_digital_dev *ddev, + struct nfc_target *target, + struct digital_sensb_res *sensb_res) +{ + struct digital_attrib_req *attrib_req; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, sizeof(*attrib_req)); + if (!skb) + return -ENOMEM; + + attrib_req = (struct digital_attrib_req *)skb_put(skb, + sizeof(*attrib_req)); + + attrib_req->cmd = DIGITAL_CMD_ATTRIB_REQ; + memcpy(attrib_req->nfcid0, sensb_res->nfcid0, + sizeof(attrib_req->nfcid0)); + attrib_req->param1 = DIGITAL_ATTRIB_P1_TR0_DEFAULT | + DIGITAL_ATTRIB_P1_TR1_DEFAULT; + attrib_req->param2 = DIGITAL_ATTRIB_P2_LISTEN_POLL_1 | + DIGITAL_ATTRIB_P2_POLL_LISTEN_1 | + DIGITAL_ATTRIB_P2_MAX_FRAME_256; + attrib_req->param3 = sensb_res->proto_info[1] & 0x07; + attrib_req->param4 = DIGITAL_ATTRIB_P4_DID(0); + + rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_attrib_res, + target); + if (rc) + kfree_skb(skb); + + return rc; +} + +static void digital_in_recv_sensb_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = NULL; + struct digital_sensb_res *sensb_res; + u8 fsci; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (resp->len != sizeof(*sensb_res)) { + PROTOCOL_ERR("5.6.2.1"); + rc = -EIO; + goto exit; + } + + sensb_res = (struct digital_sensb_res *)resp->data; + + if (sensb_res->cmd != DIGITAL_CMD_SENSB_RES) { + PROTOCOL_ERR("5.6.2"); + rc = -EIO; + goto exit; + } + + if (!(sensb_res->proto_info[1] & BIT(0))) { + PROTOCOL_ERR("5.6.2.12"); + rc = -EIO; + goto exit; + } + + if (sensb_res->proto_info[1] & BIT(3)) { + PROTOCOL_ERR("5.6.2.16"); + rc = -EIO; + goto exit; + } + + fsci = DIGITAL_SENSB_FSCI(sensb_res->proto_info[1]); + if (fsci >= 8) + ddev->target_fsc = DIGITAL_ATS_MAX_FSC; + else + ddev->target_fsc = digital_ats_fsc[fsci]; + + target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); + if (!target) { + rc = -ENOMEM; + goto exit; + } + + rc = digital_in_send_attrib_req(ddev, target, sensb_res); + +exit: + dev_kfree_skb(resp); + + if (rc) { + kfree(target); + digital_poll_next_tech(ddev); + } +} + +int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + struct digital_sensb_req *sensb_req; + struct sk_buff *skb; + int rc; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, + NFC_DIGITAL_RF_TECH_106B); + if (rc) + return rc; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCB); + if (rc) + return rc; + + skb = digital_skb_alloc(ddev, sizeof(*sensb_req)); + if (!skb) + return -ENOMEM; + + sensb_req = (struct digital_sensb_req *)skb_put(skb, + sizeof(*sensb_req)); + + sensb_req->cmd = DIGITAL_CMD_SENSB_REQ; + sensb_req->afi = 0x00; /* All families and sub-families */ + sensb_req->param = DIGITAL_SENSB_N(0); + + rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensb_res, + NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { @@ -877,6 +1091,18 @@ exit: dev_kfree_skb(resp); } +static void digital_tg_recv_atr_or_sensf_req(struct nfc_digital_dev *ddev, + void *arg, struct sk_buff *resp) +{ + if (!IS_ERR(resp) && (resp->len >= 2) && + (resp->data[1] == DIGITAL_CMD_SENSF_REQ)) + digital_tg_recv_sensf_req(ddev, arg, resp); + else + digital_tg_recv_atr_req(ddev, arg, resp); + + return; +} + static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev, struct digital_sensf_req *sensf_req) { @@ -887,7 +1113,7 @@ static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev, size = sizeof(struct digital_sensf_res); - if (sensf_req->rc != DIGITAL_SENSF_REQ_RC_NONE) + if (sensf_req->rc == DIGITAL_SENSF_REQ_RC_NONE) size -= sizeof(sensf_res->rd); skb = digital_skb_alloc(ddev, size); @@ -922,7 +1148,7 @@ static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev, digital_skb_add_crc_f(skb); rc = digital_tg_send_cmd(ddev, skb, 300, - digital_tg_recv_atr_req, NULL); + digital_tg_recv_atr_or_sensf_req, NULL); if (rc) kfree_skb(skb); diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index a9f4d2e..677d24b 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -26,6 +26,8 @@ #include "hci.h" +#define MAX_FWI 4949 + static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, const u8 *param, size_t param_len, data_exchange_cb_t cb, void *cb_context) @@ -37,7 +39,7 @@ static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, * for all commands? */ return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd, - param, param_len, cb, cb_context, 3000); + param, param_len, cb, cb_context, MAX_FWI); } /* @@ -82,7 +84,7 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, NFC_HCI_HCP_COMMAND, cmd, param, param_len, nfc_hci_execute_cb, &hcp_ew, - 3000); + MAX_FWI); if (hcp_ew.exec_result < 0) return hcp_ew.exec_result; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index d45b638..4740370 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -225,7 +225,7 @@ int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) goto exit; } - targets->sens_res = be16_to_cpu(*(u16 *)atqa_skb->data); + targets->sens_res = be16_to_cpu(*(__be16 *)atqa_skb->data); targets->sel_res = sak_skb->data[0]; r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE, @@ -380,34 +380,31 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev) if (r < 0) goto disconnect_all; - if (skb->len && skb->len == strlen(hdev->init_data.session_id)) - if (memcmp(hdev->init_data.session_id, skb->data, - skb->len) == 0) { - /* TODO ELa: restore gate<->pipe table from - * some TBD location. - * note: it doesn't seem possible to get the chip - * currently open gate/pipe table. - * It is only possible to obtain the supported - * gate list. - */ + if (skb->len && skb->len == strlen(hdev->init_data.session_id) && + (memcmp(hdev->init_data.session_id, skb->data, + skb->len) == 0) && hdev->ops->load_session) { + /* Restore gate<->pipe table from some proprietary location. */ - /* goto exit - * For now, always do a full initialization */ - } + r = hdev->ops->load_session(hdev); - r = nfc_hci_disconnect_all_gates(hdev); - if (r < 0) - goto exit; + if (r < 0) + goto disconnect_all; + } else { - r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, - hdev->init_data.gates); - if (r < 0) - goto disconnect_all; + r = nfc_hci_disconnect_all_gates(hdev); + if (r < 0) + goto exit; - r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, - NFC_HCI_ADMIN_SESSION_IDENTITY, - hdev->init_data.session_id, - strlen(hdev->init_data.session_id)); + r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, + hdev->init_data.gates); + if (r < 0) + goto disconnect_all; + + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_SESSION_IDENTITY, + hdev->init_data.session_id, + strlen(hdev->init_data.session_id)); + } if (r == 0) goto exit; diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index bec6ed1..a3ad69a 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -387,7 +387,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) __net_timestamp(skb); - nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); + nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); return nfc_data_exchange(dev, local->target_idx, skb, nfc_llcp_recv, local); diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index b467195..f6278da 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -680,16 +680,17 @@ void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local, continue; if (skb_copy == NULL) { - skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE, + skb_copy = __pskb_copy(skb, NFC_RAW_HEADER_SIZE, GFP_ATOMIC); if (skb_copy == NULL) continue; - data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE); + data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE); data[0] = local->dev ? local->dev->idx : 0xFF; - data[1] = direction; + data[1] = direction & 0x01; + data[1] |= (RAW_PAYLOAD_LLCP << 1); } nskb = skb_clone(skb_copy, GFP_ATOMIC); @@ -747,7 +748,7 @@ static void nfc_llcp_tx_work(struct work_struct *work) __net_timestamp(skb); nfc_llcp_send_to_raw_sock(local, skb, - NFC_LLCP_DIRECTION_TX); + NFC_DIRECTION_TX); ret = nfc_data_exchange(local->dev, local->target_idx, skb, nfc_llcp_recv, local); @@ -1476,7 +1477,7 @@ static void nfc_llcp_rx_work(struct work_struct *work) __net_timestamp(skb); - nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX); + nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX); nfc_llcp_rx_skb(local, skb); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 6c34ac97..2b400e1 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -861,6 +861,10 @@ static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) /* Get rid of skb owner, prior to sending to the driver. */ skb_orphan(skb); + /* Send copy to sniffer */ + nfc_send_to_raw_sock(ndev->nfc_dev, skb, + RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); + return ndev->ops->send(ndev, skb); } @@ -935,6 +939,11 @@ static void nci_rx_work(struct work_struct *work) struct sk_buff *skb; while ((skb = skb_dequeue(&ndev->rx_q))) { + + /* Send copy to sniffer */ + nfc_send_to_raw_sock(ndev->nfc_dev, skb, + RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); + /* Process frame */ switch (nci_mt(skb->data)) { case NCI_MT_RSP_PKT: diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 1e90509..f8f6af2 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -366,7 +366,6 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev, struct nci_rf_intf_activated_ntf *ntf, __u8 *data) { struct activation_params_poll_nfc_dep *poll; - int i; switch (ntf->activation_rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: @@ -374,10 +373,8 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev, poll = &ntf->activation_params.poll_nfc_dep; poll->atr_res_len = min_t(__u8, *data++, 63); pr_debug("atr_res_len %d\n", poll->atr_res_len); - if (poll->atr_res_len > 0) { - for (i = 0; i < poll->atr_res_len; i++) - poll->atr_res[poll->atr_res_len-1-i] = data[i]; - } + if (poll->atr_res_len > 0) + memcpy(poll->atr_res, data, poll->atr_res_len); break; default: diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 9d6e74f..88d6006 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -40,6 +40,12 @@ struct nfc_rawsock { struct work_struct tx_work; bool tx_work_scheduled; }; + +struct nfc_sock_list { + struct hlist_head head; + rwlock_t lock; +}; + #define nfc_rawsock(sk) ((struct nfc_rawsock *) sk) #define to_rawsock_sk(_tx_work) \ ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work)) diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index c27a6e8..55eefee 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -27,6 +27,24 @@ #include "nfc.h" +static struct nfc_sock_list raw_sk_list = { + .lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock) +}; + +static void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk) +{ + write_lock(&l->lock); + sk_add_node(sk, &l->head); + write_unlock(&l->lock); +} + +static void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk) +{ + write_lock(&l->lock); + sk_del_node_init(sk); + write_unlock(&l->lock); +} + static void rawsock_write_queue_purge(struct sock *sk) { pr_debug("sk=%p\n", sk); @@ -57,6 +75,9 @@ static int rawsock_release(struct socket *sock) if (!sk) return 0; + if (sock->type == SOCK_RAW) + nfc_sock_unlink(&raw_sk_list, sk); + sock_orphan(sk); sock_put(sk); @@ -275,6 +296,26 @@ static const struct proto_ops rawsock_ops = { .mmap = sock_no_mmap, }; +static const struct proto_ops rawsock_raw_ops = { + .family = PF_NFC, + .owner = THIS_MODULE, + .release = rawsock_release, + .bind = sock_no_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = rawsock_recvmsg, + .mmap = sock_no_mmap, +}; + static void rawsock_destruct(struct sock *sk) { pr_debug("sk=%p\n", sk); @@ -300,10 +341,13 @@ static int rawsock_create(struct net *net, struct socket *sock, pr_debug("sock=%p\n", sock); - if (sock->type != SOCK_SEQPACKET) + if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW)) return -ESOCKTNOSUPPORT; - sock->ops = &rawsock_ops; + if (sock->type == SOCK_RAW) + sock->ops = &rawsock_raw_ops; + else + sock->ops = &rawsock_ops; sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto); if (!sk) @@ -313,13 +357,53 @@ static int rawsock_create(struct net *net, struct socket *sock, sk->sk_protocol = nfc_proto->id; sk->sk_destruct = rawsock_destruct; sock->state = SS_UNCONNECTED; - - INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); - nfc_rawsock(sk)->tx_work_scheduled = false; + if (sock->type == SOCK_RAW) + nfc_sock_link(&raw_sk_list, sk); + else { + INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); + nfc_rawsock(sk)->tx_work_scheduled = false; + } return 0; } +void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, + u8 payload_type, u8 direction) +{ + struct sk_buff *skb_copy = NULL, *nskb; + struct sock *sk; + u8 *data; + + read_lock(&raw_sk_list.lock); + + sk_for_each(sk, &raw_sk_list.head) { + if (!skb_copy) { + skb_copy = __pskb_copy(skb, NFC_RAW_HEADER_SIZE, + GFP_ATOMIC); + if (!skb_copy) + continue; + + data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE); + + data[0] = dev ? dev->idx : 0xFF; + data[1] = direction & 0x01; + data[1] |= (payload_type << 1); + } + + nskb = skb_clone(skb_copy, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&raw_sk_list.lock); + + kfree_skb(skb_copy); +} +EXPORT_SYMBOL(nfc_send_to_raw_sock); + static struct proto rawsock_proto = { .name = "NFC_RAW", .owner = THIS_MODULE, diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 2c77e7b..c36856a 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -134,8 +134,8 @@ static int set_eth_addr(struct sk_buff *skb, skb_postpull_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); - memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_ALEN); - memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_ALEN); + ether_addr_copy(eth_hdr(skb)->h_source, eth_key->eth_src); + ether_addr_copy(eth_hdr(skb)->h_dest, eth_key->eth_dst); ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index a3276e3..0d407bc 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -44,11 +44,11 @@ #include <linux/netfilter_ipv4.h> #include <linux/inetdevice.h> #include <linux/list.h> -#include <linux/lockdep.h> #include <linux/openvswitch.h> #include <linux/rculist.h> #include <linux/dmi.h> -#include <linux/workqueue.h> +#include <linux/genetlink.h> +#include <net/genetlink.h> #include <net/genetlink.h> #include <net/net_namespace.h> #include <net/netns/generic.h> @@ -62,6 +62,31 @@ int ovs_net_id __read_mostly; +static struct genl_family dp_packet_genl_family; +static struct genl_family dp_flow_genl_family; +static struct genl_family dp_datapath_genl_family; + +static struct genl_multicast_group ovs_dp_flow_multicast_group = { + .name = OVS_FLOW_MCGROUP +}; + +static struct genl_multicast_group ovs_dp_datapath_multicast_group = { + .name = OVS_DATAPATH_MCGROUP +}; + +struct genl_multicast_group ovs_dp_vport_multicast_group = { + .name = OVS_VPORT_MCGROUP +}; + +/* Check if need to build a reply message. + * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */ +static bool ovs_must_notify(struct genl_info *info, + const struct genl_multicast_group *grp) +{ + return info->nlhdr->nlmsg_flags & NLM_F_ECHO || + netlink_has_listeners(genl_info_net(info)->genl_sock, 0); +} + static void ovs_notify(struct genl_family *family, struct sk_buff *skb, struct genl_info *info) { @@ -173,6 +198,7 @@ static struct hlist_head *vport_hash_bucket(const struct datapath *dp, return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)]; } +/* Called with ovs_mutex or RCU read lock. */ struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no) { struct vport *vport; @@ -262,16 +288,6 @@ out: u64_stats_update_end(&stats->syncp); } -static struct genl_family dp_packet_genl_family = { - .id = GENL_ID_GENERATE, - .hdrsize = sizeof(struct ovs_header), - .name = OVS_PACKET_FAMILY, - .version = OVS_PACKET_VERSION, - .maxattr = OVS_PACKET_ATTR_MAX, - .netnsok = true, - .parallel_ops = true, -}; - int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, const struct dp_upcall_info *upcall_info) { @@ -524,7 +540,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) packet->protocol = htons(ETH_P_802_2); /* Build an sw_flow for sending this packet. */ - flow = ovs_flow_alloc(false); + flow = ovs_flow_alloc(); err = PTR_ERR(flow); if (IS_ERR(flow)) goto err_kfree_skb; @@ -590,6 +606,18 @@ static const struct genl_ops dp_packet_genl_ops[] = { } }; +static struct genl_family dp_packet_genl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = sizeof(struct ovs_header), + .name = OVS_PACKET_FAMILY, + .version = OVS_PACKET_VERSION, + .maxattr = OVS_PACKET_ATTR_MAX, + .netnsok = true, + .parallel_ops = true, + .ops = dp_packet_genl_ops, + .n_ops = ARRAY_SIZE(dp_packet_genl_ops), +}; + static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats, struct ovs_dp_megaflow_stats *mega_stats) { @@ -621,26 +649,6 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats, } } -static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = { - [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED }, - [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED }, - [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG }, -}; - -static struct genl_family dp_flow_genl_family = { - .id = GENL_ID_GENERATE, - .hdrsize = sizeof(struct ovs_header), - .name = OVS_FLOW_FAMILY, - .version = OVS_FLOW_VERSION, - .maxattr = OVS_FLOW_ATTR_MAX, - .netnsok = true, - .parallel_ops = true, -}; - -static struct genl_multicast_group ovs_dp_flow_multicast_group = { - .name = OVS_FLOW_MCGROUP -}; - static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) { return NLMSG_ALIGN(sizeof(struct ovs_header)) @@ -652,8 +660,8 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */ } -/* Called with ovs_mutex. */ -static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, +/* Called with ovs_mutex or RCU read lock. */ +static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { @@ -670,7 +678,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, if (!ovs_header) return -EMSGSIZE; - ovs_header->dp_ifindex = get_dpifindex(dp); + ovs_header->dp_ifindex = dp_ifindex; /* Fill flow key. */ nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY); @@ -693,6 +701,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, nla_nest_end(skb, nla); ovs_flow_stats_get(flow, &stats, &used, &tcp_flags); + if (used && nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used))) goto nla_put_failure; @@ -720,9 +729,9 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, const struct sw_flow_actions *sf_acts; sf_acts = rcu_dereference_ovsl(flow->sf_acts); - err = ovs_nla_put_actions(sf_acts->actions, sf_acts->actions_len, skb); + if (!err) nla_nest_end(skb, start); else { @@ -743,113 +752,128 @@ error: return err; } -static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow, - struct genl_info *info) +/* May not be called with RCU read lock. */ +static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts, + struct genl_info *info, + bool always) { - size_t len; + struct sk_buff *skb; + + if (!always && !ovs_must_notify(info, &ovs_dp_flow_multicast_group)) + return NULL; - len = ovs_flow_cmd_msg_size(ovsl_dereference(flow->sf_acts)); + skb = genlmsg_new_unicast(ovs_flow_cmd_msg_size(acts), info, GFP_KERNEL); + if (!skb) + return ERR_PTR(-ENOMEM); - return genlmsg_new_unicast(len, info, GFP_KERNEL); + return skb; } -static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow, - struct datapath *dp, - struct genl_info *info, - u8 cmd) +/* Called with ovs_mutex. */ +static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow, + int dp_ifindex, + struct genl_info *info, u8 cmd, + bool always) { struct sk_buff *skb; int retval; - skb = ovs_flow_cmd_alloc_info(flow, info); - if (!skb) - return ERR_PTR(-ENOMEM); + skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), info, + always); + if (!skb || IS_ERR(skb)) + return skb; - retval = ovs_flow_cmd_fill_info(flow, dp, skb, info->snd_portid, - info->snd_seq, 0, cmd); + retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb, + info->snd_portid, info->snd_seq, 0, + cmd); BUG_ON(retval < 0); return skb; } -static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) +static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; - struct sw_flow_key key, masked_key; - struct sw_flow *flow = NULL; + struct sw_flow *flow, *new_flow; struct sw_flow_mask mask; struct sk_buff *reply; struct datapath *dp; - struct sw_flow_actions *acts = NULL; + struct sw_flow_actions *acts; struct sw_flow_match match; - bool exact_5tuple; int error; - /* Extract key. */ + /* Must have key and actions. */ error = -EINVAL; if (!a[OVS_FLOW_ATTR_KEY]) goto error; + if (!a[OVS_FLOW_ATTR_ACTIONS]) + goto error; - ovs_match_init(&match, &key, &mask); - error = ovs_nla_get_match(&match, &exact_5tuple, + /* Most of the time we need to allocate a new flow, do it before + * locking. + */ + new_flow = ovs_flow_alloc(); + if (IS_ERR(new_flow)) { + error = PTR_ERR(new_flow); + goto error; + } + + /* Extract key. */ + ovs_match_init(&match, &new_flow->unmasked_key, &mask); + error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); if (error) - goto error; + goto err_kfree_flow; + + ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask); /* Validate actions. */ - if (a[OVS_FLOW_ATTR_ACTIONS]) { - acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); - error = PTR_ERR(acts); - if (IS_ERR(acts)) - goto error; + acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); + error = PTR_ERR(acts); + if (IS_ERR(acts)) + goto err_kfree_flow; - ovs_flow_mask_key(&masked_key, &key, &mask); - error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], - &masked_key, 0, &acts); - if (error) { - OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); - goto err_kfree; - } - } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { - error = -EINVAL; - goto error; + error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key, + 0, &acts); + if (error) { + OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); + goto err_kfree_acts; + } + + reply = ovs_flow_cmd_alloc_info(acts, info, false); + if (IS_ERR(reply)) { + error = PTR_ERR(reply); + goto err_kfree_acts; } ovs_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); - error = -ENODEV; - if (!dp) + if (unlikely(!dp)) { + error = -ENODEV; goto err_unlock_ovs; - + } /* Check if this is a duplicate flow */ - flow = ovs_flow_tbl_lookup(&dp->table, &key); - if (!flow) { - /* Bail out if we're not allowed to create a new flow. */ - error = -ENOENT; - if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) - goto err_unlock_ovs; - - /* Allocate flow. */ - flow = ovs_flow_alloc(!exact_5tuple); - if (IS_ERR(flow)) { - error = PTR_ERR(flow); - goto err_unlock_ovs; - } - - flow->key = masked_key; - flow->unmasked_key = key; - rcu_assign_pointer(flow->sf_acts, acts); + flow = ovs_flow_tbl_lookup(&dp->table, &new_flow->unmasked_key); + if (likely(!flow)) { + rcu_assign_pointer(new_flow->sf_acts, acts); /* Put flow in bucket. */ - error = ovs_flow_tbl_insert(&dp->table, flow, &mask); - if (error) { + error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask); + if (unlikely(error)) { acts = NULL; - goto err_flow_free; + goto err_unlock_ovs; } - reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); + if (unlikely(reply)) { + error = ovs_flow_cmd_fill_info(new_flow, + ovs_header->dp_ifindex, + reply, info->snd_portid, + info->snd_seq, 0, + OVS_FLOW_CMD_NEW); + BUG_ON(error < 0); + } + ovs_unlock(); } else { - /* We found a matching flow. */ struct sw_flow_actions *old_acts; /* Bail out if we're not allowed to modify an existing flow. @@ -858,40 +882,154 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) * request. We also accept NLM_F_EXCL in case that bug ever * gets fixed. */ - error = -EEXIST; - if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW && - info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) + if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE + | NLM_F_EXCL))) { + error = -EEXIST; goto err_unlock_ovs; - + } /* The unmasked key has to be the same for flow updates. */ - if (!ovs_flow_cmp_unmasked_key(flow, &match)) + if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) { + error = -EEXIST; goto err_unlock_ovs; - + } /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); + + if (unlikely(reply)) { + error = ovs_flow_cmd_fill_info(flow, + ovs_header->dp_ifindex, + reply, info->snd_portid, + info->snd_seq, 0, + OVS_FLOW_CMD_NEW); + BUG_ON(error < 0); + } + ovs_unlock(); + ovs_nla_free_flow_actions(old_acts); + ovs_flow_free(new_flow, false); + } + + if (reply) + ovs_notify(&dp_flow_genl_family, reply, info); + return 0; + +err_unlock_ovs: + ovs_unlock(); + kfree_skb(reply); +err_kfree_acts: + kfree(acts); +err_kfree_flow: + ovs_flow_free(new_flow, false); +error: + return error; +} + +static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr **a = info->attrs; + struct ovs_header *ovs_header = info->userhdr; + struct sw_flow_key key, masked_key; + struct sw_flow *flow; + struct sw_flow_mask mask; + struct sk_buff *reply = NULL; + struct datapath *dp; + struct sw_flow_actions *old_acts = NULL, *acts = NULL; + struct sw_flow_match match; + int error; + + /* Extract key. */ + error = -EINVAL; + if (!a[OVS_FLOW_ATTR_KEY]) + goto error; + + ovs_match_init(&match, &key, &mask); + error = ovs_nla_get_match(&match, + a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); + if (error) + goto error; + + /* Validate actions. */ + if (a[OVS_FLOW_ATTR_ACTIONS]) { + acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); + error = PTR_ERR(acts); + if (IS_ERR(acts)) + goto error; + + ovs_flow_mask_key(&masked_key, &key, &mask); + error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], + &masked_key, 0, &acts); + if (error) { + OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); + goto err_kfree_acts; + } + } + + /* Can allocate before locking if have acts. */ + if (acts) { + reply = ovs_flow_cmd_alloc_info(acts, info, false); + if (IS_ERR(reply)) { + error = PTR_ERR(reply); + goto err_kfree_acts; + } + } - reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); + ovs_lock(); + dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); + if (unlikely(!dp)) { + error = -ENODEV; + goto err_unlock_ovs; + } + /* Check that the flow exists. */ + flow = ovs_flow_tbl_lookup(&dp->table, &key); + if (unlikely(!flow)) { + error = -ENOENT; + goto err_unlock_ovs; + } + /* The unmasked key has to be the same for flow updates. */ + if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) { + error = -EEXIST; + goto err_unlock_ovs; + } + /* Update actions, if present. */ + if (likely(acts)) { + old_acts = ovsl_dereference(flow->sf_acts); + rcu_assign_pointer(flow->sf_acts, acts); - /* Clear stats. */ - if (a[OVS_FLOW_ATTR_CLEAR]) - ovs_flow_stats_clear(flow); + if (unlikely(reply)) { + error = ovs_flow_cmd_fill_info(flow, + ovs_header->dp_ifindex, + reply, info->snd_portid, + info->snd_seq, 0, + OVS_FLOW_CMD_NEW); + BUG_ON(error < 0); + } + } else { + /* Could not alloc without acts before locking. */ + reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, + info, OVS_FLOW_CMD_NEW, false); + if (unlikely(IS_ERR(reply))) { + error = PTR_ERR(reply); + goto err_unlock_ovs; + } } + + /* Clear stats. */ + if (a[OVS_FLOW_ATTR_CLEAR]) + ovs_flow_stats_clear(flow); ovs_unlock(); - if (!IS_ERR(reply)) + if (reply) ovs_notify(&dp_flow_genl_family, reply, info); - else - genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0, - 0, PTR_ERR(reply)); + if (old_acts) + ovs_nla_free_flow_actions(old_acts); + return 0; -err_flow_free: - ovs_flow_free(flow, false); err_unlock_ovs: ovs_unlock(); -err_kfree: + kfree_skb(reply); +err_kfree_acts: kfree(acts); error: return error; @@ -914,7 +1052,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) } ovs_match_init(&match, &key, NULL); - err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL); + err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); if (err) return err; @@ -931,7 +1069,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) goto unlock; } - reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); + reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info, + OVS_FLOW_CMD_NEW, true); if (IS_ERR(reply)) { err = PTR_ERR(reply); goto unlock; @@ -955,45 +1094,53 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) struct sw_flow_match match; int err; + if (likely(a[OVS_FLOW_ATTR_KEY])) { + ovs_match_init(&match, &key, NULL); + err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); + if (unlikely(err)) + return err; + } + ovs_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); - if (!dp) { + if (unlikely(!dp)) { err = -ENODEV; goto unlock; } - if (!a[OVS_FLOW_ATTR_KEY]) { + if (unlikely(!a[OVS_FLOW_ATTR_KEY])) { err = ovs_flow_tbl_flush(&dp->table); goto unlock; } - ovs_match_init(&match, &key, NULL); - err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL); - if (err) - goto unlock; - flow = ovs_flow_tbl_lookup(&dp->table, &key); - if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { + if (unlikely(!flow || !ovs_flow_cmp_unmasked_key(flow, &match))) { err = -ENOENT; goto unlock; } - reply = ovs_flow_cmd_alloc_info(flow, info); - if (!reply) { - err = -ENOMEM; - goto unlock; - } - ovs_flow_tbl_remove(&dp->table, flow); + ovs_unlock(); - err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid, - info->snd_seq, 0, OVS_FLOW_CMD_DEL); - BUG_ON(err < 0); + reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts, + info, false); + if (likely(reply)) { + if (likely(!IS_ERR(reply))) { + rcu_read_lock(); /*To keep RCU checker happy. */ + err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, + reply, info->snd_portid, + info->snd_seq, 0, + OVS_FLOW_CMD_DEL); + rcu_read_unlock(); + BUG_ON(err < 0); + + ovs_notify(&dp_flow_genl_family, reply, info); + } else { + netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, PTR_ERR(reply)); + } + } ovs_flow_free(flow, true); - ovs_unlock(); - - ovs_notify(&dp_flow_genl_family, reply, info); return 0; unlock: ovs_unlock(); @@ -1024,7 +1171,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) if (!flow) break; - if (ovs_flow_cmd_fill_info(flow, dp, skb, + if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_FLOW_CMD_NEW) < 0) @@ -1037,11 +1184,17 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -static const struct genl_ops dp_flow_genl_ops[] = { +static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = { + [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED }, + [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED }, + [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG }, +}; + +static struct genl_ops dp_flow_genl_ops[] = { { .cmd = OVS_FLOW_CMD_NEW, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = flow_policy, - .doit = ovs_flow_cmd_new_or_set + .doit = ovs_flow_cmd_new }, { .cmd = OVS_FLOW_CMD_DEL, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ @@ -1057,28 +1210,22 @@ static const struct genl_ops dp_flow_genl_ops[] = { { .cmd = OVS_FLOW_CMD_SET, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = flow_policy, - .doit = ovs_flow_cmd_new_or_set, + .doit = ovs_flow_cmd_set, }, }; -static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { - [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, - [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 }, - [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 }, -}; - -static struct genl_family dp_datapath_genl_family = { +static struct genl_family dp_flow_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), - .name = OVS_DATAPATH_FAMILY, - .version = OVS_DATAPATH_VERSION, - .maxattr = OVS_DP_ATTR_MAX, + .name = OVS_FLOW_FAMILY, + .version = OVS_FLOW_VERSION, + .maxattr = OVS_FLOW_ATTR_MAX, .netnsok = true, .parallel_ops = true, -}; - -static struct genl_multicast_group ovs_dp_datapath_multicast_group = { - .name = OVS_DATAPATH_MCGROUP + .ops = dp_flow_genl_ops, + .n_ops = ARRAY_SIZE(dp_flow_genl_ops), + .mcgrps = &ovs_dp_flow_multicast_group, + .n_mcgrps = 1, }; static size_t ovs_dp_cmd_msg_size(void) @@ -1093,6 +1240,7 @@ static size_t ovs_dp_cmd_msg_size(void) return msgsize; } +/* Called with ovs_mutex or RCU read lock. */ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { @@ -1108,9 +1256,7 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, ovs_header->dp_ifindex = get_dpifindex(dp); - rcu_read_lock(); err = nla_put_string(skb, OVS_DP_ATTR_NAME, ovs_dp_name(dp)); - rcu_read_unlock(); if (err) goto nla_put_failure; @@ -1135,25 +1281,12 @@ error: return -EMSGSIZE; } -static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, - struct genl_info *info, u8 cmd) +static struct sk_buff *ovs_dp_cmd_alloc_info(struct genl_info *info) { - struct sk_buff *skb; - int retval; - - skb = genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL); - if (!skb) - return ERR_PTR(-ENOMEM); - - retval = ovs_dp_cmd_fill_info(dp, skb, info->snd_portid, info->snd_seq, 0, cmd); - if (retval < 0) { - kfree_skb(skb); - return ERR_PTR(retval); - } - return skb; + return genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL); } -/* Called with ovs_mutex. */ +/* Called with rcu_read_lock or ovs_mutex. */ static struct datapath *lookup_datapath(struct net *net, struct ovs_header *ovs_header, struct nlattr *a[OVS_DP_ATTR_MAX + 1]) @@ -1165,10 +1298,8 @@ static struct datapath *lookup_datapath(struct net *net, else { struct vport *vport; - rcu_read_lock(); vport = ovs_vport_locate(net, nla_data(a[OVS_DP_ATTR_NAME])); dp = vport && vport->port_no == OVSP_LOCAL ? vport->dp : NULL; - rcu_read_unlock(); } return dp ? dp : ERR_PTR(-ENODEV); } @@ -1205,12 +1336,14 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID]) goto err; - ovs_lock(); + reply = ovs_dp_cmd_alloc_info(info); + if (!reply) + return -ENOMEM; err = -ENOMEM; dp = kzalloc(sizeof(*dp), GFP_KERNEL); if (dp == NULL) - goto err_unlock_ovs; + goto err_free_reply; ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); @@ -1245,6 +1378,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) ovs_dp_change(dp, a); + /* So far only local changes have been made, now need the lock. */ + ovs_lock(); + vport = new_vport(&parms); if (IS_ERR(vport)) { err = PTR_ERR(vport); @@ -1263,10 +1399,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) goto err_destroy_ports_array; } - reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW); - err = PTR_ERR(reply); - if (IS_ERR(reply)) - goto err_destroy_local_port; + err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, + info->snd_seq, 0, OVS_DP_CMD_NEW); + BUG_ON(err < 0); ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id); list_add_tail_rcu(&dp->list_node, &ovs_net->dps); @@ -1276,9 +1411,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) ovs_notify(&dp_datapath_genl_family, reply, info); return 0; -err_destroy_local_port: - ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); err_destroy_ports_array: + ovs_unlock(); kfree(dp->ports); err_destroy_percpu: free_percpu(dp->stats_percpu); @@ -1287,8 +1421,8 @@ err_destroy_table: err_free_dp: release_net(ovs_dp_get_net(dp)); kfree(dp); -err_unlock_ovs: - ovs_unlock(); +err_free_reply: + kfree_skb(reply); err: return err; } @@ -1326,16 +1460,19 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) struct datapath *dp; int err; + reply = ovs_dp_cmd_alloc_info(info); + if (!reply) + return -ENOMEM; + ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); err = PTR_ERR(dp); if (IS_ERR(dp)) - goto unlock; + goto err_unlock_free; - reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_DEL); - err = PTR_ERR(reply); - if (IS_ERR(reply)) - goto unlock; + err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, + info->snd_seq, 0, OVS_DP_CMD_DEL); + BUG_ON(err < 0); __dp_destroy(dp); ovs_unlock(); @@ -1343,8 +1480,10 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) ovs_notify(&dp_datapath_genl_family, reply, info); return 0; -unlock: + +err_unlock_free: ovs_unlock(); + kfree_skb(reply); return err; } @@ -1354,29 +1493,30 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) struct datapath *dp; int err; + reply = ovs_dp_cmd_alloc_info(info); + if (!reply) + return -ENOMEM; + ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); err = PTR_ERR(dp); if (IS_ERR(dp)) - goto unlock; + goto err_unlock_free; ovs_dp_change(dp, info->attrs); - reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW); - if (IS_ERR(reply)) { - err = PTR_ERR(reply); - genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0, - 0, err); - err = 0; - goto unlock; - } + err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, + info->snd_seq, 0, OVS_DP_CMD_NEW); + BUG_ON(err < 0); ovs_unlock(); ovs_notify(&dp_datapath_genl_family, reply, info); return 0; -unlock: + +err_unlock_free: ovs_unlock(); + kfree_skb(reply); return err; } @@ -1386,24 +1526,26 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) struct datapath *dp; int err; - ovs_lock(); + reply = ovs_dp_cmd_alloc_info(info); + if (!reply) + return -ENOMEM; + + rcu_read_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); if (IS_ERR(dp)) { err = PTR_ERR(dp); - goto unlock; - } - - reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW); - if (IS_ERR(reply)) { - err = PTR_ERR(reply); - goto unlock; + goto err_unlock_free; } + err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, + info->snd_seq, 0, OVS_DP_CMD_NEW); + BUG_ON(err < 0); + rcu_read_unlock(); - ovs_unlock(); return genlmsg_reply(reply, info); -unlock: - ovs_unlock(); +err_unlock_free: + rcu_read_unlock(); + kfree_skb(reply); return err; } @@ -1430,7 +1572,13 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -static const struct genl_ops dp_datapath_genl_ops[] = { +static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { + [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, + [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 }, + [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 }, +}; + +static struct genl_ops dp_datapath_genl_ops[] = { { .cmd = OVS_DP_CMD_NEW, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = datapath_policy, @@ -1454,27 +1602,18 @@ static const struct genl_ops dp_datapath_genl_ops[] = { }, }; -static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = { - [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, - [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) }, - [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 }, - [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 }, - [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 }, - [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, -}; - -struct genl_family dp_vport_genl_family = { +static struct genl_family dp_datapath_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), - .name = OVS_VPORT_FAMILY, - .version = OVS_VPORT_VERSION, - .maxattr = OVS_VPORT_ATTR_MAX, + .name = OVS_DATAPATH_FAMILY, + .version = OVS_DATAPATH_VERSION, + .maxattr = OVS_DP_ATTR_MAX, .netnsok = true, .parallel_ops = true, -}; - -static struct genl_multicast_group ovs_dp_vport_multicast_group = { - .name = OVS_VPORT_MCGROUP + .ops = dp_datapath_genl_ops, + .n_ops = ARRAY_SIZE(dp_datapath_genl_ops), + .mcgrps = &ovs_dp_datapath_multicast_group, + .n_mcgrps = 1, }; /* Called with ovs_mutex or RCU read lock. */ @@ -1516,7 +1655,12 @@ error: return err; } -/* Called with ovs_mutex or RCU read lock. */ +static struct sk_buff *ovs_vport_cmd_alloc_info(void) +{ + return nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +} + +/* Called with ovs_mutex, only via ovs_dp_notify_wq(). */ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, u32 seq, u8 cmd) { @@ -1578,33 +1722,35 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) u32 port_no; int err; - err = -EINVAL; if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] || !a[OVS_VPORT_ATTR_UPCALL_PID]) - goto exit; + return -EINVAL; + + port_no = a[OVS_VPORT_ATTR_PORT_NO] + ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0; + if (port_no >= DP_MAX_PORTS) + return -EFBIG; + + reply = ovs_vport_cmd_alloc_info(); + if (!reply) + return -ENOMEM; ovs_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); err = -ENODEV; if (!dp) - goto exit_unlock; - - if (a[OVS_VPORT_ATTR_PORT_NO]) { - port_no = nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]); - - err = -EFBIG; - if (port_no >= DP_MAX_PORTS) - goto exit_unlock; + goto exit_unlock_free; + if (port_no) { vport = ovs_vport_ovsl(dp, port_no); err = -EBUSY; if (vport) - goto exit_unlock; + goto exit_unlock_free; } else { for (port_no = 1; ; port_no++) { if (port_no >= DP_MAX_PORTS) { err = -EFBIG; - goto exit_unlock; + goto exit_unlock_free; } vport = ovs_vport_ovsl(dp, port_no); if (!vport) @@ -1622,22 +1768,19 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) vport = new_vport(&parms); err = PTR_ERR(vport); if (IS_ERR(vport)) - goto exit_unlock; + goto exit_unlock_free; - err = 0; - reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq, - OVS_VPORT_CMD_NEW); - if (IS_ERR(reply)) { - err = PTR_ERR(reply); - ovs_dp_detach_port(vport); - goto exit_unlock; - } + err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, + info->snd_seq, 0, OVS_VPORT_CMD_NEW); + BUG_ON(err < 0); + ovs_unlock(); ovs_notify(&dp_vport_genl_family, reply, info); + return 0; -exit_unlock: +exit_unlock_free: ovs_unlock(); -exit: + kfree_skb(reply); return err; } @@ -1648,28 +1791,26 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) struct vport *vport; int err; + reply = ovs_vport_cmd_alloc_info(); + if (!reply) + return -ENOMEM; + ovs_lock(); vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); err = PTR_ERR(vport); if (IS_ERR(vport)) - goto exit_unlock; + goto exit_unlock_free; if (a[OVS_VPORT_ATTR_TYPE] && nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type) { err = -EINVAL; - goto exit_unlock; - } - - reply = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!reply) { - err = -ENOMEM; - goto exit_unlock; + goto exit_unlock_free; } if (a[OVS_VPORT_ATTR_OPTIONS]) { err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]); if (err) - goto exit_free; + goto exit_unlock_free; } if (a[OVS_VPORT_ATTR_UPCALL_PID]) @@ -1683,10 +1824,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) ovs_notify(&dp_vport_genl_family, reply, info); return 0; -exit_free: - kfree_skb(reply); -exit_unlock: +exit_unlock_free: ovs_unlock(); + kfree_skb(reply); return err; } @@ -1697,30 +1837,33 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) struct vport *vport; int err; + reply = ovs_vport_cmd_alloc_info(); + if (!reply) + return -ENOMEM; + ovs_lock(); vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); err = PTR_ERR(vport); if (IS_ERR(vport)) - goto exit_unlock; + goto exit_unlock_free; if (vport->port_no == OVSP_LOCAL) { err = -EINVAL; - goto exit_unlock; + goto exit_unlock_free; } - reply = ovs_vport_cmd_build_info(vport, info->snd_portid, - info->snd_seq, OVS_VPORT_CMD_DEL); - err = PTR_ERR(reply); - if (IS_ERR(reply)) - goto exit_unlock; - - err = 0; + err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, + info->snd_seq, 0, OVS_VPORT_CMD_DEL); + BUG_ON(err < 0); ovs_dp_detach_port(vport); + ovs_unlock(); ovs_notify(&dp_vport_genl_family, reply, info); + return 0; -exit_unlock: +exit_unlock_free: ovs_unlock(); + kfree_skb(reply); return err; } @@ -1732,24 +1875,25 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info) struct vport *vport; int err; + reply = ovs_vport_cmd_alloc_info(); + if (!reply) + return -ENOMEM; + rcu_read_lock(); vport = lookup_vport(sock_net(skb->sk), ovs_header, a); err = PTR_ERR(vport); if (IS_ERR(vport)) - goto exit_unlock; - - reply = ovs_vport_cmd_build_info(vport, info->snd_portid, - info->snd_seq, OVS_VPORT_CMD_NEW); - err = PTR_ERR(reply); - if (IS_ERR(reply)) - goto exit_unlock; - + goto exit_unlock_free; + err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, + info->snd_seq, 0, OVS_VPORT_CMD_NEW); + BUG_ON(err < 0); rcu_read_unlock(); return genlmsg_reply(reply, info); -exit_unlock: +exit_unlock_free: rcu_read_unlock(); + kfree_skb(reply); return err; } @@ -1792,7 +1936,16 @@ out: return skb->len; } -static const struct genl_ops dp_vport_genl_ops[] = { +static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = { + [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, + [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) }, + [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 }, + [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 }, + [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 }, + [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, +}; + +static struct genl_ops dp_vport_genl_ops[] = { { .cmd = OVS_VPORT_CMD_NEW, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = vport_policy, @@ -1816,26 +1969,25 @@ static const struct genl_ops dp_vport_genl_ops[] = { }, }; -struct genl_family_and_ops { - struct genl_family *family; - const struct genl_ops *ops; - int n_ops; - const struct genl_multicast_group *group; +struct genl_family dp_vport_genl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = sizeof(struct ovs_header), + .name = OVS_VPORT_FAMILY, + .version = OVS_VPORT_VERSION, + .maxattr = OVS_VPORT_ATTR_MAX, + .netnsok = true, + .parallel_ops = true, + .ops = dp_vport_genl_ops, + .n_ops = ARRAY_SIZE(dp_vport_genl_ops), + .mcgrps = &ovs_dp_vport_multicast_group, + .n_mcgrps = 1, }; -static const struct genl_family_and_ops dp_genl_families[] = { - { &dp_datapath_genl_family, - dp_datapath_genl_ops, ARRAY_SIZE(dp_datapath_genl_ops), - &ovs_dp_datapath_multicast_group }, - { &dp_vport_genl_family, - dp_vport_genl_ops, ARRAY_SIZE(dp_vport_genl_ops), - &ovs_dp_vport_multicast_group }, - { &dp_flow_genl_family, - dp_flow_genl_ops, ARRAY_SIZE(dp_flow_genl_ops), - &ovs_dp_flow_multicast_group }, - { &dp_packet_genl_family, - dp_packet_genl_ops, ARRAY_SIZE(dp_packet_genl_ops), - NULL }, +static struct genl_family * const dp_genl_families[] = { + &dp_datapath_genl_family, + &dp_vport_genl_family, + &dp_flow_genl_family, + &dp_packet_genl_family, }; static void dp_unregister_genl(int n_families) @@ -1843,33 +1995,25 @@ static void dp_unregister_genl(int n_families) int i; for (i = 0; i < n_families; i++) - genl_unregister_family(dp_genl_families[i].family); + genl_unregister_family(dp_genl_families[i]); } static int dp_register_genl(void) { - int n_registered; int err; int i; - n_registered = 0; for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) { - const struct genl_family_and_ops *f = &dp_genl_families[i]; - f->family->ops = f->ops; - f->family->n_ops = f->n_ops; - f->family->mcgrps = f->group; - f->family->n_mcgrps = f->group ? 1 : 0; - err = genl_register_family(f->family); + err = genl_register_family(dp_genl_families[i]); if (err) goto error; - n_registered++; } return 0; error: - dp_unregister_genl(n_registered); + dp_unregister_genl(i); return err; } diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 0531738..7ede507 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -194,7 +194,9 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb); void ovs_dp_notify_wq(struct work_struct *work); -#define OVS_NLERR(fmt, ...) \ - pr_info_once("netlink: " fmt, ##__VA_ARGS__) - +#define OVS_NLERR(fmt, ...) \ +do { \ + if (net_ratelimit()) \ + pr_info("netlink: " fmt, ##__VA_ARGS__); \ +} while (0) #endif /* datapath.h */ diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 2998989..334751c 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -64,88 +64,110 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies) void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb) { struct flow_stats *stats; - __be16 tcp_flags = 0; - - if (!flow->stats.is_percpu) - stats = flow->stats.stat; - else - stats = this_cpu_ptr(flow->stats.cpu_stats); - - if ((flow->key.eth.type == htons(ETH_P_IP) || - flow->key.eth.type == htons(ETH_P_IPV6)) && - flow->key.ip.frag != OVS_FRAG_TYPE_LATER && - flow->key.ip.proto == IPPROTO_TCP && - likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) { - tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb)); + __be16 tcp_flags = flow->key.tp.flags; + int node = numa_node_id(); + + stats = rcu_dereference(flow->stats[node]); + + /* Check if already have node-specific stats. */ + if (likely(stats)) { + spin_lock(&stats->lock); + /* Mark if we write on the pre-allocated stats. */ + if (node == 0 && unlikely(flow->stats_last_writer != node)) + flow->stats_last_writer = node; + } else { + stats = rcu_dereference(flow->stats[0]); /* Pre-allocated. */ + spin_lock(&stats->lock); + + /* If the current NUMA-node is the only writer on the + * pre-allocated stats keep using them. + */ + if (unlikely(flow->stats_last_writer != node)) { + /* A previous locker may have already allocated the + * stats, so we need to check again. If node-specific + * stats were already allocated, we update the pre- + * allocated stats as we have already locked them. + */ + if (likely(flow->stats_last_writer != NUMA_NO_NODE) + && likely(!rcu_dereference(flow->stats[node]))) { + /* Try to allocate node-specific stats. */ + struct flow_stats *new_stats; + + new_stats = + kmem_cache_alloc_node(flow_stats_cache, + GFP_THISNODE | + __GFP_NOMEMALLOC, + node); + if (likely(new_stats)) { + new_stats->used = jiffies; + new_stats->packet_count = 1; + new_stats->byte_count = skb->len; + new_stats->tcp_flags = tcp_flags; + spin_lock_init(&new_stats->lock); + + rcu_assign_pointer(flow->stats[node], + new_stats); + goto unlock; + } + } + flow->stats_last_writer = node; + } } - spin_lock(&stats->lock); stats->used = jiffies; stats->packet_count++; stats->byte_count += skb->len; stats->tcp_flags |= tcp_flags; +unlock: spin_unlock(&stats->lock); } -static void stats_read(struct flow_stats *stats, - struct ovs_flow_stats *ovs_stats, - unsigned long *used, __be16 *tcp_flags) -{ - spin_lock(&stats->lock); - if (!*used || time_after(stats->used, *used)) - *used = stats->used; - *tcp_flags |= stats->tcp_flags; - ovs_stats->n_packets += stats->packet_count; - ovs_stats->n_bytes += stats->byte_count; - spin_unlock(&stats->lock); -} - -void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats, +/* Must be called with rcu_read_lock or ovs_mutex. */ +void ovs_flow_stats_get(const struct sw_flow *flow, + struct ovs_flow_stats *ovs_stats, unsigned long *used, __be16 *tcp_flags) { - int cpu; + int node; *used = 0; *tcp_flags = 0; memset(ovs_stats, 0, sizeof(*ovs_stats)); - local_bh_disable(); - if (!flow->stats.is_percpu) { - stats_read(flow->stats.stat, ovs_stats, used, tcp_flags); - } else { - for_each_possible_cpu(cpu) { - struct flow_stats *stats; + for_each_node(node) { + struct flow_stats *stats = rcu_dereference_ovsl(flow->stats[node]); - stats = per_cpu_ptr(flow->stats.cpu_stats, cpu); - stats_read(stats, ovs_stats, used, tcp_flags); + if (stats) { + /* Local CPU may write on non-local stats, so we must + * block bottom-halves here. + */ + spin_lock_bh(&stats->lock); + if (!*used || time_after(stats->used, *used)) + *used = stats->used; + *tcp_flags |= stats->tcp_flags; + ovs_stats->n_packets += stats->packet_count; + ovs_stats->n_bytes += stats->byte_count; + spin_unlock_bh(&stats->lock); } } - local_bh_enable(); -} - -static void stats_reset(struct flow_stats *stats) -{ - spin_lock(&stats->lock); - stats->used = 0; - stats->packet_count = 0; - stats->byte_count = 0; - stats->tcp_flags = 0; - spin_unlock(&stats->lock); } +/* Called with ovs_mutex. */ void ovs_flow_stats_clear(struct sw_flow *flow) { - int cpu; - - local_bh_disable(); - if (!flow->stats.is_percpu) { - stats_reset(flow->stats.stat); - } else { - for_each_possible_cpu(cpu) { - stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu)); + int node; + + for_each_node(node) { + struct flow_stats *stats = ovsl_dereference(flow->stats[node]); + + if (stats) { + spin_lock_bh(&stats->lock); + stats->used = 0; + stats->packet_count = 0; + stats->byte_count = 0; + stats->tcp_flags = 0; + spin_unlock_bh(&stats->lock); } } - local_bh_enable(); } static int check_header(struct sk_buff *skb, int len) @@ -332,8 +354,8 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, /* The ICMPv6 type and code fields use the 16-bit transport port * fields, so we need to store them in 16-bit network byte order. */ - key->ipv6.tp.src = htons(icmp->icmp6_type); - key->ipv6.tp.dst = htons(icmp->icmp6_code); + key->tp.src = htons(icmp->icmp6_type); + key->tp.dst = htons(icmp->icmp6_code); if (icmp->icmp6_code == 0 && (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION || @@ -372,14 +394,14 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, && opt_len == 8) { if (unlikely(!is_zero_ether_addr(key->ipv6.nd.sll))) goto invalid; - memcpy(key->ipv6.nd.sll, - &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN); + ether_addr_copy(key->ipv6.nd.sll, + &nd->opt[offset+sizeof(*nd_opt)]); } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR && opt_len == 8) { if (unlikely(!is_zero_ether_addr(key->ipv6.nd.tll))) goto invalid; - memcpy(key->ipv6.nd.tll, - &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN); + ether_addr_copy(key->ipv6.nd.tll, + &nd->opt[offset+sizeof(*nd_opt)]); } icmp_len -= opt_len; @@ -439,8 +461,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) * header in the linear data area. */ eth = eth_hdr(skb); - memcpy(key->eth.src, eth->h_source, ETH_ALEN); - memcpy(key->eth.dst, eth->h_dest, ETH_ALEN); + ether_addr_copy(key->eth.src, eth->h_source); + ether_addr_copy(key->eth.dst, eth->h_dest); __skb_pull(skb, 2 * ETH_ALEN); /* We are going to push all headers that we pull, so no need to @@ -495,21 +517,21 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) if (key->ip.proto == IPPROTO_TCP) { if (tcphdr_ok(skb)) { struct tcphdr *tcp = tcp_hdr(skb); - key->ipv4.tp.src = tcp->source; - key->ipv4.tp.dst = tcp->dest; - key->ipv4.tp.flags = TCP_FLAGS_BE16(tcp); + key->tp.src = tcp->source; + key->tp.dst = tcp->dest; + key->tp.flags = TCP_FLAGS_BE16(tcp); } } else if (key->ip.proto == IPPROTO_UDP) { if (udphdr_ok(skb)) { struct udphdr *udp = udp_hdr(skb); - key->ipv4.tp.src = udp->source; - key->ipv4.tp.dst = udp->dest; + key->tp.src = udp->source; + key->tp.dst = udp->dest; } } else if (key->ip.proto == IPPROTO_SCTP) { if (sctphdr_ok(skb)) { struct sctphdr *sctp = sctp_hdr(skb); - key->ipv4.tp.src = sctp->source; - key->ipv4.tp.dst = sctp->dest; + key->tp.src = sctp->source; + key->tp.dst = sctp->dest; } } else if (key->ip.proto == IPPROTO_ICMP) { if (icmphdr_ok(skb)) { @@ -517,8 +539,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) /* The ICMP type and code fields use the 16-bit * transport port fields, so we need to store * them in 16-bit network byte order. */ - key->ipv4.tp.src = htons(icmp->type); - key->ipv4.tp.dst = htons(icmp->code); + key->tp.src = htons(icmp->type); + key->tp.dst = htons(icmp->code); } } @@ -538,8 +560,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) key->ip.proto = ntohs(arp->ar_op); memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src)); memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst)); - memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN); - memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN); + ether_addr_copy(key->ipv4.arp.sha, arp->ar_sha); + ether_addr_copy(key->ipv4.arp.tha, arp->ar_tha); } } else if (key->eth.type == htons(ETH_P_IPV6)) { int nh_len; /* IPv6 Header + Extensions */ @@ -564,21 +586,21 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) if (key->ip.proto == NEXTHDR_TCP) { if (tcphdr_ok(skb)) { struct tcphdr *tcp = tcp_hdr(skb); - key->ipv6.tp.src = tcp->source; - key->ipv6.tp.dst = tcp->dest; - key->ipv6.tp.flags = TCP_FLAGS_BE16(tcp); + key->tp.src = tcp->source; + key->tp.dst = tcp->dest; + key->tp.flags = TCP_FLAGS_BE16(tcp); } } else if (key->ip.proto == NEXTHDR_UDP) { if (udphdr_ok(skb)) { struct udphdr *udp = udp_hdr(skb); - key->ipv6.tp.src = udp->source; - key->ipv6.tp.dst = udp->dest; + key->tp.src = udp->source; + key->tp.dst = udp->dest; } } else if (key->ip.proto == NEXTHDR_SCTP) { if (sctphdr_ok(skb)) { struct sctphdr *sctp = sctp_hdr(skb); - key->ipv6.tp.src = sctp->source; - key->ipv6.tp.dst = sctp->dest; + key->tp.src = sctp->source; + key->tp.dst = sctp->dest; } } else if (key->ip.proto == NEXTHDR_ICMP) { if (icmp6hdr_ok(skb)) { diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 2d770e2..ac395d2 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -47,7 +47,7 @@ struct ovs_key_ipv4_tunnel { __be16 tun_flags; u8 ipv4_tos; u8 ipv4_ttl; -}; +} __packed __aligned(4); /* Minimize padding. */ static inline void ovs_flow_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key, const struct iphdr *iph, __be64 tun_id, @@ -71,7 +71,7 @@ struct sw_flow_key { u32 priority; /* Packet QoS priority. */ u32 skb_mark; /* SKB mark. */ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ - } phy; + } __packed phy; /* Safe when right after 'tun_key'. */ struct { u8 src[ETH_ALEN]; /* Ethernet source address. */ u8 dst[ETH_ALEN]; /* Ethernet destination address. */ @@ -84,23 +84,21 @@ struct sw_flow_key { u8 ttl; /* IP TTL/hop limit. */ u8 frag; /* One of OVS_FRAG_TYPE_*. */ } ip; + struct { + __be16 src; /* TCP/UDP/SCTP source port. */ + __be16 dst; /* TCP/UDP/SCTP destination port. */ + __be16 flags; /* TCP flags. */ + } tp; union { struct { struct { __be32 src; /* IP source address. */ __be32 dst; /* IP destination address. */ } addr; - union { - struct { - __be16 src; /* TCP/UDP/SCTP source port. */ - __be16 dst; /* TCP/UDP/SCTP destination port. */ - __be16 flags; /* TCP flags. */ - } tp; - struct { - u8 sha[ETH_ALEN]; /* ARP source hardware address. */ - u8 tha[ETH_ALEN]; /* ARP target hardware address. */ - } arp; - }; + struct { + u8 sha[ETH_ALEN]; /* ARP source hardware address. */ + u8 tha[ETH_ALEN]; /* ARP target hardware address. */ + } arp; } ipv4; struct { struct { @@ -109,11 +107,6 @@ struct sw_flow_key { } addr; __be32 label; /* IPv6 flow label. */ struct { - __be16 src; /* TCP/UDP/SCTP source port. */ - __be16 dst; /* TCP/UDP/SCTP destination port. */ - __be16 flags; /* TCP flags. */ - } tp; - struct { struct in6_addr target; /* ND target address. */ u8 sll[ETH_ALEN]; /* ND source link layer address. */ u8 tll[ETH_ALEN]; /* ND target link layer address. */ @@ -155,24 +148,22 @@ struct flow_stats { __be16 tcp_flags; /* Union of seen TCP flags. */ }; -struct sw_flow_stats { - bool is_percpu; - union { - struct flow_stats *stat; - struct flow_stats __percpu *cpu_stats; - }; -}; - struct sw_flow { struct rcu_head rcu; struct hlist_node hash_node[2]; u32 hash; - + int stats_last_writer; /* NUMA-node id of the last writer on + * 'stats[0]'. + */ struct sw_flow_key key; struct sw_flow_key unmasked_key; struct sw_flow_mask *mask; struct sw_flow_actions __rcu *sf_acts; - struct sw_flow_stats stats; + struct flow_stats __rcu *stats[]; /* One for each NUMA node. First one + * is allocated at flow creation time, + * the rest are allocated on demand + * while holding the 'stats[0].lock'. + */ }; struct arp_eth_header { @@ -189,10 +180,10 @@ struct arp_eth_header { unsigned char ar_tip[4]; /* target IP address */ } __packed; -void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb); -void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *stats, +void ovs_flow_stats_update(struct sw_flow *, struct sk_buff *); +void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *, unsigned long *used, __be16 *tcp_flags); -void ovs_flow_stats_clear(struct sw_flow *flow); +void ovs_flow_stats_clear(struct sw_flow *); u64 ovs_flow_used_time(unsigned long flow_jiffies); int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *); diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 4d000ac..d757848 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -16,6 +16,8 @@ * 02110-1301, USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "flow.h" #include "datapath.h" #include <linux/uaccess.h> @@ -202,11 +204,11 @@ static bool match_validate(const struct sw_flow_match *match, if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6; - if (match->key->ipv6.tp.src == + if (match->key->tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) || - match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { + match->key->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { key_expected |= 1 << OVS_KEY_ATTR_ND; - if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff))) + if (match->mask && (match->mask->key.tp.src == htons(0xffff))) mask_allowed |= 1 << OVS_KEY_ATTR_ND; } } @@ -216,14 +218,14 @@ static bool match_validate(const struct sw_flow_match *match, if ((key_attrs & key_expected) != key_expected) { /* Key attributes check failed. */ OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n", - key_attrs, key_expected); + (unsigned long long)key_attrs, (unsigned long long)key_expected); return false; } if ((mask_attrs & mask_allowed) != mask_attrs) { /* Mask attributes check failed. */ OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n", - mask_attrs, mask_allowed); + (unsigned long long)mask_attrs, (unsigned long long)mask_allowed); return false; } @@ -266,20 +268,6 @@ static bool is_all_zero(const u8 *fp, size_t size) return true; } -static bool is_all_set(const u8 *fp, size_t size) -{ - int i; - - if (!fp) - return false; - - for (i = 0; i < size; i++) - if (fp[i] != 0xff) - return false; - - return true; -} - static int __parse_flow_nlattrs(const struct nlattr *attr, const struct nlattr *a[], u64 *attrsp, bool nz) @@ -501,9 +489,8 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, return 0; } -static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple, - u64 attrs, const struct nlattr **a, - bool is_mask) +static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, + const struct nlattr **a, bool is_mask) { int err; u64 orig_attrs = attrs; @@ -560,11 +547,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); } - if (is_mask && exact_5tuple) { - if (match->mask->key.eth.type != htons(0xffff)) - *exact_5tuple = false; - } - if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { const struct ovs_key_ipv4 *ipv4_key; @@ -587,13 +569,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple SW_FLOW_KEY_PUT(match, ipv4.addr.dst, ipv4_key->ipv4_dst, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_IPV4); - - if (is_mask && exact_5tuple && *exact_5tuple) { - if (ipv4_key->ipv4_proto != 0xff || - ipv4_key->ipv4_src != htonl(0xffffffff) || - ipv4_key->ipv4_dst != htonl(0xffffffff)) - *exact_5tuple = false; - } } if (attrs & (1 << OVS_KEY_ATTR_IPV6)) { @@ -625,13 +600,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple is_mask); attrs &= ~(1 << OVS_KEY_ATTR_IPV6); - - if (is_mask && exact_5tuple && *exact_5tuple) { - if (ipv6_key->ipv6_proto != 0xff || - !is_all_set((u8 *)ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src)) || - !is_all_set((u8 *)ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst))) - *exact_5tuple = false; - } } if (attrs & (1 << OVS_KEY_ATTR_ARP)) { @@ -662,32 +630,18 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple const struct ovs_key_tcp *tcp_key; tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - tcp_key->tcp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - tcp_key->tcp_dst, is_mask); - } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - tcp_key->tcp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - tcp_key->tcp_dst, is_mask); - } + SW_FLOW_KEY_PUT(match, tp.src, tcp_key->tcp_src, is_mask); + SW_FLOW_KEY_PUT(match, tp.dst, tcp_key->tcp_dst, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_TCP); - - if (is_mask && exact_5tuple && *exact_5tuple && - (tcp_key->tcp_src != htons(0xffff) || - tcp_key->tcp_dst != htons(0xffff))) - *exact_5tuple = false; } if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) { if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.flags, + SW_FLOW_KEY_PUT(match, tp.flags, nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), is_mask); } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.flags, + SW_FLOW_KEY_PUT(match, tp.flags, nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), is_mask); } @@ -698,40 +652,17 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple const struct ovs_key_udp *udp_key; udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - udp_key->udp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - udp_key->udp_dst, is_mask); - } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - udp_key->udp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - udp_key->udp_dst, is_mask); - } + SW_FLOW_KEY_PUT(match, tp.src, udp_key->udp_src, is_mask); + SW_FLOW_KEY_PUT(match, tp.dst, udp_key->udp_dst, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_UDP); - - if (is_mask && exact_5tuple && *exact_5tuple && - (udp_key->udp_src != htons(0xffff) || - udp_key->udp_dst != htons(0xffff))) - *exact_5tuple = false; } if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { const struct ovs_key_sctp *sctp_key; sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]); - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - sctp_key->sctp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - sctp_key->sctp_dst, is_mask); - } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - sctp_key->sctp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - sctp_key->sctp_dst, is_mask); - } + SW_FLOW_KEY_PUT(match, tp.src, sctp_key->sctp_src, is_mask); + SW_FLOW_KEY_PUT(match, tp.dst, sctp_key->sctp_dst, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_SCTP); } @@ -739,9 +670,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple const struct ovs_key_icmp *icmp_key; icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); - SW_FLOW_KEY_PUT(match, ipv4.tp.src, + SW_FLOW_KEY_PUT(match, tp.src, htons(icmp_key->icmp_type), is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + SW_FLOW_KEY_PUT(match, tp.dst, htons(icmp_key->icmp_code), is_mask); attrs &= ~(1 << OVS_KEY_ATTR_ICMP); } @@ -750,9 +681,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, bool *exact_5tuple const struct ovs_key_icmpv6 *icmpv6_key; icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); - SW_FLOW_KEY_PUT(match, ipv6.tp.src, + SW_FLOW_KEY_PUT(match, tp.src, htons(icmpv6_key->icmpv6_type), is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + SW_FLOW_KEY_PUT(match, tp.dst, htons(icmpv6_key->icmpv6_code), is_mask); attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6); } @@ -800,7 +731,6 @@ static void sw_flow_mask_set(struct sw_flow_mask *mask, * attribute specifies the mask field of the wildcarded flow. */ int ovs_nla_get_match(struct sw_flow_match *match, - bool *exact_5tuple, const struct nlattr *key, const struct nlattr *mask) { @@ -848,13 +778,10 @@ int ovs_nla_get_match(struct sw_flow_match *match, } } - err = ovs_key_from_nlattrs(match, NULL, key_attrs, a, false); + err = ovs_key_from_nlattrs(match, key_attrs, a, false); if (err) return err; - if (exact_5tuple) - *exact_5tuple = true; - if (mask) { err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); if (err) @@ -892,7 +819,7 @@ int ovs_nla_get_match(struct sw_flow_match *match, } } - err = ovs_key_from_nlattrs(match, exact_5tuple, mask_attrs, a, true); + err = ovs_key_from_nlattrs(match, mask_attrs, a, true); if (err) return err; } else { @@ -982,8 +909,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, goto nla_put_failure; eth_key = nla_data(nla); - memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN); - memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN); + ether_addr_copy(eth_key->eth_src, output->eth.src); + ether_addr_copy(eth_key->eth_dst, output->eth.dst); if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { __be16 eth_type; @@ -1055,8 +982,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, arp_key->arp_sip = output->ipv4.addr.src; arp_key->arp_tip = output->ipv4.addr.dst; arp_key->arp_op = htons(output->ip.proto); - memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN); - memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN); + ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha); + ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha); } if ((swkey->eth.type == htons(ETH_P_IP) || @@ -1070,19 +997,11 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, if (!nla) goto nla_put_failure; tcp_key = nla_data(nla); - if (swkey->eth.type == htons(ETH_P_IP)) { - tcp_key->tcp_src = output->ipv4.tp.src; - tcp_key->tcp_dst = output->ipv4.tp.dst; - if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS, - output->ipv4.tp.flags)) - goto nla_put_failure; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - tcp_key->tcp_src = output->ipv6.tp.src; - tcp_key->tcp_dst = output->ipv6.tp.dst; - if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS, - output->ipv6.tp.flags)) - goto nla_put_failure; - } + tcp_key->tcp_src = output->tp.src; + tcp_key->tcp_dst = output->tp.dst; + if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS, + output->tp.flags)) + goto nla_put_failure; } else if (swkey->ip.proto == IPPROTO_UDP) { struct ovs_key_udp *udp_key; @@ -1090,13 +1009,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, if (!nla) goto nla_put_failure; udp_key = nla_data(nla); - if (swkey->eth.type == htons(ETH_P_IP)) { - udp_key->udp_src = output->ipv4.tp.src; - udp_key->udp_dst = output->ipv4.tp.dst; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - udp_key->udp_src = output->ipv6.tp.src; - udp_key->udp_dst = output->ipv6.tp.dst; - } + udp_key->udp_src = output->tp.src; + udp_key->udp_dst = output->tp.dst; } else if (swkey->ip.proto == IPPROTO_SCTP) { struct ovs_key_sctp *sctp_key; @@ -1104,13 +1018,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, if (!nla) goto nla_put_failure; sctp_key = nla_data(nla); - if (swkey->eth.type == htons(ETH_P_IP)) { - sctp_key->sctp_src = swkey->ipv4.tp.src; - sctp_key->sctp_dst = swkey->ipv4.tp.dst; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - sctp_key->sctp_src = swkey->ipv6.tp.src; - sctp_key->sctp_dst = swkey->ipv6.tp.dst; - } + sctp_key->sctp_src = output->tp.src; + sctp_key->sctp_dst = output->tp.dst; } else if (swkey->eth.type == htons(ETH_P_IP) && swkey->ip.proto == IPPROTO_ICMP) { struct ovs_key_icmp *icmp_key; @@ -1119,8 +1028,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, if (!nla) goto nla_put_failure; icmp_key = nla_data(nla); - icmp_key->icmp_type = ntohs(output->ipv4.tp.src); - icmp_key->icmp_code = ntohs(output->ipv4.tp.dst); + icmp_key->icmp_type = ntohs(output->tp.src); + icmp_key->icmp_code = ntohs(output->tp.dst); } else if (swkey->eth.type == htons(ETH_P_IPV6) && swkey->ip.proto == IPPROTO_ICMPV6) { struct ovs_key_icmpv6 *icmpv6_key; @@ -1130,8 +1039,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, if (!nla) goto nla_put_failure; icmpv6_key = nla_data(nla); - icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src); - icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst); + icmpv6_key->icmpv6_type = ntohs(output->tp.src); + icmpv6_key->icmpv6_code = ntohs(output->tp.dst); if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION || icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { @@ -1143,8 +1052,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, nd_key = nla_data(nla); memcpy(nd_key->nd_target, &output->ipv6.nd.target, sizeof(nd_key->nd_target)); - memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN); - memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN); + ether_addr_copy(nd_key->nd_sll, output->ipv6.nd.sll); + ether_addr_copy(nd_key->nd_tll, output->ipv6.nd.tll); } } } @@ -1309,13 +1218,10 @@ static int validate_and_copy_sample(const struct nlattr *attr, static int validate_tp_port(const struct sw_flow_key *flow_key) { - if (flow_key->eth.type == htons(ETH_P_IP)) { - if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst) - return 0; - } else if (flow_key->eth.type == htons(ETH_P_IPV6)) { - if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst) - return 0; - } + if ((flow_key->eth.type == htons(ETH_P_IP) || + flow_key->eth.type == htons(ETH_P_IPV6)) && + (flow_key->tp.src || flow_key->tp.dst)) + return 0; return -EINVAL; } diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h index b31fbe2..4401510 100644 --- a/net/openvswitch/flow_netlink.h +++ b/net/openvswitch/flow_netlink.h @@ -45,7 +45,6 @@ int ovs_nla_put_flow(const struct sw_flow_key *, int ovs_nla_get_flow_metadata(struct sw_flow *flow, const struct nlattr *attr); int ovs_nla_get_match(struct sw_flow_match *match, - bool *exact_5tuple, const struct nlattr *, const struct nlattr *); diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index 3c268b3..574c3ab 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -48,6 +48,7 @@ #define REHASH_INTERVAL (10 * 60 * HZ) static struct kmem_cache *flow_cache; +struct kmem_cache *flow_stats_cache __read_mostly; static u16 range_n_bytes(const struct sw_flow_key_range *range) { @@ -57,8 +58,10 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range) void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, const struct sw_flow_mask *mask) { - const long *m = (long *)((u8 *)&mask->key + mask->range.start); - const long *s = (long *)((u8 *)src + mask->range.start); + const long *m = (const long *)((const u8 *)&mask->key + + mask->range.start); + const long *s = (const long *)((const u8 *)src + + mask->range.start); long *d = (long *)((u8 *)dst + mask->range.start); int i; @@ -70,10 +73,11 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, *d++ = *s++ & *m++; } -struct sw_flow *ovs_flow_alloc(bool percpu_stats) +struct sw_flow *ovs_flow_alloc(void) { struct sw_flow *flow; - int cpu; + struct flow_stats *stats; + int node; flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); if (!flow) @@ -81,27 +85,22 @@ struct sw_flow *ovs_flow_alloc(bool percpu_stats) flow->sf_acts = NULL; flow->mask = NULL; + flow->stats_last_writer = NUMA_NO_NODE; - flow->stats.is_percpu = percpu_stats; + /* Initialize the default stat node. */ + stats = kmem_cache_alloc_node(flow_stats_cache, + GFP_KERNEL | __GFP_ZERO, 0); + if (!stats) + goto err; - if (!percpu_stats) { - flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL); - if (!flow->stats.stat) - goto err; + spin_lock_init(&stats->lock); - spin_lock_init(&flow->stats.stat->lock); - } else { - flow->stats.cpu_stats = alloc_percpu(struct flow_stats); - if (!flow->stats.cpu_stats) - goto err; + RCU_INIT_POINTER(flow->stats[0], stats); - for_each_possible_cpu(cpu) { - struct flow_stats *cpu_stats; + for_each_node(node) + if (node != 0) + RCU_INIT_POINTER(flow->stats[node], NULL); - cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu); - spin_lock_init(&cpu_stats->lock); - } - } return flow; err: kmem_cache_free(flow_cache, flow); @@ -138,11 +137,13 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets) static void flow_free(struct sw_flow *flow) { - kfree((struct sf_flow_acts __force *)flow->sf_acts); - if (flow->stats.is_percpu) - free_percpu(flow->stats.cpu_stats); - else - kfree(flow->stats.stat); + int node; + + kfree((struct sw_flow_actions __force *)flow->sf_acts); + for_each_node(node) + if (flow->stats[node]) + kmem_cache_free(flow_stats_cache, + (struct flow_stats __force *)flow->stats[node]); kmem_cache_free(flow_cache, flow); } @@ -158,25 +159,6 @@ void ovs_flow_free(struct sw_flow *flow, bool deferred) if (!flow) return; - if (flow->mask) { - struct sw_flow_mask *mask = flow->mask; - - /* ovs-lock is required to protect mask-refcount and - * mask list. - */ - ASSERT_OVSL(); - BUG_ON(!mask->ref_count); - mask->ref_count--; - - if (!mask->ref_count) { - list_del_rcu(&mask->list); - if (deferred) - kfree_rcu(mask, rcu); - else - kfree(mask); - } - } - if (deferred) call_rcu(&flow->rcu, rcu_free_flow_callback); else @@ -375,7 +357,7 @@ int ovs_flow_tbl_flush(struct flow_table *flow_table) static u32 flow_hash(const struct sw_flow_key *key, int key_start, int key_end) { - u32 *hash_key = (u32 *)((u8 *)key + key_start); + const u32 *hash_key = (const u32 *)((const u8 *)key + key_start); int hash_u32s = (key_end - key_start) >> 2; /* Make sure number of hash bytes are multiple of u32. */ @@ -397,8 +379,8 @@ static bool cmp_key(const struct sw_flow_key *key1, const struct sw_flow_key *key2, int key_start, int key_end) { - const long *cp1 = (long *)((u8 *)key1 + key_start); - const long *cp2 = (long *)((u8 *)key2 + key_start); + const long *cp1 = (const long *)((const u8 *)key1 + key_start); + const long *cp2 = (const long *)((const u8 *)key2 + key_start); long diffs = 0; int i; @@ -490,6 +472,25 @@ static struct table_instance *table_instance_expand(struct table_instance *ti) return table_instance_rehash(ti, ti->n_buckets * 2); } +/* Remove 'mask' from the mask list, if it is not needed any more. */ +static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask) +{ + if (mask) { + /* ovs-lock is required to protect mask-refcount and + * mask list. + */ + ASSERT_OVSL(); + BUG_ON(!mask->ref_count); + mask->ref_count--; + + if (!mask->ref_count) { + list_del_rcu(&mask->list); + kfree_rcu(mask, rcu); + } + } +} + +/* Must be called with OVS mutex held. */ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) { struct table_instance *ti = ovsl_dereference(table->ti); @@ -497,6 +498,11 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) BUG_ON(table->count == 0); hlist_del_rcu(&flow->hash_node[ti->node_ver]); table->count--; + + /* RCU delete the mask. 'flow->mask' is not NULLed, as it should be + * accessible as long as the RCU read lock is held. + */ + flow_mask_remove(table, flow->mask); } static struct sw_flow_mask *mask_alloc(void) @@ -513,8 +519,8 @@ static struct sw_flow_mask *mask_alloc(void) static bool mask_equal(const struct sw_flow_mask *a, const struct sw_flow_mask *b) { - u8 *a_ = (u8 *)&a->key + a->range.start; - u8 *b_ = (u8 *)&b->key + b->range.start; + const u8 *a_ = (const u8 *)&a->key + a->range.start; + const u8 *b_ = (const u8 *)&b->key + b->range.start; return (a->range.end == b->range.end) && (a->range.start == b->range.start) @@ -559,6 +565,7 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, return 0; } +/* Must be called with OVS mutex held. */ int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, struct sw_flow_mask *mask) { @@ -597,16 +604,28 @@ int ovs_flow_init(void) BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long)); BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long)); - flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0, - 0, NULL); + flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow) + + (num_possible_nodes() + * sizeof(struct flow_stats *)), + 0, 0, NULL); if (flow_cache == NULL) return -ENOMEM; + flow_stats_cache + = kmem_cache_create("sw_flow_stats", sizeof(struct flow_stats), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (flow_stats_cache == NULL) { + kmem_cache_destroy(flow_cache); + flow_cache = NULL; + return -ENOMEM; + } + return 0; } /* Uninitializes the flow module. */ void ovs_flow_exit(void) { + kmem_cache_destroy(flow_stats_cache); kmem_cache_destroy(flow_cache); } diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h index baaeb10..ca8a582 100644 --- a/net/openvswitch/flow_table.h +++ b/net/openvswitch/flow_table.h @@ -52,10 +52,12 @@ struct flow_table { unsigned int count; }; +extern struct kmem_cache *flow_stats_cache; + int ovs_flow_init(void); void ovs_flow_exit(void); -struct sw_flow *ovs_flow_alloc(bool percpu_stats); +struct sw_flow *ovs_flow_alloc(void); void ovs_flow_free(struct sw_flow *, bool deferred); int ovs_flow_tbl_init(struct flow_table *); diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index 0856f01..35ec4fe 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -256,7 +256,7 @@ static void gre_tnl_destroy(struct vport *vport) ovs_net = net_generic(net, ovs_net_id); - rcu_assign_pointer(ovs_net->vport_net.gre_vport, NULL); + RCU_INIT_POINTER(ovs_net->vport_net.gre_vport, NULL); ovs_vport_deferred_free(vport); gre_exit(); } diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index d7e50a1..8d721e6 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -172,7 +172,7 @@ void ovs_vport_deferred_free(struct vport *vport); */ static inline void *vport_priv(const struct vport *vport) { - return (u8 *)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN); + return (u8 *)(uintptr_t)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN); } /** @@ -185,9 +185,9 @@ static inline void *vport_priv(const struct vport *vport) * the result of a hash table lookup. @priv must point to the start of the * private data area. */ -static inline struct vport *vport_from_priv(const void *priv) +static inline struct vport *vport_from_priv(void *priv) { - return (struct vport *)(priv - ALIGN(sizeof(struct vport), VPORT_ALIGN)); + return (struct vport *)((u8 *)priv - ALIGN(sizeof(struct vport), VPORT_ALIGN)); } void ovs_vport_receive(struct vport *, struct sk_buff *, diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index 37be6e2..1dde91e 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -298,7 +298,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context) rds_ib_stats_inc(s_ib_tx_cq_event); if (wc.wr_id == RDS_IB_ACK_WR_ID) { - if (ic->i_ack_queued + HZ/2 < jiffies) + if (time_after(jiffies, ic->i_ack_queued + HZ/2)) rds_ib_stats_inc(s_ib_tx_stalled); rds_ib_ack_send_complete(ic); continue; @@ -315,7 +315,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context) rm = rds_ib_send_unmap_op(ic, send, wc.status); - if (send->s_queued + HZ/2 < jiffies) + if (time_after(jiffies, send->s_queued + HZ/2)) rds_ib_stats_inc(s_ib_tx_stalled); if (send->s_op) { diff --git a/net/rds/iw_send.c b/net/rds/iw_send.c index e40c3c5..9105ea0 100644 --- a/net/rds/iw_send.c +++ b/net/rds/iw_send.c @@ -232,7 +232,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context) } if (wc.wr_id == RDS_IW_ACK_WR_ID) { - if (ic->i_ack_queued + HZ/2 < jiffies) + if (time_after(jiffies, ic->i_ack_queued + HZ/2)) rds_iw_stats_inc(s_iw_tx_stalled); rds_iw_ack_send_complete(ic); continue; @@ -267,7 +267,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context) send->s_wr.opcode = 0xdead; send->s_wr.num_sge = 1; - if (send->s_queued + HZ/2 < jiffies) + if (time_after(jiffies, send->s_queued + HZ/2)) rds_iw_stats_inc(s_iw_tx_stalled); /* If a RDMA operation produced an error, signal this right diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index c2be901..6cd9d1d 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -168,7 +168,7 @@ static int rds_rdma_listen_init(void) return ret; } - sin.sin_family = AF_INET, + sin.sin_family = AF_INET; sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY); sin.sin_port = (__force u16)htons(RDS_PORT); diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index 4e638f8..23ab4dcd 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -153,7 +153,7 @@ int rds_tcp_listen_init(void) sock->sk->sk_data_ready = rds_tcp_listen_data_ready; write_unlock_bh(&sock->sk->sk_callback_lock); - sin.sin_family = PF_INET, + sin.sin_family = PF_INET; sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY); sin.sin_port = (__force u16)htons(RDS_TCP_PORT); diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index bd2a5b9..14c98e4 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -36,8 +36,6 @@ struct rfkill_gpio_data { struct gpio_desc *shutdown_gpio; struct rfkill *rfkill_dev; - char *reset_name; - char *shutdown_name; struct clk *clk; bool clk_enabled; @@ -47,17 +45,14 @@ static int rfkill_gpio_set_power(void *data, bool blocked) { struct rfkill_gpio_data *rfkill = data; - if (blocked) { - gpiod_set_value(rfkill->shutdown_gpio, 0); - gpiod_set_value(rfkill->reset_gpio, 0); - if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled) - clk_disable(rfkill->clk); - } else { - if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled) - clk_enable(rfkill->clk); - gpiod_set_value(rfkill->reset_gpio, 1); - gpiod_set_value(rfkill->shutdown_gpio, 1); - } + if (!blocked && !IS_ERR(rfkill->clk) && !rfkill->clk_enabled) + clk_enable(rfkill->clk); + + gpiod_set_value_cansleep(rfkill->shutdown_gpio, !blocked); + gpiod_set_value_cansleep(rfkill->reset_gpio, !blocked); + + if (blocked && !IS_ERR(rfkill->clk) && rfkill->clk_enabled) + clk_disable(rfkill->clk); rfkill->clk_enabled = blocked; @@ -87,10 +82,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev) { struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; struct rfkill_gpio_data *rfkill; - const char *clk_name = NULL; struct gpio_desc *gpio; int ret; - int len; rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL); if (!rfkill) @@ -101,28 +94,15 @@ static int rfkill_gpio_probe(struct platform_device *pdev) if (ret) return ret; } else if (pdata) { - clk_name = pdata->power_clk_name; rfkill->name = pdata->name; rfkill->type = pdata->type; } else { return -ENODEV; } - len = strlen(rfkill->name); - rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL); - if (!rfkill->reset_name) - return -ENOMEM; - - rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL); - if (!rfkill->shutdown_name) - return -ENOMEM; + rfkill->clk = devm_clk_get(&pdev->dev, NULL); - snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name); - snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name); - - rfkill->clk = devm_clk_get(&pdev->dev, clk_name); - - gpio = devm_gpiod_get_index(&pdev->dev, rfkill->reset_name, 0); + gpio = devm_gpiod_get_index(&pdev->dev, "reset", 0); if (!IS_ERR(gpio)) { ret = gpiod_direction_output(gpio, 0); if (ret) @@ -130,7 +110,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev) rfkill->reset_gpio = gpio; } - gpio = devm_gpiod_get_index(&pdev->dev, rfkill->shutdown_name, 1); + gpio = devm_gpiod_get_index(&pdev->dev, "shutdown", 1); if (!IS_ERR(gpio)) { ret = gpiod_direction_output(gpio, 0); if (ret) @@ -146,14 +126,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev) return -EINVAL; } - if (pdata && pdata->gpio_runtime_setup) { - ret = pdata->gpio_runtime_setup(pdev); - if (ret) { - dev_err(&pdev->dev, "can't set up gpio\n"); - return ret; - } - } - rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev, rfkill->type, &rfkill_gpio_ops, rfkill); @@ -174,20 +146,23 @@ static int rfkill_gpio_probe(struct platform_device *pdev) static int rfkill_gpio_remove(struct platform_device *pdev) { struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); - struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; - if (pdata && pdata->gpio_runtime_close) - pdata->gpio_runtime_close(pdev); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); return 0; } +#ifdef CONFIG_ACPI static const struct acpi_device_id rfkill_acpi_match[] = { + { "BCM2E1A", RFKILL_TYPE_BLUETOOTH }, + { "BCM2E39", RFKILL_TYPE_BLUETOOTH }, + { "BCM2E3D", RFKILL_TYPE_BLUETOOTH }, { "BCM4752", RFKILL_TYPE_GPS }, + { "LNV4752", RFKILL_TYPE_GPS }, { }, }; +#endif static struct platform_driver rfkill_gpio_driver = { .probe = rfkill_gpio_probe, diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 7633a75..0ad0807 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -99,7 +99,7 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, _debug("tktlen: %x", tktlen); if (tktlen > AFSTOKEN_RK_TIX_MAX) return -EKEYREJECTED; - if (8 * 4 + tktlen != toklen) + if (toklen < 8 * 4 + tktlen) return -EKEYREJECTED; plen = sizeof(*token) + sizeof(*token->kad) + tktlen; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 1618696..13f64df 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -160,7 +160,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, { struct sock_filter *bpf_ops, *bpf_old; struct tcf_exts exts; - struct sock_fprog tmp; + struct sock_fprog_kern tmp; struct sk_filter *fp, *fp_old; u16 bpf_size, bpf_len; u32 classid; @@ -191,7 +191,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size); tmp.len = bpf_len; - tmp.filter = (struct sock_filter __user *) bpf_ops; + tmp.filter = bpf_ops; ret = sk_unattached_filter_create(&fp, &tmp); if (ret) diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index d11d0a4..c721cd4 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -188,6 +188,12 @@ static const struct nla_policy tcindex_policy[TCA_TCINDEX_MAX + 1] = { [TCA_TCINDEX_CLASSID] = { .type = NLA_U32 }, }; +static void tcindex_filter_result_init(struct tcindex_filter_result *r) +{ + memset(r, 0, sizeof(*r)); + tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); +} + static int tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, u32 handle, struct tcindex_data *p, @@ -207,15 +213,11 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, return err; memcpy(&cp, p, sizeof(cp)); - memset(&new_filter_result, 0, sizeof(new_filter_result)); - tcf_exts_init(&new_filter_result.exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); + tcindex_filter_result_init(&new_filter_result); + tcindex_filter_result_init(&cr); if (old_r) - memcpy(&cr, r, sizeof(cr)); - else { - memset(&cr, 0, sizeof(cr)); - tcf_exts_init(&cr.exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); - } + cr.res = r->res; if (tb[TCA_TCINDEX_HASH]) cp.hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); @@ -267,9 +269,14 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, err = -ENOMEM; if (!cp.perfect && !cp.h) { if (valid_perfect_hash(&cp)) { + int i; + cp.perfect = kcalloc(cp.hash, sizeof(*r), GFP_KERNEL); if (!cp.perfect) goto errout; + for (i = 0; i < cp.hash; i++) + tcf_exts_init(&cp.perfect[i].exts, TCA_TCINDEX_ACT, + TCA_TCINDEX_POLICE); balloc = 1; } else { cp.h = kcalloc(cp.hash, sizeof(f), GFP_KERNEL); @@ -295,14 +302,17 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, tcf_bind_filter(tp, &cr.res, base); } - tcf_exts_change(tp, &cr.exts, &e); + if (old_r) + tcf_exts_change(tp, &r->exts, &e); + else + tcf_exts_change(tp, &cr.exts, &e); tcf_tree_lock(tp); if (old_r && old_r != r) - memset(old_r, 0, sizeof(*old_r)); + tcindex_filter_result_init(old_r); memcpy(p, &cp, sizeof(cp)); - memcpy(r, &cr, sizeof(cr)); + r->res = cr.res; if (r == &new_filter_result) { struct tcindex_filter **fp; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 4dc5d9e..1999592 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -943,7 +943,6 @@ static struct inet_protosw sctpv6_seqpacket_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctpv6_prot, .ops = &inet6_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; static struct inet_protosw sctpv6_stream_protosw = { @@ -951,7 +950,6 @@ static struct inet_protosw sctpv6_stream_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctpv6_prot, .ops = &inet6_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG, }; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index af5afca..6789d785 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1017,7 +1017,6 @@ static struct inet_protosw sctp_seqpacket_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctp_prot, .ops = &inet_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; static struct inet_protosw sctp_stream_protosw = { @@ -1025,7 +1024,6 @@ static struct inet_protosw sctp_stream_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctp_prot, .ops = &inet_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2af76ea..4298996 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6946,7 +6946,8 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, newsk->sk_type = sk->sk_type; newsk->sk_bound_dev_if = sk->sk_bound_dev_if; newsk->sk_flags = sk->sk_flags; - newsk->sk_no_check = sk->sk_no_check; + newsk->sk_no_check_tx = sk->sk_no_check_tx; + newsk->sk_no_check_rx = sk->sk_no_check_rx; newsk->sk_reuse = sk->sk_reuse; newsk->sk_shutdown = sk->sk_shutdown; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 25a3dcf..1dec604 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -866,8 +866,6 @@ static void xs_reset_transport(struct sock_xprt *transport) xs_restore_old_callbacks(transport, sk); write_unlock_bh(&sk->sk_callback_lock); - sk->sk_no_check = 0; - trace_rpc_socket_close(&transport->xprt, sock); sock_release(sock); } @@ -2046,7 +2044,6 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_user_data = xprt; sk->sk_data_ready = xs_udp_data_ready; sk->sk_write_space = xs_udp_write_space; - sk->sk_no_check = UDP_CSUM_NORCV; sk->sk_allocation = GFP_ATOMIC; xprt_set_connected(xprt); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index ac08966..08d87fc 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -985,10 +985,11 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg, return 0; } -static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo) +static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) { struct sock *sk = sock->sk; DEFINE_WAIT(wait); + long timeo = *timeop; int err; for (;;) { @@ -1013,6 +1014,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo) break; } finish_wait(sk_sleep(sk), &wait); + *timeop = timeo; return err; } @@ -1056,7 +1058,7 @@ static int tipc_recvmsg(struct kiocb *iocb, struct socket *sock, restart: /* Look for a message in receive queue; wait if necessary */ - res = tipc_wait_for_rcvmsg(sock, timeo); + res = tipc_wait_for_rcvmsg(sock, &timeo); if (res) goto exit; @@ -1154,7 +1156,7 @@ static int tipc_recv_stream(struct kiocb *iocb, struct socket *sock, restart: /* Look for a message in receive queue; wait if necessary */ - res = tipc_wait_for_rcvmsg(sock, timeo); + res = tipc_wait_for_rcvmsg(sock, &timeo); if (res) goto exit; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index bb7e8ba..7b9114e 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1492,10 +1492,14 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, if (len > sk->sk_sndbuf - 32) goto out; - if (len > SKB_MAX_ALLOC) + if (len > SKB_MAX_ALLOC) { data_len = min_t(size_t, len - SKB_MAX_ALLOC, MAX_SKB_FRAGS * PAGE_SIZE); + data_len = PAGE_ALIGN(data_len); + + BUILD_BUG_ON(SKB_MAX_ALLOC < PAGE_SIZE); + } skb = sock_alloc_send_pskb(sk, len - data_len, data_len, msg->msg_flags & MSG_DONTWAIT, &err, @@ -1670,6 +1674,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, data_len = max_t(int, 0, size - SKB_MAX_HEAD(0)); + data_len = min_t(size_t, size, PAGE_ALIGN(data_len)); + skb = sock_alloc_send_pskb(sk, size - data_len, data_len, msg->msg_flags & MSG_DONTWAIT, &err, get_order(UNIX_SKB_FRAGS_SZ)); diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 3e02ade..bdad1f9 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -6,8 +6,8 @@ #include "rdev-ops.h" -static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool notify) +int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool notify) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 84d686e..992b340 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -370,8 +370,8 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_P2P_DEVICE: - case NL80211_IFTYPE_UNSPECIFIED: break; + case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: WARN_ON(1); } @@ -796,8 +796,7 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, !cfg80211_go_permissive_chan(rdev, chandef->chan)) prohibited_flags |= IEEE80211_CHAN_NO_IR; - if (cfg80211_chandef_dfs_required(wiphy, chandef, - NL80211_IFTYPE_UNSPECIFIED) > 0 && + if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 && cfg80211_chandef_dfs_available(wiphy, chandef)) { /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ prohibited_flags = IEEE80211_CHAN_DISABLED; diff --git a/net/wireless/core.c b/net/wireless/core.c index b3ff369..a1c4065 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -130,7 +130,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, newname)) pr_err("failed to rename debugfs dir to %s!\n", newname); - nl80211_notify_dev_rename(rdev); + nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); return 0; } @@ -210,15 +210,12 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, } } -static int cfg80211_rfkill_set_block(void *data, bool blocked) +void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) { - struct cfg80211_registered_device *rdev = data; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct wireless_dev *wdev; - if (!blocked) - return 0; - - rtnl_lock(); + ASSERT_RTNL(); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->netdev) { @@ -234,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) break; } } +} +EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); +static int cfg80211_rfkill_set_block(void *data, bool blocked) +{ + struct cfg80211_registered_device *rdev = data; + + if (!blocked) + return 0; + + rtnl_lock(); + cfg80211_shutdown_all_interfaces(&rdev->wiphy); rtnl_unlock(); return 0; @@ -394,6 +402,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.rts_threshold = (u32) -1; rdev->wiphy.coverage_class = 0; + rdev->wiphy.max_num_csa_counters = 1; + return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); @@ -650,6 +660,8 @@ int wiphy_register(struct wiphy *wiphy) return res; } + nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); + return 0; } EXPORT_SYMBOL(wiphy_register); @@ -688,9 +700,10 @@ void wiphy_unregister(struct wiphy *wiphy) rfkill_unregister(rdev->rfkill); rtnl_lock(); + nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); rdev->wiphy.registered = false; - BUG_ON(!list_empty(&rdev->wdev_list)); + WARN_ON(!list_empty(&rdev->wdev_list)); /* * First remove the hardware from everywhere, this makes @@ -792,23 +805,23 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, rdev->num_running_monitor_ifaces += num; } -void cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +void __cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; ASSERT_RTNL(); + ASSERT_WDEV_LOCK(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, true); + __cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) __cfg80211_stop_sched_scan(rdev, false); - wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.ie); wdev->wext.ie = NULL; @@ -817,20 +830,49 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, #endif cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); - wdev_unlock(wdev); break; case NL80211_IFTYPE_MESH_POINT: - cfg80211_leave_mesh(rdev, dev); + __cfg80211_leave_mesh(rdev, dev); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - cfg80211_stop_ap(rdev, dev, true); + __cfg80211_stop_ap(rdev, dev, true); break; default: break; } } +void cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + wdev_lock(wdev); + __cfg80211_leave(rdev, wdev); + wdev_unlock(wdev); +} + +void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + trace_cfg80211_stop_iface(wiphy, wdev); + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_STOPPED; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_stop_iface); + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ptr) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 681b8fa..e9afbf1 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -185,6 +185,7 @@ enum cfg80211_event_type { EVENT_ROAMED, EVENT_DISCONNECTED, EVENT_IBSS_JOINED, + EVENT_STOPPED, }; struct cfg80211_event { @@ -281,6 +282,8 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf); +int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, @@ -288,6 +291,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, struct cfg80211_chan_def *chandef); /* AP */ +int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool notify); int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev, bool notify); @@ -441,6 +446,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +void __cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); void cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index b35da8d..40c37fc 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -68,17 +68,7 @@ function parse_reg_rule() sub(/,/, "", units) dfs_cac = $9 if (units == "mW") { - if (power == 100) { - power = 20 - } else if (power == 200) { - power = 23 - } else if (power == 500) { - power = 27 - } else if (power == 1000) { - power = 30 - } else { - print "Unknown power value in database!" - } + power = 10 * log(power)/log(10) } else { dfs_cac = $8 } @@ -117,7 +107,7 @@ function parse_reg_rule() } flags = flags "0" - printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags + printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %.0f, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags rules++ } diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 6b50588..8f345da 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -420,8 +420,8 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; + memcpy(wdev->ssid, ssid, len); wdev->wext.ibss.ssid = wdev->ssid; - memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; wdev_lock(wdev); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 3ddfb7c..092300b 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -238,8 +238,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, return 0; } -static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) +int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0f1b18f2..ba4f172 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -371,8 +371,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 }, [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG }, [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, - [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, - [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, + [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY }, + [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, @@ -386,6 +386,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, + [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, }; /* policy for the key attributes */ @@ -970,8 +971,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, c->max_interfaces)) goto nla_put_failure; if (large && - nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, - c->radar_detect_widths)) + (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + c->radar_detect_widths) || + nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, + c->radar_detect_regions))) goto nla_put_failure; nla_nest_end(msg, nl_combi); @@ -1223,6 +1226,7 @@ struct nl80211_dump_wiphy_state { }; static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, + enum nl80211_commands cmd, struct sk_buff *msg, u32 portid, u32 seq, int flags, struct nl80211_dump_wiphy_state *state) { @@ -1237,7 +1241,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.mgmt_stypes; u32 features; - hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); + hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -ENOBUFS; @@ -1251,6 +1255,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, cfg80211_rdev_list_generation)) goto nla_put_failure; + if (cmd != NL80211_CMD_NEW_WIPHY) + goto finish; + switch (state->split_start) { case 0: if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, @@ -1667,11 +1674,19 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, } nla_nest_end(msg, nested); } + state->split_start++; + break; + case 12: + if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH && + nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS, + rdev->wiphy.max_num_csa_counters)) + goto nla_put_failure; /* done */ state->split_start = 0; break; } + finish: return genlmsg_end(msg, hdr); nla_put_failure: @@ -1746,7 +1761,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) continue; /* attempt to fit multiple wiphy data chunks into the skb */ do { - ret = nl80211_send_wiphy(rdev, skb, + ret = nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, + skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, state); @@ -1801,7 +1817,8 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_wiphy(rdev, msg, info->snd_portid, info->snd_seq, 0, + if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, msg, + info->snd_portid, info->snd_seq, 0, &state) < 0) { nlmsg_free(msg); return -ENOBUFS; @@ -3640,6 +3657,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED, sinfo->tx_failed)) goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) && + nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT, + sinfo->expected_throughput)) + goto nla_put_failure; if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) && nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, sinfo->beacon_loss_count)) @@ -5820,7 +5841,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, return -EBUSY; err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, - NL80211_IFTYPE_UNSPECIFIED); + wdev->iftype); if (err < 0) return err; @@ -5861,6 +5882,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) u8 radar_detect_width = 0; int err; bool need_new_beacon = false; + int len, i; if (!rdev->ops->channel_switch || !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) @@ -5919,26 +5941,55 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) return -EINVAL; - params.counter_offset_beacon = - nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); - if (params.counter_offset_beacon >= params.beacon_csa.tail_len) + len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); + if (!len || (len % sizeof(u16))) return -EINVAL; - /* sanity check - counters should be the same */ - if (params.beacon_csa.tail[params.counter_offset_beacon] != - params.count) + params.n_counter_offsets_beacon = len / sizeof(u16); + if (rdev->wiphy.max_num_csa_counters && + (params.n_counter_offsets_beacon > + rdev->wiphy.max_num_csa_counters)) return -EINVAL; + params.counter_offsets_beacon = + nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); + + /* sanity checks - counters should fit and be the same */ + for (i = 0; i < params.n_counter_offsets_beacon; i++) { + u16 offset = params.counter_offsets_beacon[i]; + + if (offset >= params.beacon_csa.tail_len) + return -EINVAL; + + if (params.beacon_csa.tail[offset] != params.count) + return -EINVAL; + } + if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) { - params.counter_offset_presp = - nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); - if (params.counter_offset_presp >= - params.beacon_csa.probe_resp_len) + len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); + if (!len || (len % sizeof(u16))) return -EINVAL; - if (params.beacon_csa.probe_resp[params.counter_offset_presp] != - params.count) + params.n_counter_offsets_presp = len / sizeof(u16); + if (rdev->wiphy.max_num_csa_counters && + (params.n_counter_offsets_beacon > + rdev->wiphy.max_num_csa_counters)) return -EINVAL; + + params.counter_offsets_presp = + nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); + + /* sanity checks - counters should fit and be the same */ + for (i = 0; i < params.n_counter_offsets_presp; i++) { + u16 offset = params.counter_offsets_presp[i]; + + if (offset >= params.beacon_csa.probe_resp_len) + return -EINVAL; + + if (params.beacon_csa.probe_resp[offset] != + params.count) + return -EINVAL; + } } skip_beacons: @@ -7784,6 +7835,27 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!chandef.chan && params.offchan) return -EINVAL; + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); + params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + + if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) { + int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]); + int i; + + if (len % sizeof(u16)) + return -EINVAL; + + params.n_csa_offsets = len / sizeof(u16); + params.csa_offsets = + nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]); + + /* check that all the offsets fit the frame */ + for (i = 0; i < params.n_csa_offsets; i++) { + if (params.csa_offsets[i] >= params.len) + return -EINVAL; + } + } + if (!params.dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) @@ -7797,8 +7869,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); - params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); params.chan = chandef.chan; err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); if (err) @@ -8495,6 +8565,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) { + u8 *mask_pat; + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), nla_len(pat), NULL); err = -EINVAL; @@ -8518,19 +8590,18 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) goto error; new_triggers.patterns[i].pkt_offset = pkt_offset; - new_triggers.patterns[i].mask = - kmalloc(mask_len + pat_len, GFP_KERNEL); - if (!new_triggers.patterns[i].mask) { + mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!mask_pat) { err = -ENOMEM; goto error; } - new_triggers.patterns[i].pattern = - new_triggers.patterns[i].mask + mask_len; - memcpy(new_triggers.patterns[i].mask, - nla_data(pat_tb[NL80211_PKTPAT_MASK]), + new_triggers.patterns[i].mask = mask_pat; + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); + mask_pat += mask_len; + new_triggers.patterns[i].pattern = mask_pat; new_triggers.patterns[i].pattern_len = pat_len; - memcpy(new_triggers.patterns[i].pattern, + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); i++; @@ -8722,6 +8793,8 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], rem) { + u8 *mask_pat; + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), nla_len(pat), NULL); if (!pat_tb[NL80211_PKTPAT_MASK] || @@ -8743,17 +8816,19 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, return -EINVAL; new_rule->patterns[i].pkt_offset = pkt_offset; - new_rule->patterns[i].mask = - kmalloc(mask_len + pat_len, GFP_KERNEL); - if (!new_rule->patterns[i].mask) + mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!mask_pat) return -ENOMEM; - new_rule->patterns[i].pattern = - new_rule->patterns[i].mask + mask_len; - memcpy(new_rule->patterns[i].mask, - nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); + + new_rule->patterns[i].mask = mask_pat; + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), + mask_len); + + mask_pat += mask_len; + new_rule->patterns[i].pattern = mask_pat; new_rule->patterns[i].pattern_len = pat_len; - memcpy(new_rule->patterns[i].pattern, - nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), + pat_len); i++; } @@ -10033,16 +10108,20 @@ static const struct genl_ops nl80211_ops[] = { /* notification functions */ -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) +void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, + enum nl80211_commands cmd) { struct sk_buff *msg; struct nl80211_dump_wiphy_state state = {}; + WARN_ON(cmd != NL80211_CMD_NEW_WIPHY && + cmd != NL80211_CMD_DEL_WIPHY); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; - if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, &state) < 0) { + if (nl80211_send_wiphy(rdev, cmd, msg, 0, 0, 0, &state) < 0) { nlmsg_free(msg); return; } diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 1e6df96..49c9a48 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -5,7 +5,8 @@ int nl80211_init(void); void nl80211_exit(void); -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, + enum nl80211_commands cmd); void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 00cdf73..d95bbe3 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -199,7 +199,7 @@ static inline int rdev_change_station(struct cfg80211_registered_device *rdev, } static inline int rdev_get_station(struct cfg80211_registered_device *rdev, - struct net_device *dev, u8 *mac, + struct net_device *dev, const u8 *mac, struct station_info *sinfo) { int ret; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e78f532..558b0e3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1876,7 +1876,8 @@ static void reg_process_hint(struct regulatory_request *reg_request) case NL80211_REGDOM_SET_BY_USER: treatment = reg_process_hint_user(reg_request); if (treatment == REG_REQ_IGNORE || - treatment == REG_REQ_ALREADY_SET) + treatment == REG_REQ_ALREADY_SET || + treatment == REG_REQ_USER_HINT_HANDLED) return; queue_delayed_work(system_power_efficient_wq, ®_timeout, msecs_to_jiffies(3142)); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e7329bb..0798c62 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -891,6 +891,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; struct cfg80211_internal_bss tmp = {}, *res; + bool signal_valid; if (WARN_ON(!wiphy)) return NULL; @@ -927,8 +928,9 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, rcu_assign_pointer(tmp.pub.beacon_ies, ies); rcu_assign_pointer(tmp.pub.ies, ies); - res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, - rx_channel == channel); + signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= + wiphy->max_adj_channel_rssi_comp; + res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid); if (!res) return NULL; @@ -952,6 +954,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; + bool signal_valid; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); @@ -999,8 +1002,9 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, - rx_channel == channel); + signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= + wiphy->max_adj_channel_rssi_comp; + res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid); if (!res) return NULL; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 0c0844b..8bbeeb3 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -149,7 +149,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_SCAN_AGAIN: return cfg80211_conn_scan(wdev); case CFG80211_CONN_AUTHENTICATE_NEXT: - BUG_ON(!rdev->ops->auth); + if (WARN_ON(!rdev->ops->auth)) + return -EOPNOTSUPP; wdev->conn->state = CFG80211_CONN_AUTHENTICATING; return cfg80211_mlme_auth(rdev, wdev->netdev, params->channel, params->auth_type, @@ -161,7 +162,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_AUTH_FAILED: return -ENOTCONN; case CFG80211_CONN_ASSOCIATE_NEXT: - BUG_ON(!rdev->ops->assoc); + if (WARN_ON(!rdev->ops->assoc)) + return -EOPNOTSUPP; wdev->conn->state = CFG80211_CONN_ASSOCIATING; if (wdev->conn->prev_bssid_valid) req.prev_bssid = wdev->conn->prev_bssid; @@ -877,7 +879,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, } void cfg80211_disconnected(struct net_device *dev, u16 reason, - u8 *ie, size_t ie_len, gfp_t gfp) + const u8 *ie, size_t ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index f3c13ff..560ed77 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1876,29 +1876,33 @@ TRACE_EVENT(rdev_channel_switch, WIPHY_ENTRY NETDEV_ENTRY CHAN_DEF_ENTRY - __field(u16, counter_offset_beacon) - __field(u16, counter_offset_presp) __field(bool, radar_required) __field(bool, block_tx) __field(u8, count) + __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon) + __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; CHAN_DEF_ASSIGN(¶ms->chandef); - __entry->counter_offset_beacon = params->counter_offset_beacon; - __entry->counter_offset_presp = params->counter_offset_presp; __entry->radar_required = params->radar_required; __entry->block_tx = params->block_tx; __entry->count = params->count; + memcpy(__get_dynamic_array(bcn_ofs), + params->counter_offsets_beacon, + params->n_counter_offsets_beacon * sizeof(u16)); + + /* probe response offsets are optional */ + if (params->n_counter_offsets_presp) + memcpy(__get_dynamic_array(pres_ofs), + params->counter_offsets_presp, + params->n_counter_offsets_presp * sizeof(u16)); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT - ", block_tx: %d, count: %u, radar_required: %d" - ", counter offsets (beacon/presp): %u/%u", + ", block_tx: %d, count: %u, radar_required: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, - __entry->block_tx, __entry->count, __entry->radar_required, - __entry->counter_offset_beacon, - __entry->counter_offset_presp) + __entry->block_tx, __entry->count, __entry->radar_required) ); TRACE_EVENT(rdev_set_qos_map, @@ -2636,6 +2640,21 @@ TRACE_EVENT(cfg80211_ft_event, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap)) ); +TRACE_EVENT(cfg80211_stop_iface, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, + WIPHY_PR_ARG, WDEV_PR_ARG) +); + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/util.c b/net/wireless/util.c index 7c47fa0..728f1c0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -476,7 +476,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, EXPORT_SYMBOL(ieee80211_data_to_8023); int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, u8 *bssid, bool qos) + enum nl80211_iftype iftype, + const u8 *bssid, bool qos) { struct ieee80211_hdr hdr; u16 hdrlen, ethertype; @@ -839,6 +840,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid, ev->ij.channel); break; + case EVENT_STOPPED: + __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev); + break; } wdev_unlock(wdev); @@ -1271,10 +1275,20 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, void *data), void *data) { + const struct ieee80211_regdomain *regdom; + enum nl80211_dfs_regions region = 0; int i, j, iftype; int num_interfaces = 0; u32 used_iftypes = 0; + if (radar_detect) { + rcu_read_lock(); + regdom = rcu_dereference(cfg80211_regdomain); + if (regdom) + region = regdom->dfs_region; + rcu_read_unlock(); + } + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { num_interfaces += iftype_num[iftype]; if (iftype_num[iftype] > 0 && @@ -1315,6 +1329,10 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if (radar_detect != (c->radar_detect_widths & radar_detect)) goto cont; + if (radar_detect && c->radar_detect_regions && + !(c->radar_detect_regions & BIT(region))) + goto cont; + /* Finally check that all iftypes that we're currently * using are actually part of this combination. If they * aren't then we can't use this combination and have @@ -1528,6 +1546,24 @@ unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy) } EXPORT_SYMBOL(ieee80211_get_num_supported_channels); +int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + wdev = dev->ieee80211_ptr; + if (!wdev) + return -EOPNOTSUPP; + + rdev = wiphy_to_rdev(wdev->wiphy); + if (!rdev->ops->get_station) + return -EOPNOTSUPP; + + return rdev_get_station(rdev, dev, mac_addr, sinfo); +} +EXPORT_SYMBOL(cfg80211_get_station); + /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ const unsigned char rfc1042_header[] __aligned(2) = diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 3bb2cdc..c51e8f7b 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -199,6 +199,7 @@ int xfrm_output(struct sk_buff *skb) return xfrm_output2(skb); } +EXPORT_SYMBOL_GPL(xfrm_output); int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) { @@ -213,6 +214,7 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) return -EAFNOSUPPORT; return inner_mode->afinfo->extract_output(x, skb); } +EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); void xfrm_local_error(struct sk_buff *skb, int mtu) { @@ -233,7 +235,4 @@ void xfrm_local_error(struct sk_buff *skb, int mtu) afinfo->local_error(skb, mtu); xfrm_state_put_afinfo(afinfo); } - -EXPORT_SYMBOL_GPL(xfrm_output); -EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); EXPORT_SYMBOL_GPL(xfrm_local_error); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index e63f242..a8ef510 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -769,7 +769,7 @@ EXPORT_SYMBOL(xfrm_policy_byid); #ifdef CONFIG_SECURITY_NETWORK_XFRM static inline int -xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info) +xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) { int dir, err = 0; @@ -783,10 +783,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi continue; err = security_xfrm_policy_delete(pol->security); if (err) { - xfrm_audit_policy_delete(pol, 0, - audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + xfrm_audit_policy_delete(pol, 0, task_valid); return err; } } @@ -800,9 +797,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi pol->security); if (err) { xfrm_audit_policy_delete(pol, 0, - audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + task_valid); return err; } } @@ -812,19 +807,19 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi } #else static inline int -xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info) +xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) { return 0; } #endif -int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) +int xfrm_policy_flush(struct net *net, u8 type, bool task_valid) { int dir, err = 0, cnt = 0; write_lock_bh(&net->xfrm.xfrm_policy_lock); - err = xfrm_policy_flush_secctx_check(net, type, audit_info); + err = xfrm_policy_flush_secctx_check(net, type, task_valid); if (err) goto out; @@ -841,9 +836,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) write_unlock_bh(&net->xfrm.xfrm_policy_lock); cnt++; - xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + xfrm_audit_policy_delete(pol, 1, task_valid); xfrm_policy_kill(pol); @@ -862,10 +855,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) write_unlock_bh(&net->xfrm.xfrm_policy_lock); cnt++; - xfrm_audit_policy_delete(pol, 1, - audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + xfrm_audit_policy_delete(pol, 1, task_valid); xfrm_policy_kill(pol); write_lock_bh(&net->xfrm.xfrm_policy_lock); @@ -2860,21 +2850,14 @@ out_byidx: static void xfrm_policy_fini(struct net *net) { - struct xfrm_audit audit_info; unsigned int sz; int dir; flush_work(&net->xfrm.policy_hash_work); #ifdef CONFIG_XFRM_SUB_POLICY - audit_info.loginuid = INVALID_UID; - audit_info.sessionid = (unsigned int)-1; - audit_info.secid = 0; - xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info); + xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false); #endif - audit_info.loginuid = INVALID_UID; - audit_info.sessionid = (unsigned int)-1; - audit_info.secid = 0; - xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); + xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false); WARN_ON(!list_empty(&net->xfrm.policy_all)); @@ -2989,15 +2972,14 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, } } -void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int sessionid, u32 secid) +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SPD-add"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); @@ -3005,14 +2987,14 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, EXPORT_SYMBOL_GPL(xfrm_audit_policy_add); void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int sessionid, u32 secid) + bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SPD-delete"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 8e9c781..0ab5413 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -463,9 +463,7 @@ expired: if (!err) km_state_expired(x, 1, 0); - xfrm_audit_state_delete(x, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current), 0); + xfrm_audit_state_delete(x, err ? 0 : 1, true); out: spin_unlock(&x->lock); @@ -562,7 +560,7 @@ EXPORT_SYMBOL(xfrm_state_delete); #ifdef CONFIG_SECURITY_NETWORK_XFRM static inline int -xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info) +xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) { int i, err = 0; @@ -572,10 +570,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { if (xfrm_id_proto_match(x->id.proto, proto) && (err = security_xfrm_state_delete(x)) != 0) { - xfrm_audit_state_delete(x, 0, - audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + xfrm_audit_state_delete(x, 0, task_valid); return err; } } @@ -585,18 +580,18 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi } #else static inline int -xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info) +xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) { return 0; } #endif -int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info) +int xfrm_state_flush(struct net *net, u8 proto, bool task_valid) { int i, err = 0, cnt = 0; spin_lock_bh(&net->xfrm.xfrm_state_lock); - err = xfrm_state_flush_secctx_check(net, proto, audit_info); + err = xfrm_state_flush_secctx_check(net, proto, task_valid); if (err) goto out; @@ -612,9 +607,7 @@ restart: err = xfrm_state_delete(x); xfrm_audit_state_delete(x, err ? 0 : 1, - audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + task_valid); xfrm_state_put(x); if (!err) cnt++; @@ -2128,14 +2121,10 @@ out_bydst: void xfrm_state_fini(struct net *net) { - struct xfrm_audit audit_info; unsigned int sz; flush_work(&net->xfrm.state_hash_work); - audit_info.loginuid = INVALID_UID; - audit_info.sessionid = (unsigned int)-1; - audit_info.secid = 0; - xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info); + xfrm_state_flush(net, IPSEC_PROTO_ANY, false); flush_work(&net->xfrm.state_gc_work); WARN_ON(!list_empty(&net->xfrm.state_all)); @@ -2198,30 +2187,28 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, } } -void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, unsigned int sessionid, u32 secid) +void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SAD-add"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); xfrm_audit_helper_sainfo(x, audit_buf); audit_log_format(audit_buf, " res=%u", result); audit_log_end(audit_buf); } EXPORT_SYMBOL_GPL(xfrm_audit_state_add); -void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, unsigned int sessionid, u32 secid) +void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SAD-delete"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); xfrm_audit_helper_sainfo(x, audit_buf); audit_log_format(audit_buf, " res=%u", result); audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 51398ae..fd9a16a 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -597,9 +597,6 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_state *x; int err; struct km_event c; - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - u32 sid; err = verify_newsa_info(p, attrs); if (err) @@ -615,8 +612,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, else err = xfrm_state_update(x); - security_task_getsecid(current, &sid); - xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); + xfrm_audit_state_add(x, err ? 0 : 1, true); if (err < 0) { x->km.state = XFRM_STATE_DEAD; @@ -676,9 +672,6 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, int err = -ESRCH; struct km_event c; struct xfrm_usersa_id *p = nlmsg_data(nlh); - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - u32 sid; x = xfrm_user_state_lookup(net, p, attrs, &err); if (x == NULL) @@ -703,8 +696,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_notify(x, &c); out: - security_task_getsecid(current, &sid); - xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); + xfrm_audit_state_delete(x, err ? 0 : 1, true); xfrm_state_put(x); return err; } @@ -1414,9 +1406,6 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; int err; int excl; - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - u32 sid; err = verify_newpolicy_info(p); if (err) @@ -1435,8 +1424,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, * a type XFRM_MSG_UPDPOLICY - JHS */ excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; err = xfrm_policy_insert(p->dir, xp, excl); - security_task_getsecid(current, &sid); - xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); + xfrm_audit_policy_add(xp, err ? 0 : 1, true); if (err) { security_xfrm_policy_free(xp->security); @@ -1673,13 +1661,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, NETLINK_CB(skb).portid); } } else { - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - u32 sid; - - security_task_getsecid(current, &sid); - xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, - sid); + xfrm_audit_policy_delete(xp, err ? 0 : 1, true); if (err != 0) goto out; @@ -1704,13 +1686,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct net *net = sock_net(skb->sk); struct km_event c; struct xfrm_usersa_flush *p = nlmsg_data(nlh); - struct xfrm_audit audit_info; int err; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &audit_info.secid); - err = xfrm_state_flush(net, p->proto, &audit_info); + err = xfrm_state_flush(net, p->proto, true); if (err) { if (err == -ESRCH) /* empty table */ return 0; @@ -1894,16 +1872,12 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; u8 type = XFRM_POLICY_TYPE_MAIN; int err; - struct xfrm_audit audit_info; err = copy_from_user_policy_type(&type, attrs); if (err) return err; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &audit_info.secid); - err = xfrm_policy_flush(net, type, &audit_info); + err = xfrm_policy_flush(net, type, true); if (err) { if (err == -ESRCH) /* empty table */ return 0; @@ -1969,14 +1943,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, err = 0; if (up->hard) { - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - u32 sid; - - security_task_getsecid(current, &sid); xfrm_policy_delete(xp, p->dir); - xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); - + xfrm_audit_policy_delete(xp, 1, true); } else { // reset the timers here? WARN(1, "Dont know what to do with soft policy expire\n"); @@ -2012,13 +1980,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_expired(x, ue->hard, nlh->nlmsg_pid); if (ue->hard) { - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - u32 sid; - - security_task_getsecid(current, &sid); __xfrm_state_delete(x); - xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); + xfrm_audit_state_delete(x, 1, true); } err = 0; out: diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh index fd8fa9a..5b3add3 100755 --- a/scripts/checksyscalls.sh +++ b/scripts/checksyscalls.sh @@ -25,7 +25,7 @@ cat << EOF #define __IGNORE_rmdir /* unlinkat */ #define __IGNORE_lchown /* fchownat */ #define __IGNORE_access /* faccessat */ -#define __IGNORE_rename /* renameat */ +#define __IGNORE_rename /* renameat2 */ #define __IGNORE_readlink /* readlinkat */ #define __IGNORE_symlink /* symlinkat */ #define __IGNORE_utimes /* futimesat */ @@ -37,6 +37,9 @@ cat << EOF #define __IGNORE_lstat64 /* fstatat64 */ #endif +/* Missing flags argument */ +#define __IGNORE_renameat /* renameat2 */ + /* CLOEXEC flag */ #define __IGNORE_pipe /* pipe2 */ #define __IGNORE_dup2 /* dup3 */ diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 8fb1488..97130f8 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -66,7 +66,6 @@ extern int apparmor_initialized __initdata; char *aa_split_fqname(char *args, char **ns_name); void aa_info_message(const char *str); void *__aa_kvmalloc(size_t size, gfp_t flags); -void kvfree(void *buffer); static inline void *kvmalloc(size_t size) { diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 6968992..c1827e0 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -104,17 +104,3 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) } return buffer; } - -/** - * kvfree - free an allocation do by kvmalloc - * @buffer: buffer to free (MAYBE_NULL) - * - * Free a buffer allocated by kvmalloc - */ -void kvfree(void *buffer) -{ - if (is_vmalloc_addr(buffer)) - vfree(buffer); - else - kfree(buffer); -} diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 8365909..9134dbf 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -306,57 +306,138 @@ static int devcgroup_seq_show(struct seq_file *m, void *v) } /** - * may_access - verifies if a new exception is part of what is allowed - * by a dev cgroup based on the default policy + - * exceptions. This is used to make sure a child cgroup - * won't have more privileges than its parent or to - * verify if a certain access is allowed. - * @dev_cgroup: dev cgroup to be tested against - * @refex: new exception - * @behavior: behavior of the exception + * match_exception - iterates the exception list trying to find a complete match + * @exceptions: list of exceptions + * @type: device type (DEV_BLOCK or DEV_CHAR) + * @major: device file major number, ~0 to match all + * @minor: device file minor number, ~0 to match all + * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD) + * + * It is considered a complete match if an exception is found that will + * contain the entire range of provided parameters. + * + * Return: true in case it matches an exception completely */ -static bool may_access(struct dev_cgroup *dev_cgroup, - struct dev_exception_item *refex, - enum devcg_behavior behavior) +static bool match_exception(struct list_head *exceptions, short type, + u32 major, u32 minor, short access) { struct dev_exception_item *ex; - bool match = false; - rcu_lockdep_assert(rcu_read_lock_held() || - lockdep_is_held(&devcgroup_mutex), - "device_cgroup::may_access() called without proper synchronization"); + list_for_each_entry_rcu(ex, exceptions, list) { + if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) + continue; + if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR)) + continue; + if (ex->major != ~0 && ex->major != major) + continue; + if (ex->minor != ~0 && ex->minor != minor) + continue; + /* provided access cannot have more than the exception rule */ + if (access & (~ex->access)) + continue; + return true; + } + return false; +} + +/** + * match_exception_partial - iterates the exception list trying to find a partial match + * @exceptions: list of exceptions + * @type: device type (DEV_BLOCK or DEV_CHAR) + * @major: device file major number, ~0 to match all + * @minor: device file minor number, ~0 to match all + * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD) + * + * It is considered a partial match if an exception's range is found to + * contain *any* of the devices specified by provided parameters. This is + * used to make sure no extra access is being granted that is forbidden by + * any of the exception list. + * + * Return: true in case the provided range mat matches an exception completely + */ +static bool match_exception_partial(struct list_head *exceptions, short type, + u32 major, u32 minor, short access) +{ + struct dev_exception_item *ex; - list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) { - if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) + list_for_each_entry_rcu(ex, exceptions, list) { + if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) continue; - if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR)) + if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR)) continue; - if (ex->major != ~0 && ex->major != refex->major) + /* + * We must be sure that both the exception and the provided + * range aren't masking all devices + */ + if (ex->major != ~0 && major != ~0 && ex->major != major) continue; - if (ex->minor != ~0 && ex->minor != refex->minor) + if (ex->minor != ~0 && minor != ~0 && ex->minor != minor) continue; - if (refex->access & (~ex->access)) + /* + * In order to make sure the provided range isn't matching + * an exception, all its access bits shouldn't match the + * exception's access bits + */ + if (!(access & ex->access)) continue; - match = true; - break; + return true; } + return false; +} + +/** + * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions + * @dev_cgroup: dev cgroup to be tested against + * @refex: new exception + * @behavior: behavior of the exception's dev_cgroup + * + * This is used to make sure a child cgroup won't have more privileges + * than its parent + */ +static bool verify_new_ex(struct dev_cgroup *dev_cgroup, + struct dev_exception_item *refex, + enum devcg_behavior behavior) +{ + bool match = false; + + rcu_lockdep_assert(rcu_read_lock_held() || + lockdep_is_held(&devcgroup_mutex), + "device_cgroup:verify_new_ex called without proper synchronization"); if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) { if (behavior == DEVCG_DEFAULT_ALLOW) { - /* the exception will deny access to certain devices */ + /* + * new exception in the child doesn't matter, only + * adding extra restrictions + */ return true; } else { - /* the exception will allow access to certain devices */ + /* + * new exception in the child will add more devices + * that can be acessed, so it can't match any of + * parent's exceptions, even slightly + */ + match = match_exception_partial(&dev_cgroup->exceptions, + refex->type, + refex->major, + refex->minor, + refex->access); + if (match) - /* - * a new exception allowing access shouldn't - * match an parent's exception - */ return false; return true; } } else { - /* only behavior == DEVCG_DEFAULT_DENY allowed here */ + /* + * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore + * the new exception will add access to more devices and must + * be contained completely in an parent's exception to be + * allowed + */ + match = match_exception(&dev_cgroup->exceptions, refex->type, + refex->major, refex->minor, + refex->access); + if (match) /* parent has an exception that matches the proposed */ return true; @@ -378,7 +459,38 @@ static int parent_has_perm(struct dev_cgroup *childcg, if (!parent) return 1; - return may_access(parent, ex, childcg->behavior); + return verify_new_ex(parent, ex, childcg->behavior); +} + +/** + * parent_allows_removal - verify if it's ok to remove an exception + * @childcg: child cgroup from where the exception will be removed + * @ex: exception being removed + * + * When removing an exception in cgroups with default ALLOW policy, it must + * be checked if removing it will give the child cgroup more access than the + * parent. + * + * Return: true if it's ok to remove exception, false otherwise + */ +static bool parent_allows_removal(struct dev_cgroup *childcg, + struct dev_exception_item *ex) +{ + struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); + + if (!parent) + return true; + + /* It's always allowed to remove access to devices */ + if (childcg->behavior == DEVCG_DEFAULT_DENY) + return true; + + /* + * Make sure you're not removing part or a whole exception existing in + * the parent cgroup + */ + return !match_exception_partial(&parent->exceptions, ex->type, + ex->major, ex->minor, ex->access); } /** @@ -616,17 +728,21 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, switch (filetype) { case DEVCG_ALLOW: - if (!parent_has_perm(devcgroup, &ex)) - return -EPERM; /* * If the default policy is to allow by default, try to remove * an matching exception instead. And be silent about it: we * don't want to break compatibility */ if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { + /* Check if the parent allows removing it first */ + if (!parent_allows_removal(devcgroup, &ex)) + return -EPERM; dev_exception_rm(devcgroup, &ex); - return 0; + break; } + + if (!parent_has_perm(devcgroup, &ex)) + return -EPERM; rc = dev_exception_add(devcgroup, &ex); break; case DEVCG_DENY: @@ -704,18 +820,18 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor, short access) { struct dev_cgroup *dev_cgroup; - struct dev_exception_item ex; - int rc; - - memset(&ex, 0, sizeof(ex)); - ex.type = type; - ex.major = major; - ex.minor = minor; - ex.access = access; + bool rc; rcu_read_lock(); dev_cgroup = task_devcgroup(current); - rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior); + if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) + /* Can't match any of the exceptions, even partially */ + rc = !match_exception_partial(&dev_cgroup->exceptions, + type, major, minor, access); + else + /* Need to match completely one exception to be allowed */ + rc = match_exception(&dev_cgroup->exceptions, type, major, + minor, access); rcu_read_unlock(); if (!rc) diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index 6496822..1ff78ec 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -818,12 +818,14 @@ int snd_sbmixer_new(struct snd_sb *chip) return err; break; case SB_HW_DT019X: - if ((err = snd_sbmixer_init(chip, - snd_dt019x_controls, - ARRAY_SIZE(snd_dt019x_controls), - snd_dt019x_init_values, - ARRAY_SIZE(snd_dt019x_init_values), - "DT019X")) < 0) + err = snd_sbmixer_init(chip, + snd_dt019x_controls, + ARRAY_SIZE(snd_dt019x_controls), + snd_dt019x_init_values, + ARRAY_SIZE(snd_dt019x_init_values), + "DT019X"); + if (err < 0) + return err; break; default: strcpy(card->mixername, "???"); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b540ad7..2c54629 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1367,6 +1367,12 @@ static int azx_first_init(struct azx *chip) /* initialize streams */ azx_init_stream(chip); + /* workaround for Broadwell HDMI: the first stream is broken, + * so mask it by keeping it as if opened + */ + if (pci->vendor == 0x8086 && pci->device == 0x160c) + chip->azx_dev[0].opened = 1; + /* initialize chip */ azx_init_pci(chip); azx_init_chip(chip, (probe_only[dev] & 2) == 0); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 0cb5b89..b4218a1 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1127,8 +1127,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, AMP_OUT_UNMUTE); eld = &per_pin->sink_eld; - if (!eld->monitor_present) + if (!eld->monitor_present) { + hdmi_set_channel_count(codec, per_pin->cvt_nid, channels); return; + } if (!non_pcm && per_pin->chmap_set) ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); @@ -3330,6 +3332,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, +{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, { .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi }, @@ -3385,6 +3388,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0044"); MODULE_ALIAS("snd-hda-codec-id:10de0051"); MODULE_ALIAS("snd-hda-codec-id:10de0060"); MODULE_ALIAS("snd-hda-codec-id:10de0067"); +MODULE_ALIAS("snd-hda-codec-id:10de0071"); MODULE_ALIAS("snd-hda-codec-id:10de8001"); MODULE_ALIAS("snd-hda-codec-id:11069f80"); MODULE_ALIAS("snd-hda-codec-id:11069f81"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c1952c9..49e884f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4616,13 +4616,17 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0653, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0657, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0658, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x065c, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x065f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0662, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0667, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0674, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x067e, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x067f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0680, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index fa158cf..d1929de 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -376,7 +376,7 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, reg = AIC31XX_ADCFLAG; break; default: - dev_err(w->codec->dev, "Unknown widget '%s' calling %s/n", + dev_err(w->codec->dev, "Unknown widget '%s' calling %s\n", w->name, __func__); return -EINVAL; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 5522d25..ecd26dd 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -154,6 +154,7 @@ static struct reg_default wm8962_reg[] = { { 40, 0x0000 }, /* R40 - SPKOUTL volume */ { 41, 0x0000 }, /* R41 - SPKOUTR volume */ + { 49, 0x0010 }, /* R49 - Class D Control 1 */ { 51, 0x0003 }, /* R51 - Class D Control 2 */ { 56, 0x0506 }, /* R56 - Clocking 4 */ @@ -795,7 +796,6 @@ static bool wm8962_volatile_register(struct device *dev, unsigned int reg) case WM8962_ALC2: case WM8962_THERMAL_SHUTDOWN_STATUS: case WM8962_ADDITIONAL_CONTROL_4: - case WM8962_CLASS_D_CONTROL_1: case WM8962_DC_SERVO_6: case WM8962_INTERRUPT_STATUS_1: case WM8962_INTERRUPT_STATUS_2: @@ -2929,13 +2929,22 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, static int wm8962_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - int val; + int val, ret; if (mute) - val = WM8962_DAC_MUTE; + val = WM8962_DAC_MUTE | WM8962_DAC_MUTE_ALT; else val = 0; + /** + * The DAC mute bit is mirrored in two registers, update both to keep + * the register cache consistent. + */ + ret = snd_soc_update_bits(codec, WM8962_CLASS_D_CONTROL_1, + WM8962_DAC_MUTE_ALT, val); + if (ret < 0) + return ret; + return snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1, WM8962_DAC_MUTE, val); } diff --git a/sound/soc/codecs/wm8962.h b/sound/soc/codecs/wm8962.h index a1a5d52..910aafd 100644 --- a/sound/soc/codecs/wm8962.h +++ b/sound/soc/codecs/wm8962.h @@ -1954,6 +1954,10 @@ #define WM8962_SPKOUTL_ENA_MASK 0x0040 /* SPKOUTL_ENA */ #define WM8962_SPKOUTL_ENA_SHIFT 6 /* SPKOUTL_ENA */ #define WM8962_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */ +#define WM8962_DAC_MUTE_ALT 0x0010 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_ALT_MASK 0x0010 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_ALT_SHIFT 4 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_ALT_WIDTH 1 /* DAC_MUTE */ #define WM8962_SPKOUTL_PGA_MUTE 0x0002 /* SPKOUTL_PGA_MUTE */ #define WM8962_SPKOUTL_PGA_MUTE_MASK 0x0002 /* SPKOUTL_PGA_MUTE */ #define WM8962_SPKOUTL_PGA_MUTE_SHIFT 1 /* SPKOUTL_PGA_MUTE */ diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index c8e5db1..496ce2e 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -258,10 +258,16 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, return -EINVAL; } - if (ratio == 1) { + /* Only EXTAL source can be output directly without using PSR and PM */ + if (ratio == 1 && clksrc == esai_priv->extalclk) { /* Bypass all the dividers if not being needed */ ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO; goto out; + } else if (ratio < 2) { + /* The ratio should be no less than 2 if using other sources */ + dev_err(dai->dev, "failed to derive required HCK%c rate\n", + tx ? 'T' : 'R'); + return -EINVAL; } ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0); @@ -307,7 +313,8 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; } - if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { + /* The ratio should be contented by FP alone if bypassing PM and PSR */ + if (!esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n"); return -EINVAL; } @@ -454,12 +461,6 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream, } if (!dai->active) { - /* Reset Port C */ - regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, - ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); - regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, - ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); - /* Set synchronous mode */ regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, ESAI_SAICR_SYNC, esai_priv->synchronous ? @@ -519,6 +520,11 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); + /* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); return 0; } diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index ac86993..267717a 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -145,7 +145,7 @@ static const struct file_operations audmux_debugfs_fops = { .llseek = default_llseek, }; -static void __init audmux_debugfs_init(void) +static void audmux_debugfs_init(void) { int i; char buf[20]; diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/sst-acpi.c index 5d06eec..18aee77 100644 --- a/sound/soc/intel/sst-acpi.c +++ b/sound/soc/intel/sst-acpi.c @@ -138,6 +138,7 @@ static int sst_acpi_probe(struct platform_device *pdev) sst_pdata = &sst_acpi->sst_pdata; sst_pdata->id = desc->sst_id; + sst_pdata->dma_dev = dev; sst_acpi->desc = desc; sst_acpi->mach = mach; diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c index a50bf7f..adf0aca 100644 --- a/sound/soc/intel/sst-baytrail-dsp.c +++ b/sound/soc/intel/sst-baytrail-dsp.c @@ -324,7 +324,7 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata) memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET, &pdata->fw_base, sizeof(u32)); - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32)); if (ret) return ret; diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index d0eaeee..0d31dbb 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -542,16 +542,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, void *data) { struct sst_byt_stream *stream; + struct sst_dsp *sst = byt->dsp; + unsigned long flags; stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (stream == NULL) return NULL; + spin_lock_irqsave(&sst->spinlock, flags); list_add(&stream->node, &byt->stream_list); stream->notify_position = notify_position; stream->pdata = data; stream->byt = byt; stream->str_id = id; + spin_unlock_irqrestore(&sst->spinlock, flags); return stream; } @@ -630,6 +634,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) { u64 header; int ret = 0; + struct sst_dsp *sst = byt->dsp; + unsigned long flags; if (!stream->commited) goto out; @@ -644,8 +650,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) stream->commited = false; out: + spin_lock_irqsave(&sst->spinlock, flags); list_del(&stream->node); kfree(stream); + spin_unlock_irqrestore(&sst->spinlock, flags); return ret; } diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h index 30ca14a..4012134 100644 --- a/sound/soc/intel/sst-dsp-priv.h +++ b/sound/soc/intel/sst-dsp-priv.h @@ -228,6 +228,7 @@ struct sst_dsp { spinlock_t spinlock; /* IPC locking */ struct mutex mutex; /* DSP FW lock */ struct device *dev; + struct device *dma_dev; void *thread_context; int irq; u32 id; diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c index 0c129fd..0b715b2 100644 --- a/sound/soc/intel/sst-dsp.c +++ b/sound/soc/intel/sst-dsp.c @@ -337,6 +337,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev, spin_lock_init(&sst->spinlock); mutex_init(&sst->mutex); sst->dev = dev; + sst->dma_dev = pdata->dma_dev; sst->thread_context = sst_dev->thread_context; sst->sst_dev = sst_dev; sst->id = pdata->id; diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h index 74052b5..e44423b 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/sst-dsp.h @@ -169,6 +169,7 @@ struct sst_pdata { u32 dma_base; u32 dma_size; int dma_engine; + struct device *dma_dev; /* DSP */ u32 id; diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index f768710..928f228 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -57,14 +57,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, sst_fw->private = private; sst_fw->size = fw->size; - err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32)); - if (err < 0) { - kfree(sst_fw); - return NULL; - } - /* allocate DMA buffer to store FW data */ - sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size, + sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size, &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL); if (!sst_fw->dma_buf) { dev_err(dsp->dev, "error: DMA alloc failed\n"); @@ -106,7 +100,7 @@ void sst_fw_free(struct sst_fw *sst_fw) list_del(&sst_fw->list); mutex_unlock(&dsp->mutex); - dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, + dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf, sst_fw->dmable_fw_paddr); kfree(sst_fw); } @@ -202,6 +196,9 @@ static int block_alloc_contiguous(struct sst_module *module, size -= block->size; } + list_for_each_entry(block, &tmp, list) + list_add(&block->module_list, &module->block_list); + list_splice(&tmp, &dsp->used_block_list); return 0; } @@ -247,8 +244,7 @@ static int block_alloc(struct sst_module *module, /* do we span > 1 blocks */ if (data->size > block->size) { ret = block_alloc_contiguous(module, data, - block->offset + block->size, - data->size - block->size); + block->offset, data->size); if (ret == 0) return ret; } @@ -344,7 +340,7 @@ static int block_alloc_fixed(struct sst_module *module, err = block_alloc_contiguous(module, data, block->offset + block->size, - data->size - block->size + data->offset - block->offset); + data->size - block->size); if (err < 0) return -ENOMEM; @@ -371,15 +367,10 @@ static int block_alloc_fixed(struct sst_module *module, if (data->offset >= block->offset && data->offset < block_end) { err = block_alloc_contiguous(module, data, - block->offset + block->size, - data->size - block->size); + block->offset, data->size); if (err < 0) return -ENOMEM; - /* add block */ - block->data_type = data->data_type; - list_move(&block->list, &dsp->used_block_list); - list_add(&block->module_list, &module->block_list); return 0; } diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index f5ebf36..535f517 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -433,7 +433,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) int ret = -ENODEV, i, j, region_count; u32 offset, size; - dev = sst->dev; + dev = sst->dma_dev; switch (sst->id) { case SST_DEV_ID_LYNX_POINT: @@ -466,7 +466,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) return ret; } - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31)); if (ret) return ret; diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 50e4246..e7996b3 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1159,11 +1159,14 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, void *data) { struct sst_hsw_stream *stream; + struct sst_dsp *sst = hsw->dsp; + unsigned long flags; stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (stream == NULL) return NULL; + spin_lock_irqsave(&sst->spinlock, flags); list_add(&stream->node, &hsw->stream_list); stream->notify_position = notify_position; stream->pdata = data; @@ -1172,6 +1175,7 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, /* work to process notification messages */ INIT_WORK(&stream->notify_work, hsw_notification_work); + spin_unlock_irqrestore(&sst->spinlock, flags); return stream; } @@ -1180,6 +1184,8 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) { u32 header; int ret = 0; + struct sst_dsp *sst = hsw->dsp; + unsigned long flags; /* dont free DSP streams that are not commited */ if (!stream->commited) @@ -1201,8 +1207,11 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) trace_hsw_stream_free_req(stream, &stream->free_req); out: + cancel_work_sync(&stream->notify_work); + spin_lock_irqsave(&sst->spinlock, flags); list_del(&stream->node); kfree(stream); + spin_unlock_irqrestore(&sst->spinlock, flags); return ret; } @@ -1538,10 +1547,28 @@ int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) } /* Stream pointer positions */ -int sst_hsw_get_dsp_position(struct sst_hsw *hsw, +u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw, struct sst_hsw_stream *stream) { - return stream->rpos.position; + u32 rpos; + + sst_dsp_read(hsw->dsp, &rpos, + stream->reply.read_position_register_address, sizeof(rpos)); + + return rpos; +} + +/* Stream presentation (monotonic) positions */ +u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw, + struct sst_hsw_stream *stream) +{ + u64 ppos; + + sst_dsp_read(hsw->dsp, &ppos, + stream->reply.presentation_position_register_address, + sizeof(ppos)); + + return ppos; } int sst_hsw_stream_set_write_position(struct sst_hsw *hsw, diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index d517929..2ac194a 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -464,7 +464,9 @@ int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw, struct sst_hsw_stream *stream, u32 *position); int sst_hsw_stream_set_write_position(struct sst_hsw *hsw, struct sst_hsw_stream *stream, u32 stage_id, u32 position); -int sst_hsw_get_dsp_position(struct sst_hsw *hsw, +u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw, + struct sst_hsw_stream *stream); +u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw, struct sst_hsw_stream *stream); /* HW port config */ diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 0a32dd1..9d5f64a 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -99,6 +99,7 @@ struct hsw_pcm_data { struct snd_compr_stream *cstream; unsigned int wpos; struct mutex mutex; + bool allocated; }; /* private data for the driver */ @@ -107,12 +108,14 @@ struct hsw_priv_data { struct sst_hsw *hsw; /* page tables */ - unsigned char *pcm_pg[HSW_PCM_COUNT][2]; + struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; /* DAI data */ struct hsw_pcm_data pcm[HSW_PCM_COUNT]; }; +static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data); + static inline u32 hsw_mixer_to_ipc(unsigned int value) { if (value >= ARRAY_SIZE(volume_map)) @@ -273,28 +276,26 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { }; /* Create DMA buffer page table for DSP */ -static int create_adsp_page_table(struct hsw_priv_data *pdata, - struct snd_soc_pcm_runtime *rtd, - unsigned char *dma_area, size_t size, int pcm, int stream) +static int create_adsp_page_table(struct snd_pcm_substream *substream, + struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd, + unsigned char *dma_area, size_t size, int pcm) { - int i, pages; + struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); + int i, pages, stream = substream->stream; - if (size % PAGE_SIZE) - pages = (size / PAGE_SIZE) + 1; - else - pages = size / PAGE_SIZE; + pages = snd_sgbuf_aligned_pages(size); dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n", dma_area, size, pages); for (i = 0; i < pages; i++) { u32 idx = (((i << 2) + i)) >> 1; - u32 pfn = (virt_to_phys(dma_area + i * PAGE_SIZE)) >> PAGE_SHIFT; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; u32 *pg_table; dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - pg_table = (u32*)(pdata->pcm_pg[pcm][stream] + idx); + pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx); if (i & 1) *pg_table |= (pfn << 4); @@ -317,12 +318,36 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, struct sst_hsw *hsw = pdata->hsw; struct sst_module *module_data; struct sst_dsp *dsp; + struct snd_dma_buffer *dmab; enum sst_hsw_stream_type stream_type; enum sst_hsw_stream_path_id path_id; u32 rate, bits, map, pages, module_id; u8 channels; int ret; + /* check if we are being called a subsequent time */ + if (pcm_data->allocated) { + ret = sst_hsw_stream_reset(hsw, pcm_data->stream); + if (ret < 0) + dev_dbg(rtd->dev, "error: reset stream failed %d\n", + ret); + + ret = sst_hsw_stream_free(hsw, pcm_data->stream); + if (ret < 0) { + dev_dbg(rtd->dev, "error: free stream failed %d\n", + ret); + return ret; + } + pcm_data->allocated = false; + + pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id, + hsw_notify_pointer, pcm_data); + if (pcm_data->stream == NULL) { + dev_err(rtd->dev, "error: failed to create stream\n"); + return -EINVAL; + } + } + /* stream direction */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) path_id = SST_HSW_STREAM_PATH_SSP0_OUT; @@ -416,8 +441,10 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = create_adsp_page_table(pdata, rtd, runtime->dma_area, - runtime->dma_bytes, rtd->cpu_dai->id, substream->stream); + dmab = snd_pcm_get_dma_buf(substream); + + ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area, + runtime->dma_bytes, rtd->cpu_dai->id); if (ret < 0) return ret; @@ -430,9 +457,9 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, pages = runtime->dma_bytes / PAGE_SIZE; ret = sst_hsw_stream_buffer(hsw, pcm_data->stream, - virt_to_phys(pdata->pcm_pg[rtd->cpu_dai->id][substream->stream]), + pdata->dmab[rtd->cpu_dai->id][substream->stream].addr, pages, runtime->dma_bytes, 0, - (u32)(virt_to_phys(runtime->dma_area) >> PAGE_SHIFT)); + snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT); if (ret < 0) { dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret); return ret; @@ -474,6 +501,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, dev_err(rtd->dev, "error: failed to commit stream %d\n", ret); return ret; } + pcm_data->allocated = true; ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1); if (ret < 0) @@ -541,12 +569,14 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); struct sst_hsw *hsw = pdata->hsw; snd_pcm_uframes_t offset; + uint64_t ppos; + u32 position = sst_hsw_get_dsp_position(hsw, pcm_data->stream); - offset = bytes_to_frames(runtime, - sst_hsw_get_dsp_position(hsw, pcm_data->stream)); + offset = bytes_to_frames(runtime, position); + ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream); - dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n", - frames_to_bytes(runtime, (u32)offset)); + dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n", + position, ppos); return offset; } @@ -606,6 +636,7 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream) dev_dbg(rtd->dev, "error: free stream failed %d\n", ret); goto out; } + pcm_data->allocated = 0; pcm_data->stream = NULL; out: @@ -621,7 +652,7 @@ static struct snd_pcm_ops hsw_pcm_ops = { .hw_free = hsw_pcm_hw_free, .trigger = hsw_pcm_trigger, .pointer = hsw_pcm_pointer, - .mmap = snd_pcm_lib_default_mmap, + .page = snd_pcm_sgbuf_ops_page, }; static void hsw_pcm_free(struct snd_pcm *pcm) @@ -632,17 +663,16 @@ static void hsw_pcm_free(struct snd_pcm *pcm) static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; + struct snd_soc_platform *platform = rtd->platform; + struct sst_pdata *pdata = dev_get_platdata(platform->dev); + struct device *dev = pdata->dma_dev; int ret = 0; - ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV, - rtd->card->dev, + SNDRV_DMA_TYPE_DEV_SG, + dev, hsw_pcm_hardware.buffer_bytes_max, hsw_pcm_hardware.buffer_bytes_max); if (ret) { @@ -742,11 +772,14 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) { struct sst_pdata *pdata = dev_get_platdata(platform->dev); struct hsw_priv_data *priv_data; - int i; + struct device *dma_dev; + int i, ret = 0; if (!pdata) return -ENODEV; + dma_dev = pdata->dma_dev; + priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL); priv_data->hsw = pdata->dsp; snd_soc_platform_set_drvdata(platform, priv_data); @@ -758,15 +791,17 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) /* playback */ if (hsw_dais[i].playback.channels_min) { - priv_data->pcm_pg[i][0] = kzalloc(PAGE_SIZE, GFP_DMA); - if (priv_data->pcm_pg[i][0] == NULL) + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, + PAGE_SIZE, &priv_data->dmab[i][0]); + if (ret < 0) goto err; } /* capture */ if (hsw_dais[i].capture.channels_min) { - priv_data->pcm_pg[i][1] = kzalloc(PAGE_SIZE, GFP_DMA); - if (priv_data->pcm_pg[i][1] == NULL) + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, + PAGE_SIZE, &priv_data->dmab[i][1]); + if (ret < 0) goto err; } } @@ -776,11 +811,11 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) err: for (;i >= 0; i--) { if (hsw_dais[i].playback.channels_min) - kfree(priv_data->pcm_pg[i][0]); + snd_dma_free_pages(&priv_data->dmab[i][0]); if (hsw_dais[i].capture.channels_min) - kfree(priv_data->pcm_pg[i][1]); + snd_dma_free_pages(&priv_data->dmab[i][1]); } - return -ENOMEM; + return ret; } static int hsw_pcm_remove(struct snd_soc_platform *platform) @@ -791,9 +826,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform) for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { if (hsw_dais[i].playback.channels_min) - kfree(priv_data->pcm_pg[i][0]); + snd_dma_free_pages(&priv_data->dmab[i][0]); if (hsw_dais[i].capture.channels_min) - kfree(priv_data->pcm_pg[i][1]); + snd_dma_free_pages(&priv_data->dmab[i][1]); } return 0; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 215b668..8942447 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -197,13 +197,12 @@ static void rsnd_dma_complete(void *data) * rsnd_dai_pointer_update() will be called twice, * ant it will breaks io->byte_pos */ - - rsnd_dai_pointer_update(io, io->byte_per_period); - if (dma->submit_loop) rsnd_dma_continue(dma); rsnd_unlock(priv, flags); + + rsnd_dai_pointer_update(io, io->byte_per_period); } static void __rsnd_dma_start(struct rsnd_dma *dma) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7769b0a..6d6ceee 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1612,8 +1612,11 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) "ASoC: Failed to turn on bias: %d\n", ret); } - /* Prepare for a STADDBY->ON or ON->STANDBY transition */ - if (d->bias_level != d->target_bias_level) { + /* Prepare for a transition to ON or away from ON */ + if ((d->target_bias_level == SND_SOC_BIAS_ON && + d->bias_level != SND_SOC_BIAS_ON) || + (d->target_bias_level != SND_SOC_BIAS_ON && + d->bias_level == SND_SOC_BIAS_ON)) { ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); if (ret != 0) dev_err(d->dev, @@ -3475,8 +3478,11 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) cpu_dai = rtd->cpu_dai; codec_dai = rtd->codec_dai; - /* dynamic FE links have no fixed DAI mapping */ - if (rtd->dai_link->dynamic) + /* + * dynamic FE links have no fixed DAI mapping. + * CODEC<->CODEC links have no direct connection. + */ + if (rtd->dai_link->dynamic || rtd->dai_link->params) continue; /* there is no point in connecting BE DAI links with dummies */ diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2cedf09..a391de0 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1675,7 +1675,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) continue; if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) diff --git a/sound/usb/card.c b/sound/usb/card.c index 893d5a1..c3b5b7d 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -651,7 +651,7 @@ int snd_usb_autoresume(struct snd_usb_audio *chip) int err = -ENODEV; down_read(&chip->shutdown_rwsem); - if (chip->probing) + if (chip->probing && chip->in_pm) err = 0; else if (!chip->shutdown) err = usb_autopm_get_interface(chip->pm_intf); @@ -663,7 +663,7 @@ int snd_usb_autoresume(struct snd_usb_audio *chip) void snd_usb_autosuspend(struct snd_usb_audio *chip) { down_read(&chip->shutdown_rwsem); - if (!chip->shutdown && !chip->probing) + if (!chip->shutdown && !chip->probing && !chip->in_pm) usb_autopm_put_interface(chip->pm_intf); up_read(&chip->shutdown_rwsem); } @@ -695,8 +695,9 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) chip->autosuspended = 1; } - list_for_each_entry(mixer, &chip->mixer_list, list) - snd_usb_mixer_suspend(mixer); + if (chip->num_suspended_intf == 1) + list_for_each_entry(mixer, &chip->mixer_list, list) + snd_usb_mixer_suspend(mixer); return 0; } @@ -711,6 +712,8 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) return 0; if (--chip->num_suspended_intf) return 0; + + chip->in_pm = 1; /* * ALSA leaves material resumption to user space * we just notify and restart the mixers @@ -726,6 +729,7 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) chip->autosuspended = 0; err_out: + chip->in_pm = 0; return err; } diff --git a/sound/usb/card.h b/sound/usb/card.h index 9867ab8..97acb90 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -92,6 +92,7 @@ struct snd_usb_endpoint { unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int syncmaxsize; /* sync endpoint packet size */ unsigned int fill_max:1; /* fill max packet size always */ + unsigned int udh01_fb_quirk:1; /* corrupted feedback data */ unsigned int datainterval; /* log_2 of data packet interval */ unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned char silence_value; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index e70a87e..289f582 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -471,6 +471,10 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, ep->syncinterval = 3; ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); + + if (chip->usb_id == USB_ID(0x0644, 0x8038) /* TEAC UD-H01 */ && + ep->syncmaxsize == 4) + ep->udh01_fb_quirk = 1; } list_add_tail(&ep->list, &chip->ep_list); @@ -1105,7 +1109,16 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, if (f == 0) return; - if (unlikely(ep->freqshift == INT_MIN)) { + if (unlikely(sender->udh01_fb_quirk)) { + /* + * The TEAC UD-H01 firmware sometimes changes the feedback value + * by +/- 0x1.0000. + */ + if (f < ep->freqn - 0x8000) + f += 0x10000; + else if (f > ep->freqn + 0x8000) + f -= 0x10000; + } else if (unlikely(ep->freqshift == INT_MIN)) { /* * The first time we see a feedback value, determine its format * by shifting it left or right until it matches the nominal diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 131336d..c62a165 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1501,9 +1501,8 @@ static void retire_playback_urb(struct snd_usb_substream *subs, * The error should be lower than 2ms since the estimate relies * on two reads of a counter updated every ms. */ - if (printk_ratelimit() && - abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) - dev_dbg(&subs->dev->dev, + if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) + dev_dbg_ratelimited(&subs->dev->dev, "delay: estimated %d, actual %d\n", est_delay, subs->last_delay); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 25c4c7e..91d0380 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -40,6 +40,7 @@ struct snd_usb_audio { struct rw_semaphore shutdown_rwsem; unsigned int shutdown:1; unsigned int probing:1; + unsigned int in_pm:1; unsigned int autosuspended:1; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ diff --git a/tools/Makefile b/tools/Makefile index bcae806..9a617ad 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -44,6 +44,9 @@ cpupower: FORCE cgroup firewire hv guest usb virtio vm net: FORCE $(call descend,$@) +liblockdep: FORCE + $(call descend,lib/lockdep) + libapikfs: FORCE $(call descend,lib/api) @@ -91,6 +94,9 @@ cpupower_clean: cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean: $(call descend,$(@:_clean=),clean) +liblockdep_clean: + $(call descend,lib/lockdep,clean) + libapikfs_clean: $(call descend,lib/api,clean) diff --git a/tools/lib/lockdep/Makefile b/tools/lib/lockdep/Makefile index cb09d3f..bba2f52 100644 --- a/tools/lib/lockdep/Makefile +++ b/tools/lib/lockdep/Makefile @@ -1,8 +1,7 @@ # file format version FILE_VERSION = 1 -MAKEFLAGS += --no-print-directory -LIBLOCKDEP_VERSION=$(shell make -sC ../../.. kernelversion) +LIBLOCKDEP_VERSION=$(shell make --no-print-directory -sC ../../.. kernelversion) # Makefiles suck: This macro sets a default value of $(2) for the # variable named by $(1), unless the variable has been set by @@ -231,7 +230,7 @@ install_lib: all_cmd install: install_lib clean: - $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d + $(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d $(RM) tags TAGS endif # skip-makefile diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c index cfe0cdc..c5baf9c 100644 --- a/tools/net/bpf_jit_disasm.c +++ b/tools/net/bpf_jit_disasm.c @@ -43,8 +43,7 @@ static void get_exec_path(char *tpath, size_t size) free(path); } -static void get_asm_insns(uint8_t *image, size_t len, unsigned long base, - int opcodes) +static void get_asm_insns(uint8_t *image, size_t len, int opcodes) { int count, i, pc = 0; char tpath[256]; @@ -107,13 +106,13 @@ static void put_klog_buff(char *buff) } static int get_last_jit_image(char *haystack, size_t hlen, - uint8_t *image, size_t ilen, - unsigned long *base) + uint8_t *image, size_t ilen) { char *ptr, *pptr, *tmp; off_t off = 0; int ret, flen, proglen, pass, ulen = 0; regmatch_t pmatch[1]; + unsigned long base; regex_t regex; if (hlen == 0) @@ -136,7 +135,7 @@ static int get_last_jit_image(char *haystack, size_t hlen, ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so); ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx", - &flen, &proglen, &pass, base); + &flen, &proglen, &pass, &base); if (ret != 4) return 0; @@ -162,7 +161,7 @@ static int get_last_jit_image(char *haystack, size_t hlen, assert(ulen == proglen); printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n", proglen, pass, flen); - printf("%lx + <x>:\n", *base); + printf("%lx + <x>:\n", base); regfree(®ex); return ulen; @@ -172,8 +171,7 @@ int main(int argc, char **argv) { int len, klen, opcodes = 0; char *kbuff; - unsigned long base; - uint8_t image[4096]; + static uint8_t image[32768]; if (argc > 1) { if (!strncmp("-o", argv[argc - 1], 2)) { @@ -189,9 +187,9 @@ int main(int argc, char **argv) kbuff = get_klog_buff(&klen); - len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base); - if (len > 0 && base > 0) - get_asm_insns(image, len, base, opcodes); + len = get_last_jit_image(kbuff, klen, image, sizeof(image)); + if (len > 0) + get_asm_insns(image, len, opcodes); put_klog_buff(kbuff); |