1
0
Fork 0

Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next

Pull networking updates from David Miller:
 "Another merge window, another pull full of stuff:

   1) Support alternative names for network devices, from Jiri Pirko.

   2) Introduce per-netns netdev notifiers, also from Jiri Pirko.

   3) Support MSG_PEEK in vsock/virtio, from Matias Ezequiel Vara
      Larsen.

   4) Allow compiling out the TLS TOE code, from Jakub Kicinski.

   5) Add several new tracepoints to the kTLS code, also from Jakub.

   6) Support set channels ethtool callback in ena driver, from Sameeh
      Jubran.

   7) New SCTP events SCTP_ADDR_ADDED, SCTP_ADDR_REMOVED,
      SCTP_ADDR_MADE_PRIM, and SCTP_SEND_FAILED_EVENT. From Xin Long.

   8) Add XDP support to mvneta driver, from Lorenzo Bianconi.

   9) Lots of netfilter hw offload fixes, cleanups and enhancements,
      from Pablo Neira Ayuso.

  10) PTP support for aquantia chips, from Egor Pomozov.

  11) Add UDP segmentation offload support to igb, ixgbe, and i40e. From
      Josh Hunt.

  12) Add smart nagle to tipc, from Jon Maloy.

  13) Support L2 field rewrite by TC offloads in bnxt_en, from Venkat
      Duvvuru.

  14) Add a flow mask cache to OVS, from Tonghao Zhang.

  15) Add XDP support to ice driver, from Maciej Fijalkowski.

  16) Add AF_XDP support to ice driver, from Krzysztof Kazimierczak.

  17) Support UDP GSO offload in atlantic driver, from Igor Russkikh.

  18) Support it in stmmac driver too, from Jose Abreu.

  19) Support TIPC encryption and auth, from Tuong Lien.

  20) Introduce BPF trampolines, from Alexei Starovoitov.

  21) Make page_pool API more numa friendly, from Saeed Mahameed.

  22) Introduce route hints to ipv4 and ipv6, from Paolo Abeni.

  23) Add UDP segmentation offload to cxgb4, Rahul Lakkireddy"

* git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1857 commits)
  libbpf: Fix usage of u32 in userspace code
  mm: Implement no-MMU variant of vmalloc_user_node_flags
  slip: Fix use-after-free Read in slip_open
  net: dsa: sja1105: fix sja1105_parse_rgmii_delays()
  macvlan: schedule bc_work even if error
  enetc: add support Credit Based Shaper(CBS) for hardware offload
  net: phy: add helpers phy_(un)lock_mdio_bus
  mdio_bus: don't use managed reset-controller
  ax88179_178a: add ethtool_op_get_ts_info()
  mlxsw: spectrum_router: Fix use of uninitialized adjacency index
  mlxsw: spectrum_router: After underlay moves, demote conflicting tunnels
  bpf: Simplify __bpf_arch_text_poke poke type handling
  bpf: Introduce BPF_TRACE_x helper for the tracing tests
  bpf: Add bpf_jit_blinding_enabled for !CONFIG_BPF_JIT
  bpf, testing: Add various tail call test cases
  bpf, x86: Emit patchable direct jump as tail call
  bpf: Constant map key tracking for prog array pokes
  bpf: Add poke dependency tracking for prog array maps
  bpf: Add initial poke descriptor table for jit images
  bpf: Move owner type, jited info into array auxiliary data
  ...
alistair/sunxi64-5.5-dsi
Linus Torvalds 2019-11-25 20:02:57 -08:00
commit 386403a115
1641 changed files with 105453 additions and 31128 deletions

View File

@ -51,6 +51,14 @@ Description:
packet processing. See the network driver for the exact
meaning of this value.
What: /sys/class/<iface>/statistics/rx_errors
Date: April 2005
KernelVersion: 2.6.12
Contact: netdev@vger.kernel.org
Description:
Indicates the number of receive errors on this network device.
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
@ -88,6 +96,14 @@ Description:
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_nohandler
Date: February 2016
KernelVersion: 4.6
Contact: netdev@vger.kernel.org
Description:
Indicates the number of received packets that were dropped on
an inactive device by the network core.
What: /sys/class/<iface>/statistics/rx_over_errors
Date: April 2005
KernelVersion: 2.6.12

View File

@ -47,6 +47,15 @@ Program types
prog_flow_dissector
Testing BPF
===========
.. toctree::
:maxdepth: 1
s390
.. Links:
.. _Documentation/networking/filter.txt: ../networking/filter.txt
.. _man-pages: https://www.kernel.org/doc/man-pages/

View File

@ -142,3 +142,6 @@ BPF flow dissector doesn't support exporting all the metadata that in-kernel
C-based implementation can export. Notable example is single VLAN (802.1Q)
and double VLAN (802.1AD) tags. Please refer to the ``struct bpf_flow_keys``
for a set of information that's currently can be exported from the BPF context.
When BPF flow dissector is attached to the root network namespace (machine-wide
policy), users can't override it in their child network namespaces.

View File

@ -0,0 +1,205 @@
===================
Testing BPF on s390
===================
1. Introduction
***************
IBM Z are mainframe computers, which are descendants of IBM System/360 from
year 1964. They are supported by the Linux kernel under the name "s390". This
document describes how to test BPF in an s390 QEMU guest.
2. One-time setup
*****************
The following is required to build and run the test suite:
* s390 GCC
* s390 development headers and libraries
* Clang with BPF support
* QEMU with s390 support
* Disk image with s390 rootfs
Debian supports installing compiler and libraries for s390 out of the box.
Users of other distros may use debootstrap in order to set up a Debian chroot::
sudo debootstrap \
--variant=minbase \
--include=sudo \
testing \
./s390-toolchain
sudo mount --rbind /dev ./s390-toolchain/dev
sudo mount --rbind /proc ./s390-toolchain/proc
sudo mount --rbind /sys ./s390-toolchain/sys
sudo chroot ./s390-toolchain
Once on Debian, the build prerequisites can be installed as follows::
sudo dpkg --add-architecture s390x
sudo apt-get update
sudo apt-get install \
bc \
bison \
cmake \
debootstrap \
dwarves \
flex \
g++ \
gcc \
g++-s390x-linux-gnu \
gcc-s390x-linux-gnu \
gdb-multiarch \
git \
make \
python3 \
qemu-system-misc \
qemu-utils \
rsync \
libcap-dev:s390x \
libelf-dev:s390x \
libncurses-dev
Latest Clang targeting BPF can be installed as follows::
git clone https://github.com/llvm/llvm-project.git
ln -s ../../clang llvm-project/llvm/tools/
mkdir llvm-project-build
cd llvm-project-build
cmake \
-DLLVM_TARGETS_TO_BUILD=BPF \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/clang-bpf \
../llvm-project/llvm
make
sudo make install
export PATH=/opt/clang-bpf/bin:$PATH
The disk image can be prepared using a loopback mount and debootstrap::
qemu-img create -f raw ./s390.img 1G
sudo losetup -f ./s390.img
sudo mkfs.ext4 /dev/loopX
mkdir ./s390.rootfs
sudo mount /dev/loopX ./s390.rootfs
sudo debootstrap \
--foreign \
--arch=s390x \
--variant=minbase \
--include=" \
iproute2, \
iputils-ping, \
isc-dhcp-client, \
kmod, \
libcap2, \
libelf1, \
netcat, \
procps" \
testing \
./s390.rootfs
sudo umount ./s390.rootfs
sudo losetup -d /dev/loopX
3. Compilation
**************
In addition to the usual Kconfig options required to run the BPF test suite, it
is also helpful to select::
CONFIG_NET_9P=y
CONFIG_9P_FS=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_VIRTIO_PCI=y
as that would enable a very easy way to share files with the s390 virtual
machine.
Compiling kernel, modules and testsuite, as well as preparing gdb scripts to
simplify debugging, can be done using the following commands::
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- menuconfig
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- bzImage modules scripts_gdb
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- \
-C tools/testing/selftests \
TARGETS=bpf \
INSTALL_PATH=$PWD/tools/testing/selftests/kselftest_install \
install
4. Running the test suite
*************************
The virtual machine can be started as follows::
qemu-system-s390x \
-cpu max,zpci=on \
-smp 2 \
-m 4G \
-kernel linux/arch/s390/boot/compressed/vmlinux \
-drive file=./s390.img,if=virtio,format=raw \
-nographic \
-append 'root=/dev/vda rw console=ttyS1' \
-virtfs local,path=./linux,security_model=none,mount_tag=linux \
-object rng-random,filename=/dev/urandom,id=rng0 \
-device virtio-rng-ccw,rng=rng0 \
-netdev user,id=net0 \
-device virtio-net-ccw,netdev=net0
When using this on a real IBM Z, ``-enable-kvm`` may be added for better
performance. When starting the virtual machine for the first time, disk image
setup must be finalized using the following command::
/debootstrap/debootstrap --second-stage
Directory with the code built on the host as well as ``/proc`` and ``/sys``
need to be mounted as follows::
mkdir -p /linux
mount -t 9p linux /linux
mount -t proc proc /proc
mount -t sysfs sys /sys
After that, the test suite can be run using the following commands::
cd /linux/tools/testing/selftests/kselftest_install
./run_kselftest.sh
As usual, tests can be also run individually::
cd /linux/tools/testing/selftests/bpf
./test_verifier
5. Debugging
************
It is possible to debug the s390 kernel using QEMU GDB stub, which is activated
by passing ``-s`` to QEMU.
It is preferable to turn KASLR off, so that gdb would know where to find the
kernel image in memory, by building the kernel with::
RANDOMIZE_BASE=n
GDB can then be attached using the following command::
gdb-multiarch -ex 'target remote localhost:1234' ./vmlinux
6. Network
**********
In case one needs to use the network in the virtual machine in order to e.g.
install additional packages, it can be configured using::
dhclient eth0
7. Links
********
This document is a compilation of techniques, whose more comprehensive
descriptions can be found by following these links:
- `Debootstrap <https://wiki.debian.org/EmDebian/CrossDebootstrap>`_
- `Multiarch <https://wiki.debian.org/Multiarch/HOWTO>`_
- `Building LLVM <https://llvm.org/docs/CMake.html>`_
- `Cross-compiling the kernel <https://wiki.gentoo.org/wiki/Embedded_Handbook/General/Cross-compiling_the_kernel>`_
- `QEMU s390x Guest Support <https://wiki.qemu.org/Documentation/Platforms/S390X>`_
- `Plan 9 folder sharing over Virtio <https://wiki.qemu.org/Documentation/9psetup>`_
- `Using GDB with QEMU <https://wiki.osdev.org/Kernel_Debugging#Use_GDB_with_QEMU>`_

View File

@ -44,6 +44,12 @@ Optional properties:
Admission Control Block supports reporting the number of packets in-flight in a
switch queue
- resets: a single phandle and reset identifier pair. See
Documentation/devicetree/binding/reset/reset.txt for details.
- reset-names: If the "reset" property is specified, this property should have
the value "switch" to denote the switch reset line.
Port subnodes:
Optional properties:

View File

@ -2,7 +2,7 @@
Required properties:
- compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2",
"brcm,genet-v3", "brcm,genet-v4", "brcm,genet-v5".
"brcm,genet-v3", "brcm,genet-v4", "brcm,genet-v5", "brcm,bcm2711-genet-v5".
- reg: address and length of the register set for the device
- interrupts and/or interrupts-extended: must be two cells, the first cell
is the general purpose interrupt line, while the second cell is the

View File

@ -14,6 +14,8 @@ Required properties:
* "brcm,bcm4330-bt"
* "brcm,bcm43438-bt"
* "brcm,bcm4345c5"
* "brcm,bcm43540-bt"
* "brcm,bcm4335a0"
Optional properties:

View File

@ -121,6 +121,11 @@ properties:
and is useful for determining certain configuration settings
such as flow control thresholds.
sfp:
$ref: /schemas/types.yaml#definitions/phandle
description:
Specifies a reference to a node representing a SFP cage.
tx-fifo-depth:
$ref: /schemas/types.yaml#definitions/uint32
description:

View File

@ -153,6 +153,11 @@ properties:
Delay after the reset was deasserted in microseconds. If
this property is missing the delay will be skipped.
sfp:
$ref: /schemas/types.yaml#definitions/phandle
description:
Specifies a reference to a node representing a SFP cage.
required:
- reg

View File

@ -9,6 +9,7 @@ Required properties:
- "aspeed,ast2400-mac"
- "aspeed,ast2500-mac"
- "aspeed,ast2600-mac"
- reg: Address and length of the register set for the device
- interrupts: Should contain ethernet controller interrupt
@ -23,6 +24,13 @@ Optional properties:
- no-hw-checksum: Used to disable HW checksum support. Here for backward
compatibility as the driver now should have correct defaults based on
the SoC.
- clocks: In accordance with the generic clock bindings. Must describe the MAC
IP clock, and optionally an RMII RCLK gate for the AST2500/AST2600. The
required MAC clock must be the first cell.
- clock-names:
- "MACCLK": The MAC IP clock
- "RCLK": Clock gate for the RMII RCLK
Example:

View File

@ -10,6 +10,11 @@ Optional properties:
absent, "rmii" is assumed.
- use-iram: Use LPC32xx internal SRAM (IRAM) for DMA buffering
Optional subnodes:
- mdio : specifies the mdio bus, used as a container for phy nodes according to
phy.txt in the same directory
Example:
mac: ethernet@31060000 {

View File

@ -0,0 +1,46 @@
* NXP Semiconductors PN532 NFC Controller
Required properties:
- compatible: Should be
- "nxp,pn532" Place a node with this inside the devicetree node of the bus
where the NFC chip is connected to.
Currently the kernel has phy bindings for uart and i2c.
- "nxp,pn532-i2c" (DEPRECATED) only works for the i2c binding.
- "nxp,pn533-i2c" (DEPRECATED) only works for the i2c binding.
Required properties if connected on i2c:
- clock-frequency: I²C work frequency.
- reg: for the I²C bus address. This is fixed at 0x24 for the PN532.
- interrupts: GPIO interrupt to which the chip is connected
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 PN532 on I2C2):
&i2c2 {
pn532: nfc@24 {
compatible = "nxp,pn532";
reg = <0x24>;
clock-frequency = <400000>;
interrupt-parent = <&gpio1>;
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
};
};
Example (for PN532 connected via uart):
uart4: serial@49042000 {
compatible = "ti,omap3-uart";
pn532: nfc {
compatible = "nxp,pn532";
};
};

View File

@ -1,29 +0,0 @@
* NXP Semiconductors PN532 NFC Controller
Required properties:
- compatible: Should be "nxp,pn532-i2c" or "nxp,pn533-i2c".
- clock-frequency: I²C work frequency.
- reg: address on the bus
- interrupts: GPIO interrupt to which the chip is connected
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 PN532 on I2C2):
&i2c2 {
pn532: pn532@24 {
compatible = "nxp,pn532-i2c";
reg = <0x24>;
clock-frequency = <400000>;
interrupt-parent = <&gpio1>;
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
};
};

View File

@ -0,0 +1,111 @@
# SPDX-License-Identifier: GPL-2.0+
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/qca,ar803x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Atheros AR803x PHY
maintainers:
- Andrew Lunn <andrew@lunn.ch>
- Florian Fainelli <f.fainelli@gmail.com>
- Heiner Kallweit <hkallweit1@gmail.com>
description: |
Bindings for Qualcomm Atheros AR803x PHYs
allOf:
- $ref: ethernet-phy.yaml#
properties:
qca,clk-out-frequency:
description: Clock output frequency in Hertz.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 25000000, 50000000, 62500000, 125000000 ]
qca,clk-out-strength:
description: Clock output driver strength.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 0, 1, 2 ]
qca,keep-pll-enabled:
description: |
If set, keep the PLL enabled even if there is no link. Useful if you
want to use the clock output without an ethernet link.
Only supported on the AR8031.
type: boolean
vddio-supply:
description: |
RGMII I/O voltage regulator (see regulator/regulator.yaml).
The PHY supports RGMII I/O voltages of 1.5V, 1.8V and 2.5V. You can
either connect this to the vddio-regulator (1.5V / 1.8V) or the
vddh-regulator (2.5V).
Only supported on the AR8031.
vddio-regulator:
type: object
description:
Initial data for the VDDIO regulator. Set this to 1.5V or 1.8V.
allOf:
- $ref: /schemas/regulator/regulator.yaml
vddh-regulator:
type: object
description:
Dummy subnode to model the external connection of the PHY VDDH
regulator to VDDIO.
allOf:
- $ref: /schemas/regulator/regulator.yaml
examples:
- |
#include <dt-bindings/net/qca-ar803x.h>
ethernet {
#address-cells = <1>;
#size-cells = <0>;
phy-mode = "rgmii-id";
ethernet-phy@0 {
reg = <0>;
qca,clk-out-frequency = <125000000>;
qca,clk-out-strength = <AR803X_STRENGTH_FULL>;
vddio-supply = <&vddio>;
vddio: vddio-regulator {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
};
};
- |
#include <dt-bindings/net/qca-ar803x.h>
ethernet {
#address-cells = <1>;
#size-cells = <0>;
phy-mode = "rgmii-id";
ethernet-phy@0 {
reg = <0>;
qca,clk-out-frequency = <50000000>;
qca,keep-pll-enabled;
vddio-supply = <&vddh>;
vddh: vddh-regulator {
};
};
};

View File

@ -0,0 +1,114 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/renesas,ether.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas Electronics SH EtherMAC
allOf:
- $ref: ethernet-controller.yaml#
maintainers:
- Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
properties:
compatible:
oneOf:
- items:
- enum:
- renesas,gether-r8a7740 # device is a part of R8A7740 SoC
- renesas,gether-r8a77980 # device is a part of R8A77980 SoC
- renesas,ether-r7s72100 # device is a part of R7S72100 SoC
- renesas,ether-r7s9210 # device is a part of R7S9210 SoC
- items:
- enum:
- renesas,ether-r8a7778 # device is a part of R8A7778 SoC
- renesas,ether-r8a7779 # device is a part of R8A7779 SoC
- enum:
- renesas,rcar-gen1-ether # a generic R-Car Gen1 device
- items:
- enum:
- renesas,ether-r8a7745 # device is a part of R8A7745 SoC
- renesas,ether-r8a7743 # device is a part of R8A7743 SoC
- renesas,ether-r8a7790 # device is a part of R8A7790 SoC
- renesas,ether-r8a7791 # device is a part of R8A7791 SoC
- renesas,ether-r8a7793 # device is a part of R8A7793 SoC
- renesas,ether-r8a7794 # device is a part of R8A7794 SoC
- enum:
- renesas,rcar-gen2-ether # a generic R-Car Gen2 or RZ/G1 device
reg:
items:
- description: E-DMAC/feLic registers
- description: TSU registers
minItems: 1
interrupts:
maxItems: 1
'#address-cells':
description: number of address cells for the MDIO bus
const: 1
'#size-cells':
description: number of size cells on the MDIO bus
const: 0
clocks:
maxItems: 1
pinctrl-0: true
pinctrl-names: true
renesas,no-ether-link:
type: boolean
description:
specify when a board does not provide a proper Ether LINK signal
renesas,ether-link-active-low:
type: boolean
description:
specify when the Ether LINK signal is active-low instead of normal
active-high
required:
- compatible
- reg
- interrupts
- phy-mode
- phy-handle
- '#address-cells'
- '#size-cells'
- clocks
- pinctrl-0
examples:
# Lager board
- |
#include <dt-bindings/clock/r8a7790-clock.h>
#include <dt-bindings/interrupt-controller/irq.h>
ethernet@ee700000 {
compatible = "renesas,ether-r8a7790", "renesas,rcar-gen2-ether";
reg = <0 0xee700000 0 0x400>;
interrupt-parent = <&gic>;
interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp8_clks R8A7790_CLK_ETHER>;
phy-mode = "rmii";
phy-handle = <&phy1>;
pinctrl-0 = <&ether_pins>;
pinctrl-names = "default";
renesas,ether-link-active-low;
#address-cells = <1>;
#size-cells = <0>;
phy1: ethernet-phy@1 {
reg = <1>;
interrupt-parent = <&irqc0>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
pinctrl-0 = <&phy1_pins>;
pinctrl-names = "default";
};
};

View File

@ -1,69 +0,0 @@
* Renesas Electronics SH EtherMAC
This file provides information on what the device node for the SH EtherMAC
interface contains.
Required properties:
- compatible: Must contain one or more of the following:
"renesas,gether-r8a7740" if the device is a part of R8A7740 SoC.
"renesas,ether-r8a7743" if the device is a part of R8A7743 SoC.
"renesas,ether-r8a7745" if the device is a part of R8A7745 SoC.
"renesas,ether-r8a7778" if the device is a part of R8A7778 SoC.
"renesas,ether-r8a7779" if the device is a part of R8A7779 SoC.
"renesas,ether-r8a7790" if the device is a part of R8A7790 SoC.
"renesas,ether-r8a7791" if the device is a part of R8A7791 SoC.
"renesas,ether-r8a7793" if the device is a part of R8A7793 SoC.
"renesas,ether-r8a7794" if the device is a part of R8A7794 SoC.
"renesas,gether-r8a77980" if the device is a part of R8A77980 SoC.
"renesas,ether-r7s72100" if the device is a part of R7S72100 SoC.
"renesas,ether-r7s9210" if the device is a part of R7S9210 SoC.
"renesas,rcar-gen1-ether" for a generic R-Car Gen1 device.
"renesas,rcar-gen2-ether" for a generic R-Car Gen2 or RZ/G1
device.
When compatible with the generic version, nodes must list
the SoC-specific version corresponding to the platform
first followed by the generic version.
- reg: offset and length of (1) the E-DMAC/feLic register block (required),
(2) the TSU register block (optional).
- interrupts: interrupt specifier for the sole interrupt.
- phy-mode: see ethernet.txt file in the same directory.
- phy-handle: see ethernet.txt file in the same directory.
- #address-cells: number of address cells for the MDIO bus, must be equal to 1.
- #size-cells: number of size cells on the MDIO bus, must be equal to 0.
- clocks: clock phandle and specifier pair.
- pinctrl-0: phandle, referring to a default pin configuration node.
Optional properties:
- pinctrl-names: pin configuration state name ("default").
- renesas,no-ether-link: boolean, specify when a board does not provide a proper
Ether LINK signal.
- renesas,ether-link-active-low: boolean, specify when the Ether LINK signal is
active-low instead of normal active-high.
Example (Lager board):
ethernet@ee700000 {
compatible = "renesas,ether-r8a7790",
"renesas,rcar-gen2-ether";
reg = <0 0xee700000 0 0x400>;
interrupt-parent = <&gic>;
interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp8_clks R8A7790_CLK_ETHER>;
phy-mode = "rmii";
phy-handle = <&phy1>;
pinctrl-0 = <&ether_pins>;
pinctrl-names = "default";
renesas,ether-link-active-low;
#address-cells = <1>;
#size-cells = <0>;
phy1: ethernet-phy@1 {
reg = <1>;
interrupt-parent = <&irqc0>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
pinctrl-0 = <&phy1_pins>;
pinctrl-names = "default";
};
};

View File

@ -0,0 +1,240 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/ti,cpsw-switch.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI SoC Ethernet Switch Controller (CPSW) Device Tree Bindings
maintainers:
- Grygorii Strashko <grygorii.strashko@ti.com>
- Sekhar Nori <nsekhar@ti.com>
description:
The 3-port switch gigabit ethernet subsystem provides ethernet packet
communication and can be configured as an ethernet switch. It provides the
gigabit media independent interface (GMII),reduced gigabit media
independent interface (RGMII), reduced media independent interface (RMII),
the management data input output (MDIO) for physical layer device (PHY)
management.
properties:
compatible:
oneOf:
- const: ti,cpsw-switch
- items:
- const: ti,am335x-cpsw-switch
- const: ti,cpsw-switch
- items:
- const: ti,am4372-cpsw-switch
- const: ti,cpsw-switch
- items:
- const: ti,dra7-cpsw-switch
- const: ti,cpsw-switch
reg:
maxItems: 1
description:
The physical base address and size of full the CPSW module IO range
ranges: true
clocks:
maxItems: 1
description: CPSW functional clock
clock-names:
maxItems: 1
items:
- const: fck
interrupts:
items:
- description: RX_THRESH interrupt
- description: RX interrupt
- description: TX interrupt
- description: MISC interrupt
interrupt-names:
items:
- const: "rx_thresh"
- const: "rx"
- const: "tx"
- const: "misc"
pinctrl-names: true
syscon:
$ref: /schemas/types.yaml#definitions/phandle
description:
Phandle to the system control device node which provides access to
efuse IO range with MAC addresses
ethernet-ports:
type: object
properties:
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^port@[0-9]+$":
type: object
minItems: 1
maxItems: 2
description: CPSW external ports
allOf:
- $ref: ethernet-controller.yaml#
properties:
reg:
maxItems: 1
enum: [1, 2]
description: CPSW port number
phys:
$ref: /schemas/types.yaml#definitions/phandle-array
maxItems: 1
description: phandle on phy-gmii-sel PHY
label:
$ref: /schemas/types.yaml#/definitions/string-array
maxItems: 1
description: label associated with this port
ti,dual-emac-pvid:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
minimum: 1
maximum: 1024
description:
Specifies default PORT VID to be used to segregate
ports. Default value - CPSW port number.
required:
- reg
- phys
mdio:
type: object
allOf:
- $ref: "ti,davinci-mdio.yaml#"
description:
CPSW MDIO bus.
cpts:
type: object
description:
The Common Platform Time Sync (CPTS) module
properties:
clocks:
maxItems: 1
description: CPTS reference clock
clock-names:
maxItems: 1
items:
- const: cpts
cpts_clock_mult:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Numerator to convert input clock ticks into ns
cpts_clock_shift:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Denominator to convert input clock ticks into ns.
Mult and shift will be calculated basing on CPTS rftclk frequency if
both cpts_clock_shift and cpts_clock_mult properties are not provided.
required:
- clocks
- clock-names
required:
- compatible
- reg
- ranges
- clocks
- clock-names
- interrupts
- interrupt-names
- '#address-cells'
- '#size-cells'
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/dra7.h>
mac_sw: switch@0 {
compatible = "ti,dra7-cpsw-switch","ti,cpsw-switch";
reg = <0x0 0x4000>;
ranges = <0 0 0x4000>;
clocks = <&gmac_main_clk>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <1>;
syscon = <&scm_conf>;
inctrl-names = "default", "sleep";
interrupts = <GIC_SPI 334 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 335 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "rx_thresh", "rx", "tx", "misc";
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
cpsw_port1: port@1 {
reg = <1>;
label = "port1";
mac-address = [ 00 00 00 00 00 00 ];
phys = <&phy_gmii_sel 1>;
phy-handle = <&ethphy0_sw>;
phy-mode = "rgmii";
ti,dual_emac_pvid = <1>;
};
cpsw_port2: port@2 {
reg = <2>;
label = "wan";
mac-address = [ 00 00 00 00 00 00 ];
phys = <&phy_gmii_sel 2>;
phy-handle = <&ethphy1_sw>;
phy-mode = "rgmii";
ti,dual_emac_pvid = <2>;
};
};
davinci_mdio_sw: mdio@1000 {
compatible = "ti,cpsw-mdio","ti,davinci_mdio";
reg = <0x1000 0x100>;
clocks = <&gmac_clkctrl DRA7_GMAC_GMAC_CLKCTRL 0>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <0>;
bus_freq = <1000000>;
ethphy0_sw: ethernet-phy@0 {
reg = <0>;
};
ethphy1_sw: ethernet-phy@1 {
reg = <1>;
};
};
cpts {
clocks = <&gmac_clkctrl DRA7_GMAC_GMAC_CLKCTRL 25>;
clock-names = "cpts";
};
};

View File

@ -0,0 +1,84 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019 Texas Instruments Incorporated
%YAML 1.2
---
$id: "http://devicetree.org/schemas/net/ti,dp83869.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: TI DP83869 ethernet PHY
allOf:
- $ref: "ethernet-controller.yaml#"
maintainers:
- Dan Murphy <dmurphy@ti.com>
description: |
The DP83869HM device is a robust, fully-featured Gigabit (PHY) transceiver
with integrated PMD sublayers that supports 10BASE-Te, 100BASE-TX and
1000BASE-T Ethernet protocols. The DP83869 also supports 1000BASE-X and
100BASE-FX Fiber protocols.
This device interfaces to the MAC layer through Reduced GMII (RGMII) and
SGMII The DP83869HM supports Media Conversion in Managed mode. In this mode,
the DP83869HM can run 1000BASE-X-to-1000BASE-T and 100BASE-FX-to-100BASE-TX
conversions. The DP83869HM can also support Bridge Conversion from RGMII to
SGMII and SGMII to RGMII.
Specifications about the charger can be found at:
http://www.ti.com/lit/ds/symlink/dp83869hm.pdf
properties:
reg:
maxItems: 1
ti,min-output-impedance:
type: boolean
description: |
MAC Interface Impedance control to set the programmable output impedance
to a minimum value (35 ohms).
ti,max-output-impedance:
type: boolean
description: |
MAC Interface Impedance control to set the programmable output impedance
to a maximum value (70 ohms).
tx-fifo-depth:
$ref: /schemas/types.yaml#definitions/uint32
description: |
Transmitt FIFO depth see dt-bindings/net/ti-dp83869.h for values
rx-fifo-depth:
$ref: /schemas/types.yaml#definitions/uint32
description: |
Receive FIFO depth see dt-bindings/net/ti-dp83869.h for values
ti,clk-output-sel:
$ref: /schemas/types.yaml#definitions/uint32
description: |
Muxing option for CLK_OUT pin see dt-bindings/net/ti-dp83869.h for values.
ti,op-mode:
$ref: /schemas/types.yaml#definitions/uint32
description: |
Operational mode for the PHY. If this is not set then the operational
mode is set by the straps. see dt-bindings/net/ti-dp83869.h for values
required:
- reg
examples:
- |
#include <dt-bindings/net/ti-dp83869.h>
mdio0 {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {
reg = <0>;
tx-fifo-depth = <DP83869_PHYCR_FIFO_DEPTH_4_B_NIB>;
rx-fifo-depth = <DP83869_PHYCR_FIFO_DEPTH_4_B_NIB>;
ti,op-mode = <DP83869_RGMII_COPPER_ETHERNET>;
ti,max-output-impedance = "true";
ti,clk-output-sel = <DP83869_CLK_O_SEL_CHN_A_RCLK>;
};
};

View File

@ -81,6 +81,12 @@ Optional properties:
Definition: Name of external front end module used. Some valid FEM names
for example: "microsemi-lx5586", "sky85703-11"
and "sky85803" etc.
- qcom,snoc-host-cap-8bit-quirk:
Usage: Optional
Value type: <empty>
Definition: Quirk specifying that the firmware expects the 8bit version
of the host capability QMI request
- qcom,xo-cal-data: xo cal offset to be configured in xo trim register.
Example (to supply PCI based wifi block details):

View File

@ -0,0 +1,69 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/ptp/ptp-idtcm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IDT ClockMatrix (TM) PTP Clock Device Tree Bindings
maintainers:
- Vincent Cheng <vincent.cheng.xh@renesas.com>
properties:
compatible:
enum:
# For System Synchronizer
- idt,8a34000
- idt,8a34001
- idt,8a34002
- idt,8a34003
- idt,8a34004
- idt,8a34005
- idt,8a34006
- idt,8a34007
- idt,8a34008
- idt,8a34009
# For Port Synchronizer
- idt,8a34010
- idt,8a34011
- idt,8a34012
- idt,8a34013
- idt,8a34014
- idt,8a34015
- idt,8a34016
- idt,8a34017
- idt,8a34018
- idt,8a34019
# For Universal Frequency Translator (UFT)
- idt,8a34040
- idt,8a34041
- idt,8a34042
- idt,8a34043
- idt,8a34044
- idt,8a34045
- idt,8a34046
- idt,8a34047
- idt,8a34048
- idt,8a34049
reg:
maxItems: 1
description:
I2C slave address of the device.
required:
- compatible
- reg
examples:
- |
i2c@1 {
compatible = "abc,acme-1234";
reg = <0x01 0x400>;
#address-cells = <1>;
#size-cells = <0>;
phc@5b {
compatible = "idt,8a34000";
reg = <0x5b>;
};
};

View File

@ -40,13 +40,13 @@ allocates memory for this UMEM using whatever means it feels is most
appropriate (malloc, mmap, huge pages, etc). This memory area is then
registered with the kernel using the new setsockopt XDP_UMEM_REG. The
UMEM also has two rings: the FILL ring and the COMPLETION ring. The
fill ring is used by the application to send down addr for the kernel
FILL ring is used by the application to send down addr for the kernel
to fill in with RX packet data. References to these frames will then
appear in the RX ring once each packet has been received. The
completion ring, on the other hand, contains frame addr that the
COMPLETION ring, on the other hand, contains frame addr that the
kernel has transmitted completely and can now be used again by user
space, for either TX or RX. Thus, the frame addrs appearing in the
completion ring are addrs that were previously transmitted using the
COMPLETION ring are addrs that were previously transmitted using the
TX ring. In summary, the RX and FILL rings are used for the RX path
and the TX and COMPLETION rings are used for the TX path.
@ -91,11 +91,16 @@ Concepts
========
In order to use an AF_XDP socket, a number of associated objects need
to be setup.
to be setup. These objects and their options are explained in the
following sections.
Jonathan Corbet has also written an excellent article on LWN,
"Accelerating networking with AF_XDP". It can be found at
https://lwn.net/Articles/750845/.
For an overview on how AF_XDP works, you can also take a look at the
Linux Plumbers paper from 2018 on the subject:
http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf. Do
NOT consult the paper from 2017 on "AF_PACKET v4", the first attempt
at AF_XDP. Nearly everything changed since then. Jonathan Corbet has
also written an excellent article on LWN, "Accelerating networking
with AF_XDP". It can be found at https://lwn.net/Articles/750845/.
UMEM
----
@ -113,22 +118,22 @@ the next socket B can do this by setting the XDP_SHARED_UMEM flag in
struct sockaddr_xdp member sxdp_flags, and passing the file descriptor
of A to struct sockaddr_xdp member sxdp_shared_umem_fd.
The UMEM has two single-producer/single-consumer rings, that are used
The UMEM has two single-producer/single-consumer rings that are used
to transfer ownership of UMEM frames between the kernel and the
user-space application.
Rings
-----
There are a four different kind of rings: Fill, Completion, RX and
There are a four different kind of rings: FILL, COMPLETION, RX and
TX. All rings are single-producer/single-consumer, so the user-space
application need explicit synchronization of multiple
processes/threads are reading/writing to them.
The UMEM uses two rings: Fill and Completion. Each socket associated
The UMEM uses two rings: FILL and COMPLETION. Each socket associated
with the UMEM must have an RX queue, TX queue or both. Say, that there
is a setup with four sockets (all doing TX and RX). Then there will be
one Fill ring, one Completion ring, four TX rings and four RX rings.
one FILL ring, one COMPLETION ring, four TX rings and four RX rings.
The rings are head(producer)/tail(consumer) based rings. A producer
writes the data ring at the index pointed out by struct xdp_ring
@ -146,7 +151,7 @@ The size of the rings need to be of size power of two.
UMEM Fill Ring
~~~~~~~~~~~~~~
The Fill ring is used to transfer ownership of UMEM frames from
The FILL ring is used to transfer ownership of UMEM frames from
user-space to kernel-space. The UMEM addrs are passed in the ring. As
an example, if the UMEM is 64k and each chunk is 4k, then the UMEM has
16 chunks and can pass addrs between 0 and 64k.
@ -164,8 +169,8 @@ chunks mode, then the incoming addr will be left untouched.
UMEM Completion Ring
~~~~~~~~~~~~~~~~~~~~
The Completion Ring is used transfer ownership of UMEM frames from
kernel-space to user-space. Just like the Fill ring, UMEM indicies are
The COMPLETION Ring is used transfer ownership of UMEM frames from
kernel-space to user-space. Just like the FILL ring, UMEM indices are
used.
Frames passed from the kernel to user-space are frames that has been
@ -181,7 +186,7 @@ The RX ring is the receiving side of a socket. Each entry in the ring
is a struct xdp_desc descriptor. The descriptor contains UMEM offset
(addr) and the length of the data (len).
If no frames have been passed to kernel via the Fill ring, no
If no frames have been passed to kernel via the FILL ring, no
descriptors will (or can) appear on the RX ring.
The user application consumes struct xdp_desc descriptors from this
@ -199,8 +204,24 @@ be relaxed in the future.
The user application produces struct xdp_desc descriptors to this
ring.
Libbpf
======
Libbpf is a helper library for eBPF and XDP that makes using these
technologies a lot simpler. It also contains specific helper functions
in tools/lib/bpf/xsk.h for facilitating the use of AF_XDP. It
contains two types of functions: those that can be used to make the
setup of AF_XDP socket easier and ones that can be used in the data
plane to access the rings safely and quickly. To see an example on how
to use this API, please take a look at the sample application in
samples/bpf/xdpsock_usr.c which uses libbpf for both setup and data
plane operations.
We recommend that you use this library unless you have become a power
user. It will make your program a lot simpler.
XSKMAP / BPF_MAP_TYPE_XSKMAP
----------------------------
============================
On XDP side there is a BPF map type BPF_MAP_TYPE_XSKMAP (XSKMAP) that
is used in conjunction with bpf_redirect_map() to pass the ingress
@ -216,21 +237,202 @@ queue 17. Only the XDP program executing for eth0 and queue 17 will
successfully pass data to the socket. Please refer to the sample
application (samples/bpf/) in for an example.
Configuration Flags and Socket Options
======================================
These are the various configuration flags that can be used to control
and monitor the behavior of AF_XDP sockets.
XDP_COPY and XDP_ZERO_COPY bind flags
-------------------------------------
When you bind to a socket, the kernel will first try to use zero-copy
copy. If zero-copy is not supported, it will fall back on using copy
mode, i.e. copying all packets out to user space. But if you would
like to force a certain mode, you can use the following flags. If you
pass the XDP_COPY flag to the bind call, the kernel will force the
socket into copy mode. If it cannot use copy mode, the bind call will
fail with an error. Conversely, the XDP_ZERO_COPY flag will force the
socket into zero-copy mode or fail.
XDP_SHARED_UMEM bind flag
-------------------------
This flag enables you to bind multiple sockets to the same UMEM, but
only if they share the same queue id. In this mode, each socket has
their own RX and TX rings, but the UMEM (tied to the fist socket
created) only has a single FILL ring and a single COMPLETION
ring. To use this mode, create the first socket and bind it in the normal
way. Create a second socket and create an RX and a TX ring, or at
least one of them, but no FILL or COMPLETION rings as the ones from
the first socket will be used. In the bind call, set he
XDP_SHARED_UMEM option and provide the initial socket's fd in the
sxdp_shared_umem_fd field. You can attach an arbitrary number of extra
sockets this way.
What socket will then a packet arrive on? This is decided by the XDP
program. Put all the sockets in the XSK_MAP and just indicate which
index in the array you would like to send each packet to. A simple
round-robin example of distributing packets is shown below:
.. code-block:: c
#include <linux/bpf.h>
#include "bpf_helpers.h"
#define MAX_SOCKS 16
struct {
__uint(type, BPF_MAP_TYPE_XSKMAP);
__uint(max_entries, MAX_SOCKS);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} xsks_map SEC(".maps");
static unsigned int rr;
SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
{
rr = (rr + 1) & (MAX_SOCKS - 1);
return bpf_redirect_map(&xsks_map, rr, XDP_DROP);
}
Note, that since there is only a single set of FILL and COMPLETION
rings, and they are single producer, single consumer rings, you need
to make sure that multiple processes or threads do not use these rings
concurrently. There are no synchronization primitives in the
libbpf code that protects multiple users at this point in time.
Libbpf uses this mode if you create more than one socket tied to the
same umem. However, note that you need to supply the
XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD libbpf_flag with the
xsk_socket__create calls and load your own XDP program as there is no
built in one in libbpf that will route the traffic for you.
XDP_USE_NEED_WAKEUP bind flag
-----------------------------
This option adds support for a new flag called need_wakeup that is
present in the FILL ring and the TX ring, the rings for which user
space is a producer. When this option is set in the bind call, the
need_wakeup flag will be set if the kernel needs to be explicitly
woken up by a syscall to continue processing packets. If the flag is
zero, no syscall is needed.
If the flag is set on the FILL ring, the application needs to call
poll() to be able to continue to receive packets on the RX ring. This
can happen, for example, when the kernel has detected that there are no
more buffers on the FILL ring and no buffers left on the RX HW ring of
the NIC. In this case, interrupts are turned off as the NIC cannot
receive any packets (as there are no buffers to put them in), and the
need_wakeup flag is set so that user space can put buffers on the
FILL ring and then call poll() so that the kernel driver can put these
buffers on the HW ring and start to receive packets.
If the flag is set for the TX ring, it means that the application
needs to explicitly notify the kernel to send any packets put on the
TX ring. This can be accomplished either by a poll() call, as in the
RX path, or by calling sendto().
An example of how to use this flag can be found in
samples/bpf/xdpsock_user.c. An example with the use of libbpf helpers
would look like this for the TX path:
.. code-block:: c
if (xsk_ring_prod__needs_wakeup(&my_tx_ring))
sendto(xsk_socket__fd(xsk_handle), NULL, 0, MSG_DONTWAIT, NULL, 0);
I.e., only use the syscall if the flag is set.
We recommend that you always enable this mode as it usually leads to
better performance especially if you run the application and the
driver on the same core, but also if you use different cores for the
application and the kernel driver, as it reduces the number of
syscalls needed for the TX path.
XDP_{RX|TX|UMEM_FILL|UMEM_COMPLETION}_RING setsockopts
------------------------------------------------------
These setsockopts sets the number of descriptors that the RX, TX,
FILL, and COMPLETION rings respectively should have. It is mandatory
to set the size of at least one of the RX and TX rings. If you set
both, you will be able to both receive and send traffic from your
application, but if you only want to do one of them, you can save
resources by only setting up one of them. Both the FILL ring and the
COMPLETION ring are mandatory as you need to have a UMEM tied to your
socket. But if the XDP_SHARED_UMEM flag is used, any socket after the
first one does not have a UMEM and should in that case not have any
FILL or COMPLETION rings created as the ones from the shared umem will
be used. Note, that the rings are single-producer single-consumer, so
do not try to access them from multiple processes at the same
time. See the XDP_SHARED_UMEM section.
In libbpf, you can create Rx-only and Tx-only sockets by supplying
NULL to the rx and tx arguments, respectively, to the
xsk_socket__create function.
If you create a Tx-only socket, we recommend that you do not put any
packets on the fill ring. If you do this, drivers might think you are
going to receive something when you in fact will not, and this can
negatively impact performance.
XDP_UMEM_REG setsockopt
-----------------------
This setsockopt registers a UMEM to a socket. This is the area that
contain all the buffers that packet can recide in. The call takes a
pointer to the beginning of this area and the size of it. Moreover, it
also has parameter called chunk_size that is the size that the UMEM is
divided into. It can only be 2K or 4K at the moment. If you have an
UMEM area that is 128K and a chunk size of 2K, this means that you
will be able to hold a maximum of 128K / 2K = 64 packets in your UMEM
area and that your largest packet size can be 2K.
There is also an option to set the headroom of each single buffer in
the UMEM. If you set this to N bytes, it means that the packet will
start N bytes into the buffer leaving the first N bytes for the
application to use. The final option is the flags field, but it will
be dealt with in separate sections for each UMEM flag.
XDP_STATISTICS getsockopt
-------------------------
Gets drop statistics of a socket that can be useful for debug
purposes. The supported statistics are shown below:
.. code-block:: c
struct xdp_statistics {
__u64 rx_dropped; /* Dropped for reasons other than invalid desc */
__u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
__u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
};
XDP_OPTIONS getsockopt
----------------------
Gets options from an XDP socket. The only one supported so far is
XDP_OPTIONS_ZEROCOPY which tells you if zero-copy is on or not.
Usage
=====
In order to use AF_XDP sockets there are two parts needed. The
In order to use AF_XDP sockets two parts are needed. The
user-space application and the XDP program. For a complete setup and
usage example, please refer to the sample application. The user-space
side is xdpsock_user.c and the XDP side is part of libbpf.
The XDP code sample included in tools/lib/bpf/xsk.c is the following::
The XDP code sample included in tools/lib/bpf/xsk.c is the following:
.. code-block:: c
SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
{
int index = ctx->rx_queue_index;
// A set entry here means that the correspnding queue_id
// A set entry here means that the corresponding queue_id
// has an active AF_XDP socket bound to it.
if (bpf_map_lookup_elem(&xsks_map, &index))
return bpf_redirect_map(&xsks_map, index, 0);
@ -238,7 +440,10 @@ The XDP code sample included in tools/lib/bpf/xsk.c is the following::
return XDP_PASS;
}
Naive ring dequeue and enqueue could look like this::
A simple but not so performance ring dequeue and enqueue could look
like this:
.. code-block:: c
// struct xdp_rxtx_ring {
// __u32 *producer;
@ -287,17 +492,16 @@ Naive ring dequeue and enqueue could look like this::
return 0;
}
For a more optimized version, please refer to the sample application.
But please use the libbpf functions as they are optimized and ready to
use. Will make your life easier.
Sample application
==================
There is a xdpsock benchmarking/test application included that
demonstrates how to use AF_XDP sockets with both private and shared
UMEMs. Say that you would like your UDP traffic from port 4242 to end
up in queue 16, that we will enable AF_XDP on. Here, we use ethtool
for this::
demonstrates how to use AF_XDP sockets with private UMEMs. Say that
you would like your UDP traffic from port 4242 to end up in queue 16,
that we will enable AF_XDP on. Here, we use ethtool for this::
ethtool -N p3p2 rx-flow-hash udp4 fn
ethtool -N p3p2 flow-type udp4 src-port 4242 dst-port 4242 \
@ -311,13 +515,18 @@ using::
For XDP_SKB mode, use the switch "-S" instead of "-N" and all options
can be displayed with "-h", as usual.
This sample application uses libbpf to make the setup and usage of
AF_XDP simpler. If you want to know how the raw uapi of AF_XDP is
really used to make something more advanced, take a look at the libbpf
code in tools/lib/bpf/xsk.[ch].
FAQ
=======
Q: I am not seeing any traffic on the socket. What am I doing wrong?
A: When a netdev of a physical NIC is initialized, Linux usually
allocates one Rx and Tx queue pair per core. So on a 8 core system,
allocates one RX and TX queue pair per core. So on a 8 core system,
queue ids 0 to 7 will be allocated, one per core. In the AF_XDP
bind call or the xsk_socket__create libbpf function call, you
specify a specific queue id to bind to and it is only the traffic
@ -343,9 +552,21 @@ A: When a netdev of a physical NIC is initialized, Linux usually
sudo ethtool -N <interface> flow-type udp4 src-port 4242 dst-port \
4242 action 2
A number of other ways are possible all up to the capabilitites of
A number of other ways are possible all up to the capabilities of
the NIC you have.
Q: Can I use the XSKMAP to implement a switch betwen different umems
in copy mode?
A: The short answer is no, that is not supported at the moment. The
XSKMAP can only be used to switch traffic coming in on queue id X
to sockets bound to the same queue id X. The XSKMAP can contain
sockets bound to different queue ids, for example X and Y, but only
traffic goming in from queue id Y can be directed to sockets bound
to the same queue id Y. In zero-copy mode, you should use the
switch, or other distribution mechanism, in your NIC to direct
traffic to the correct queue id and socket.
Credits
=======

View File

@ -1,5 +1,5 @@
aQuantia AQtion Driver for the aQuantia Multi-Gigabit PCI Express Family of
Ethernet Adapters
Marvell(Aquantia) AQtion Driver for the aQuantia Multi-Gigabit PCI Express
Family of Ethernet Adapters
=============================================================================
Contents
@ -325,6 +325,46 @@ Supported ethtool options
Example:
ethtool -N eth0 flow-type udp4 action 0 loc 32
UDP GSO hardware offload
---------------------------------
UDP GSO allows to boost UDP tx rates by offloading UDP headers allocation
into hardware. A special userspace socket option is required for this,
could be validated with /kernel/tools/testing/selftests/net/
udpgso_bench_tx -u -4 -D 10.0.1.1 -s 6300 -S 100
Will cause sending out of 100 byte sized UDP packets formed from single
6300 bytes user buffer.
UDP GSO is configured by:
ethtool -K eth0 tx-udp-segmentation on
Private flags (testing)
---------------------------------
Atlantic driver supports private flags for hardware custom features:
$ ethtool --show-priv-flags ethX
Private flags for ethX:
DMASystemLoopback : off
PKTSystemLoopback : off
DMANetworkLoopback : off
PHYInternalLoopback: off
PHYExternalLoopback: off
Example:
$ ethtool --set-priv-flags ethX DMASystemLoopback on
DMASystemLoopback: DMA Host loopback.
PKTSystemLoopback: Packet buffer host loopback.
DMANetworkLoopback: Network side loopback on DMA block.
PHYInternalLoopback: Internal loopback on Phy.
PHYExternalLoopback: External loopback on Phy (with loopback ethernet cable).
Command Line Parameters
=======================
The following command line parameters are available on atlantic driver:
@ -426,7 +466,7 @@ Support
If an issue is identified with the released source code on the supported
kernel with a supported adapter, email the specific information related
to the issue to support@aquantia.com
to the issue to aqn_support@marvell.com
License
=======

View File

@ -129,9 +129,9 @@ CONFIG_AQUANTIA_PHY=y
DPAA Ethernet Frame Processing
==============================
On Rx, buffers for the incoming frames are retrieved from one of the three
existing buffers pools. The driver initializes and seeds these, each with
buffers of different sizes: 1KB, 2KB and 4KB.
On Rx, buffers for the incoming frames are retrieved from the buffers found
in the dedicated interface buffer pool. The driver initializes and seeds these
with one page buffers.
On Tx, all transmitted frames are returned to the driver through Tx
confirmation frame queues. The driver is then responsible for freeing the
@ -254,7 +254,7 @@ The following statistics are exported for each interface through ethtool:
The driver also exports the following information in sysfs:
- the FQ IDs for each FQ type
/sys/devices/platform/dpaa-ethernet.0/net/<int>/fqids
/sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/fqids
- the IDs of the buffer pools in use
/sys/devices/platform/dpaa-ethernet.0/net/<int>/bpids
- the ID of the buffer pool in use
/sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/bpids

View File

@ -8,3 +8,4 @@ DPAA2 Documentation
overview
dpio-driver
ethernet-driver
mac-phy-support

View File

@ -0,0 +1,191 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
=======================
DPAA2 MAC / PHY support
=======================
:Copyright: |copy| 2019 NXP
Overview
--------
The DPAA2 MAC / PHY support consists of a set of APIs that help DPAA2 network
drivers (dpaa2-eth, dpaa2-ethsw) interract with the PHY library.
DPAA2 Software Architecture
---------------------------
Among other DPAA2 objects, the fsl-mc bus exports DPNI objects (abstracting a
network interface) and DPMAC objects (abstracting a MAC). The dpaa2-eth driver
probes on the DPNI object and connects to and configures a DPMAC object with
the help of phylink.
Data connections may be established between a DPNI and a DPMAC, or between two
DPNIs. Depending on the connection type, the netif_carrier_[on/off] is handled
directly by the dpaa2-eth driver or by phylink.
.. code-block:: none
Sources of abstracted link state information presented by the MC firmware
+--------------------------------------+
+------------+ +---------+ | xgmac_mdio |
| net_device | | phylink |--| +-----+ +-----+ +-----+ +-----+ |
+------------+ +---------+ | | PHY | | PHY | | PHY | | PHY | |
| | | +-----+ +-----+ +-----+ +-----+ |
+------------------------------------+ | External MDIO bus |
| dpaa2-eth | +--------------------------------------+
+------------------------------------+
| | Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | MC firmware
| /| V
+----------+ / | +----------+
| | / | | |
| | | | | |
| DPNI |<------| |<------| DPMAC |
| | | | | |
| | \ |<---+ | |
+----------+ \ | | +----------+
\| |
|
+--------------------------------------+
| MC firmware polling MAC PCS for link |
| +-----+ +-----+ +-----+ +-----+ |
| | PCS | | PCS | | PCS | | PCS | |
| +-----+ +-----+ +-----+ +-----+ |
| Internal MDIO bus |
+--------------------------------------+
Depending on an MC firmware configuration setting, each MAC may be in one of two modes:
- DPMAC_LINK_TYPE_FIXED: the link state management is handled exclusively by
the MC firmware by polling the MAC PCS. Without the need to register a
phylink instance, the dpaa2-eth driver will not bind to the connected dpmac
object at all.
- DPMAC_LINK_TYPE_PHY: The MC firmware is left waiting for link state update
events, but those are in fact passed strictly between the dpaa2-mac (based on
phylink) and its attached net_device driver (dpaa2-eth, dpaa2-ethsw),
effectively bypassing the firmware.
Implementation
--------------
At probe time or when a DPNI's endpoint is dynamically changed, the dpaa2-eth
is responsible to find out if the peer object is a DPMAC and if this is the
case, to integrate it with PHYLINK using the dpaa2_mac_connect() API, which
will do the following:
- look up the device tree for PHYLINK-compatible of binding (phy-handle)
- will create a PHYLINK instance associated with the received net_device
- connect to the PHY using phylink_of_phy_connect()
The following phylink_mac_ops callback are implemented:
- .validate() will populate the supported linkmodes with the MAC capabilities
only when the phy_interface_t is RGMII_* (at the moment, this is the only
link type supported by the driver).
- .mac_config() will configure the MAC in the new configuration using the
dpmac_set_link_state() MC firmware API.
- .mac_link_up() / .mac_link_down() will update the MAC link using the same
API described above.
At driver unbind() or when the DPNI object is disconnected from the DPMAC, the
dpaa2-eth driver calls dpaa2_mac_disconnect() which will, in turn, disconnect
from the PHY and destroy the PHYLINK instance.
In case of a DPNI-DPMAC connection, an 'ip link set dev eth0 up' would start
the following sequence of operations:
(1) phylink_start() called from .dev_open().
(2) The .mac_config() and .mac_link_up() callbacks are called by PHYLINK.
(3) In order to configure the HW MAC, the MC Firmware API
dpmac_set_link_state() is called.
(4) The firmware will eventually setup the HW MAC in the new configuration.
(5) A netif_carrier_on() call is made directly from PHYLINK on the associated
net_device.
(6) The dpaa2-eth driver handles the LINK_STATE_CHANGE irq in order to
enable/disable Rx taildrop based on the pause frame settings.
.. code-block:: none
+---------+ +---------+
| PHYLINK |-------------->| eth0 |
+---------+ (5) +---------+
(1) ^ |
| |
| v (2)
+-----------------------------------+
| dpaa2-eth |
+-----------------------------------+
| ^ (6)
| |
v (3) |
+---------+---------------+---------+
| DPMAC | | DPNI |
+---------+ +---------+
| MC Firmware |
+-----------------------------------+
|
|
v (4)
+-----------------------------------+
| HW MAC |
+-----------------------------------+
In case of a DPNI-DPNI connection, a usual sequence of operations looks like
the following:
(1) ip link set dev eth0 up
(2) The dpni_enable() MC API called on the associated fsl_mc_device.
(3) ip link set dev eth1 up
(4) The dpni_enable() MC API called on the associated fsl_mc_device.
(5) The LINK_STATE_CHANGED irq is received by both instances of the dpaa2-eth
driver because now the operational link state is up.
(6) The netif_carrier_on() is called on the exported net_device from
link_state_update().
.. code-block:: none
+---------+ +---------+
| eth0 | | eth1 |
+---------+ +---------+
| ^ ^ |
| | | |
(1) v | (6) (6) | v (3)
+---------+ +---------+
|dpaa2-eth| |dpaa2-eth|
+---------+ +---------+
| ^ ^ |
| | | |
(2) v | (5) (5) | v (4)
+---------+---------------+---------+
| DPNI | | DPNI |
+---------+ +---------+
| MC Firmware |
+-----------------------------------+
Exported API
------------
Any DPAA2 driver that drivers endpoints of DPMAC objects should service its
_EVENT_ENDPOINT_CHANGED irq and connect/disconnect from the associated DPMAC
when necessary using the below listed API::
- int dpaa2_mac_connect(struct dpaa2_mac *mac);
- void dpaa2_mac_disconnect(struct dpaa2_mac *mac);
A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED.
One can check for this condition using the below API::
- bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io);
Before connection to a MAC, the caller must allocate and populate the
dpaa2_mac structure with the associated net_device, a pointer to the MC portal
to be used and the actual fsl_mc_device structure of the DPMAC.

View File

@ -154,6 +154,27 @@ User command examples:
values:
cmode runtime value smfs
enable_roce: RoCE enablement state
----------------------------------
RoCE enablement state controls driver support for RoCE traffic.
When RoCE is disabled, there is no gid table, only raw ethernet QPs are supported and traffic on the well known UDP RoCE port is handled as raw ethernet traffic.
To change RoCE enablement state a user must change the driverinit cmode value and run devlink reload.
User command examples:
- Disable RoCE::
$ devlink dev param set pci/0000:06:00.0 name enable_roce value false cmode driverinit
$ devlink dev reload pci/0000:06:00.0
- Read RoCE enablement state::
$ devlink dev param show pci/0000:06:00.0 name enable_roce
pci/0000:06:00.0:
name enable_roce type generic
values:
cmode driverinit value true
Devlink health reporters
========================

View File

@ -0,0 +1,209 @@
* Texas Instruments CPSW switchdev based ethernet driver 2.0
- Port renaming
On older udev versions renaming of ethX to swXpY will not be automatically
supported
In order to rename via udev:
ip -d link show dev sw0p1 | grep switchid
SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==<switchid>, \
ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}"
====================
# Dual mac mode
====================
- The new (cpsw_new.c) driver is operating in dual-emac mode by default, thus
working as 2 individual network interfaces. Main differences from legacy CPSW
driver are:
- optimized promiscuous mode: The P0_UNI_FLOOD (both ports) is enabled in
addition to ALLMULTI (current port) instead of ALE_BYPASS.
So, Ports in promiscuous mode will keep possibility of mcast and vlan filtering,
which is provides significant benefits when ports are joined to the same bridge,
but without enabling "switch" mode, or to different bridges.
- learning disabled on ports as it make not too much sense for
segregated ports - no forwarding in HW.
- enabled basic support for devlink.
devlink dev show
platform/48484000.switch
devlink dev param show
platform/48484000.switch:
name switch_mode type driver-specific
values:
cmode runtime value false
name ale_bypass type driver-specific
values:
cmode runtime value false
Devlink configuration parameters
====================
See Documentation/networking/devlink-params-ti-cpsw-switch.txt
====================
# Bridging in dual mac mode
====================
The dual_mac mode requires two vids to be reserved for internal purposes,
which, by default, equal CPSW Port numbers. As result, bridge has to be
configured in vlan unaware mode or default_pvid has to be adjusted.
ip link add name br0 type bridge
ip link set dev br0 type bridge vlan_filtering 0
echo 0 > /sys/class/net/br0/bridge/default_pvid
ip link set dev sw0p1 master br0
ip link set dev sw0p2 master br0
- or -
ip link add name br0 type bridge
ip link set dev br0 type bridge vlan_filtering 0
echo 100 > /sys/class/net/br0/bridge/default_pvid
ip link set dev br0 type bridge vlan_filtering 1
ip link set dev sw0p1 master br0
ip link set dev sw0p2 master br0
====================
# Enabling "switch"
====================
The Switch mode can be enabled by configuring devlink driver parameter
"switch_mode" to 1/true:
devlink dev param set platform/48484000.switch \
name switch_mode value 1 cmode runtime
This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
Port's netdev devices have to be in UP before joining to the bridge to avoid
overwriting of bridge configuration as CPSW switch driver copletly reloads its
configuration when first Port changes its state to UP.
When the both interfaces joined the bridge - CPSW switch driver will enable
marking packets with offload_fwd_mark flag unless "ale_bypass=0"
All configuration is implemented via switchdev API.
====================
# Bridge setup
====================
devlink dev param set platform/48484000.switch \
name switch_mode value 1 cmode runtime
ip link add name br0 type bridge
ip link set dev br0 type bridge ageing_time 1000
ip link set dev sw0p1 up
ip link set dev sw0p2 up
ip link set dev sw0p1 master br0
ip link set dev sw0p2 master br0
[*] bridge vlan add dev br0 vid 1 pvid untagged self
[*] if vlan_filtering=1. where default_pvid=1
=================
# On/off STP
=================
ip link set dev BRDEV type bridge stp_state 1/0
Note. Steps [*] are mandatory.
====================
# VLAN configuration
====================
bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
Note. This step is mandatory for bridge/default_pvid.
=================
# Add extra VLANs
=================
1. untagged:
bridge vlan add dev sw0p1 vid 100 pvid untagged master
bridge vlan add dev sw0p2 vid 100 pvid untagged master
bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100
2. tagged:
bridge vlan add dev sw0p1 vid 100 master
bridge vlan add dev sw0p2 vid 100 master
bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100
====
FDBs
====
FDBs are automatically added on the appropriate switch port upon detection
Manually adding FDBs:
bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs
====
MDBs
====
MDBs are automatically added on the appropriate switch port upon detection
Manually adding MDBs:
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs
==================
Multicast flooding
==================
CPU port mcast_flooding is always on
Turning flooding on/off on swithch ports:
bridge link set dev sw0p1 mcast_flood on/off
==================
Access and Trunk port
==================
bridge vlan add dev sw0p1 vid 100 pvid untagged master
bridge vlan add dev sw0p2 vid 100 master
bridge vlan add dev br0 vid 100 self
ip link add link br0 name br0.100 type vlan id 100
Note. Setting PVID on Bridge device itself working only for
default VLAN (default_pvid).
=====================
NFS
=====================
The only way for NFS to work is by chrooting to a minimal environment when
switch configuration that will affect connectivity is needed.
Assuming you are booting NFS with eth1 interface(the script is hacky and
it's just there to prove NFS is doable).
setup.sh:
#!/bin/sh
mkdir proc
mount -t proc none /proc
ifconfig br0 > /dev/null
if [ $? -ne 0 ]; then
echo "Setting up bridge"
ip link add name br0 type bridge
ip link set dev br0 type bridge ageing_time 1000
ip link set dev br0 type bridge vlan_filtering 1
ip link set eth1 down
ip link set eth1 name sw0p1
ip link set dev sw0p1 up
ip link set dev sw0p2 up
ip link set dev sw0p2 master br0
ip link set dev sw0p1 master br0
bridge vlan add dev br0 vid 1 pvid untagged self
ifconfig sw0p1 0.0.0.0
udhchc -i br0
fi
umount /proc
run_nfs.sh:
#!/bin/sh
mkdir /tmp/root/bin -p
mkdir /tmp/root/lib -p
cp -r /lib/ /tmp/root/
cp -r /bin/ /tmp/root/
cp /sbin/ip /tmp/root/bin
cp /sbin/bridge /tmp/root/bin
cp /sbin/ifconfig /tmp/root/bin
cp /sbin/udhcpc /tmp/root/bin
cp /path/to/setup.sh /tmp/root/bin
chroot /tmp/root/ busybox sh /bin/setup.sh
run ./run_nfs.sh

View File

@ -0,0 +1,17 @@
flow_steering_mode [DEVICE, DRIVER-SPECIFIC]
Controls the flow steering mode of the driver.
Two modes are supported:
1. 'dmfs' - Device managed flow steering.
2. 'smfs - Software/Driver managed flow steering.
In DMFS mode, the HW steering entities are created and
managed through the Firmware.
In SMFS mode, the HW steering entities are created and
managed though by the driver directly into Hardware
without firmware intervention.
Type: String
Configuration mode: runtime
enable_roce [DEVICE, GENERIC]
Enable handling of RoCE traffic in the device.
Defaultly enabled.
Configuration mode: driverinit

View File

@ -0,0 +1,7 @@
ATU_hash [DEVICE, DRIVER-SPECIFIC]
Select one of four possible hashing algorithms for
MAC addresses in the Address Translation Unit.
A value of 3 seems to work better than the default of
1 when many MAC addresses have the same OUI.
Configuration mode: runtime
Type: u8. 0-3 valid.

View File

@ -0,0 +1,10 @@
ale_bypass [DEVICE, DRIVER-SPECIFIC]
Allows to enable ALE_CONTROL(4).BYPASS mode for debug purposes.
All packets will be sent to the Host port only if enabled.
Type: bool
Configuration mode: runtime
switch_mode [DEVICE, DRIVER-SPECIFIC]
Enable switch mode
Type: bool
Configuration mode: runtime

View File

@ -65,3 +65,7 @@ reset_dev_on_drv_probe [DEVICE, GENERIC]
Reset only if device firmware can be found in the
filesystem.
Type: u8
enable_roce [DEVICE, GENERIC]
Enable handling of RoCE traffic in the device.
Type: Boolean

View File

@ -162,6 +162,67 @@ be added to the following table:
- ``drop``
- Traps packets that the device decided to drop because they could not be
enqueued to a transmission queue which is full
* - ``non_ip``
- ``drop``
- Traps packets that the device decided to drop because they need to
undergo a layer 3 lookup, but are not IP or MPLS packets
* - ``uc_dip_over_mc_dmac``
- ``drop``
- Traps packets that the device decided to drop because they need to be
routed and they have a unicast destination IP and a multicast destination
MAC
* - ``dip_is_loopback_address``
- ``drop``
- Traps packets that the device decided to drop because they need to be
routed and their destination IP is the loopback address (i.e., 127.0.0.0/8
and ::1/128)
* - ``sip_is_mc``
- ``drop``
- Traps packets that the device decided to drop because they need to be
routed and their source IP is multicast (i.e., 224.0.0.0/8 and ff::/8)
* - ``sip_is_loopback_address``
- ``drop``
- Traps packets that the device decided to drop because they need to be
routed and their source IP is the loopback address (i.e., 127.0.0.0/8 and ::1/128)
* - ``ip_header_corrupted``
- ``drop``
- Traps packets that the device decided to drop because they need to be
routed and their IP header is corrupted: wrong checksum, wrong IP version
or too short Internet Header Length (IHL)
* - ``ipv4_sip_is_limited_bc``
- ``drop``
- Traps packets that the device decided to drop because they need to be
routed and their source IP is limited broadcast (i.e., 255.255.255.255/32)
* - ``ipv6_mc_dip_reserved_scope``
- ``drop``
- Traps IPv6 packets that the device decided to drop because they need to
be routed and their IPv6 multicast destination IP has a reserved scope
(i.e., ffx0::/16)
* - ``ipv6_mc_dip_interface_local_scope``
- ``drop``
- Traps IPv6 packets that the device decided to drop because they need to
be routed and their IPv6 multicast destination IP has an interface-local scope
(i.e., ffx1::/16)
* - ``mtu_value_is_too_small``
- ``exception``
- Traps packets that should have been routed by the device, but were bigger
than the MTU of the egress interface
* - ``unresolved_neigh``
- ``exception``
- Traps packets that did not have a matching IP neighbour after routing
* - ``mc_reverse_path_forwarding``
- ``exception``
- Traps multicast IP packets that failed reverse-path forwarding (RPF)
check during multicast routing
* - ``reject_route``
- ``exception``
- Traps packets that hit reject routes (i.e., "unreachable", "prohibit")
* - ``ipv4_lpm_miss``
- ``exception``
- Traps unicast IPv4 packets that did not match any route
* - ``ipv6_lpm_miss``
- ``exception``
- Traps unicast IPv6 packets that did not match any route
Driver-specific Packet Traps
============================

View File

@ -770,10 +770,10 @@ Some core changes of the new internal format:
callq foo
mov %rax,%r13
mov %rbx,%rdi
mov $0x2,%esi
mov $0x3,%edx
mov $0x4,%ecx
mov $0x5,%r8d
mov $0x6,%esi
mov $0x7,%edx
mov $0x8,%ecx
mov $0x9,%r8d
callq bar
add %r13,%rax
mov -0x228(%rbp),%rbx

View File

@ -33,6 +33,7 @@ Contents:
scaling
tls
tls-offload
nfc
.. only:: subproject and html

View File

@ -2091,6 +2091,28 @@ pf_enable - INTEGER
Default: 1
pf_expose - INTEGER
Unset or enable/disable pf (pf is short for potentially failed) state
exposure. Applications can control the exposure of the PF path state
in the SCTP_PEER_ADDR_CHANGE event and the SCTP_GET_PEER_ADDR_INFO
sockopt. When it's unset, no SCTP_PEER_ADDR_CHANGE event with
SCTP_ADDR_PF state will be sent and a SCTP_PF-state transport info
can be got via SCTP_GET_PEER_ADDR_INFO sockopt; When it's enabled,
a SCTP_PEER_ADDR_CHANGE event will be sent for a transport becoming
SCTP_PF state and a SCTP_PF-state transport info can be got via
SCTP_GET_PEER_ADDR_INFO sockopt; When it's diabled, no
SCTP_PEER_ADDR_CHANGE event will be sent and it returns -EACCES when
trying to get a SCTP_PF-state transport info via SCTP_GET_PEER_ADDR_INFO
sockopt.
0: Unset pf state exposure, Compatible with old applications.
1: Disable pf state exposure.
2: Enable pf state exposure.
Default: 0
addip_noauth_enable - BOOLEAN
Dynamic Address Reconfiguration (ADD-IP) requires the use of
authentication to protect the operations of adding or removing new
@ -2173,6 +2195,18 @@ pf_retrans - INTEGER
Default: 0
ps_retrans - INTEGER
Primary.Switchover.Max.Retrans (PSMR), it's a tunable parameter coming
from section-5 "Primary Path Switchover" in rfc7829. The primary path
will be changed to another active path when the path error counter on
the old primary path exceeds PSMR, so that "the SCTP sender is allowed
to continue data transmission on a new working path even when the old
primary destination address becomes active again". Note this feature
is disabled by initializing 'ps_retrans' per netns as 0xffff by default,
and its value can't be less than 'pf_retrans' when changing by sysctl.
Default: 0xffff
rto_initial - INTEGER
The initial round trip timeout value in milliseconds that will be used
in calculating round trip times. This is the initial time interval

View File

@ -1,3 +1,4 @@
===================
Linux NFC subsystem
===================
@ -8,7 +9,7 @@ This document covers the architecture overview, the device driver interface
description and the userspace interface description.
Architecture overview
---------------------
=====================
The NFC subsystem is responsible for:
- NFC adapters management;
@ -25,33 +26,34 @@ The control operations are available to userspace via generic netlink.
The low-level data exchange interface is provided by the new socket family
PF_NFC. The NFC_SOCKPROTO_RAW performs raw communication with NFC targets.
.. code-block:: none
+--------------------------------------+
| USER SPACE |
+--------------------------------------+
^ ^
| low-level | control
| data exchange | operations
| |
| v
| +-----------+
| AF_NFC | netlink |
| socket +-----------+
| raw ^
| |
v v
+---------+ +-----------+
| rawsock | <--------> | core |
+---------+ +-----------+
^
|
v
+-----------+
| driver |
+-----------+
+--------------------------------------+
| USER SPACE |
+--------------------------------------+
^ ^
| low-level | control
| data exchange | operations
| |
| v
| +-----------+
| AF_NFC | netlink |
| socket +-----------+
| raw ^
| |
v v
+---------+ +-----------+
| rawsock | <--------> | core |
+---------+ +-----------+
^
|
v
+-----------+
| driver |
+-----------+
Device Driver Interface
-----------------------
=======================
When registering on the NFC subsystem, the device driver must inform the core
of the set of supported NFC protocols and the set of ops callbacks. The ops
@ -64,7 +66,7 @@ callbacks that must be implemented are the following:
* data_exchange - send data and receive the response (transceive operation)
Userspace interface
--------------------
===================
The userspace interface is divided in control operations and low-level data
exchange operation.
@ -82,7 +84,7 @@ The operations are composed by commands and events, all listed below:
* NFC_EVENT_DEVICE_ADDED - reports an NFC device addition
* NFC_EVENT_DEVICE_REMOVED - reports an NFC device removal
* NFC_EVENT_TARGETS_FOUND - reports START_POLL results when 1 or more targets
are found
are found
The user must call START_POLL to poll for NFC targets, passing the desired NFC
protocols through NFC_ATTR_PROTOCOLS attribute. The device remains in polling
@ -101,14 +103,14 @@ it's closed.
LOW-LEVEL DATA EXCHANGE:
The userspace must use PF_NFC sockets to perform any data communication with
targets. All NFC sockets use AF_NFC:
targets. All NFC sockets use AF_NFC::
struct sockaddr_nfc {
sa_family_t sa_family;
__u32 dev_idx;
__u32 target_idx;
__u32 nfc_protocol;
};
struct sockaddr_nfc {
sa_family_t sa_family;
__u32 dev_idx;
__u32 target_idx;
__u32 nfc_protocol;
};
To establish a connection with one target, the user must create an
NFC_SOCKPROTO_RAW socket and call the 'connect' syscall with the sockaddr_nfc

View File

@ -352,7 +352,8 @@ Fills the phydev structure with up-to-date information about the current
settings in the PHY.
::
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
int phy_ethtool_ksettings_set(struct phy_device *phydev,
const struct ethtool_link_ksettings *cmd);
Ethtool convenience functions.
::

View File

@ -213,3 +213,29 @@ A patchset to OpenSSL to use ktls as the record layer is
of calling send directly after a handshake using gnutls.
Since it doesn't implement a full record layer, control
messages are not supported.
Statistics
==========
TLS implementation exposes the following per-namespace statistics
(``/proc/net/tls_stat``):
- ``TlsCurrTxSw``, ``TlsCurrRxSw`` -
number of TX and RX sessions currently installed where host handles
cryptography
- ``TlsCurrTxDevice``, ``TlsCurrRxDevice`` -
number of TX and RX sessions currently installed where NIC handles
cryptography
- ``TlsTxSw``, ``TlsRxSw`` -
number of TX and RX sessions opened with host cryptography
- ``TlsTxDevice``, ``TlsRxDevice`` -
number of TX and RX sessions opened with NIC cryptography
- ``TlsDecryptError`` -
record decryption failed (e.g. due to incorrect authentication tag)
- ``TlsDeviceRxResync`` -
number of RX resyncs sent to NICs handling cryptography

View File

@ -1182,14 +1182,21 @@ S: Maintained
F: drivers/media/i2c/aptina-pll.*
AQUANTIA ETHERNET DRIVER (atlantic)
M: Igor Russkikh <igor.russkikh@aquantia.com>
M: Igor Russkikh <irusskikh@marvell.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.aquantia.com
W: https://www.marvell.com/
Q: http://patchwork.ozlabs.org/project/netdev/list/
F: drivers/net/ethernet/aquantia/atlantic/
F: Documentation/networking/device_drivers/aquantia/atlantic.txt
AQUANTIA ETHERNET DRIVER PTP SUBSYSTEM
M: Egor Pomozov <epomozov@marvell.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.aquantia.com
F: drivers/net/ethernet/aquantia/atlantic/aq_ptp*
ARC FRAMEBUFFER DRIVER
M: Jaya Kumar <jayalk@intworks.biz>
S: Maintained
@ -5062,10 +5069,14 @@ M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/freescale/dpaa2/dpaa2-eth*
F: drivers/net/ethernet/freescale/dpaa2/dpaa2-mac*
F: drivers/net/ethernet/freescale/dpaa2/dpni*
F: drivers/net/ethernet/freescale/dpaa2/dpmac*
F: drivers/net/ethernet/freescale/dpaa2/dpkg.h
F: drivers/net/ethernet/freescale/dpaa2/Makefile
F: drivers/net/ethernet/freescale/dpaa2/Kconfig
F: Documentation/networking/device_drivers/freescale/dpaa2/ethernet-driver.rst
F: Documentation/networking/device_drivers/freescale/dpaa2/mac-phy-support.rst
DPAA2 ETHERNET SWITCH DRIVER
M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
@ -6159,10 +6170,12 @@ S: Maintained
F: Documentation/ABI/testing/sysfs-class-net-phydev
F: Documentation/devicetree/bindings/net/ethernet-phy.yaml
F: Documentation/devicetree/bindings/net/mdio*
F: Documentation/devicetree/bindings/net/qca,ar803x.yaml
F: Documentation/networking/phy.rst
F: drivers/net/phy/
F: drivers/of/of_mdio.c
F: drivers/of/of_net.c
F: include/dt-bindings/net/qca-ar803x.h
F: include/linux/*mdio*.h
F: include/linux/of_net.h
F: include/linux/phy.h
@ -7482,8 +7495,8 @@ F: drivers/platform/x86/tc1100-wmi.c
HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
M: Jaroslav Kysela <perex@perex.cz>
S: Maintained
F: drivers/net/ethernet/hp/hp100.*
S: Obsolete
F: drivers/staging/hp/hp100.*
HPET: High Precision Event Timers driver
M: Clemens Ladisch <clemens@ladisch.de>
@ -9798,6 +9811,7 @@ S: Maintained
F: drivers/net/dsa/mv88e6xxx/
F: include/linux/platform_data/mv88e6xxx.h
F: Documentation/devicetree/bindings/net/dsa/marvell.txt
F: Documentation/networking/devlink-params-mv88e6xxx.txt
MARVELL ARMADA DRM SUPPORT
M: Russell King <linux@armlinux.org.uk>
@ -10877,6 +10891,7 @@ M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/mscc/
F: include/soc/mscc/ocelot*
MICROSOFT SURFACE PRO 3 BUTTON DRIVER
M: Chen Yu <yu.c.chen@intel.com>
@ -13881,7 +13896,7 @@ R: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
L: netdev@vger.kernel.org
L: linux-renesas-soc@vger.kernel.org
F: Documentation/devicetree/bindings/net/renesas,*.txt
F: Documentation/devicetree/bindings/net/sh_eth.txt
F: Documentation/devicetree/bindings/net/renesas,*.yaml
F: drivers/net/ethernet/renesas/
F: include/linux/sh_eth.h
@ -17405,6 +17420,14 @@ S: Maintained
F: drivers/input/serio/userio.c
F: include/uapi/linux/userio.h
VITESSE FELIX ETHERNET SWITCH DRIVER
M: Vladimir Oltean <vladimir.oltean@nxp.com>
M: Claudiu Manoil <claudiu.manoil@nxp.com>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/dsa/ocelot/*
F: net/dsa/tag_ocelot.c
VIVID VIRTUAL VIDEO DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org

View File

@ -186,3 +186,30 @@
pinctrl-1 = <&mmc2_pins_hs>;
pinctrl-2 = <&mmc2_pins_ddr_rev20 &mmc2_iodelay_ddr_conf>;
};
&mac_sw {
pinctrl-names = "default", "sleep";
status = "okay";
};
&cpsw_port1 {
phy-handle = <&ethphy0_sw>;
phy-mode = "rgmii";
ti,dual-emac-pvid = <1>;
};
&cpsw_port2 {
phy-handle = <&ethphy1_sw>;
phy-mode = "rgmii";
ti,dual-emac-pvid = <2>;
};
&davinci_mdio_sw {
ethphy0_sw: ethernet-phy@0 {
reg = <0>;
};
ethphy1_sw: ethernet-phy@1 {
reg = <1>;
};
};

View File

@ -27,3 +27,8 @@
pinctrl-1 = <&mmc2_pins_hs>;
pinctrl-2 = <&mmc2_pins_ddr_rev20>;
};
&mac {
status = "okay";
dual_emac;
};

View File

@ -35,3 +35,8 @@
pinctrl-1 = <&mmc2_pins_default>;
pinctrl-2 = <&mmc2_pins_default>;
};
&mac {
status = "okay";
dual_emac;
};

View File

@ -363,11 +363,6 @@
ext-clk-src;
};
&mac {
status = "okay";
dual_emac;
};
&cpsw_emac0 {
phy-handle = <&ethphy0>;
phy-mode = "rgmii";

View File

@ -3079,6 +3079,58 @@
phys = <&phy_gmii_sel 2>;
};
};
mac_sw: switch@0 {
compatible = "ti,dra7-cpsw-switch","ti,cpsw-switch";
reg = <0x0 0x4000>;
ranges = <0 0 0x4000>;
clocks = <&gmac_main_clk>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <1>;
syscon = <&scm_conf>;
status = "disabled";
interrupts = <GIC_SPI 334 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 335 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "rx_thresh", "rx", "tx", "misc";
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
cpsw_port1: port@1 {
reg = <1>;
label = "port1";
mac-address = [ 00 00 00 00 00 00 ];
phys = <&phy_gmii_sel 1>;
};
cpsw_port2: port@2 {
reg = <2>;
label = "port2";
mac-address = [ 00 00 00 00 00 00 ];
phys = <&phy_gmii_sel 2>;
};
};
davinci_mdio_sw: mdio@1000 {
compatible = "ti,cpsw-mdio","ti,davinci_mdio";
clocks = <&gmac_main_clk>;
clock-names = "fck";
#address-cells = <1>;
#size-cells = <0>;
bus_freq = <1000000>;
reg = <0x1000 0x100>;
};
cpts {
clocks = <&gmac_clkctrl DRA7_GMAC_GMAC_CLKCTRL 25>;
clock-names = "cpts";
};
};
};
};
};

View File

@ -554,3 +554,4 @@ CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_SCHEDSTATS=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_TI_CPSW_SWITCHDEV=y

View File

@ -12,6 +12,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
@ -22,7 +23,6 @@
#include <linux/spi/spi.h>
#include <linux/spi/pxa2xx_spi.h>
#include <linux/can/platform/mcp251x.h>
#include <linux/regulator/machine.h>
#include "generic.h"
@ -69,8 +69,9 @@ static struct pxa2xx_spi_chip mcp251x_chip_info4 = {
.gpio_cs = ICONTROL_MCP251x_nCS4
};
static struct mcp251x_platform_data mcp251x_info = {
.oscillator_frequency = 16E6,
static const struct property_entry mcp251x_properties[] = {
PROPERTY_ENTRY_U32("clock-frequency", 16000000),
{}
};
static struct spi_board_info mcp251x_board_info[] = {
@ -79,7 +80,7 @@ static struct spi_board_info mcp251x_board_info[] = {
.max_speed_hz = 6500000,
.bus_num = 3,
.chip_select = 0,
.platform_data = &mcp251x_info,
.properties = mcp251x_properties,
.controller_data = &mcp251x_chip_info1,
.irq = PXA_GPIO_TO_IRQ(ICONTROL_MCP251x_nIRQ1)
},

View File

@ -13,6 +13,7 @@
#include <linux/leds.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/serial_8250.h>
@ -27,7 +28,6 @@
#include <linux/platform_data/i2c-pxa.h>
#include <linux/platform_data/pca953x.h>
#include <linux/apm-emulation.h>
#include <linux/can/platform/mcp251x.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
@ -428,14 +428,15 @@ static struct gpiod_lookup_table can_regulator_gpiod_table = {
},
};
static struct mcp251x_platform_data zeus_mcp2515_pdata = {
.oscillator_frequency = 16*1000*1000,
static const struct property_entry mcp251x_properties[] = {
PROPERTY_ENTRY_U32("clock-frequency", 16000000),
{}
};
static struct spi_board_info zeus_spi_board_info[] = {
[0] = {
.modalias = "mcp2515",
.platform_data = &zeus_mcp2515_pdata,
.properties = mcp251x_properties,
.irq = PXA_GPIO_TO_IRQ(ZEUS_CAN_GPIO),
.max_speed_hz = 1*1000*1000,
.bus_num = 3,

View File

@ -17,7 +17,7 @@ typedef struct
#define LOCAL_INIT(i) { (i) }
static __inline__ long local_read(local_t *l)
static __inline__ long local_read(const local_t *l)
{
return READ_ONCE(l->v);
}

View File

@ -23,6 +23,8 @@
#include <linux/filter.h>
#include <linux/init.h>
#include <linux/bpf.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <asm/cacheflush.h>
#include <asm/dis.h>
#include <asm/facility.h>
@ -38,10 +40,11 @@ struct bpf_jit {
int size; /* Size of program and literal pool */
int size_prg; /* Size of program */
int prg; /* Current position in program */
int lit_start; /* Start of literal pool */
int lit; /* Current position in literal pool */
int lit32_start; /* Start of 32-bit literal pool */
int lit32; /* Current position in 32-bit literal pool */
int lit64_start; /* Start of 64-bit literal pool */
int lit64; /* Current position in 64-bit literal pool */
int base_ip; /* Base address for literal pool */
int ret0_ip; /* Address of return 0 */
int exit_ip; /* Address of exit */
int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */
int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */
@ -49,14 +52,10 @@ struct bpf_jit {
int labels[1]; /* Labels for local jumps */
};
#define BPF_SIZE_MAX 0xffff /* Max size for program (16 bit branches) */
#define SEEN_MEM (1 << 0) /* use mem[] for temporary storage */
#define SEEN_RET0 (1 << 1) /* ret0_ip points to a valid return 0 */
#define SEEN_LITERAL (1 << 2) /* code uses literals */
#define SEEN_FUNC (1 << 3) /* calls C functions */
#define SEEN_TAIL_CALL (1 << 4) /* code uses tail calls */
#define SEEN_REG_AX (1 << 5) /* code uses constant blinding */
#define SEEN_MEM BIT(0) /* use mem[] for temporary storage */
#define SEEN_LITERAL BIT(1) /* code uses literals */
#define SEEN_FUNC BIT(2) /* calls C functions */
#define SEEN_TAIL_CALL BIT(3) /* code uses tail calls */
#define SEEN_STACK (SEEN_FUNC | SEEN_MEM)
/*
@ -131,13 +130,13 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define _EMIT2(op) \
({ \
if (jit->prg_buf) \
*(u16 *) (jit->prg_buf + jit->prg) = op; \
*(u16 *) (jit->prg_buf + jit->prg) = (op); \
jit->prg += 2; \
})
#define EMIT2(op, b1, b2) \
({ \
_EMIT2(op | reg(b1, b2)); \
_EMIT2((op) | reg(b1, b2)); \
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
})
@ -145,20 +144,20 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define _EMIT4(op) \
({ \
if (jit->prg_buf) \
*(u32 *) (jit->prg_buf + jit->prg) = op; \
*(u32 *) (jit->prg_buf + jit->prg) = (op); \
jit->prg += 4; \
})
#define EMIT4(op, b1, b2) \
({ \
_EMIT4(op | reg(b1, b2)); \
_EMIT4((op) | reg(b1, b2)); \
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
})
#define EMIT4_RRF(op, b1, b2, b3) \
({ \
_EMIT4(op | reg_high(b3) << 8 | reg(b1, b2)); \
_EMIT4((op) | reg_high(b3) << 8 | reg(b1, b2)); \
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
REG_SET_SEEN(b3); \
@ -167,13 +166,13 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define _EMIT4_DISP(op, disp) \
({ \
unsigned int __disp = (disp) & 0xfff; \
_EMIT4(op | __disp); \
_EMIT4((op) | __disp); \
})
#define EMIT4_DISP(op, b1, b2, disp) \
({ \
_EMIT4_DISP(op | reg_high(b1) << 16 | \
reg_high(b2) << 8, disp); \
_EMIT4_DISP((op) | reg_high(b1) << 16 | \
reg_high(b2) << 8, (disp)); \
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
})
@ -181,21 +180,27 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define EMIT4_IMM(op, b1, imm) \
({ \
unsigned int __imm = (imm) & 0xffff; \
_EMIT4(op | reg_high(b1) << 16 | __imm); \
_EMIT4((op) | reg_high(b1) << 16 | __imm); \
REG_SET_SEEN(b1); \
})
#define EMIT4_PCREL(op, pcrel) \
({ \
long __pcrel = ((pcrel) >> 1) & 0xffff; \
_EMIT4(op | __pcrel); \
_EMIT4((op) | __pcrel); \
})
#define EMIT4_PCREL_RIC(op, mask, target) \
({ \
int __rel = ((target) - jit->prg) / 2; \
_EMIT4((op) | (mask) << 20 | (__rel & 0xffff)); \
})
#define _EMIT6(op1, op2) \
({ \
if (jit->prg_buf) { \
*(u32 *) (jit->prg_buf + jit->prg) = op1; \
*(u16 *) (jit->prg_buf + jit->prg + 4) = op2; \
*(u32 *) (jit->prg_buf + jit->prg) = (op1); \
*(u16 *) (jit->prg_buf + jit->prg + 4) = (op2); \
} \
jit->prg += 6; \
})
@ -203,20 +208,20 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define _EMIT6_DISP(op1, op2, disp) \
({ \
unsigned int __disp = (disp) & 0xfff; \
_EMIT6(op1 | __disp, op2); \
_EMIT6((op1) | __disp, op2); \
})
#define _EMIT6_DISP_LH(op1, op2, disp) \
({ \
u32 _disp = (u32) disp; \
u32 _disp = (u32) (disp); \
unsigned int __disp_h = _disp & 0xff000; \
unsigned int __disp_l = _disp & 0x00fff; \
_EMIT6(op1 | __disp_l, op2 | __disp_h >> 4); \
_EMIT6((op1) | __disp_l, (op2) | __disp_h >> 4); \
})
#define EMIT6_DISP_LH(op1, op2, b1, b2, b3, disp) \
({ \
_EMIT6_DISP_LH(op1 | reg(b1, b2) << 16 | \
_EMIT6_DISP_LH((op1) | reg(b1, b2) << 16 | \
reg_high(b3) << 8, op2, disp); \
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
@ -226,8 +231,8 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define EMIT6_PCREL_LABEL(op1, op2, b1, b2, label, mask) \
({ \
int rel = (jit->labels[label] - jit->prg) >> 1; \
_EMIT6(op1 | reg(b1, b2) << 16 | (rel & 0xffff), \
op2 | mask << 12); \
_EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), \
(op2) | (mask) << 12); \
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
})
@ -235,66 +240,81 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
#define EMIT6_PCREL_IMM_LABEL(op1, op2, b1, imm, label, mask) \
({ \
int rel = (jit->labels[label] - jit->prg) >> 1; \
_EMIT6(op1 | (reg_high(b1) | mask) << 16 | \
(rel & 0xffff), op2 | (imm & 0xff) << 8); \
_EMIT6((op1) | (reg_high(b1) | (mask)) << 16 | \
(rel & 0xffff), (op2) | ((imm) & 0xff) << 8); \
REG_SET_SEEN(b1); \
BUILD_BUG_ON(((unsigned long) imm) > 0xff); \
BUILD_BUG_ON(((unsigned long) (imm)) > 0xff); \
})
#define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \
({ \
/* Branch instruction needs 6 bytes */ \
int rel = (addrs[i + off + 1] - (addrs[i + 1] - 6)) / 2;\
_EMIT6(op1 | reg(b1, b2) << 16 | (rel & 0xffff), op2 | mask); \
int rel = (addrs[(i) + (off) + 1] - (addrs[(i) + 1] - 6)) / 2;\
_EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), (op2) | (mask));\
REG_SET_SEEN(b1); \
REG_SET_SEEN(b2); \
})
#define EMIT6_PCREL_RILB(op, b, target) \
({ \
int rel = (target - jit->prg) / 2; \
_EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff); \
unsigned int rel = (int)((target) - jit->prg) / 2; \
_EMIT6((op) | reg_high(b) << 16 | rel >> 16, rel & 0xffff);\
REG_SET_SEEN(b); \
})
#define EMIT6_PCREL_RIL(op, target) \
({ \
int rel = (target - jit->prg) / 2; \
_EMIT6(op | rel >> 16, rel & 0xffff); \
unsigned int rel = (int)((target) - jit->prg) / 2; \
_EMIT6((op) | rel >> 16, rel & 0xffff); \
})
#define EMIT6_PCREL_RILC(op, mask, target) \
({ \
EMIT6_PCREL_RIL((op) | (mask) << 20, (target)); \
})
#define _EMIT6_IMM(op, imm) \
({ \
unsigned int __imm = (imm); \
_EMIT6(op | (__imm >> 16), __imm & 0xffff); \
_EMIT6((op) | (__imm >> 16), __imm & 0xffff); \
})
#define EMIT6_IMM(op, b1, imm) \
({ \
_EMIT6_IMM(op | reg_high(b1) << 16, imm); \
_EMIT6_IMM((op) | reg_high(b1) << 16, imm); \
REG_SET_SEEN(b1); \
})
#define _EMIT_CONST_U32(val) \
({ \
unsigned int ret; \
ret = jit->lit32; \
if (jit->prg_buf) \
*(u32 *)(jit->prg_buf + jit->lit32) = (u32)(val);\
jit->lit32 += 4; \
ret; \
})
#define EMIT_CONST_U32(val) \
({ \
unsigned int ret; \
ret = jit->lit - jit->base_ip; \
jit->seen |= SEEN_LITERAL; \
_EMIT_CONST_U32(val) - jit->base_ip; \
})
#define _EMIT_CONST_U64(val) \
({ \
unsigned int ret; \
ret = jit->lit64; \
if (jit->prg_buf) \
*(u32 *) (jit->prg_buf + jit->lit) = (u32) val; \
jit->lit += 4; \
*(u64 *)(jit->prg_buf + jit->lit64) = (u64)(val);\
jit->lit64 += 8; \
ret; \
})
#define EMIT_CONST_U64(val) \
({ \
unsigned int ret; \
ret = jit->lit - jit->base_ip; \
jit->seen |= SEEN_LITERAL; \
if (jit->prg_buf) \
*(u64 *) (jit->prg_buf + jit->lit) = (u64) val; \
jit->lit += 8; \
ret; \
_EMIT_CONST_U64(val) - jit->base_ip; \
})
#define EMIT_ZERO(b1) \
@ -306,6 +326,67 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
} \
})
/*
* Return whether this is the first pass. The first pass is special, since we
* don't know any sizes yet, and thus must be conservative.
*/
static bool is_first_pass(struct bpf_jit *jit)
{
return jit->size == 0;
}
/*
* Return whether this is the code generation pass. The code generation pass is
* special, since we should change as little as possible.
*/
static bool is_codegen_pass(struct bpf_jit *jit)
{
return jit->prg_buf;
}
/*
* Return whether "rel" can be encoded as a short PC-relative offset
*/
static bool is_valid_rel(int rel)
{
return rel >= -65536 && rel <= 65534;
}
/*
* Return whether "off" can be reached using a short PC-relative offset
*/
static bool can_use_rel(struct bpf_jit *jit, int off)
{
return is_valid_rel(off - jit->prg);
}
/*
* Return whether given displacement can be encoded using
* Long-Displacement Facility
*/
static bool is_valid_ldisp(int disp)
{
return disp >= -524288 && disp <= 524287;
}
/*
* Return whether the next 32-bit literal pool entry can be referenced using
* Long-Displacement Facility
*/
static bool can_use_ldisp_for_lit32(struct bpf_jit *jit)
{
return is_valid_ldisp(jit->lit32 - jit->base_ip);
}
/*
* Return whether the next 64-bit literal pool entry can be referenced using
* Long-Displacement Facility
*/
static bool can_use_ldisp_for_lit64(struct bpf_jit *jit)
{
return is_valid_ldisp(jit->lit64 - jit->base_ip);
}
/*
* Fill whole space with illegal instructions
*/
@ -383,9 +464,18 @@ static int get_end(struct bpf_jit *jit, int start)
*/
static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
{
const int last = 15, save_restore_size = 6;
int re = 6, rs;
if (is_first_pass(jit)) {
/*
* We don't know yet which registers are used. Reserve space
* conservatively.
*/
jit->prg += (last - re + 1) * save_restore_size;
return;
}
do {
rs = get_start(jit, re);
if (!rs)
@ -396,7 +486,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
else
restore_regs(jit, rs, re, stack_depth);
re++;
} while (re <= 15);
} while (re <= last);
}
/*
@ -420,21 +510,28 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth)
/* Save registers */
save_restore_regs(jit, REGS_SAVE, stack_depth);
/* Setup literal pool */
if (jit->seen & SEEN_LITERAL) {
/* basr %r13,0 */
EMIT2(0x0d00, REG_L, REG_0);
jit->base_ip = jit->prg;
if (is_first_pass(jit) || (jit->seen & SEEN_LITERAL)) {
if (!is_first_pass(jit) &&
is_valid_ldisp(jit->size - (jit->prg + 2))) {
/* basr %l,0 */
EMIT2(0x0d00, REG_L, REG_0);
jit->base_ip = jit->prg;
} else {
/* larl %l,lit32_start */
EMIT6_PCREL_RILB(0xc0000000, REG_L, jit->lit32_start);
jit->base_ip = jit->lit32_start;
}
}
/* Setup stack and backchain */
if (jit->seen & SEEN_STACK) {
if (jit->seen & SEEN_FUNC)
if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) {
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
/* lgr %w1,%r15 (backchain) */
EMIT4(0xb9040000, REG_W1, REG_15);
/* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */
EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED);
/* aghi %r15,-STK_OFF */
EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth));
if (jit->seen & SEEN_FUNC)
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
/* stg %w1,152(%r15) (backchain) */
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
REG_15, 152);
@ -446,12 +543,6 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth)
*/
static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
{
/* Return 0 */
if (jit->seen & SEEN_RET0) {
jit->ret0_ip = jit->prg;
/* lghi %b0,0 */
EMIT4_IMM(0xa7090000, BPF_REG_0, 0);
}
jit->exit_ip = jit->prg;
/* Load exit code: lgr %r2,%b0 */
EMIT4(0xb9040000, REG_2, BPF_REG_0);
@ -476,7 +567,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
_EMIT2(0x07fe);
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable &&
(jit->seen & SEEN_FUNC)) {
(is_first_pass(jit) || (jit->seen & SEEN_FUNC))) {
jit->r1_thunk_ip = jit->prg;
/* Generate __s390_indirect_jump_r1 thunk */
if (test_facility(35)) {
@ -506,16 +597,14 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
int i, bool extra_pass)
{
struct bpf_insn *insn = &fp->insnsi[i];
int jmp_off, last, insn_count = 1;
u32 dst_reg = insn->dst_reg;
u32 src_reg = insn->src_reg;
int last, insn_count = 1;
u32 *addrs = jit->addrs;
s32 imm = insn->imm;
s16 off = insn->off;
unsigned int mask;
if (dst_reg == BPF_REG_AX || src_reg == BPF_REG_AX)
jit->seen |= SEEN_REG_AX;
switch (insn->code) {
/*
* BPF_MOV
@ -549,9 +638,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
u64 imm64;
imm64 = (u64)(u32) insn[0].imm | ((u64)(u32) insn[1].imm) << 32;
/* lg %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0004, dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm64));
/* lgrl %dst,imm */
EMIT6_PCREL_RILB(0xc4080000, dst_reg, _EMIT_CONST_U64(imm64));
insn_count = 2;
break;
}
@ -680,9 +768,18 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT4_IMM(0xa7080000, REG_W0, 0);
/* lr %w1,%dst */
EMIT2(0x1800, REG_W1, dst_reg);
/* dl %w0,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0, REG_L,
EMIT_CONST_U32(imm));
if (!is_first_pass(jit) && can_use_ldisp_for_lit32(jit)) {
/* dl %w0,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0, REG_L,
EMIT_CONST_U32(imm));
} else {
/* lgfrl %dst,imm */
EMIT6_PCREL_RILB(0xc40c0000, dst_reg,
_EMIT_CONST_U32(imm));
jit->seen |= SEEN_LITERAL;
/* dlr %w0,%dst */
EMIT4(0xb9970000, REG_W0, dst_reg);
}
/* llgfr %dst,%rc */
EMIT4(0xb9160000, dst_reg, rc_reg);
if (insn_is_zext(&insn[1]))
@ -704,9 +801,18 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT4_IMM(0xa7090000, REG_W0, 0);
/* lgr %w1,%dst */
EMIT4(0xb9040000, REG_W1, dst_reg);
/* dlg %w0,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0, REG_L,
EMIT_CONST_U64(imm));
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
/* dlg %w0,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0, REG_L,
EMIT_CONST_U64(imm));
} else {
/* lgrl %dst,imm */
EMIT6_PCREL_RILB(0xc4080000, dst_reg,
_EMIT_CONST_U64(imm));
jit->seen |= SEEN_LITERAL;
/* dlgr %w0,%dst */
EMIT4(0xb9870000, REG_W0, dst_reg);
}
/* lgr %dst,%rc */
EMIT4(0xb9040000, dst_reg, rc_reg);
break;
@ -729,9 +835,19 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT_ZERO(dst_reg);
break;
case BPF_ALU64 | BPF_AND | BPF_K: /* dst = dst & imm */
/* ng %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0080, dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm));
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
/* ng %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0080,
dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm));
} else {
/* lgrl %w0,imm */
EMIT6_PCREL_RILB(0xc4080000, REG_W0,
_EMIT_CONST_U64(imm));
jit->seen |= SEEN_LITERAL;
/* ngr %dst,%w0 */
EMIT4(0xb9800000, dst_reg, REG_W0);
}
break;
/*
* BPF_OR
@ -751,9 +867,19 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT_ZERO(dst_reg);
break;
case BPF_ALU64 | BPF_OR | BPF_K: /* dst = dst | imm */
/* og %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0081, dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm));
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
/* og %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0081,
dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm));
} else {
/* lgrl %w0,imm */
EMIT6_PCREL_RILB(0xc4080000, REG_W0,
_EMIT_CONST_U64(imm));
jit->seen |= SEEN_LITERAL;
/* ogr %dst,%w0 */
EMIT4(0xb9810000, dst_reg, REG_W0);
}
break;
/*
* BPF_XOR
@ -775,9 +901,19 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT_ZERO(dst_reg);
break;
case BPF_ALU64 | BPF_XOR | BPF_K: /* dst = dst ^ imm */
/* xg %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0082, dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm));
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
/* xg %dst,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0082,
dst_reg, REG_0, REG_L,
EMIT_CONST_U64(imm));
} else {
/* lgrl %w0,imm */
EMIT6_PCREL_RILB(0xc4080000, REG_W0,
_EMIT_CONST_U64(imm));
jit->seen |= SEEN_LITERAL;
/* xgr %dst,%w0 */
EMIT4(0xb9820000, dst_reg, REG_W0);
}
break;
/*
* BPF_LSH
@ -1023,9 +1159,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
REG_SET_SEEN(BPF_REG_5);
jit->seen |= SEEN_FUNC;
/* lg %w1,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L,
EMIT_CONST_U64(func));
/* lgrl %w1,func */
EMIT6_PCREL_RILB(0xc4080000, REG_W1, _EMIT_CONST_U64(func));
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable) {
/* brasl %r14,__s390_indirect_jump_r1 */
EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip);
@ -1054,9 +1189,17 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
/* llgf %w1,map.max_entries(%b2) */
EMIT6_DISP_LH(0xe3000000, 0x0016, REG_W1, REG_0, BPF_REG_2,
offsetof(struct bpf_array, map.max_entries));
/* clrj %b3,%w1,0xa,label0: if (u32)%b3 >= (u32)%w1 goto out */
EMIT6_PCREL_LABEL(0xec000000, 0x0077, BPF_REG_3,
REG_W1, 0, 0xa);
/* if ((u32)%b3 >= (u32)%w1) goto out; */
if (!is_first_pass(jit) && can_use_rel(jit, jit->labels[0])) {
/* clrj %b3,%w1,0xa,label0 */
EMIT6_PCREL_LABEL(0xec000000, 0x0077, BPF_REG_3,
REG_W1, 0, 0xa);
} else {
/* clr %b3,%w1 */
EMIT2(0x1500, BPF_REG_3, REG_W1);
/* brcl 0xa,label0 */
EMIT6_PCREL_RILC(0xc0040000, 0xa, jit->labels[0]);
}
/*
* if (tail_call_cnt++ > MAX_TAIL_CALL_CNT)
@ -1071,9 +1214,16 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT4_IMM(0xa7080000, REG_W0, 1);
/* laal %w1,%w0,off(%r15) */
EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W1, REG_W0, REG_15, off);
/* clij %w1,MAX_TAIL_CALL_CNT,0x2,label0 */
EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007f, REG_W1,
MAX_TAIL_CALL_CNT, 0, 0x2);
if (!is_first_pass(jit) && can_use_rel(jit, jit->labels[0])) {
/* clij %w1,MAX_TAIL_CALL_CNT,0x2,label0 */
EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007f, REG_W1,
MAX_TAIL_CALL_CNT, 0, 0x2);
} else {
/* clfi %w1,MAX_TAIL_CALL_CNT */
EMIT6_IMM(0xc20f0000, REG_W1, MAX_TAIL_CALL_CNT);
/* brcl 0x2,label0 */
EMIT6_PCREL_RILC(0xc0040000, 0x2, jit->labels[0]);
}
/*
* prog = array->ptrs[index];
@ -1085,11 +1235,16 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT4(0xb9160000, REG_1, BPF_REG_3);
/* sllg %r1,%r1,3: %r1 *= 8 */
EMIT6_DISP_LH(0xeb000000, 0x000d, REG_1, REG_1, REG_0, 3);
/* lg %r1,prog(%b2,%r1) */
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_1, BPF_REG_2,
/* ltg %r1,prog(%b2,%r1) */
EMIT6_DISP_LH(0xe3000000, 0x0002, REG_1, BPF_REG_2,
REG_1, offsetof(struct bpf_array, ptrs));
/* clgij %r1,0,0x8,label0 */
EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007d, REG_1, 0, 0, 0x8);
if (!is_first_pass(jit) && can_use_rel(jit, jit->labels[0])) {
/* brc 0x8,label0 */
EMIT4_PCREL_RIC(0xa7040000, 0x8, jit->labels[0]);
} else {
/* brcl 0x8,label0 */
EMIT6_PCREL_RILC(0xc0040000, 0x8, jit->labels[0]);
}
/*
* Restore registers before calling function
@ -1110,7 +1265,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
break;
case BPF_JMP | BPF_EXIT: /* return b0 */
last = (i == fp->len - 1) ? 1 : 0;
if (last && !(jit->seen & SEEN_RET0))
if (last)
break;
/* j <exit> */
EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
@ -1246,36 +1401,83 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
goto branch_oc;
branch_ks:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* lgfi %w1,imm (load sign extend imm) */
EMIT6_IMM(0xc0010000, REG_W1, imm);
/* crj or cgrj %dst,%w1,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
dst_reg, REG_W1, i, off, mask);
/* cfi or cgfi %dst,imm */
EMIT6_IMM(is_jmp32 ? 0xc20d0000 : 0xc20c0000,
dst_reg, imm);
if (!is_first_pass(jit) &&
can_use_rel(jit, addrs[i + off + 1])) {
/* brc mask,off */
EMIT4_PCREL_RIC(0xa7040000,
mask >> 12, addrs[i + off + 1]);
} else {
/* brcl mask,off */
EMIT6_PCREL_RILC(0xc0040000,
mask >> 12, addrs[i + off + 1]);
}
break;
branch_ku:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* lgfi %w1,imm (load sign extend imm) */
EMIT6_IMM(0xc0010000, REG_W1, imm);
/* clrj or clgrj %dst,%w1,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
dst_reg, REG_W1, i, off, mask);
/* clfi or clgfi %dst,imm */
EMIT6_IMM(is_jmp32 ? 0xc20f0000 : 0xc20e0000,
dst_reg, imm);
if (!is_first_pass(jit) &&
can_use_rel(jit, addrs[i + off + 1])) {
/* brc mask,off */
EMIT4_PCREL_RIC(0xa7040000,
mask >> 12, addrs[i + off + 1]);
} else {
/* brcl mask,off */
EMIT6_PCREL_RILC(0xc0040000,
mask >> 12, addrs[i + off + 1]);
}
break;
branch_xs:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* crj or cgrj %dst,%src,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
dst_reg, src_reg, i, off, mask);
if (!is_first_pass(jit) &&
can_use_rel(jit, addrs[i + off + 1])) {
/* crj or cgrj %dst,%src,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
dst_reg, src_reg, i, off, mask);
} else {
/* cr or cgr %dst,%src */
if (is_jmp32)
EMIT2(0x1900, dst_reg, src_reg);
else
EMIT4(0xb9200000, dst_reg, src_reg);
/* brcl mask,off */
EMIT6_PCREL_RILC(0xc0040000,
mask >> 12, addrs[i + off + 1]);
}
break;
branch_xu:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* clrj or clgrj %dst,%src,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
dst_reg, src_reg, i, off, mask);
if (!is_first_pass(jit) &&
can_use_rel(jit, addrs[i + off + 1])) {
/* clrj or clgrj %dst,%src,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
dst_reg, src_reg, i, off, mask);
} else {
/* clr or clgr %dst,%src */
if (is_jmp32)
EMIT2(0x1500, dst_reg, src_reg);
else
EMIT4(0xb9210000, dst_reg, src_reg);
/* brcl mask,off */
EMIT6_PCREL_RILC(0xc0040000,
mask >> 12, addrs[i + off + 1]);
}
break;
branch_oc:
/* brc mask,jmp_off (branch instruction needs 4 bytes) */
jmp_off = addrs[i + off + 1] - (addrs[i + 1] - 4);
EMIT4_PCREL(0xa7040000 | mask << 8, jmp_off);
if (!is_first_pass(jit) &&
can_use_rel(jit, addrs[i + off + 1])) {
/* brc mask,off */
EMIT4_PCREL_RIC(0xa7040000,
mask >> 12, addrs[i + off + 1]);
} else {
/* brcl mask,off */
EMIT6_PCREL_RILC(0xc0040000,
mask >> 12, addrs[i + off + 1]);
}
break;
}
default: /* too complex, give up */
@ -1285,29 +1487,68 @@ branch_oc:
return insn_count;
}
/*
* Return whether new i-th instruction address does not violate any invariant
*/
static bool bpf_is_new_addr_sane(struct bpf_jit *jit, int i)
{
/* On the first pass anything goes */
if (is_first_pass(jit))
return true;
/* The codegen pass must not change anything */
if (is_codegen_pass(jit))
return jit->addrs[i] == jit->prg;
/* Passes in between must not increase code size */
return jit->addrs[i] >= jit->prg;
}
/*
* Update the address of i-th instruction
*/
static int bpf_set_addr(struct bpf_jit *jit, int i)
{
if (!bpf_is_new_addr_sane(jit, i))
return -1;
jit->addrs[i] = jit->prg;
return 0;
}
/*
* Compile eBPF program into s390x code
*/
static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp,
bool extra_pass)
{
int i, insn_count;
int i, insn_count, lit32_size, lit64_size;
jit->lit = jit->lit_start;
jit->lit32 = jit->lit32_start;
jit->lit64 = jit->lit64_start;
jit->prg = 0;
bpf_jit_prologue(jit, fp->aux->stack_depth);
if (bpf_set_addr(jit, 0) < 0)
return -1;
for (i = 0; i < fp->len; i += insn_count) {
insn_count = bpf_jit_insn(jit, fp, i, extra_pass);
if (insn_count < 0)
return -1;
/* Next instruction address */
jit->addrs[i + insn_count] = jit->prg;
if (bpf_set_addr(jit, i + insn_count) < 0)
return -1;
}
bpf_jit_epilogue(jit, fp->aux->stack_depth);
jit->lit_start = jit->prg;
jit->size = jit->lit;
lit32_size = jit->lit32 - jit->lit32_start;
lit64_size = jit->lit64 - jit->lit64_start;
jit->lit32_start = jit->prg;
if (lit32_size)
jit->lit32_start = ALIGN(jit->lit32_start, 4);
jit->lit64_start = jit->lit32_start + lit32_size;
if (lit64_size)
jit->lit64_start = ALIGN(jit->lit64_start, 8);
jit->size = jit->lit64_start + lit64_size;
jit->size_prg = jit->prg;
return 0;
}
@ -1369,7 +1610,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
}
memset(&jit, 0, sizeof(jit));
jit.addrs = kcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL);
jit.addrs = kvcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL);
if (jit.addrs == NULL) {
fp = orig_fp;
goto out;
@ -1388,12 +1629,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
/*
* Final pass: Allocate and generate program
*/
if (jit.size >= BPF_SIZE_MAX) {
fp = orig_fp;
goto free_addrs;
}
header = bpf_jit_binary_alloc(jit.size, &jit.prg_buf, 2, jit_fill_hole);
header = bpf_jit_binary_alloc(jit.size, &jit.prg_buf, 8, jit_fill_hole);
if (!header) {
fp = orig_fp;
goto free_addrs;
@ -1422,7 +1658,7 @@ skip_init_ctx:
if (!fp->is_func || extra_pass) {
bpf_prog_fill_jited_linfo(fp, jit.addrs + 1);
free_addrs:
kfree(jit.addrs);
kvfree(jit.addrs);
kfree(jit_data);
fp->aux->jit_data = NULL;
}

View File

@ -26,10 +26,11 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
#define POKE_MAX_OPCODE_SIZE 5
struct text_poke_loc {
void *detour;
void *addr;
size_t len;
const char opcode[POKE_MAX_OPCODE_SIZE];
int len;
s32 rel32;
u8 opcode;
const u8 text[POKE_MAX_OPCODE_SIZE];
};
extern void text_poke_early(void *addr, const void *opcode, size_t len);
@ -51,8 +52,10 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len);
extern void *text_poke(void *addr, const void *opcode, size_t len);
extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
extern int poke_int3_handler(struct pt_regs *regs);
extern void text_poke_bp(void *addr, const void *opcode, size_t len, void *handler);
extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);
extern void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries);
extern void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
const void *opcode, size_t len, const void *emulate);
extern int after_bootmem;
extern __ro_after_init struct mm_struct *poking_mm;
extern __ro_after_init unsigned long poking_addr;
@ -63,8 +66,17 @@ static inline void int3_emulate_jmp(struct pt_regs *regs, unsigned long ip)
regs->ip = ip;
}
#define INT3_INSN_SIZE 1
#define CALL_INSN_SIZE 5
#define INT3_INSN_SIZE 1
#define INT3_INSN_OPCODE 0xCC
#define CALL_INSN_SIZE 5
#define CALL_INSN_OPCODE 0xE8
#define JMP32_INSN_SIZE 5
#define JMP32_INSN_OPCODE 0xE9
#define JMP8_INSN_SIZE 2
#define JMP8_INSN_OPCODE 0xEB
static inline void int3_emulate_push(struct pt_regs *regs, unsigned long val)
{

View File

@ -956,16 +956,15 @@ NOKPROBE_SYMBOL(patch_cmp);
int poke_int3_handler(struct pt_regs *regs)
{
struct text_poke_loc *tp;
unsigned char int3 = 0xcc;
void *ip;
/*
* Having observed our INT3 instruction, we now must observe
* bp_patching.nr_entries.
*
* nr_entries != 0 INT3
* WMB RMB
* write INT3 if (nr_entries)
* nr_entries != 0 INT3
* WMB RMB
* write INT3 if (nr_entries)
*
* Idem for other elements in bp_patching.
*/
@ -978,9 +977,9 @@ int poke_int3_handler(struct pt_regs *regs)
return 0;
/*
* Discount the sizeof(int3). See text_poke_bp_batch().
* Discount the INT3. See text_poke_bp_batch().
*/
ip = (void *) regs->ip - sizeof(int3);
ip = (void *) regs->ip - INT3_INSN_SIZE;
/*
* Skip the binary search if there is a single member in the vector.
@ -997,8 +996,28 @@ int poke_int3_handler(struct pt_regs *regs)
return 0;
}
/* set up the specified breakpoint detour */
regs->ip = (unsigned long) tp->detour;
ip += tp->len;
switch (tp->opcode) {
case INT3_INSN_OPCODE:
/*
* Someone poked an explicit INT3, they'll want to handle it,
* do not consume.
*/
return 0;
case CALL_INSN_OPCODE:
int3_emulate_call(regs, (long)ip + tp->rel32);
break;
case JMP32_INSN_OPCODE:
case JMP8_INSN_OPCODE:
int3_emulate_jmp(regs, (long)ip + tp->rel32);
break;
default:
BUG();
}
return 1;
}
@ -1014,7 +1033,7 @@ NOKPROBE_SYMBOL(poke_int3_handler);
* synchronization using int3 breakpoint.
*
* The way it is done:
* - For each entry in the vector:
* - For each entry in the vector:
* - add a int3 trap to the address that will be patched
* - sync cores
* - For each entry in the vector:
@ -1027,9 +1046,9 @@ NOKPROBE_SYMBOL(poke_int3_handler);
*/
void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
{
int patched_all_but_first = 0;
unsigned char int3 = 0xcc;
unsigned char int3 = INT3_INSN_OPCODE;
unsigned int i;
int do_sync;
lockdep_assert_held(&text_mutex);
@ -1053,16 +1072,16 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
/*
* Second step: update all but the first byte of the patched range.
*/
for (i = 0; i < nr_entries; i++) {
for (do_sync = 0, i = 0; i < nr_entries; i++) {
if (tp[i].len - sizeof(int3) > 0) {
text_poke((char *)tp[i].addr + sizeof(int3),
(const char *)tp[i].opcode + sizeof(int3),
(const char *)tp[i].text + sizeof(int3),
tp[i].len - sizeof(int3));
patched_all_but_first++;
do_sync++;
}
}
if (patched_all_but_first) {
if (do_sync) {
/*
* According to Intel, this core syncing is very likely
* not necessary and we'd be safe even without it. But
@ -1075,10 +1094,17 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
* Third step: replace the first byte (int3) by the first byte of
* replacing opcode.
*/
for (i = 0; i < nr_entries; i++)
text_poke(tp[i].addr, tp[i].opcode, sizeof(int3));
for (do_sync = 0, i = 0; i < nr_entries; i++) {
if (tp[i].text[0] == INT3_INSN_OPCODE)
continue;
text_poke(tp[i].addr, tp[i].text, sizeof(int3));
do_sync++;
}
if (do_sync)
on_each_cpu(do_sync_core, NULL, 1);
on_each_cpu(do_sync_core, NULL, 1);
/*
* sync_core() implies an smp_mb() and orders this store against
* the writing of the new instruction.
@ -1087,6 +1113,60 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
bp_patching.nr_entries = 0;
}
void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
const void *opcode, size_t len, const void *emulate)
{
struct insn insn;
if (!opcode)
opcode = (void *)tp->text;
else
memcpy((void *)tp->text, opcode, len);
if (!emulate)
emulate = opcode;
kernel_insn_init(&insn, emulate, MAX_INSN_SIZE);
insn_get_length(&insn);
BUG_ON(!insn_complete(&insn));
BUG_ON(len != insn.length);
tp->addr = addr;
tp->len = len;
tp->opcode = insn.opcode.bytes[0];
switch (tp->opcode) {
case INT3_INSN_OPCODE:
break;
case CALL_INSN_OPCODE:
case JMP32_INSN_OPCODE:
case JMP8_INSN_OPCODE:
tp->rel32 = insn.immediate.value;
break;
default: /* assume NOP */
switch (len) {
case 2: /* NOP2 -- emulate as JMP8+0 */
BUG_ON(memcmp(emulate, ideal_nops[len], len));
tp->opcode = JMP8_INSN_OPCODE;
tp->rel32 = 0;
break;
case 5: /* NOP5 -- emulate as JMP32+0 */
BUG_ON(memcmp(emulate, ideal_nops[NOP_ATOMIC5], len));
tp->opcode = JMP32_INSN_OPCODE;
tp->rel32 = 0;
break;
default: /* unknown instruction */
BUG();
}
break;
}
}
/**
* text_poke_bp() -- update instructions on live kernel on SMP
* @addr: address to patch
@ -1098,20 +1178,10 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
* dynamically allocated memory. This function should be used when it is
* not possible to allocate memory.
*/
void text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate)
{
struct text_poke_loc tp = {
.detour = handler,
.addr = addr,
.len = len,
};
if (len > POKE_MAX_OPCODE_SIZE) {
WARN_ONCE(1, "len is larger than %d\n", POKE_MAX_OPCODE_SIZE);
return;
}
memcpy((void *)tp.opcode, opcode, len);
struct text_poke_loc tp;
text_poke_loc_init(&tp, addr, opcode, len, emulate);
text_poke_bp_batch(&tp, 1);
}

View File

@ -89,8 +89,7 @@ static void __ref __jump_label_transform(struct jump_entry *entry,
return;
}
text_poke_bp((void *)jump_entry_code(entry), &code, JUMP_LABEL_NOP_SIZE,
(void *)jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
text_poke_bp((void *)jump_entry_code(entry), &code, JUMP_LABEL_NOP_SIZE, NULL);
}
void arch_jump_label_transform(struct jump_entry *entry,
@ -147,11 +146,9 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry,
}
__jump_label_set_jump_code(entry, type,
(union jump_code_union *) &tp->opcode, 0);
(union jump_code_union *)&tp->text, 0);
tp->addr = entry_code;
tp->detour = entry_code + JUMP_LABEL_NOP_SIZE;
tp->len = JUMP_LABEL_NOP_SIZE;
text_poke_loc_init(tp, entry_code, NULL, JUMP_LABEL_NOP_SIZE, NULL);
tp_vec_nr++;

View File

@ -437,8 +437,7 @@ void arch_optimize_kprobes(struct list_head *oplist)
insn_buff[0] = RELATIVEJUMP_OPCODE;
*(s32 *)(&insn_buff[1]) = rel;
text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE,
op->optinsn.insn);
text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE, NULL);
list_del_init(&op->list);
}
@ -448,12 +447,18 @@ void arch_optimize_kprobes(struct list_head *oplist)
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
{
u8 insn_buff[RELATIVEJUMP_SIZE];
u8 emulate_buff[RELATIVEJUMP_SIZE];
/* Set int3 to first byte for kprobes */
insn_buff[0] = BREAKPOINT_INSTRUCTION;
memcpy(insn_buff + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);
emulate_buff[0] = RELATIVEJUMP_OPCODE;
*(s32 *)(&emulate_buff[1]) = (s32)((long)op->optinsn.insn -
((long)op->kp.addr + RELATIVEJUMP_SIZE));
text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE,
op->optinsn.insn);
emulate_buff);
}
/*

View File

@ -13,7 +13,7 @@ CFLAGS_REMOVE_mem_encrypt_identity.o = -pg
endif
obj-y := init.o init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \
pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o
pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o maccess.o
# Make sure __phys_addr has no stackprotector
nostackp := $(call cc-option, -fno-stack-protector)

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/uaccess.h>
#include <linux/kernel.h>
#ifdef CONFIG_X86_64
static __always_inline u64 canonical_address(u64 vaddr, u8 vaddr_bits)
{
return ((s64)vaddr << (64 - vaddr_bits)) >> (64 - vaddr_bits);
}
static __always_inline bool invalid_probe_range(u64 vaddr)
{
/*
* Range covering the highest possible canonical userspace address
* as well as non-canonical address range. For the canonical range
* we also need to include the userspace guard page.
*/
return vaddr < TASK_SIZE_MAX + PAGE_SIZE ||
canonical_address(vaddr, boot_cpu_data.x86_virt_bits) != vaddr;
}
#else
static __always_inline bool invalid_probe_range(u64 vaddr)
{
return vaddr < TASK_SIZE_MAX;
}
#endif
long probe_kernel_read_strict(void *dst, const void *src, size_t size)
{
if (unlikely(invalid_probe_range((unsigned long)src)))
return -EFAULT;
return __probe_kernel_read(dst, src, size);
}
long strncpy_from_unsafe_strict(char *dst, const void *unsafe_addr, long count)
{
if (unlikely(invalid_probe_range((unsigned long)unsafe_addr)))
return -EFAULT;
return __strncpy_from_unsafe(dst, unsafe_addr, count);
}

View File

@ -9,9 +9,11 @@
#include <linux/filter.h>
#include <linux/if_vlan.h>
#include <linux/bpf.h>
#include <linux/memory.h>
#include <asm/extable.h>
#include <asm/set_memory.h>
#include <asm/nospec-branch.h>
#include <asm/text-patching.h>
static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
{
@ -96,6 +98,7 @@ static int bpf_size_to_x86_bytes(int bpf_size)
/* Pick a register outside of BPF range for JIT internal work */
#define AUX_REG (MAX_BPF_JIT_REG + 1)
#define X86_REG_R9 (MAX_BPF_JIT_REG + 2)
/*
* The following table maps BPF registers to x86-64 registers.
@ -104,8 +107,8 @@ static int bpf_size_to_x86_bytes(int bpf_size)
* register in load/store instructions, it always needs an
* extra byte of encoding and is callee saved.
*
* Also x86-64 register R9 is unused. x86-64 register R10 is
* used for blinding (if enabled).
* x86-64 register R9 is not used by BPF programs, but can be used by BPF
* trampoline. x86-64 register R10 is used for blinding (if enabled).
*/
static const int reg2hex[] = {
[BPF_REG_0] = 0, /* RAX */
@ -121,6 +124,20 @@ static const int reg2hex[] = {
[BPF_REG_FP] = 5, /* RBP readonly */
[BPF_REG_AX] = 2, /* R10 temp register */
[AUX_REG] = 3, /* R11 temp register */
[X86_REG_R9] = 1, /* R9 register, 6th function argument */
};
static const int reg2pt_regs[] = {
[BPF_REG_0] = offsetof(struct pt_regs, ax),
[BPF_REG_1] = offsetof(struct pt_regs, di),
[BPF_REG_2] = offsetof(struct pt_regs, si),
[BPF_REG_3] = offsetof(struct pt_regs, dx),
[BPF_REG_4] = offsetof(struct pt_regs, cx),
[BPF_REG_5] = offsetof(struct pt_regs, r8),
[BPF_REG_6] = offsetof(struct pt_regs, bx),
[BPF_REG_7] = offsetof(struct pt_regs, r13),
[BPF_REG_8] = offsetof(struct pt_regs, r14),
[BPF_REG_9] = offsetof(struct pt_regs, r15),
};
/*
@ -135,6 +152,7 @@ static bool is_ereg(u32 reg)
BIT(BPF_REG_7) |
BIT(BPF_REG_8) |
BIT(BPF_REG_9) |
BIT(X86_REG_R9) |
BIT(BPF_REG_AX));
}
@ -186,7 +204,10 @@ struct jit_context {
#define BPF_MAX_INSN_SIZE 128
#define BPF_INSN_SAFETY 64
#define PROLOGUE_SIZE 20
/* Number of bytes emit_patch() needs to generate instructions */
#define X86_PATCH_SIZE 5
#define PROLOGUE_SIZE 25
/*
* Emit x86-64 prologue code for BPF program and check its size.
@ -195,8 +216,13 @@ struct jit_context {
static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
{
u8 *prog = *pprog;
int cnt = 0;
int cnt = X86_PATCH_SIZE;
/* BPF trampoline can be made to work without these nops,
* but let's waste 5 bytes for now and optimize later
*/
memcpy(prog, ideal_nops[NOP_ATOMIC5], cnt);
prog += cnt;
EMIT1(0x55); /* push rbp */
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
/* sub rsp, rounded_stack_depth */
@ -213,6 +239,89 @@ static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
*pprog = prog;
}
static int emit_patch(u8 **pprog, void *func, void *ip, u8 opcode)
{
u8 *prog = *pprog;
int cnt = 0;
s64 offset;
offset = func - (ip + X86_PATCH_SIZE);
if (!is_simm32(offset)) {
pr_err("Target call %p is out of range\n", func);
return -ERANGE;
}
EMIT1_off32(opcode, offset);
*pprog = prog;
return 0;
}
static int emit_call(u8 **pprog, void *func, void *ip)
{
return emit_patch(pprog, func, ip, 0xE8);
}
static int emit_jump(u8 **pprog, void *func, void *ip)
{
return emit_patch(pprog, func, ip, 0xE9);
}
static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
void *old_addr, void *new_addr,
const bool text_live)
{
const u8 *nop_insn = ideal_nops[NOP_ATOMIC5];
u8 old_insn[X86_PATCH_SIZE];
u8 new_insn[X86_PATCH_SIZE];
u8 *prog;
int ret;
memcpy(old_insn, nop_insn, X86_PATCH_SIZE);
if (old_addr) {
prog = old_insn;
ret = t == BPF_MOD_CALL ?
emit_call(&prog, old_addr, ip) :
emit_jump(&prog, old_addr, ip);
if (ret)
return ret;
}
memcpy(new_insn, nop_insn, X86_PATCH_SIZE);
if (new_addr) {
prog = new_insn;
ret = t == BPF_MOD_CALL ?
emit_call(&prog, new_addr, ip) :
emit_jump(&prog, new_addr, ip);
if (ret)
return ret;
}
ret = -EBUSY;
mutex_lock(&text_mutex);
if (memcmp(ip, old_insn, X86_PATCH_SIZE))
goto out;
if (memcmp(ip, new_insn, X86_PATCH_SIZE)) {
if (text_live)
text_poke_bp(ip, new_insn, X86_PATCH_SIZE, NULL);
else
memcpy(ip, new_insn, X86_PATCH_SIZE);
}
ret = 0;
out:
mutex_unlock(&text_mutex);
return ret;
}
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
void *old_addr, void *new_addr)
{
if (!is_kernel_text((long)ip) &&
!is_bpf_text_address((long)ip))
/* BPF poking in modules is not supported */
return -EINVAL;
return __bpf_arch_text_poke(ip, t, old_addr, new_addr, true);
}
/*
* Generate the following code:
*
@ -227,7 +336,7 @@ static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
* goto *(prog->bpf_func + prologue_size);
* out:
*/
static void emit_bpf_tail_call(u8 **pprog)
static void emit_bpf_tail_call_indirect(u8 **pprog)
{
u8 *prog = *pprog;
int label1, label2, label3;
@ -294,6 +403,68 @@ static void emit_bpf_tail_call(u8 **pprog)
*pprog = prog;
}
static void emit_bpf_tail_call_direct(struct bpf_jit_poke_descriptor *poke,
u8 **pprog, int addr, u8 *image)
{
u8 *prog = *pprog;
int cnt = 0;
/*
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
* goto out;
*/
EMIT2_off32(0x8B, 0x85, -36 - MAX_BPF_STACK); /* mov eax, dword ptr [rbp - 548] */
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
EMIT2(X86_JA, 14); /* ja out */
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
EMIT2_off32(0x89, 0x85, -36 - MAX_BPF_STACK); /* mov dword ptr [rbp -548], eax */
poke->ip = image + (addr - X86_PATCH_SIZE);
poke->adj_off = PROLOGUE_SIZE;
memcpy(prog, ideal_nops[NOP_ATOMIC5], X86_PATCH_SIZE);
prog += X86_PATCH_SIZE;
/* out: */
*pprog = prog;
}
static void bpf_tail_call_direct_fixup(struct bpf_prog *prog)
{
struct bpf_jit_poke_descriptor *poke;
struct bpf_array *array;
struct bpf_prog *target;
int i, ret;
for (i = 0; i < prog->aux->size_poke_tab; i++) {
poke = &prog->aux->poke_tab[i];
WARN_ON_ONCE(READ_ONCE(poke->ip_stable));
if (poke->reason != BPF_POKE_REASON_TAIL_CALL)
continue;
array = container_of(poke->tail_call.map, struct bpf_array, map);
mutex_lock(&array->aux->poke_mutex);
target = array->ptrs[poke->tail_call.key];
if (target) {
/* Plain memcpy is used when image is not live yet
* and still not locked as read-only. Once poke
* location is active (poke->ip_stable), any parallel
* bpf_arch_text_poke() might occur still on the
* read-write image until we finally locked it as
* read-only. Both modifications on the given image
* are under text_mutex to avoid interference.
*/
ret = __bpf_arch_text_poke(poke->ip, BPF_MOD_JUMP, NULL,
(u8 *)target->bpf_func +
poke->adj_off, false);
BUG_ON(ret < 0);
}
WRITE_ONCE(poke->ip_stable, true);
mutex_unlock(&array->aux->poke_mutex);
}
}
static void emit_mov_imm32(u8 **pprog, bool sign_propagate,
u32 dst_reg, const u32 imm32)
{
@ -377,6 +548,96 @@ static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)
*pprog = prog;
}
/* LDX: dst_reg = *(u8*)(src_reg + off) */
static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
{
u8 *prog = *pprog;
int cnt = 0;
switch (size) {
case BPF_B:
/* Emit 'movzx rax, byte ptr [rax + off]' */
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6);
break;
case BPF_H:
/* Emit 'movzx rax, word ptr [rax + off]' */
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7);
break;
case BPF_W:
/* Emit 'mov eax, dword ptr [rax+0x14]' */
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B);
else
EMIT1(0x8B);
break;
case BPF_DW:
/* Emit 'mov rax, qword ptr [rax+0x14]' */
EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
break;
}
/*
* 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(off))
EMIT2(add_2reg(0x40, src_reg, dst_reg), off);
else
EMIT1_off32(add_2reg(0x80, src_reg, dst_reg), off);
*pprog = prog;
}
/* STX: *(u8*)(dst_reg + off) = src_reg */
static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
{
u8 *prog = *pprog;
int cnt = 0;
switch (size) {
case BPF_B:
/* Emit 'mov byte ptr [rax + off], al' */
if (is_ereg(dst_reg) || is_ereg(src_reg) ||
/* We have to add extra byte for x86 SIL, DIL regs */
src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
else
EMIT1(0x88);
break;
case BPF_H:
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89);
else
EMIT2(0x66, 0x89);
break;
case BPF_W:
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89);
else
EMIT1(0x89);
break;
case BPF_DW:
EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
break;
}
if (is_imm8(off))
EMIT2(add_2reg(0x40, dst_reg, src_reg), off);
else
EMIT1_off32(add_2reg(0x80, dst_reg, src_reg), off);
*pprog = prog;
}
static bool ex_handler_bpf(const struct exception_table_entry *x,
struct pt_regs *regs, int trapnr,
unsigned long error_code, unsigned long fault_addr)
{
u32 reg = x->fixup >> 8;
/* jump over faulting load and clear dest register */
*(unsigned long *)((void *)regs + reg) = 0;
regs->ip += x->fixup & 0xff;
return true;
}
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
int oldproglen, struct jit_context *ctx)
{
@ -384,7 +645,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
int insn_cnt = bpf_prog->len;
bool seen_exit = false;
u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY];
int i, cnt = 0;
int i, cnt = 0, excnt = 0;
int proglen = 0;
u8 *prog = temp;
@ -747,64 +1008,64 @@ st: if (is_imm8(insn->off))
/* STX: *(u8*)(dst_reg + off) = src_reg */
case BPF_STX | BPF_MEM | BPF_B:
/* Emit 'mov byte ptr [rax + off], al' */
if (is_ereg(dst_reg) || is_ereg(src_reg) ||
/* We have to add extra byte for x86 SIL, DIL regs */
src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
else
EMIT1(0x88);
goto stx;
case BPF_STX | BPF_MEM | BPF_H:
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89);
else
EMIT2(0x66, 0x89);
goto stx;
case BPF_STX | BPF_MEM | BPF_W:
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89);
else
EMIT1(0x89);
goto stx;
case BPF_STX | BPF_MEM | BPF_DW:
EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
stx: if (is_imm8(insn->off))
EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
else
EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
insn->off);
emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
break;
/* LDX: dst_reg = *(u8*)(src_reg + off) */
case BPF_LDX | BPF_MEM | BPF_B:
/* Emit 'movzx rax, byte ptr [rax + off]' */
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6);
goto ldx;
case BPF_LDX | BPF_PROBE_MEM | BPF_B:
case BPF_LDX | BPF_MEM | BPF_H:
/* Emit 'movzx rax, word ptr [rax + off]' */
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7);
goto ldx;
case BPF_LDX | BPF_PROBE_MEM | BPF_H:
case BPF_LDX | BPF_MEM | BPF_W:
/* Emit 'mov eax, dword ptr [rax+0x14]' */
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B);
else
EMIT1(0x8B);
goto ldx;
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
case BPF_LDX | BPF_MEM | BPF_DW:
/* Emit 'mov rax, qword ptr [rax+0x14]' */
EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
ldx: /*
* If insn->off == 0 we can save one extra byte, but
* special case of x86 R13 which always needs an offset
* is not worth the hassle
*/
if (is_imm8(insn->off))
EMIT2(add_2reg(0x40, src_reg, dst_reg), insn->off);
else
EMIT1_off32(add_2reg(0x80, src_reg, dst_reg),
insn->off);
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {
struct exception_table_entry *ex;
u8 *_insn = image + proglen;
s64 delta;
if (!bpf_prog->aux->extable)
break;
if (excnt >= bpf_prog->aux->num_exentries) {
pr_err("ex gen bug\n");
return -EFAULT;
}
ex = &bpf_prog->aux->extable[excnt++];
delta = _insn - (u8 *)&ex->insn;
if (!is_simm32(delta)) {
pr_err("extable->insn doesn't fit into 32-bit\n");
return -EFAULT;
}
ex->insn = delta;
delta = (u8 *)ex_handler_bpf - (u8 *)&ex->handler;
if (!is_simm32(delta)) {
pr_err("extable->handler doesn't fit into 32-bit\n");
return -EFAULT;
}
ex->handler = delta;
if (dst_reg > BPF_REG_9) {
pr_err("verifier error\n");
return -EFAULT;
}
/*
* Compute size of x86 insn and its target dest x86 register.
* ex_handler_bpf() will use lower 8 bits to adjust
* pt_regs->ip to jump over this x86 instruction
* and upper bits to figure out which pt_regs to zero out.
* End result: x86 insn "mov rbx, qword ptr [rax+0x14]"
* of 4 bytes will be ignored and rbx will be zero inited.
*/
ex->fixup = (prog - temp) | (reg2pt_regs[dst_reg] << 8);
}
break;
/* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */
@ -827,17 +1088,16 @@ xadd: if (is_imm8(insn->off))
/* call */
case BPF_JMP | BPF_CALL:
func = (u8 *) __bpf_call_base + imm32;
jmp_offset = func - (image + addrs[i]);
if (!imm32 || !is_simm32(jmp_offset)) {
pr_err("unsupported BPF func %d addr %p image %p\n",
imm32, func, image);
if (!imm32 || emit_call(&prog, func, image + addrs[i - 1]))
return -EINVAL;
}
EMIT1_off32(0xE8, jmp_offset);
break;
case BPF_JMP | BPF_TAIL_CALL:
emit_bpf_tail_call(&prog);
if (imm32)
emit_bpf_tail_call_direct(&bpf_prog->aux->poke_tab[imm32 - 1],
&prog, addrs[i], image);
else
emit_bpf_tail_call_indirect(&prog);
break;
/* cond jump */
@ -909,6 +1169,16 @@ xadd: if (is_imm8(insn->off))
case BPF_JMP32 | BPF_JSLT | BPF_K:
case BPF_JMP32 | BPF_JSGE | BPF_K:
case BPF_JMP32 | BPF_JSLE | BPF_K:
/* test dst_reg, dst_reg to save one extra byte */
if (imm32 == 0) {
if (BPF_CLASS(insn->code) == BPF_JMP)
EMIT1(add_2mod(0x48, dst_reg, dst_reg));
else if (is_ereg(dst_reg))
EMIT1(add_2mod(0x40, dst_reg, dst_reg));
EMIT2(0x85, add_2reg(0xC0, dst_reg, dst_reg));
goto emit_cond_jmp;
}
/* cmp dst_reg, imm8/32 */
if (BPF_CLASS(insn->code) == BPF_JMP)
EMIT1(add_1mod(0x48, dst_reg));
@ -1048,9 +1318,218 @@ emit_jmp:
addrs[i] = proglen;
prog = temp;
}
if (image && excnt != bpf_prog->aux->num_exentries) {
pr_err("extable is not populated\n");
return -EFAULT;
}
return proglen;
}
static void save_regs(struct btf_func_model *m, u8 **prog, int nr_args,
int stack_size)
{
int i;
/* Store function arguments to stack.
* For a function that accepts two pointers the sequence will be:
* mov QWORD PTR [rbp-0x10],rdi
* mov QWORD PTR [rbp-0x8],rsi
*/
for (i = 0; i < min(nr_args, 6); i++)
emit_stx(prog, bytes_to_bpf_size(m->arg_size[i]),
BPF_REG_FP,
i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
-(stack_size - i * 8));
}
static void restore_regs(struct btf_func_model *m, u8 **prog, int nr_args,
int stack_size)
{
int i;
/* Restore function arguments from stack.
* For a function that accepts two pointers the sequence will be:
* EMIT4(0x48, 0x8B, 0x7D, 0xF0); mov rdi,QWORD PTR [rbp-0x10]
* EMIT4(0x48, 0x8B, 0x75, 0xF8); mov rsi,QWORD PTR [rbp-0x8]
*/
for (i = 0; i < min(nr_args, 6); i++)
emit_ldx(prog, bytes_to_bpf_size(m->arg_size[i]),
i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
BPF_REG_FP,
-(stack_size - i * 8));
}
static int invoke_bpf(struct btf_func_model *m, u8 **pprog,
struct bpf_prog **progs, int prog_cnt, int stack_size)
{
u8 *prog = *pprog;
int cnt = 0, i;
for (i = 0; i < prog_cnt; i++) {
if (emit_call(&prog, __bpf_prog_enter, prog))
return -EINVAL;
/* remember prog start time returned by __bpf_prog_enter */
emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0);
/* arg1: lea rdi, [rbp - stack_size] */
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
/* arg2: progs[i]->insnsi for interpreter */
if (!progs[i]->jited)
emit_mov_imm64(&prog, BPF_REG_2,
(long) progs[i]->insnsi >> 32,
(u32) (long) progs[i]->insnsi);
/* call JITed bpf program or interpreter */
if (emit_call(&prog, progs[i]->bpf_func, prog))
return -EINVAL;
/* arg1: mov rdi, progs[i] */
emit_mov_imm64(&prog, BPF_REG_1, (long) progs[i] >> 32,
(u32) (long) progs[i]);
/* arg2: mov rsi, rbx <- start time in nsec */
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
if (emit_call(&prog, __bpf_prog_exit, prog))
return -EINVAL;
}
*pprog = prog;
return 0;
}
/* Example:
* __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
* its 'struct btf_func_model' will be nr_args=2
* The assembly code when eth_type_trans is executing after trampoline:
*
* push rbp
* mov rbp, rsp
* sub rsp, 16 // space for skb and dev
* push rbx // temp regs to pass start time
* mov qword ptr [rbp - 16], rdi // save skb pointer to stack
* mov qword ptr [rbp - 8], rsi // save dev pointer to stack
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
* mov rbx, rax // remember start time in bpf stats are enabled
* lea rdi, [rbp - 16] // R1==ctx of bpf prog
* call addr_of_jited_FENTRY_prog
* movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off
* mov rsi, rbx // prog start time
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math
* mov rdi, qword ptr [rbp - 16] // restore skb pointer from stack
* mov rsi, qword ptr [rbp - 8] // restore dev pointer from stack
* pop rbx
* leave
* ret
*
* eth_type_trans has 5 byte nop at the beginning. These 5 bytes will be
* replaced with 'call generated_bpf_trampoline'. When it returns
* eth_type_trans will continue executing with original skb and dev pointers.
*
* The assembly code when eth_type_trans is called from trampoline:
*
* push rbp
* mov rbp, rsp
* sub rsp, 24 // space for skb, dev, return value
* push rbx // temp regs to pass start time
* mov qword ptr [rbp - 24], rdi // save skb pointer to stack
* mov qword ptr [rbp - 16], rsi // save dev pointer to stack
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
* mov rbx, rax // remember start time if bpf stats are enabled
* lea rdi, [rbp - 24] // R1==ctx of bpf prog
* call addr_of_jited_FENTRY_prog // bpf prog can access skb and dev
* movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off
* mov rsi, rbx // prog start time
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math
* mov rdi, qword ptr [rbp - 24] // restore skb pointer from stack
* mov rsi, qword ptr [rbp - 16] // restore dev pointer from stack
* call eth_type_trans+5 // execute body of eth_type_trans
* mov qword ptr [rbp - 8], rax // save return value
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
* mov rbx, rax // remember start time in bpf stats are enabled
* lea rdi, [rbp - 24] // R1==ctx of bpf prog
* call addr_of_jited_FEXIT_prog // bpf prog can access skb, dev, return value
* movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off
* mov rsi, rbx // prog start time
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math
* mov rax, qword ptr [rbp - 8] // restore eth_type_trans's return value
* pop rbx
* leave
* add rsp, 8 // skip eth_type_trans's frame
* ret // return to its caller
*/
int arch_prepare_bpf_trampoline(void *image, struct btf_func_model *m, u32 flags,
struct bpf_prog **fentry_progs, int fentry_cnt,
struct bpf_prog **fexit_progs, int fexit_cnt,
void *orig_call)
{
int cnt = 0, nr_args = m->nr_args;
int stack_size = nr_args * 8;
u8 *prog;
/* x86-64 supports up to 6 arguments. 7+ can be added in the future */
if (nr_args > 6)
return -ENOTSUPP;
if ((flags & BPF_TRAMP_F_RESTORE_REGS) &&
(flags & BPF_TRAMP_F_SKIP_FRAME))
return -EINVAL;
if (flags & BPF_TRAMP_F_CALL_ORIG)
stack_size += 8; /* room for return value of orig_call */
if (flags & BPF_TRAMP_F_SKIP_FRAME)
/* skip patched call instruction and point orig_call to actual
* body of the kernel function.
*/
orig_call += X86_PATCH_SIZE;
prog = image;
EMIT1(0x55); /* push rbp */
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */
EMIT1(0x53); /* push rbx */
save_regs(m, &prog, nr_args, stack_size);
if (fentry_cnt)
if (invoke_bpf(m, &prog, fentry_progs, fentry_cnt, stack_size))
return -EINVAL;
if (flags & BPF_TRAMP_F_CALL_ORIG) {
if (fentry_cnt)
restore_regs(m, &prog, nr_args, stack_size);
/* call original function */
if (emit_call(&prog, orig_call, prog))
return -EINVAL;
/* remember return value in a stack for bpf prog to access */
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
}
if (fexit_cnt)
if (invoke_bpf(m, &prog, fexit_progs, fexit_cnt, stack_size))
return -EINVAL;
if (flags & BPF_TRAMP_F_RESTORE_REGS)
restore_regs(m, &prog, nr_args, stack_size);
if (flags & BPF_TRAMP_F_CALL_ORIG)
/* restore original return value back into RAX */
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
EMIT1(0x5B); /* pop rbx */
EMIT1(0xC9); /* leave */
if (flags & BPF_TRAMP_F_SKIP_FRAME)
/* skip our return address and return to parent */
EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */
EMIT1(0xC3); /* ret */
/* One half of the page has active running trampoline.
* Another half is an area for next trampoline.
* Make sure the trampoline generation logic doesn't overflow.
*/
if (WARN_ON_ONCE(prog - (u8 *)image > PAGE_SIZE / 2 - BPF_INSN_SAFETY))
return -EFAULT;
return 0;
}
struct x64_jit_data {
struct bpf_binary_header *header;
int *addrs;
@ -1148,12 +1627,24 @@ out_image:
break;
}
if (proglen == oldproglen) {
header = bpf_jit_binary_alloc(proglen, &image,
1, jit_fill_hole);
/*
* The number of entries in extable is the number of BPF_LDX
* insns that access kernel memory via "pointer to BTF type".
* The verifier changed their opcode from LDX|MEM|size
* to LDX|PROBE_MEM|size to make JITing easier.
*/
u32 align = __alignof__(struct exception_table_entry);
u32 extable_size = prog->aux->num_exentries *
sizeof(struct exception_table_entry);
/* allocate module memory for x86 insns and extable */
header = bpf_jit_binary_alloc(roundup(proglen, align) + extable_size,
&image, align, jit_fill_hole);
if (!header) {
prog = orig_prog;
goto out_addrs;
}
prog->aux->extable = (void *) image + roundup(proglen, align);
}
oldproglen = proglen;
cond_resched();
@ -1164,6 +1655,7 @@ out_image:
if (image) {
if (!prog->is_func || extra_pass) {
bpf_tail_call_direct_fixup(prog);
bpf_jit_binary_lock_ro(header);
} else {
jit_data->addrs = addrs;

View File

@ -1070,7 +1070,7 @@ static int fs_open(struct atm_vcc *atm_vcc)
RC_FLAGS_BFPS_BFP * bfp |
RC_FLAGS_RXBM_PSB, 0, 0);
break;
};
}
if (IS_FS50 (dev)) {
submit_command (dev, &dev->hp_txq,
QE_CMD_REG_WR | QE_CMD_IMM_INQ,

View File

@ -233,8 +233,10 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
switch (bus->chipinfo.id) {
case BCMA_CHIP_ID_BCM4313:
/* enable 12 mA drive strenth for 4313 and set chipControl
register bit 1 */
/*
* enable 12 mA drive strenth for 4313 and set chipControl
* register bit 1
*/
bcma_chipco_chipctl_maskset(cc, 0,
~BCMA_CCTRL_4313_12MA_LED_DRIVE,
BCMA_CCTRL_4313_12MA_LED_DRIVE);
@ -246,8 +248,10 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
break;
case BCMA_CHIP_ID_BCM43224:
case BCMA_CHIP_ID_BCM43421:
/* enable 12 mA drive strenth for 43224 and set chipControl
register bit 15 */
/*
* enable 12 mA drive strenth for 43224 and set chipControl
* register bit 15
*/
if (bus->chipinfo.rev == 0) {
bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL,
~BCMA_CCTRL_43224_GPIO_TOGGLE,
@ -500,8 +504,10 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
case BCMA_CHIP_ID_BCM53572:
/* 5357[ab]0, 43236[ab]0, and 6362b0 */
/* BCM5357 needs to touch PLL1_PLLCTL[02],
so offset PLL0_PLLCTL[02] by 6 */
/*
* BCM5357 needs to touch PLL1_PLLCTL[02],
* so offset PLL0_PLLCTL[02] by 6
*/
phypll_offset = (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM4749 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0;
@ -619,8 +625,10 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
case BCMA_CHIP_ID_BCM43228:
case BCMA_CHIP_ID_BCM43428:
/* LCNXN */
/* PLL Settings for spur avoidance on/off mode,
no on2 support for 43228A0 */
/*
* PLL Settings for spur avoidance on/off mode,
* no on2 support for 43228A0
*/
if (spuravoid == 1) {
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
0x01100014);

View File

@ -380,17 +380,6 @@ config BT_ATH3K
Say Y here to compile support for "Atheros firmware download driver"
into the kernel or say M to compile it as module (ath3k).
config BT_WILINK
tristate "Texas Instruments WiLink7 driver"
depends on TI_ST
help
This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS
combo devices. This makes use of shared transport line discipline
core driver to communicate with the BT core of the combo chip.
Say Y here to compile support for Texas Instrument's WiLink7 driver
into the kernel or say M to compile it as module (btwilink).
config BT_MTKSDIO
tristate "MediaTek HCI SDIO driver"
depends on MMC

View File

@ -19,7 +19,6 @@ obj-$(CONFIG_BT_INTEL) += btintel.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_MTKSDIO) += btmtksdio.o
obj-$(CONFIG_BT_MTKUART) += btmtkuart.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o

View File

@ -23,6 +23,7 @@
#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
#define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}})
#define BDADDR_BCM4345C5 (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0xc5, 0x45, 0x43}})
#define BDADDR_BCM43341B (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0x1b, 0x34, 0x43}})
@ -74,6 +75,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
!bacmp(&bda->bdaddr, BDADDR_BCM2076B1) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4324B3) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4330B1) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4334B0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
@ -326,6 +328,7 @@ struct bcm_subver_table {
static const struct bcm_subver_table bcm_uart_subver_table[] = {
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
{ 0x410d, "BCM4334B0" }, /* 002.001.013 */
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
{ 0x4204, "BCM2076B1" }, /* 002.002.004 */
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
@ -339,6 +342,7 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = {
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
{ 0x4217, "BCM4329B1" }, /* 002.002.023 */
{ 0x6106, "BCM4359C0" }, /* 003.001.006 */
{ 0x4106, "BCM4335A0" }, /* 002.001.006 */
{ }
};
@ -440,6 +444,12 @@ int btbcm_finalize(struct hci_dev *hdev)
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
/* Some devices ship with the controller default address.
* Allow the bootloader to set a valid address through the
* device tree.
*/
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_finalize);

View File

@ -709,6 +709,51 @@ done:
}
EXPORT_SYMBOL_GPL(btintel_download_firmware);
void btintel_reset_to_bootloader(struct hci_dev *hdev)
{
struct intel_reset params;
struct sk_buff *skb;
/* Send Intel Reset command. This will result in
* re-enumeration of BT controller.
*
* Intel Reset parameter description:
* reset_type : 0x00 (Soft reset),
* 0x01 (Hard reset)
* patch_enable : 0x00 (Do not enable),
* 0x01 (Enable)
* ddc_reload : 0x00 (Do not reload),
* 0x01 (Reload)
* boot_option: 0x00 (Current image),
* 0x01 (Specified boot address)
* boot_param: Boot address
*
*/
params.reset_type = 0x01;
params.patch_enable = 0x01;
params.ddc_reload = 0x01;
params.boot_option = 0x00;
params.boot_param = cpu_to_le32(0x00000000);
skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params),
&params, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "FW download error recovery failed (%ld)",
PTR_ERR(skb));
return;
}
bt_dev_info(hdev, "Intel reset sent to retry FW download");
kfree_skb(skb);
/* Current Intel BT controllers(ThP/JfP) hold the USB reset
* lines for 2ms when it receives Intel Reset in bootloader mode.
* Whereas, the upcoming Intel BT controllers will hold USB reset
* for 150ms. To keep the delay generic, 150ms is chosen here.
*/
msleep(150);
}
EXPORT_SYMBOL_GPL(btintel_reset_to_bootloader);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
MODULE_VERSION(VERSION);

View File

@ -87,6 +87,7 @@ int btintel_read_boot_params(struct hci_dev *hdev,
struct intel_boot_params *params);
int btintel_download_firmware(struct hci_dev *dev, const struct firmware *fw,
u32 *boot_param);
void btintel_reset_to_bootloader(struct hci_dev *hdev);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@ -181,4 +182,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
{
return -EOPNOTSUPP;
}
static inline void btintel_reset_to_bootloader(struct hci_dev *hdev)
{
}
#endif

View File

@ -57,6 +57,7 @@ static const struct sdio_device_id btmtksdio_table[] = {
.driver_data = (kernel_ulong_t)&mt7668_data },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(sdio, btmtksdio_table);
#define MTK_REG_CHLPCR 0x4 /* W1S */
#define C_INT_EN_SET BIT(0)

View File

@ -14,19 +14,33 @@
#define VERSION "0.1"
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
enum qca_btsoc_type soc_type)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct rome_version *ver;
struct qca_btsoc_version *ver;
char cmd;
int err = 0;
u8 event_type = HCI_EV_VENDOR;
u8 rlen = sizeof(*edl) + sizeof(*ver);
u8 rtype = EDL_APP_VER_RES_EVT;
bt_dev_dbg(hdev, "QCA Version Request");
/* Unlike other SoC's sending version command response as payload to
* VSE event. WCN3991 sends version command response as a payload to
* command complete event.
*/
if (soc_type == QCA_WCN3991) {
event_type = 0;
rlen += 1;
rtype = EDL_PATCH_VER_REQ_CMD;
}
cmd = EDL_PATCH_VER_REQ_CMD;
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
&cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
&cmd, event_type, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "Reading QCA version information failed (%d)",
@ -34,7 +48,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
return err;
}
if (skb->len != sizeof(*edl) + sizeof(*ver)) {
if (skb->len != rlen) {
bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
err = -EILSEQ;
goto out;
@ -48,18 +62,21 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
}
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_APP_VER_RES_EVT) {
edl->rtype != rtype) {
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
edl->rtype);
err = -EIO;
goto out;
}
ver = (struct rome_version *)(edl->data);
if (soc_type == QCA_WCN3991)
memmove(&edl->data, &edl->data[1], sizeof(*ver));
ver = (struct qca_btsoc_version *)(edl->data);
BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id));
BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver));
BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver));
BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rom_ver));
BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id));
/* QCA chipset version can be decided by patch and SoC
@ -67,7 +84,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
* and lower 2 bytes from patch will be used.
*/
*soc_version = (le32_to_cpu(ver->soc_id) << 16) |
(le16_to_cpu(ver->rome_ver) & 0x0000ffff);
(le16_to_cpu(ver->rom_ver) & 0x0000ffff);
if (*soc_version == 0)
err = -EILSEQ;
@ -121,7 +138,7 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
static void qca_tlv_check_data(struct rome_config *config,
static void qca_tlv_check_data(struct qca_fw_config *config,
const struct firmware *fw)
{
const u8 *data;
@ -140,8 +157,8 @@ static void qca_tlv_check_data(struct rome_config *config,
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
BT_DBG("Length\t\t : %d bytes", length);
config->dnld_mode = ROME_SKIP_EVT_NONE;
config->dnld_type = ROME_SKIP_EVT_NONE;
config->dnld_mode = QCA_SKIP_EVT_NONE;
config->dnld_type = QCA_SKIP_EVT_NONE;
switch (config->type) {
case TLV_TYPE_PATCH:
@ -223,31 +240,45 @@ static void qca_tlv_check_data(struct rome_config *config,
}
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
const u8 *data, enum rome_tlv_dnld_mode mode)
const u8 *data, enum qca_tlv_dnld_mode mode,
enum qca_btsoc_type soc_type)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct tlv_seg_resp *tlv_resp;
u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
int err = 0;
u8 event_type = HCI_EV_VENDOR;
u8 rlen = (sizeof(*edl) + sizeof(*tlv_resp));
u8 rtype = EDL_TVL_DNLD_RES_EVT;
cmd[0] = EDL_PATCH_TLV_REQ_CMD;
cmd[1] = seg_size;
memcpy(cmd + 2, data, seg_size);
if (mode == ROME_SKIP_EVT_VSE_CC || mode == ROME_SKIP_EVT_VSE)
if (mode == QCA_SKIP_EVT_VSE_CC || mode == QCA_SKIP_EVT_VSE)
return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
cmd);
/* Unlike other SoC's sending version command response as payload to
* VSE event. WCN3991 sends version command response as a payload to
* command complete event.
*/
if (soc_type == QCA_WCN3991) {
event_type = 0;
rlen = sizeof(*edl);
rtype = EDL_PATCH_TLV_REQ_CMD;
}
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
event_type, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);
return err;
}
if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) {
if (skb->len != rlen) {
bt_dev_err(hdev, "QCA TLV response size mismatch");
err = -EILSEQ;
goto out;
@ -260,13 +291,19 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
goto out;
}
tlv_resp = (struct tlv_seg_resp *)(edl->data);
if (edl->cresp != EDL_CMD_REQ_RES_EVT || edl->rtype != rtype) {
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x",
edl->cresp, edl->rtype);
err = -EIO;
}
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_TVL_DNLD_RES_EVT || tlv_resp->result != 0x00) {
if (soc_type == QCA_WCN3991)
goto out;
tlv_resp = (struct tlv_seg_resp *)(edl->data);
if (tlv_resp->result) {
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x (0x%x)",
edl->cresp, edl->rtype, tlv_resp->result);
err = -EIO;
}
out:
@ -301,7 +338,8 @@ static int qca_inject_cmd_complete_event(struct hci_dev *hdev)
}
static int qca_download_firmware(struct hci_dev *hdev,
struct rome_config *config)
struct qca_fw_config *config,
enum qca_btsoc_type soc_type)
{
const struct firmware *fw;
const u8 *segment;
@ -328,10 +366,10 @@ static int qca_download_firmware(struct hci_dev *hdev,
remain -= segsize;
/* The last segment is always acked regardless download mode */
if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
config->dnld_mode = ROME_SKIP_EVT_NONE;
config->dnld_mode = QCA_SKIP_EVT_NONE;
ret = qca_tlv_send_segment(hdev, segsize, segment,
config->dnld_mode);
config->dnld_mode, soc_type);
if (ret)
goto out;
@ -344,8 +382,8 @@ static int qca_download_firmware(struct hci_dev *hdev,
* decrease the BT in initialization time. Here we will inject a command
* complete event to avoid a command timeout error message.
*/
if (config->dnld_type == ROME_SKIP_EVT_VSE_CC ||
config->dnld_type == ROME_SKIP_EVT_VSE)
if (config->dnld_type == QCA_SKIP_EVT_VSE_CC ||
config->dnld_type == QCA_SKIP_EVT_VSE)
ret = qca_inject_cmd_complete_event(hdev);
out:
@ -382,7 +420,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver,
const char *firmware_name)
{
struct rome_config config;
struct qca_fw_config config;
int err;
u8 rom_ver = 0;
@ -405,7 +443,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
"qca/rampatch_%08x.bin", soc_ver);
}
err = qca_download_firmware(hdev, &config);
err = qca_download_firmware(hdev, &config, soc_type);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to download patch (%d)", err);
return err;
@ -426,7 +464,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
err = qca_download_firmware(hdev, &config);
err = qca_download_firmware(hdev, &config, soc_type);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
return err;

View File

@ -56,24 +56,24 @@ enum qca_baudrate {
QCA_BAUDRATE_RESERVED
};
enum rome_tlv_dnld_mode {
ROME_SKIP_EVT_NONE,
ROME_SKIP_EVT_VSE,
ROME_SKIP_EVT_CC,
ROME_SKIP_EVT_VSE_CC
enum qca_tlv_dnld_mode {
QCA_SKIP_EVT_NONE,
QCA_SKIP_EVT_VSE,
QCA_SKIP_EVT_CC,
QCA_SKIP_EVT_VSE_CC
};
enum rome_tlv_type {
enum qca_tlv_type {
TLV_TYPE_PATCH = 1,
TLV_TYPE_NVM
};
struct rome_config {
struct qca_fw_config {
u8 type;
char fwname[64];
uint8_t user_baud_rate;
enum rome_tlv_dnld_mode dnld_mode;
enum rome_tlv_dnld_mode dnld_type;
enum qca_tlv_dnld_mode dnld_mode;
enum qca_tlv_dnld_mode dnld_type;
};
struct edl_event_hdr {
@ -82,10 +82,10 @@ struct edl_event_hdr {
__u8 data[0];
} __packed;
struct rome_version {
struct qca_btsoc_version {
__le32 product_id;
__le16 patch_ver;
__le16 rome_ver;
__le16 rom_ver;
__le32 soc_id;
} __packed;
@ -125,6 +125,7 @@ enum qca_btsoc_type {
QCA_AR3002,
QCA_ROME,
QCA_WCN3990,
QCA_WCN3991,
QCA_WCN3998,
};
@ -134,12 +135,14 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver,
const char *firmware_name);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
enum qca_btsoc_type);
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
{
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3998;
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3991 ||
soc_type == QCA_WCN3998;
}
#else
@ -155,7 +158,8 @@ static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return -EOPNOTSUPP;
}
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
enum qca_btsoc_type)
{
return -EOPNOTSUPP;
}

View File

@ -418,7 +418,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
if (IS_ERR(skb)) {
rtl_dev_err(hdev, "download fw command failed (%ld)",
PTR_ERR(skb));
ret = -PTR_ERR(skb);
ret = PTR_ERR(skb);
goto out;
}
@ -778,7 +778,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)",
le16_to_cpu(entry->offset), entry->len);
break;
};
}
i += sizeof(*entry) + entry->len;
}

View File

@ -1200,7 +1200,7 @@ static int btusb_open(struct hci_dev *hdev)
if (data->setup_on_usb) {
err = data->setup_on_usb(hdev);
if (err < 0)
return err;
goto setup_fail;
}
data->intf->needs_remote_wakeup = 1;
@ -1239,6 +1239,7 @@ done:
failed:
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
setup_fail:
usb_autopm_put_interface(data->intf);
return err;
}
@ -2182,8 +2183,11 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* loaded.
*/
err = btintel_read_version(hdev, &ver);
if (err)
if (err) {
bt_dev_err(hdev, "Intel Read version failed (%d)", err);
btintel_reset_to_bootloader(hdev);
return err;
}
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
@ -2326,9 +2330,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
/* Start firmware downloading and get boot parameter */
err = btintel_download_firmware(hdev, fw, &boot_param);
if (err < 0)
if (err < 0) {
/* When FW download fails, send Intel Reset to retry
* FW download.
*/
btintel_reset_to_bootloader(hdev);
goto done;
}
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
bt_dev_info(hdev, "Waiting for firmware download to complete");
@ -2355,6 +2363,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
if (err) {
bt_dev_err(hdev, "Firmware loading timeout");
err = -ETIMEDOUT;
btintel_reset_to_bootloader(hdev);
goto done;
}
@ -2381,8 +2390,11 @@ done:
set_bit(BTUSB_BOOTING, &data->flags);
err = btintel_send_intel_reset(hdev, boot_param);
if (err)
if (err) {
bt_dev_err(hdev, "Intel Soft Reset failed (%d)", err);
btintel_reset_to_bootloader(hdev);
return err;
}
/* The bootloader will not indicate when the device is ready. This
* is done by the operational firmware sending bootup notification.
@ -2404,6 +2416,7 @@ done:
if (err) {
bt_dev_err(hdev, "Device boot timeout");
btintel_reset_to_bootloader(hdev);
return -ETIMEDOUT;
}
@ -2432,6 +2445,13 @@ done:
*/
btintel_set_event_mask(hdev, false);
/* Read the Intel version information after loading the FW */
err = btintel_read_version(hdev, &ver);
if (err)
return err;
btintel_version_info(hdev, &ver);
return 0;
}
@ -2489,8 +2509,6 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev)
return 0;
}
#ifdef CONFIG_BT_HCIBTUSB_MTK
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
@ -3051,7 +3069,6 @@ static int btusb_mtk_shutdown(struct hci_dev *hdev)
MODULE_FIRMWARE(FIRMWARE_MT7663);
MODULE_FIRMWARE(FIRMWARE_MT7668);
#endif
#ifdef CONFIG_PM
/* Configure an out-of-band gpio as wake-up pin, if specified in device tree */
@ -3411,7 +3428,6 @@ static int btusb_setup_qca(struct hci_dev *hdev)
return 0;
}
#ifdef CONFIG_BT_HCIBTUSB_BCM
static inline int __set_diag_interface(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
@ -3498,7 +3514,6 @@ static int btusb_bcm_set_diag(struct hci_dev *hdev, bool enable)
return submit_or_queue_tx_urb(hdev, urb);
}
#endif
#ifdef CONFIG_PM
static irqreturn_t btusb_oob_wake_handler(int irq, void *priv)
@ -3724,8 +3739,8 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_BCM92035)
hdev->setup = btusb_setup_bcm92035;
#ifdef CONFIG_BT_HCIBTUSB_BCM
if (id->driver_info & BTUSB_BCM_PATCHRAM) {
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) &&
(id->driver_info & BTUSB_BCM_PATCHRAM)) {
hdev->manufacturer = 15;
hdev->setup = btbcm_setup_patchram;
hdev->set_diag = btusb_bcm_set_diag;
@ -3735,7 +3750,8 @@ static int btusb_probe(struct usb_interface *intf,
data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
}
if (id->driver_info & BTUSB_BCM_APPLE) {
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) &&
(id->driver_info & BTUSB_BCM_APPLE)) {
hdev->manufacturer = 15;
hdev->setup = btbcm_setup_apple;
hdev->set_diag = btusb_bcm_set_diag;
@ -3743,7 +3759,6 @@ static int btusb_probe(struct usb_interface *intf,
/* Broadcom LM_DIAG Interface numbers are hardcoded */
data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
}
#endif
if (id->driver_info & BTUSB_INTEL) {
hdev->manufacturer = 2;
@ -3774,14 +3789,13 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_MARVELL)
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
#ifdef CONFIG_BT_HCIBTUSB_MTK
if (id->driver_info & BTUSB_MEDIATEK) {
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK) &&
(id->driver_info & BTUSB_MEDIATEK)) {
hdev->setup = btusb_mtk_setup;
hdev->shutdown = btusb_mtk_shutdown;
hdev->manufacturer = 70;
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
}
#endif
if (id->driver_info & BTUSB_SWAVE) {
set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks);
@ -3807,8 +3821,8 @@ static int btusb_probe(struct usb_interface *intf,
btusb_check_needs_reset_resume(intf);
}
#ifdef CONFIG_BT_HCIBTUSB_RTL
if (id->driver_info & BTUSB_REALTEK) {
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) &&
(id->driver_info & BTUSB_REALTEK)) {
hdev->setup = btrtl_setup_realtek;
hdev->shutdown = btrtl_shutdown_realtek;
hdev->cmd_timeout = btusb_rtl_cmd_timeout;
@ -3819,7 +3833,6 @@ static int btusb_probe(struct usb_interface *intf,
*/
set_bit(BTUSB_WAKEUP_DISABLE, &data->flags);
}
#endif
if (id->driver_info & BTUSB_AMP) {
/* AMP controllers do not support SCO packets */
@ -3887,15 +3900,13 @@ static int btusb_probe(struct usb_interface *intf,
goto out_free_dev;
}
#ifdef CONFIG_BT_HCIBTUSB_BCM
if (data->diag) {
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) {
if (!usb_driver_claim_interface(&btusb_driver,
data->diag, data))
__set_diag_interface(hdev);
else
data->diag = NULL;
}
#endif
if (enable_autosuspend)
usb_enable_autosuspend(data->udev);

View File

@ -1,337 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Texas Instrument's Bluetooth Driver For Shared Transport.
*
* Bluetooth Driver acts as interface between HCI core and
* TI Shared Transport Layer.
*
* Copyright (C) 2009-2010 Texas Instruments
* Author: Raja Mani <raja_mani@ti.com>
* Pavan Savoy <pavan_savoy@ti.com>
*/
#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci.h>
#include <linux/ti_wilink_st.h>
#include <linux/module.h>
/* Bluetooth Driver Version */
#define VERSION "1.0"
#define MAX_BT_CHNL_IDS 3
/* Number of seconds to wait for registration completion
* when ST returns PENDING status.
*/
#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
/**
* struct ti_st - driver operation structure
* @hdev: hci device pointer which binds to bt driver
* @reg_status: ST registration callback status
* @st_write: write function provided by the ST driver
* to be used by the driver during send_frame.
* @wait_reg_completion - completion sync between ti_st_open
* and st_reg_completion_cb.
*/
struct ti_st {
struct hci_dev *hdev;
int reg_status;
long (*st_write) (struct sk_buff *);
struct completion wait_reg_completion;
};
/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
{
struct hci_dev *hdev = hst->hdev;
/* Update HCI stat counters */
switch (pkt_type) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
}
/* ------- Interfaces to Shared Transport ------ */
/* Called by ST layer to indicate protocol registration completion
* status.ti_st_open() function will wait for signal from this
* API when st_register() function returns ST_PENDING.
*/
static void st_reg_completion_cb(void *priv_data, int data)
{
struct ti_st *lhst = priv_data;
/* Save registration status for use in ti_st_open() */
lhst->reg_status = data;
/* complete the wait in ti_st_open() */
complete(&lhst->wait_reg_completion);
}
/* Called by Shared Transport layer when receive data is available */
static long st_receive(void *priv_data, struct sk_buff *skb)
{
struct ti_st *lhst = priv_data;
int err;
if (!skb)
return -EFAULT;
if (!lhst) {
kfree_skb(skb);
return -EFAULT;
}
/* Forward skb to HCI core layer */
err = hci_recv_frame(lhst->hdev, skb);
if (err < 0) {
BT_ERR("Unable to push skb to HCI core(%d)", err);
return err;
}
lhst->hdev->stat.byte_rx += skb->len;
return 0;
}
/* ------- Interfaces to HCI layer ------ */
/* protocol structure registered with shared transport */
static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
{
.chnl_id = HCI_EVENT_PKT, /* HCI Events */
.hdr_len = sizeof(struct hci_event_hdr),
.offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
.len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
.reserve = 8,
},
{
.chnl_id = HCI_ACLDATA_PKT, /* ACL */
.hdr_len = sizeof(struct hci_acl_hdr),
.offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
.len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */
.reserve = 8,
},
{
.chnl_id = HCI_SCODATA_PKT, /* SCO */
.hdr_len = sizeof(struct hci_sco_hdr),
.offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen),
.len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
.reserve = 8,
},
};
/* Called from HCI core to initialize the device */
static int ti_st_open(struct hci_dev *hdev)
{
unsigned long timeleft;
struct ti_st *hst;
int err, i;
BT_DBG("%s %p", hdev->name, hdev);
/* provide contexts for callbacks from ST */
hst = hci_get_drvdata(hdev);
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
ti_st_proto[i].priv_data = hst;
ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
ti_st_proto[i].recv = st_receive;
ti_st_proto[i].reg_complete_cb = st_reg_completion_cb;
/* Prepare wait-for-completion handler */
init_completion(&hst->wait_reg_completion);
/* Reset ST registration callback status flag,
* this value will be updated in
* st_reg_completion_cb()
* function whenever it called from ST driver.
*/
hst->reg_status = -EINPROGRESS;
err = st_register(&ti_st_proto[i]);
if (!err)
goto done;
if (err != -EINPROGRESS) {
BT_ERR("st_register failed %d", err);
return err;
}
/* ST is busy with either protocol
* registration or firmware download.
*/
BT_DBG("waiting for registration "
"completion signal from ST");
timeleft = wait_for_completion_timeout
(&hst->wait_reg_completion,
msecs_to_jiffies(BT_REGISTER_TIMEOUT));
if (!timeleft) {
BT_ERR("Timeout(%d sec),didn't get reg "
"completion signal from ST",
BT_REGISTER_TIMEOUT / 1000);
return -ETIMEDOUT;
}
/* Is ST registration callback
* called with ERROR status?
*/
if (hst->reg_status != 0) {
BT_ERR("ST registration completed with invalid "
"status %d", hst->reg_status);
return -EAGAIN;
}
done:
hst->st_write = ti_st_proto[i].write;
if (!hst->st_write) {
BT_ERR("undefined ST write function");
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
/* Undo registration with ST */
err = st_unregister(&ti_st_proto[i]);
if (err)
BT_ERR("st_unregister() failed with "
"error %d", err);
hst->st_write = NULL;
}
return -EIO;
}
}
return 0;
}
/* Close device */
static int ti_st_close(struct hci_dev *hdev)
{
int err, i;
struct ti_st *hst = hci_get_drvdata(hdev);
for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
err = st_unregister(&ti_st_proto[i]);
if (err)
BT_ERR("st_unregister(%d) failed with error %d",
ti_st_proto[i].chnl_id, err);
}
hst->st_write = NULL;
return err;
}
static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct ti_st *hst;
long len;
int pkt_type;
hst = hci_get_drvdata(hdev);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
skb->len);
/* Insert skb to shared transport layer's transmit queue.
* Freeing skb memory is taken care in shared transport layer,
* so don't free skb memory here.
*/
pkt_type = hci_skb_pkt_type(skb);
len = hst->st_write(skb);
if (len < 0) {
BT_ERR("ST write failed (%ld)", len);
/* Try Again, would only fail if UART has gone bad */
return -EAGAIN;
}
/* ST accepted our skb. So, Go ahead and do rest */
hdev->stat.byte_tx += len;
ti_st_tx_complete(hst, pkt_type);
return 0;
}
static int bt_ti_probe(struct platform_device *pdev)
{
struct ti_st *hst;
struct hci_dev *hdev;
int err;
hst = devm_kzalloc(&pdev->dev, sizeof(struct ti_st), GFP_KERNEL);
if (!hst)
return -ENOMEM;
/* Expose "hciX" device to user space */
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
BT_DBG("hdev %p", hdev);
hst->hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, hst);
hdev->open = ti_st_open;
hdev->close = ti_st_close;
hdev->flush = NULL;
hdev->send = ti_st_send_frame;
err = hci_register_dev(hdev);
if (err < 0) {
BT_ERR("Can't register HCI device error %d", err);
hci_free_dev(hdev);
return err;
}
BT_DBG("HCI device registered (hdev %p)", hdev);
dev_set_drvdata(&pdev->dev, hst);
return 0;
}
static int bt_ti_remove(struct platform_device *pdev)
{
struct hci_dev *hdev;
struct ti_st *hst = dev_get_drvdata(&pdev->dev);
if (!hst)
return -EFAULT;
BT_DBG("%s", hst->hdev->name);
hdev = hst->hdev;
ti_st_close(hdev);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static struct platform_driver btwilink_driver = {
.probe = bt_ti_probe,
.remove = bt_ti_remove,
.driver = {
.name = "btwilink",
},
};
module_platform_driver(btwilink_driver);
/* ------ Module Info ------ */
MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

View File

@ -445,9 +445,11 @@ static int bcm_open(struct hci_uart *hu)
out:
if (bcm->dev) {
hci_uart_set_flow_control(hu, true);
hu->init_speed = bcm->dev->init_speed;
hu->oper_speed = bcm->dev->oper_speed;
err = bcm_gpio_set_power(bcm->dev, true);
hci_uart_set_flow_control(hu, false);
if (err)
goto err_unset_hu;
}
@ -1422,6 +1424,8 @@ static const struct of_device_id bcm_bluetooth_of_match[] = {
{ .compatible = "brcm,bcm4345c5" },
{ .compatible = "brcm,bcm4330-bt" },
{ .compatible = "brcm,bcm43438-bt" },
{ .compatible = "brcm,bcm43540-bt" },
{ .compatible = "brcm,bcm4335a0" },
{ },
};
MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match);

View File

@ -591,6 +591,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
if (*ptr == 0xc0) {
BT_ERR("Short BCSP packet");
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_START;
bcsp->rx_count = 0;
} else
@ -606,6 +607,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
BT_ERR("Error in BCSP hdr checksum");
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
@ -630,6 +632,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
bscp_get_crc(bcsp));
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;

View File

@ -621,13 +621,6 @@ static int ll_setup(struct hci_uart *hu)
serdev_device_set_flow_control(serdev, true);
if (hu->oper_speed)
speed = hu->oper_speed;
else if (hu->proto->oper_speed)
speed = hu->proto->oper_speed;
else
speed = 0;
do {
/* Reset the Bluetooth device */
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
@ -639,20 +632,6 @@ static int ll_setup(struct hci_uart *hu)
return err;
}
if (speed) {
__le32 speed_le = cpu_to_le32(speed);
struct sk_buff *skb;
skb = __hci_cmd_sync(hu->hdev,
HCI_VS_UPDATE_UART_HCI_BAUDRATE,
sizeof(speed_le), &speed_le,
HCI_INIT_TIMEOUT);
if (!IS_ERR(skb)) {
kfree_skb(skb);
serdev_device_set_baudrate(serdev, speed);
}
}
err = download_firmware(lldev);
if (!err)
break;
@ -677,7 +656,25 @@ static int ll_setup(struct hci_uart *hu)
}
/* Operational speed if any */
if (hu->oper_speed)
speed = hu->oper_speed;
else if (hu->proto->oper_speed)
speed = hu->proto->oper_speed;
else
speed = 0;
if (speed) {
__le32 speed_le = cpu_to_le32(speed);
struct sk_buff *skb;
skb = __hci_cmd_sync(hu->hdev, HCI_VS_UPDATE_UART_HCI_BAUDRATE,
sizeof(speed_le), &speed_le,
HCI_INIT_TIMEOUT);
if (!IS_ERR(skb)) {
kfree_skb(skb);
serdev_device_set_baudrate(serdev, speed);
}
}
return 0;
}

View File

@ -520,7 +520,7 @@ static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
err = skb_pad(skb, 1);
if (err)
return err;
skb_put_u8(skb, 0x00);
skb_put(skb, 1);
}
skb_queue_tail(&btdev->txq, skb);

View File

@ -43,7 +43,8 @@
#define HCI_MAX_IBS_SIZE 10
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
#define IBS_TX_IDLE_TIMEOUT_MS 2000
#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 40
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
#define CMD_TRANS_TIMEOUT_MS 100
/* susclk rate */
@ -55,6 +56,7 @@
enum qca_flags {
QCA_IBS_ENABLED,
QCA_DROP_VENDOR_EVENT,
QCA_SUSPENDING,
};
/* HCI_IBS transmit side sleep protocol states */
@ -100,6 +102,7 @@ struct qca_data {
struct work_struct ws_tx_vote_off;
unsigned long flags;
struct completion drop_ev_comp;
wait_queue_head_t suspend_wait_q;
/* For debugging purpose */
u64 ibs_sent_wacks;
@ -130,8 +133,6 @@ enum qca_speed_type {
*/
struct qca_vreg {
const char *name;
unsigned int min_uV;
unsigned int max_uV;
unsigned int load_uA;
};
@ -146,8 +147,8 @@ struct qca_vreg_data {
*/
struct qca_power {
struct device *dev;
const struct qca_vreg_data *vreg_data;
struct regulator_bulk_data *vreg_bulk;
int num_vregs;
bool vregs_on;
};
@ -162,7 +163,8 @@ struct qca_serdev {
const char *firmware_name;
};
static int qca_power_setup(struct hci_uart *hu, bool on);
static int qca_regulator_enable(struct qca_serdev *qcadev);
static void qca_regulator_disable(struct qca_serdev *qcadev);
static void qca_power_shutdown(struct hci_uart *hu);
static int qca_power_off(struct hci_dev *hdev);
@ -438,6 +440,12 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
/* Don't retransmit the HCI_IBS_WAKE_IND when suspending. */
if (test_bit(QCA_SUSPENDING, &qca->flags)) {
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return;
}
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_WAKING:
/* No WAKE_ACK, retransmit WAKE */
@ -497,6 +505,8 @@ static int qca_open(struct hci_uart *hu)
INIT_WORK(&qca->ws_rx_vote_off, qca_wq_serial_rx_clock_vote_off);
INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off);
init_waitqueue_head(&qca->suspend_wait_q);
qca->hu = hu;
init_completion(&qca->drop_ev_comp);
@ -518,7 +528,7 @@ static int qca_open(struct hci_uart *hu)
} else {
hu->init_speed = qcadev->init_speed;
hu->oper_speed = qcadev->oper_speed;
ret = qca_power_setup(hu, true);
ret = qca_regulator_enable(qcadev);
if (ret) {
destroy_workqueue(qca->workqueue);
kfree_skb(qca->rx_skb);
@ -533,7 +543,7 @@ static int qca_open(struct hci_uart *hu)
qca->wake_retrans = IBS_WAKE_RETRANS_TIMEOUT_MS;
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
qca->tx_idle_delay = IBS_HOST_TX_IDLE_TIMEOUT_MS;
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
qca->tx_idle_delay, qca->wake_retrans);
@ -648,6 +658,12 @@ static void device_want_to_wakeup(struct hci_uart *hu)
qca->ibs_recv_wakes++;
/* Don't wake the rx up when suspending. */
if (test_bit(QCA_SUSPENDING, &qca->flags)) {
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return;
}
switch (qca->rx_ibs_state) {
case HCI_IBS_RX_ASLEEP:
/* Make sure clock is on - we may have turned clock off since
@ -712,6 +728,8 @@ static void device_want_to_sleep(struct hci_uart *hu)
break;
}
wake_up_interruptible(&qca->suspend_wait_q);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
}
@ -729,6 +747,12 @@ static void device_woke_up(struct hci_uart *hu)
qca->ibs_recv_wacks++;
/* Don't react to the wake-up-acknowledgment when suspending. */
if (test_bit(QCA_SUSPENDING, &qca->flags)) {
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return;
}
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_AWAKE:
/* Expect one if we send 2 WAKEs */
@ -781,8 +805,10 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
/* Don't go to sleep in middle of patch download or
* Out-Of-Band(GPIOs control) sleep is selected.
* Don't wake the device up when suspending.
*/
if (!test_bit(QCA_IBS_ENABLED, &qca->flags)) {
if (!test_bit(QCA_IBS_ENABLED, &qca->flags) ||
test_bit(QCA_SUSPENDING, &qca->flags)) {
skb_queue_tail(&qca->txq, skb);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return 0;
@ -1188,7 +1214,7 @@ static int qca_wcn3990_init(struct hci_uart *hu)
qcadev = serdev_device_get_drvdata(hu->serdev);
if (!qcadev->bt_power->vregs_on) {
serdev_device_close(hu->serdev);
ret = qca_power_setup(hu, true);
ret = qca_regulator_enable(qcadev);
if (ret)
return ret;
@ -1262,7 +1288,7 @@ static int qca_setup(struct hci_uart *hu)
if (ret)
return ret;
ret = qca_read_soc_version(hdev, &soc_ver);
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
if (ret)
return ret;
} else {
@ -1282,7 +1308,7 @@ static int qca_setup(struct hci_uart *hu)
if (!qca_is_wcn399x(soc_type)) {
/* Get QCA version information */
ret = qca_read_soc_version(hdev, &soc_ver);
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
if (ret)
return ret;
}
@ -1332,10 +1358,21 @@ static const struct hci_uart_proto qca_proto = {
static const struct qca_vreg_data qca_soc_data_wcn3990 = {
.soc_type = QCA_WCN3990,
.vregs = (struct qca_vreg []) {
{ "vddio", 1800000, 1900000, 15000 },
{ "vddxo", 1800000, 1900000, 80000 },
{ "vddrf", 1300000, 1350000, 300000 },
{ "vddch0", 3300000, 3400000, 450000 },
{ "vddio", 15000 },
{ "vddxo", 80000 },
{ "vddrf", 300000 },
{ "vddch0", 450000 },
},
.num_vregs = 4,
};
static const struct qca_vreg_data qca_soc_data_wcn3991 = {
.soc_type = QCA_WCN3991,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
{ "vddxo", 80000 },
{ "vddrf", 300000 },
{ "vddch0", 450000 },
},
.num_vregs = 4,
};
@ -1343,19 +1380,22 @@ static const struct qca_vreg_data qca_soc_data_wcn3990 = {
static const struct qca_vreg_data qca_soc_data_wcn3998 = {
.soc_type = QCA_WCN3998,
.vregs = (struct qca_vreg []) {
{ "vddio", 1800000, 1900000, 10000 },
{ "vddxo", 1800000, 1900000, 80000 },
{ "vddrf", 1300000, 1352000, 300000 },
{ "vddch0", 3300000, 3300000, 450000 },
{ "vddio", 10000 },
{ "vddxo", 80000 },
{ "vddrf", 300000 },
{ "vddch0", 450000 },
},
.num_vregs = 4,
};
static void qca_power_shutdown(struct hci_uart *hu)
{
struct qca_serdev *qcadev;
struct qca_data *qca = hu->priv;
unsigned long flags;
qcadev = serdev_device_get_drvdata(hu->serdev);
/* From this point we go into power off state. But serial port is
* still open, stop queueing the IBS data and flush all the buffered
* data in skb's.
@ -1367,7 +1407,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
qca_power_setup(hu, false);
qca_regulator_disable(qcadev);
}
static int qca_power_off(struct hci_dev *hdev)
@ -1383,97 +1423,71 @@ static int qca_power_off(struct hci_dev *hdev)
return 0;
}
static int qca_enable_regulator(struct qca_vreg vregs,
struct regulator *regulator)
static int qca_regulator_enable(struct qca_serdev *qcadev)
{
struct qca_power *power = qcadev->bt_power;
int ret;
ret = regulator_set_voltage(regulator, vregs.min_uV,
vregs.max_uV);
/* Already enabled */
if (power->vregs_on)
return 0;
BT_DBG("enabling %d regulators)", power->num_vregs);
ret = regulator_bulk_enable(power->num_vregs, power->vreg_bulk);
if (ret)
return ret;
if (vregs.load_uA)
ret = regulator_set_load(regulator,
vregs.load_uA);
if (ret)
return ret;
return regulator_enable(regulator);
power->vregs_on = true;
return 0;
}
static void qca_disable_regulator(struct qca_vreg vregs,
struct regulator *regulator)
static void qca_regulator_disable(struct qca_serdev *qcadev)
{
regulator_disable(regulator);
regulator_set_voltage(regulator, 0, vregs.max_uV);
if (vregs.load_uA)
regulator_set_load(regulator, 0);
struct qca_power *power;
}
if (!qcadev)
return;
static int qca_power_setup(struct hci_uart *hu, bool on)
{
struct qca_vreg *vregs;
struct regulator_bulk_data *vreg_bulk;
struct qca_serdev *qcadev;
int i, num_vregs, ret = 0;
power = qcadev->bt_power;
qcadev = serdev_device_get_drvdata(hu->serdev);
if (!qcadev || !qcadev->bt_power || !qcadev->bt_power->vreg_data ||
!qcadev->bt_power->vreg_bulk)
return -EINVAL;
/* Already disabled? */
if (!power->vregs_on)
return;
vregs = qcadev->bt_power->vreg_data->vregs;
vreg_bulk = qcadev->bt_power->vreg_bulk;
num_vregs = qcadev->bt_power->vreg_data->num_vregs;
BT_DBG("on: %d", on);
if (on && !qcadev->bt_power->vregs_on) {
for (i = 0; i < num_vregs; i++) {
ret = qca_enable_regulator(vregs[i],
vreg_bulk[i].consumer);
if (ret)
break;
}
if (ret) {
BT_ERR("failed to enable regulator:%s", vregs[i].name);
/* turn off regulators which are enabled */
for (i = i - 1; i >= 0; i--)
qca_disable_regulator(vregs[i],
vreg_bulk[i].consumer);
} else {
qcadev->bt_power->vregs_on = true;
}
} else if (!on && qcadev->bt_power->vregs_on) {
/* turn off regulator in reverse order */
i = qcadev->bt_power->vreg_data->num_vregs - 1;
for ( ; i >= 0; i--)
qca_disable_regulator(vregs[i], vreg_bulk[i].consumer);
qcadev->bt_power->vregs_on = false;
}
return ret;
regulator_bulk_disable(power->num_vregs, power->vreg_bulk);
power->vregs_on = false;
}
static int qca_init_regulators(struct qca_power *qca,
const struct qca_vreg *vregs, size_t num_vregs)
{
struct regulator_bulk_data *bulk;
int ret;
int i;
qca->vreg_bulk = devm_kcalloc(qca->dev, num_vregs,
sizeof(struct regulator_bulk_data),
GFP_KERNEL);
if (!qca->vreg_bulk)
bulk = devm_kcalloc(qca->dev, num_vregs, sizeof(*bulk), GFP_KERNEL);
if (!bulk)
return -ENOMEM;
for (i = 0; i < num_vregs; i++)
qca->vreg_bulk[i].supply = vregs[i].name;
bulk[i].supply = vregs[i].name;
return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk);
ret = devm_regulator_bulk_get(qca->dev, num_vregs, bulk);
if (ret < 0)
return ret;
for (i = 0; i < num_vregs; i++) {
ret = regulator_set_load(bulk[i].consumer, vregs[i].load_uA);
if (ret)
return ret;
}
qca->vreg_bulk = bulk;
qca->num_vregs = num_vregs;
return 0;
}
static int qca_serdev_probe(struct serdev_device *serdev)
@ -1500,7 +1514,6 @@ static int qca_serdev_probe(struct serdev_device *serdev)
return -ENOMEM;
qcadev->bt_power->dev = &serdev->dev;
qcadev->bt_power->vreg_data = data;
err = qca_init_regulators(qcadev->bt_power, data->vregs,
data->num_vregs);
if (err) {
@ -1564,9 +1577,103 @@ static void qca_serdev_remove(struct serdev_device *serdev)
hci_uart_unregister_device(&qcadev->serdev_hu);
}
static int __maybe_unused qca_suspend(struct device *dev)
{
struct hci_dev *hdev = container_of(dev, struct hci_dev, dev);
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
unsigned long flags;
int ret = 0;
u8 cmd;
set_bit(QCA_SUSPENDING, &qca->flags);
/* Device is downloading patch or doesn't support in-band sleep. */
if (!test_bit(QCA_IBS_ENABLED, &qca->flags))
return 0;
cancel_work_sync(&qca->ws_awake_device);
cancel_work_sync(&qca->ws_awake_rx);
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_WAKING:
del_timer(&qca->wake_retrans_timer);
/* Fall through */
case HCI_IBS_TX_AWAKE:
del_timer(&qca->tx_idle_timer);
serdev_device_write_flush(hu->serdev);
cmd = HCI_IBS_SLEEP_IND;
ret = serdev_device_write_buf(hu->serdev, &cmd, sizeof(cmd));
if (ret < 0) {
BT_ERR("Failed to send SLEEP to device");
break;
}
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
qca->ibs_sent_slps++;
qca_wq_serial_tx_clock_vote_off(&qca->ws_tx_vote_off);
break;
case HCI_IBS_TX_ASLEEP:
break;
default:
BT_ERR("Spurious tx state %d", qca->tx_ibs_state);
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
if (ret < 0)
goto error;
serdev_device_wait_until_sent(hu->serdev,
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
/* Wait for HCI_IBS_SLEEP_IND sent by device to indicate its Tx is going
* to sleep, so that the packet does not wake the system later.
*/
ret = wait_event_interruptible_timeout(qca->suspend_wait_q,
qca->rx_ibs_state == HCI_IBS_RX_ASLEEP,
msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS));
if (ret > 0)
return 0;
if (ret == 0)
ret = -ETIMEDOUT;
error:
clear_bit(QCA_SUSPENDING, &qca->flags);
return ret;
}
static int __maybe_unused qca_resume(struct device *dev)
{
struct hci_dev *hdev = container_of(dev, struct hci_dev, dev);
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
clear_bit(QCA_SUSPENDING, &qca->flags);
return 0;
}
static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
static const struct of_device_id qca_bluetooth_of_match[] = {
{ .compatible = "qcom,qca6174-bt" },
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
{ /* sentinel */ }
};
@ -1578,6 +1685,7 @@ static struct serdev_device_driver qca_serdev_driver = {
.driver = {
.name = "hci_uart_qca",
.of_match_table = qca_bluetooth_of_match,
.pm = &qca_pm_ops,
},
};

View File

@ -104,10 +104,8 @@ static int __fsl_mc_device_match(struct device *dev, void *data)
return fsl_mc_device_match(mc_dev, obj_desc);
}
static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc
*obj_desc,
struct fsl_mc_device
*mc_bus_dev)
struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,
struct fsl_mc_device *mc_bus_dev)
{
struct device *dev;

View File

@ -554,3 +554,56 @@ int dprc_get_container_id(struct fsl_mc_io *mc_io,
return 0;
}
/**
* dprc_get_connection() - Get connected endpoint and link status if connection
* exists.
* @mc_io: Pointer to MC portal's I/O object
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
* @token: Token of DPRC object
* @endpoint1: Endpoint 1 configuration parameters
* @endpoint2: Returned endpoint 2 configuration parameters
* @state: Returned link state:
* 1 - link is up;
* 0 - link is down;
* -1 - no connection (endpoint2 information is irrelevant)
*
* Return: '0' on Success; -ENOTCONN if connection does not exist.
*/
int dprc_get_connection(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
const struct dprc_endpoint *endpoint1,
struct dprc_endpoint *endpoint2,
int *state)
{
struct dprc_cmd_get_connection *cmd_params;
struct dprc_rsp_get_connection *rsp_params;
struct fsl_mc_command cmd = { 0 };
int err, i;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONNECTION,
cmd_flags,
token);
cmd_params = (struct dprc_cmd_get_connection *)cmd.params;
cmd_params->ep1_id = cpu_to_le32(endpoint1->id);
cmd_params->ep1_interface_id = cpu_to_le16(endpoint1->if_id);
for (i = 0; i < 16; i++)
cmd_params->ep1_type[i] = endpoint1->type[i];
/* send command to mc */
err = mc_send_command(mc_io, &cmd);
if (err)
return -ENOTCONN;
/* retrieve response parameters */
rsp_params = (struct dprc_rsp_get_connection *)cmd.params;
endpoint2->id = le32_to_cpu(rsp_params->ep2_id);
endpoint2->if_id = le16_to_cpu(rsp_params->ep2_interface_id);
*state = le32_to_cpu(rsp_params->state);
for (i = 0; i < 16; i++)
endpoint2->type[i] = rsp_params->ep2_type[i];
return 0;
}

View File

@ -166,42 +166,52 @@ EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
struct device_type fsl_mc_bus_dprc_type = {
.name = "fsl_mc_bus_dprc"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type);
struct device_type fsl_mc_bus_dpni_type = {
.name = "fsl_mc_bus_dpni"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type);
struct device_type fsl_mc_bus_dpio_type = {
.name = "fsl_mc_bus_dpio"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type);
struct device_type fsl_mc_bus_dpsw_type = {
.name = "fsl_mc_bus_dpsw"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type);
struct device_type fsl_mc_bus_dpbp_type = {
.name = "fsl_mc_bus_dpbp"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type);
struct device_type fsl_mc_bus_dpcon_type = {
.name = "fsl_mc_bus_dpcon"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type);
struct device_type fsl_mc_bus_dpmcp_type = {
.name = "fsl_mc_bus_dpmcp"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type);
struct device_type fsl_mc_bus_dpmac_type = {
.name = "fsl_mc_bus_dpmac"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type);
struct device_type fsl_mc_bus_dprtc_type = {
.name = "fsl_mc_bus_dprtc"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type);
struct device_type fsl_mc_bus_dpseci_type = {
.name = "fsl_mc_bus_dpseci"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type);
static struct device_type *fsl_mc_get_device_type(const char *type)
{
@ -702,6 +712,39 @@ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
}
EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev)
{
struct fsl_mc_device *mc_bus_dev, *endpoint;
struct fsl_mc_obj_desc endpoint_desc = { 0 };
struct dprc_endpoint endpoint1 = { 0 };
struct dprc_endpoint endpoint2 = { 0 };
int state, err;
mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
strcpy(endpoint1.type, mc_dev->obj_desc.type);
endpoint1.id = mc_dev->obj_desc.id;
err = dprc_get_connection(mc_bus_dev->mc_io, 0,
mc_bus_dev->mc_handle,
&endpoint1, &endpoint2,
&state);
if (err == -ENOTCONN || state == -1)
return ERR_PTR(-ENOTCONN);
if (err < 0) {
dev_err(&mc_bus_dev->dev, "dprc_get_connection() = %d\n", err);
return ERR_PTR(err);
}
strcpy(endpoint_desc.type, endpoint2.type);
endpoint_desc.id = endpoint2.id;
endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev);
return endpoint;
}
EXPORT_SYMBOL_GPL(fsl_mc_get_endpoint);
static int parse_mc_ranges(struct device *dev,
int *paddr_cells,
int *mc_addr_cells,

View File

@ -105,6 +105,8 @@ int dpmcp_reset(struct fsl_mc_io *mc_io,
#define DPRC_CMDID_GET_OBJ_REG_V2 DPRC_CMD_V2(0x15E)
#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F)
#define DPRC_CMDID_GET_CONNECTION DPRC_CMD(0x16C)
struct dprc_cmd_open {
__le32 container_id;
};
@ -228,6 +230,22 @@ struct dprc_cmd_set_obj_irq {
u8 obj_type[16];
};
struct dprc_cmd_get_connection {
__le32 ep1_id;
__le16 ep1_interface_id;
u8 pad[2];
u8 ep1_type[16];
};
struct dprc_rsp_get_connection {
__le64 pad[3];
__le32 ep2_id;
__le16 ep2_interface_id;
__le16 pad1;
u8 ep2_type[16];
__le32 state;
};
/*
* DPRC API for managing and querying DPAA resources
*/
@ -392,6 +410,27 @@ int dprc_get_container_id(struct fsl_mc_io *mc_io,
u32 cmd_flags,
int *container_id);
/**
* struct dprc_endpoint - Endpoint description for link connect/disconnect
* operations
* @type: Endpoint object type: NULL terminated string
* @id: Endpoint object ID
* @if_id: Interface ID; should be set for endpoints with multiple
* interfaces ("dpsw", "dpdmux"); for others, always set to 0
*/
struct dprc_endpoint {
char type[16];
int id;
u16 if_id;
};
int dprc_get_connection(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
const struct dprc_endpoint *endpoint1,
struct dprc_endpoint *endpoint2,
int *state);
/*
* Data Path Buffer Pool (DPBP) API
*/
@ -574,4 +613,7 @@ void fsl_destroy_mc_io(struct fsl_mc_io *mc_io);
bool fsl_mc_is_root_dprc(struct device *dev);
struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,
struct fsl_mc_device *mc_bus_dev);
#endif /* _FSL_MC_PRIVATE_H_ */

View File

@ -35,7 +35,7 @@ config CHELSIO_IPSEC_INLINE
config CRYPTO_DEV_CHELSIO_TLS
tristate "Chelsio Crypto Inline TLS Driver"
depends on CHELSIO_T4
depends on TLS
depends on TLS_TOE
select CRYPTO_DEV_CHELSIO
---help---
Support Chelsio Inline TLS with Chelsio crypto accelerator.

View File

@ -673,16 +673,16 @@ static inline void txq_advance(struct sge_txq *q, unsigned int n)
int chcr_ipsec_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct xfrm_state *x = xfrm_input_state(skb);
unsigned int last_desc, ndesc, flits = 0;
struct ipsec_sa_entry *sa_entry;
u64 *pos, *end, *before, *sgl;
struct tx_sw_desc *sgl_sdesc;
int qidx, left, credits;
unsigned int flits = 0, ndesc;
struct adapter *adap;
struct sge_eth_txq *q;
struct port_info *pi;
dma_addr_t addr[MAX_SKB_FRAGS + 1];
struct sec_path *sp;
bool immediate = false;
struct sge_eth_txq *q;
struct adapter *adap;
struct port_info *pi;
struct sec_path *sp;
if (!x->xso.offload_handle)
return NETDEV_TX_BUSY;
@ -715,8 +715,14 @@ out_free: dev_kfree_skb_any(skb);
return NETDEV_TX_BUSY;
}
last_desc = q->q.pidx + ndesc - 1;
if (last_desc >= q->q.size)
last_desc -= q->q.size;
sgl_sdesc = &q->q.sdesc[last_desc];
if (!immediate &&
unlikely(cxgb4_map_skb(adap->pdev_dev, skb, addr) < 0)) {
unlikely(cxgb4_map_skb(adap->pdev_dev, skb, sgl_sdesc->addr) < 0)) {
memset(sgl_sdesc->addr, 0, sizeof(sgl_sdesc->addr));
q->mapping_err++;
goto out_free;
}
@ -742,17 +748,10 @@ out_free: dev_kfree_skb_any(skb);
cxgb4_inline_tx_skb(skb, &q->q, sgl);
dev_consume_skb_any(skb);
} else {
int last_desc;
cxgb4_write_sgl(skb, &q->q, (void *)sgl, end,
0, addr);
0, sgl_sdesc->addr);
skb_orphan(skb);
last_desc = q->q.pidx + ndesc - 1;
if (last_desc >= q->q.size)
last_desc -= q->q.size;
q->q.sdesc[last_desc].skb = skb;
q->q.sdesc[last_desc].sgl = (struct ulptx_sgl *)sgl;
sgl_sdesc->skb = skb;
}
txq_advance(&q->q, ndesc);

View File

@ -21,6 +21,7 @@
#include <crypto/internal/hash.h>
#include <linux/tls.h>
#include <net/tls.h>
#include <net/tls_toe.h>
#include "t4fw_api.h"
#include "t4_msg.h"
@ -118,7 +119,7 @@ struct tls_scmd {
};
struct chtls_dev {
struct tls_device tlsdev;
struct tls_toe_device tlsdev;
struct list_head list;
struct cxgb4_lld_info *lldi;
struct pci_dev *pdev;
@ -362,7 +363,7 @@ enum {
#define TCP_PAGE(sk) (sk->sk_frag.page)
#define TCP_OFF(sk) (sk->sk_frag.offset)
static inline struct chtls_dev *to_chtls_dev(struct tls_device *tlsdev)
static inline struct chtls_dev *to_chtls_dev(struct tls_toe_device *tlsdev)
{
return container_of(tlsdev, struct chtls_dev, tlsdev);
}

View File

@ -1437,7 +1437,7 @@ static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
csk->wr_max_credits))
sk->sk_write_space(sk);
if (copied >= target && !sk->sk_backlog.tail)
if (copied >= target && !READ_ONCE(sk->sk_backlog.tail))
break;
if (copied) {
@ -1470,7 +1470,7 @@ static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
break;
}
}
if (sk->sk_backlog.tail) {
if (READ_ONCE(sk->sk_backlog.tail)) {
release_sock(sk);
lock_sock(sk);
chtls_cleanup_rbuf(sk, copied);
@ -1615,7 +1615,7 @@ static int peekmsg(struct sock *sk, struct msghdr *msg,
break;
}
if (sk->sk_backlog.tail) {
if (READ_ONCE(sk->sk_backlog.tail)) {
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
@ -1743,7 +1743,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
csk->wr_max_credits))
sk->sk_write_space(sk);
if (copied >= target && !sk->sk_backlog.tail)
if (copied >= target && !READ_ONCE(sk->sk_backlog.tail))
break;
if (copied) {
@ -1774,7 +1774,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
}
}
if (sk->sk_backlog.tail) {
if (READ_ONCE(sk->sk_backlog.tail)) {
release_sock(sk);
lock_sock(sk);
chtls_cleanup_rbuf(sk, copied);

View File

@ -124,7 +124,7 @@ static void chtls_stop_listen(struct chtls_dev *cdev, struct sock *sk)
mutex_unlock(&notify_mutex);
}
static int chtls_inline_feature(struct tls_device *dev)
static int chtls_inline_feature(struct tls_toe_device *dev)
{
struct net_device *netdev;
struct chtls_dev *cdev;
@ -140,7 +140,7 @@ static int chtls_inline_feature(struct tls_device *dev)
return 0;
}
static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
static int chtls_create_hash(struct tls_toe_device *dev, struct sock *sk)
{
struct chtls_dev *cdev = to_chtls_dev(dev);
@ -149,7 +149,7 @@ static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
return 0;
}
static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk)
static void chtls_destroy_hash(struct tls_toe_device *dev, struct sock *sk)
{
struct chtls_dev *cdev = to_chtls_dev(dev);
@ -161,7 +161,7 @@ static void chtls_free_uld(struct chtls_dev *cdev)
{
int i;
tls_unregister_device(&cdev->tlsdev);
tls_toe_unregister_device(&cdev->tlsdev);
kvfree(cdev->kmap.addr);
idr_destroy(&cdev->hwtid_idr);
for (i = 0; i < (1 << RSPQ_HASH_BITS); i++)
@ -173,27 +173,27 @@ static void chtls_free_uld(struct chtls_dev *cdev)
static inline void chtls_dev_release(struct kref *kref)
{
struct tls_toe_device *dev;
struct chtls_dev *cdev;
struct tls_device *dev;
dev = container_of(kref, struct tls_device, kref);
dev = container_of(kref, struct tls_toe_device, kref);
cdev = to_chtls_dev(dev);
chtls_free_uld(cdev);
}
static void chtls_register_dev(struct chtls_dev *cdev)
{
struct tls_device *tlsdev = &cdev->tlsdev;
struct tls_toe_device *tlsdev = &cdev->tlsdev;
strlcpy(tlsdev->name, "chtls", TLS_DEVICE_NAME_MAX);
strlcpy(tlsdev->name, "chtls", TLS_TOE_DEVICE_NAME_MAX);
strlcat(tlsdev->name, cdev->lldi->ports[0]->name,
TLS_DEVICE_NAME_MAX);
TLS_TOE_DEVICE_NAME_MAX);
tlsdev->feature = chtls_inline_feature;
tlsdev->hash = chtls_create_hash;
tlsdev->unhash = chtls_destroy_hash;
tlsdev->release = chtls_dev_release;
kref_init(&tlsdev->kref);
tls_register_device(tlsdev);
tls_toe_register_device(tlsdev);
cdev->cdev_state = CHTLS_CDEV_STATE_UP;
}

View File

@ -250,7 +250,11 @@ static int fwnet_header_cache(const struct neighbour *neigh,
h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h)));
h->h_proto = type;
memcpy(h->h_dest, neigh->ha, net->addr_len);
hh->hh_len = FWNET_HLEN;
/* Pairs with the READ_ONCE() in neigh_resolve_output(),
* neigh_hh_output() and neigh_update_hhs().
*/
smp_store_release(&hh->hh_len, FWNET_HLEN);
return 0;
}

View File

@ -22,3 +22,11 @@ config BCM47XX_SPROM
In case of SoC devices SPROM content is stored on a flash used by
bootloader firmware CFE. This driver provides method to ssb and bcma
drivers to read SPROM on SoC.
config TEE_BNXT_FW
tristate "Broadcom BNXT firmware manager"
depends on (ARCH_BCM_IPROC && OPTEE) || (COMPILE_TEST && TEE)
default ARCH_BCM_IPROC
help
This module help to manage firmware on Broadcom BNXT device. The module
registers on tee bus and invoke calls to manage firmware on BNXT device.

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o
obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx_sprom.o
obj-$(CONFIG_TEE_BNXT_FW) += tee_bnxt_fw.o

View File

@ -0,0 +1,279 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 Broadcom.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/uuid.h>
#include <linux/firmware/broadcom/tee_bnxt_fw.h>
#define MAX_SHM_MEM_SZ SZ_4M
#define MAX_TEE_PARAM_ARRY_MEMB 4
enum ta_cmd {
/*
* TA_CMD_BNXT_FASTBOOT - boot bnxt device by copying f/w into sram
*
* param[0] unused
* param[1] unused
* param[2] unused
* param[3] unused
*
* Result:
* TEE_SUCCESS - Invoke command success
* TEE_ERROR_ITEM_NOT_FOUND - Corrupt f/w image found on memory
*/
TA_CMD_BNXT_FASTBOOT = 0,
/*
* TA_CMD_BNXT_COPY_COREDUMP - copy the core dump into shm
*
* param[0] (inout memref) - Coredump buffer memory reference
* param[1] (in value) - value.a: offset, data to be copied from
* value.b: size of data to be copied
* param[2] unused
* param[3] unused
*
* Result:
* TEE_SUCCESS - Invoke command success
* TEE_ERROR_BAD_PARAMETERS - Incorrect input param
* TEE_ERROR_ITEM_NOT_FOUND - Corrupt core dump
*/
TA_CMD_BNXT_COPY_COREDUMP = 3,
};
/**
* struct tee_bnxt_fw_private - OP-TEE bnxt private data
* @dev: OP-TEE based bnxt device.
* @ctx: OP-TEE context handler.
* @session_id: TA session identifier.
*/
struct tee_bnxt_fw_private {
struct device *dev;
struct tee_context *ctx;
u32 session_id;
struct tee_shm *fw_shm_pool;
};
static struct tee_bnxt_fw_private pvt_data;
static void prepare_args(int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param)
{
memset(arg, 0, sizeof(*arg));
memset(param, 0, MAX_TEE_PARAM_ARRY_MEMB * sizeof(*param));
arg->func = cmd;
arg->session = pvt_data.session_id;
arg->num_params = MAX_TEE_PARAM_ARRY_MEMB;
/* Fill invoke cmd params */
switch (cmd) {
case TA_CMD_BNXT_COPY_COREDUMP:
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
param[0].u.memref.shm = pvt_data.fw_shm_pool;
param[0].u.memref.size = MAX_SHM_MEM_SZ;
param[0].u.memref.shm_offs = 0;
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
break;
case TA_CMD_BNXT_FASTBOOT:
default:
/* Nothing to do */
break;
}
}
/**
* tee_bnxt_fw_load() - Load the bnxt firmware
* Uses an OP-TEE call to start a secure
* boot process.
* Returns 0 on success, negative errno otherwise.
*/
int tee_bnxt_fw_load(void)
{
int ret = 0;
struct tee_ioctl_invoke_arg arg;
struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB];
if (!pvt_data.ctx)
return -ENODEV;
prepare_args(TA_CMD_BNXT_FASTBOOT, &arg, param);
ret = tee_client_invoke_func(pvt_data.ctx, &arg, param);
if (ret < 0 || arg.ret != 0) {
dev_err(pvt_data.dev,
"TA_CMD_BNXT_FASTBOOT invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(tee_bnxt_fw_load);
/**
* tee_bnxt_copy_coredump() - Copy coredump from the allocated memory
* Uses an OP-TEE call to copy coredump
* @buf: destination buffer where core dump is copied into
* @offset: offset from the base address of core dump area
* @size: size of the dump
*
* Returns 0 on success, negative errno otherwise.
*/
int tee_bnxt_copy_coredump(void *buf, u32 offset, u32 size)
{
struct tee_ioctl_invoke_arg arg;
struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB];
void *core_data;
u32 rbytes = size;
u32 nbytes = 0;
int ret = 0;
if (!pvt_data.ctx)
return -ENODEV;
prepare_args(TA_CMD_BNXT_COPY_COREDUMP, &arg, param);
while (rbytes) {
nbytes = rbytes;
nbytes = min_t(u32, rbytes, param[0].u.memref.size);
/* Fill additional invoke cmd params */
param[1].u.value.a = offset;
param[1].u.value.b = nbytes;
ret = tee_client_invoke_func(pvt_data.ctx, &arg, param);
if (ret < 0 || arg.ret != 0) {
dev_err(pvt_data.dev,
"TA_CMD_BNXT_COPY_COREDUMP invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EINVAL;
}
core_data = tee_shm_get_va(pvt_data.fw_shm_pool, 0);
if (IS_ERR(core_data)) {
dev_err(pvt_data.dev, "tee_shm_get_va failed\n");
return PTR_ERR(core_data);
}
memcpy(buf, core_data, nbytes);
rbytes -= nbytes;
buf += nbytes;
offset += nbytes;
}
return 0;
}
EXPORT_SYMBOL(tee_bnxt_copy_coredump);
static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
{
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
}
static int tee_bnxt_fw_probe(struct device *dev)
{
struct tee_client_device *bnxt_device = to_tee_client_device(dev);
int ret, err = -ENODEV;
struct tee_ioctl_open_session_arg sess_arg;
struct tee_shm *fw_shm_pool;
memset(&sess_arg, 0, sizeof(sess_arg));
/* Open context with TEE driver */
pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
NULL);
if (IS_ERR(pvt_data.ctx))
return -ENODEV;
/* Open session with Bnxt load Trusted App */
memcpy(sess_arg.uuid, bnxt_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
sess_arg.num_params = 0;
ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n",
sess_arg.ret);
err = -EINVAL;
goto out_ctx;
}
pvt_data.session_id = sess_arg.session;
pvt_data.dev = dev;
fw_shm_pool = tee_shm_alloc(pvt_data.ctx, MAX_SHM_MEM_SZ,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
if (IS_ERR(fw_shm_pool)) {
tee_client_close_context(pvt_data.ctx);
dev_err(pvt_data.dev, "tee_shm_alloc failed\n");
err = PTR_ERR(fw_shm_pool);
goto out_sess;
}
pvt_data.fw_shm_pool = fw_shm_pool;
return 0;
out_sess:
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
out_ctx:
tee_client_close_context(pvt_data.ctx);
return err;
}
static int tee_bnxt_fw_remove(struct device *dev)
{
tee_shm_free(pvt_data.fw_shm_pool);
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
tee_client_close_context(pvt_data.ctx);
pvt_data.ctx = NULL;
return 0;
}
static const struct tee_client_device_id tee_bnxt_fw_id_table[] = {
{UUID_INIT(0x6272636D, 0x2019, 0x0716,
0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)},
{}
};
MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table);
static struct tee_client_driver tee_bnxt_fw_driver = {
.id_table = tee_bnxt_fw_id_table,
.driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_bnxt_fw_probe,
.remove = tee_bnxt_fw_remove,
},
};
static int __init tee_bnxt_fw_mod_init(void)
{
return driver_register(&tee_bnxt_fw_driver.driver);
}
static void __exit tee_bnxt_fw_mod_exit(void)
{
driver_unregister(&tee_bnxt_fw_driver.driver);
}
module_init(tee_bnxt_fw_mod_init);
module_exit(tee_bnxt_fw_mod_exit);
MODULE_AUTHOR("Vikas Gupta <vikas.gupta@broadcom.com>");
MODULE_DESCRIPTION("Broadcom bnxt firmware manager");
MODULE_LICENSE("GPL v2");

View File

@ -881,8 +881,8 @@ struct sdma_engine *sdma_select_user_engine(struct hfi1_devdata *dd,
cpu_id = smp_processor_id();
rcu_read_lock();
rht_node = rhashtable_lookup_fast(dd->sdma_rht, &cpu_id,
sdma_rht_params);
rht_node = rhashtable_lookup(dd->sdma_rht, &cpu_id,
sdma_rht_params);
if (rht_node && rht_node->map[vl]) {
struct sdma_rht_map_elem *map = rht_node->map[vl];

View File

@ -35,7 +35,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
int vport_index;
if (rep->vport == MLX5_VPORT_UPLINK)
profile = &uplink_rep_profile;
profile = &raw_eth_profile;
else
return mlx5_ib_set_vport_rep(dev, rep);

View File

@ -10,7 +10,7 @@
#include "mlx5_ib.h"
#ifdef CONFIG_MLX5_ESWITCH
extern const struct mlx5_ib_profile uplink_rep_profile;
extern const struct mlx5_ib_profile raw_eth_profile;
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw);
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,

View File

@ -1031,7 +1031,7 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
if (MLX5_CAP_GEN(mdev, cd))
props->device_cap_flags |= IB_DEVICE_CROSS_CHANNEL;
if (!mlx5_core_is_pf(mdev))
if (mlx5_core_is_vf(mdev))
props->device_cap_flags |= IB_DEVICE_VIRTUAL_FUNCTION;
if (mlx5_ib_port_link_layer(ibdev, 1) ==
@ -5145,8 +5145,7 @@ static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num,
immutable->pkey_tbl_len = attr.pkey_tbl_len;
immutable->gid_tbl_len = attr.gid_tbl_len;
immutable->core_cap_flags = get_core_cap_flags(ibdev, &rep);
if ((ll == IB_LINK_LAYER_INFINIBAND) || MLX5_CAP_GEN(dev->mdev, roce))
immutable->max_mad_size = IB_MGMT_MAD_SIZE;
immutable->max_mad_size = IB_MGMT_MAD_SIZE;
return 0;
}
@ -5249,11 +5248,9 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev)
{
int err;
if (MLX5_CAP_GEN(dev->mdev, roce)) {
err = mlx5_nic_vport_enable_roce(dev->mdev);
if (err)
return err;
}
err = mlx5_nic_vport_enable_roce(dev->mdev);
if (err)
return err;
err = mlx5_eth_lag_init(dev);
if (err)
@ -5262,8 +5259,7 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev)
return 0;
err_disable_roce:
if (MLX5_CAP_GEN(dev->mdev, roce))
mlx5_nic_vport_disable_roce(dev->mdev);
mlx5_nic_vport_disable_roce(dev->mdev);
return err;
}
@ -5271,8 +5267,7 @@ err_disable_roce:
static void mlx5_disable_eth(struct mlx5_ib_dev *dev)
{
mlx5_eth_lag_cleanup(dev);
if (MLX5_CAP_GEN(dev->mdev, roce))
mlx5_nic_vport_disable_roce(dev->mdev);
mlx5_nic_vport_disable_roce(dev->mdev);
}
struct mlx5_ib_counter {
@ -6444,7 +6439,7 @@ static const struct ib_device_ops mlx5_ib_dev_port_rep_ops = {
.query_port = mlx5_ib_rep_query_port,
};
static int mlx5_ib_stage_rep_non_default_cb(struct mlx5_ib_dev *dev)
static int mlx5_ib_stage_raw_eth_non_default_cb(struct mlx5_ib_dev *dev)
{
ib_set_device_ops(&dev->ib_dev, &mlx5_ib_dev_port_rep_ops);
return 0;
@ -6484,7 +6479,7 @@ static void mlx5_ib_stage_common_roce_cleanup(struct mlx5_ib_dev *dev)
mlx5_remove_netdev_notifier(dev, port_num);
}
static int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev)
static int mlx5_ib_stage_raw_eth_roce_init(struct mlx5_ib_dev *dev)
{
struct mlx5_core_dev *mdev = dev->mdev;
enum rdma_link_layer ll;
@ -6500,7 +6495,7 @@ static int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev)
return err;
}
static void mlx5_ib_stage_rep_roce_cleanup(struct mlx5_ib_dev *dev)
static void mlx5_ib_stage_raw_eth_roce_cleanup(struct mlx5_ib_dev *dev)
{
mlx5_ib_stage_common_roce_cleanup(dev);
}
@ -6807,7 +6802,7 @@ static const struct mlx5_ib_profile pf_profile = {
mlx5_ib_stage_delay_drop_cleanup),
};
const struct mlx5_ib_profile uplink_rep_profile = {
const struct mlx5_ib_profile raw_eth_profile = {
STAGE_CREATE(MLX5_IB_STAGE_INIT,
mlx5_ib_stage_init_init,
mlx5_ib_stage_init_cleanup),
@ -6818,11 +6813,11 @@ const struct mlx5_ib_profile uplink_rep_profile = {
mlx5_ib_stage_caps_init,
NULL),
STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
mlx5_ib_stage_rep_non_default_cb,
mlx5_ib_stage_raw_eth_non_default_cb,
NULL),
STAGE_CREATE(MLX5_IB_STAGE_ROCE,
mlx5_ib_stage_rep_roce_init,
mlx5_ib_stage_rep_roce_cleanup),
mlx5_ib_stage_raw_eth_roce_init,
mlx5_ib_stage_raw_eth_roce_cleanup),
STAGE_CREATE(MLX5_IB_STAGE_SRQ,
mlx5_init_srq_table,
mlx5_cleanup_srq_table),
@ -6898,6 +6893,7 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
{
const struct mlx5_ib_profile *profile;
enum rdma_link_layer ll;
struct mlx5_ib_dev *dev;
int port_type_cap;
@ -6933,7 +6929,12 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
dev->mdev = mdev;
dev->num_ports = num_ports;
return __mlx5_ib_add(dev, &pf_profile);
if (ll == IB_LINK_LAYER_ETHERNET && !mlx5_is_roce_enabled(mdev))
profile = &raw_eth_profile;
else
profile = &pf_profile;
return __mlx5_ib_add(dev, profile);
}
static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)

View File

@ -10,7 +10,7 @@ config MISDN_HFCPCI
depends on PCI
help
Enable support for cards with Cologne Chip AG's
HFC PCI chip.
HFC PCI chip.
config MISDN_HFCMULTI
tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"

View File

@ -173,8 +173,8 @@ symbolic(struct hfcusb_symbolic_list list[], const int num)
/*
* List of all supported enpoints configiration sets, used to find the
* best matching endpoint configuration within a devices' USB descriptor.
* List of all supported endpoint configuration sets, used to find the
* best matching endpoint configuration within a device's USB descriptor.
* We need at least 3 RX endpoints, and 3 TX endpoints, either
* INT-in and ISO-out, or ISO-in and ISO-out)
* with 4 RX endpoints even E-Channel logging is possible

View File

@ -27,7 +27,6 @@ MODULE_VERSION(ISAR_REV);
#define DEBUG_HW_FIRMWARE_FIFO 0x10000
static const u8 faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146";
static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121,
122, 145, 146};
#define FAXMODCNT 13

View File

@ -28,6 +28,10 @@ MODULE_PARM_DESC(disable_guest,
static bool vmci_guest_personality_initialized;
static bool vmci_host_personality_initialized;
static DEFINE_MUTEX(vmci_vsock_mutex); /* protects vmci_vsock_transport_cb */
static vmci_vsock_cb vmci_vsock_transport_cb;
static bool vmci_vsock_cb_host_called;
/*
* vmci_get_context_id() - Gets the current context ID.
*
@ -45,6 +49,69 @@ u32 vmci_get_context_id(void)
}
EXPORT_SYMBOL_GPL(vmci_get_context_id);
/*
* vmci_register_vsock_callback() - Register the VSOCK vmci_transport callback.
*
* The callback will be called when the first host or guest becomes active,
* or if they are already active when this function is called.
* To unregister the callback, call this function with NULL parameter.
*
* Returns 0 on success. -EBUSY if a callback is already registered.
*/
int vmci_register_vsock_callback(vmci_vsock_cb callback)
{
int err = 0;
mutex_lock(&vmci_vsock_mutex);
if (vmci_vsock_transport_cb && callback) {
err = -EBUSY;
goto out;
}
vmci_vsock_transport_cb = callback;
if (!vmci_vsock_transport_cb) {
vmci_vsock_cb_host_called = false;
goto out;
}
if (vmci_guest_code_active())
vmci_vsock_transport_cb(false);
if (vmci_host_users() > 0) {
vmci_vsock_cb_host_called = true;
vmci_vsock_transport_cb(true);
}
out:
mutex_unlock(&vmci_vsock_mutex);
return err;
}
EXPORT_SYMBOL_GPL(vmci_register_vsock_callback);
void vmci_call_vsock_callback(bool is_host)
{
mutex_lock(&vmci_vsock_mutex);
if (!vmci_vsock_transport_cb)
goto out;
/* In the host, this function could be called multiple times,
* but we want to register it only once.
*/
if (is_host) {
if (vmci_vsock_cb_host_called)
goto out;
vmci_vsock_cb_host_called = true;
}
vmci_vsock_transport_cb(is_host);
out:
mutex_unlock(&vmci_vsock_mutex);
}
static int __init vmci_drv_init(void)
{
int vmci_err;

View File

@ -36,10 +36,12 @@ extern struct pci_dev *vmci_pdev;
u32 vmci_get_context_id(void);
int vmci_send_datagram(struct vmci_datagram *dg);
void vmci_call_vsock_callback(bool is_host);
int vmci_host_init(void);
void vmci_host_exit(void);
bool vmci_host_code_active(void);
int vmci_host_users(void);
int vmci_guest_init(void);
void vmci_guest_exit(void);

View File

@ -637,6 +637,8 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
vmci_dev->iobase + VMCI_CONTROL_ADDR);
pci_set_drvdata(pdev, vmci_dev);
vmci_call_vsock_callback(false);
return 0;
err_free_irq:

View File

@ -108,6 +108,11 @@ bool vmci_host_code_active(void)
atomic_read(&vmci_host_active_users) > 0);
}
int vmci_host_users(void)
{
return atomic_read(&vmci_host_active_users);
}
/*
* Called on open of /dev/vmci.
*/
@ -338,6 +343,8 @@ static int vmci_host_do_init_context(struct vmci_host_dev *vmci_host_dev,
vmci_host_dev->ct_type = VMCIOBJ_CONTEXT;
atomic_inc(&vmci_host_active_users);
vmci_call_vsock_callback(true);
retval = 0;
out:

View File

@ -153,22 +153,22 @@ config IPVLAN_L3S
select NET_L3_MASTER_DEV
config IPVLAN
tristate "IP-VLAN support"
depends on INET
depends on IPV6 || !IPV6
---help---
This allows one to create virtual devices off of a main interface
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
on packets. All interfaces (including the main interface) share L2
making it transparent to the connected L2 switch.
tristate "IP-VLAN support"
depends on INET
depends on IPV6 || !IPV6
---help---
This allows one to create virtual devices off of a main interface
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
on packets. All interfaces (including the main interface) share L2
making it transparent to the connected L2 switch.
Ipvlan devices can be added using the "ip" command from the
iproute2 package starting with the iproute2-3.19 release:
Ipvlan devices can be added using the "ip" command from the
iproute2 package starting with the iproute2-3.19 release:
"ip link add link <main-dev> [ NAME ] type ipvlan"
"ip link add link <main-dev> [ NAME ] type ipvlan"
To compile this driver as a module, choose M here: the module
will be called ipvlan.
To compile this driver as a module, choose M here: the module
will be called ipvlan.
config IPVTAP
tristate "IP-VLAN based tap driver"
@ -185,11 +185,11 @@ config IPVTAP
will be called ipvtap.
config VXLAN
tristate "Virtual eXtensible Local Area Network (VXLAN)"
depends on INET
select NET_UDP_TUNNEL
select GRO_CELLS
---help---
tristate "Virtual eXtensible Local Area Network (VXLAN)"
depends on INET
select NET_UDP_TUNNEL
select GRO_CELLS
---help---
This allows one to create vxlan virtual interfaces that provide
Layer 2 Networks over Layer 3 Networks. VXLAN is often used
to tunnel virtual network infrastructure in virtualized environments.
@ -200,12 +200,12 @@ config VXLAN
will be called vxlan.
config GENEVE
tristate "Generic Network Virtualization Encapsulation"
depends on INET
depends on IPV6 || !IPV6
select NET_UDP_TUNNEL
select GRO_CELLS
---help---
tristate "Generic Network Virtualization Encapsulation"
depends on INET
depends on IPV6 || !IPV6
select NET_UDP_TUNNEL
select GRO_CELLS
---help---
This allows one to create geneve virtual interfaces that provide
Layer 2 Networks over Layer 3 Networks. GENEVE is often used
to tunnel virtual network infrastructure in virtualized environments.
@ -244,8 +244,8 @@ config MACSEC
config NETCONSOLE
tristate "Network console logging support"
---help---
If you want to log kernel messages over the network, enable this.
See <file:Documentation/networking/netconsole.txt> for details.
If you want to log kernel messages over the network, enable this.
See <file:Documentation/networking/netconsole.txt> for details.
config NETCONSOLE_DYNAMIC
bool "Dynamic reconfiguration of logging targets"
@ -362,12 +362,12 @@ config NET_VRF
support enables VRF devices.
config VSOCKMON
tristate "Virtual vsock monitoring device"
depends on VHOST_VSOCK
---help---
This option enables a monitoring net device for vsock sockets. It is
mostly intended for developers or support to debug vsock issues. If
unsure, say N.
tristate "Virtual vsock monitoring device"
depends on VHOST_VSOCK
---help---
This option enables a monitoring net device for vsock sockets. It is
mostly intended for developers or support to debug vsock issues. If
unsure, say N.
endif # NET_CORE

View File

@ -41,6 +41,8 @@
#include <linux/in.h>
#include <net/ip.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/slab.h>
@ -200,6 +202,51 @@ atomic_t netpoll_block_tx = ATOMIC_INIT(0);
unsigned int bond_net_id __read_mostly;
static const struct flow_dissector_key flow_keys_bonding_keys[] = {
{
.key_id = FLOW_DISSECTOR_KEY_CONTROL,
.offset = offsetof(struct flow_keys, control),
},
{
.key_id = FLOW_DISSECTOR_KEY_BASIC,
.offset = offsetof(struct flow_keys, basic),
},
{
.key_id = FLOW_DISSECTOR_KEY_IPV4_ADDRS,
.offset = offsetof(struct flow_keys, addrs.v4addrs),
},
{
.key_id = FLOW_DISSECTOR_KEY_IPV6_ADDRS,
.offset = offsetof(struct flow_keys, addrs.v6addrs),
},
{
.key_id = FLOW_DISSECTOR_KEY_TIPC,
.offset = offsetof(struct flow_keys, addrs.tipckey),
},
{
.key_id = FLOW_DISSECTOR_KEY_PORTS,
.offset = offsetof(struct flow_keys, ports),
},
{
.key_id = FLOW_DISSECTOR_KEY_ICMP,
.offset = offsetof(struct flow_keys, icmp),
},
{
.key_id = FLOW_DISSECTOR_KEY_VLAN,
.offset = offsetof(struct flow_keys, vlan),
},
{
.key_id = FLOW_DISSECTOR_KEY_FLOW_LABEL,
.offset = offsetof(struct flow_keys, tags),
},
{
.key_id = FLOW_DISSECTOR_KEY_GRE_KEYID,
.offset = offsetof(struct flow_keys, keyid),
},
};
static struct flow_dissector flow_keys_bonding __read_mostly;
/*-------------------------- Forward declarations ---------------------------*/
static int bond_init(struct net_device *bond_dev);
@ -3252,39 +3299,78 @@ static inline u32 bond_eth_hash(struct sk_buff *skb)
return 0;
}
static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk,
int *noff, int *proto, bool l34)
{
const struct ipv6hdr *iph6;
const struct iphdr *iph;
if (skb->protocol == htons(ETH_P_IP)) {
if (unlikely(!pskb_may_pull(skb, *noff + sizeof(*iph))))
return false;
iph = (const struct iphdr *)(skb->data + *noff);
iph_to_flow_copy_v4addrs(fk, iph);
*noff += iph->ihl << 2;
if (!ip_is_fragment(iph))
*proto = iph->protocol;
} else if (skb->protocol == htons(ETH_P_IPV6)) {
if (unlikely(!pskb_may_pull(skb, *noff + sizeof(*iph6))))
return false;
iph6 = (const struct ipv6hdr *)(skb->data + *noff);
iph_to_flow_copy_v6addrs(fk, iph6);
*noff += sizeof(*iph6);
*proto = iph6->nexthdr;
} else {
return false;
}
if (l34 && *proto >= 0)
fk->ports.ports = skb_flow_get_ports(skb, *noff, *proto);
return true;
}
/* Extract the appropriate headers based on bond's xmit policy */
static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
struct flow_keys *fk)
{
const struct ipv6hdr *iph6;
const struct iphdr *iph;
bool l34 = bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34;
int noff, proto = -1;
if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23)
return skb_flow_dissect_flow_keys(skb, fk, 0);
if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) {
memset(fk, 0, sizeof(*fk));
return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
fk, NULL, 0, 0, 0, 0);
}
fk->ports.ports = 0;
memset(&fk->icmp, 0, sizeof(fk->icmp));
noff = skb_network_offset(skb);
if (skb->protocol == htons(ETH_P_IP)) {
if (unlikely(!pskb_may_pull(skb, noff + sizeof(*iph))))
return false;
iph = ip_hdr(skb);
iph_to_flow_copy_v4addrs(fk, iph);
noff += iph->ihl << 2;
if (!ip_is_fragment(iph))
proto = iph->protocol;
} else if (skb->protocol == htons(ETH_P_IPV6)) {
if (unlikely(!pskb_may_pull(skb, noff + sizeof(*iph6))))
return false;
iph6 = ipv6_hdr(skb);
iph_to_flow_copy_v6addrs(fk, iph6);
noff += sizeof(*iph6);
proto = iph6->nexthdr;
} else {
if (!bond_flow_ip(skb, fk, &noff, &proto, l34))
return false;
/* ICMP error packets contains at least 8 bytes of the header
* of the packet which generated the error. Use this information
* to correlate ICMP error packets within the same flow which
* generated the error.
*/
if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
skb_flow_get_icmp_tci(skb, &fk->icmp, skb->data,
skb_transport_offset(skb),
skb_headlen(skb));
if (proto == IPPROTO_ICMP) {
if (!icmp_is_err(fk->icmp.type))
return true;
noff += sizeof(struct icmphdr);
} else if (proto == IPPROTO_ICMPV6) {
if (!icmpv6_is_err(fk->icmp.type))
return true;
noff += sizeof(struct icmp6hdr);
}
return bond_flow_ip(skb, fk, &noff, &proto, l34);
}
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34 && proto >= 0)
fk->ports.ports = skb_flow_get_ports(skb, noff, proto);
return true;
}
@ -3311,10 +3397,14 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
return bond_eth_hash(skb);
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 ||
bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23)
bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23) {
hash = bond_eth_hash(skb);
else
hash = (__force u32)flow.ports.ports;
} else {
if (flow.icmp.id)
memcpy(&hash, &flow.icmp, sizeof(hash));
else
memcpy(&hash, &flow.ports.ports, sizeof(hash));
}
hash ^= (__force u32)flow_get_u32_dst(&flow) ^
(__force u32)flow_get_u32_src(&flow);
hash ^= (hash >> 16);
@ -4891,6 +4981,10 @@ static int __init bonding_init(void)
goto err;
}
skb_flow_dissector_init(&flow_keys_bonding,
flow_keys_bonding_keys,
ARRAY_SIZE(flow_keys_bonding_keys));
register_netdevice_notifier(&bond_netdev_notifier);
out:
return res;

Some files were not shown because too many files have changed in this diff Show More