mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) Support offloading wireless authentication to userspace via NL80211_CMD_EXTERNAL_AUTH, from Srinivas Dasari. 2) A lot of work on network namespace setup/teardown from Kirill Tkhai. Setup and cleanup of namespaces now all run asynchronously and thus performance is significantly increased. 3) Add rx/tx timestamping support to mv88e6xxx driver, from Brandon Streiff. 4) Support zerocopy on RDS sockets, from Sowmini Varadhan. 5) Use denser instruction encoding in x86 eBPF JIT, from Daniel Borkmann. 6) Support hw offload of vlan filtering in mvpp2 dreiver, from Maxime Chevallier. 7) Support grafting of child qdiscs in mlxsw driver, from Nogah Frankel. 8) Add packet forwarding tests to selftests, from Ido Schimmel. 9) Deal with sub-optimal GSO packets better in BBR congestion control, from Eric Dumazet. 10) Support 5-tuple hashing in ipv6 multipath routing, from David Ahern. 11) Add path MTU tests to selftests, from Stefano Brivio. 12) Various bits of IPSEC offloading support for mlx5, from Aviad Yehezkel, Yossi Kuperman, and Saeed Mahameed. 13) Support RSS spreading on ntuple filters in SFC driver, from Edward Cree. 14) Lots of sockmap work from John Fastabend. Applications can use eBPF to filter sendmsg and sendpage operations. 15) In-kernel receive TLS support, from Dave Watson. 16) Add XDP support to ixgbevf, this is significant because it should allow optimized XDP usage in various cloud environments. From Tony Nguyen. 17) Add new Intel E800 series "ice" ethernet driver, from Anirudh Venkataramanan et al. 18) IP fragmentation match offload support in nfp driver, from Pieter Jansen van Vuuren. 19) Support XDP redirect in i40e driver, from Björn Töpel. 20) Add BPF_RAW_TRACEPOINT program type for accessing the arguments of tracepoints in their raw form, from Alexei Starovoitov. 21) Lots of striding RQ improvements to mlx5 driver with many performance improvements, from Tariq Toukan. 22) Use rhashtable for inet frag reassembly, from Eric Dumazet. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1678 commits) net: mvneta: improve suspend/resume net: mvneta: split rxq/txq init and txq deinit into SW and HW parts ipv6: frags: fix /proc/sys/net/ipv6/ip6frag_low_thresh net: bgmac: Fix endian access in bgmac_dma_tx_ring_free() net: bgmac: Correctly annotate register space route: check sysctl_fib_multipath_use_neigh earlier than hash fix typo in command value in drivers/net/phy/mdio-bitbang. sky2: Increase D3 delay to sky2 stops working after suspend net/mlx5e: Set EQE based as default TX interrupt moderation mode ibmvnic: Disable irqs before exiting reset from closed state net: sched: do not emit messages while holding spinlock vlan: also check phy_driver ts_info for vlan's real device Bluetooth: Mark expected switch fall-throughs Bluetooth: Set HCI_QUIRK_SIMULTANEOUS_DISCOVERY for BTUSB_QCA_ROME Bluetooth: btrsi: remove unused including <linux/version.h> Bluetooth: hci_bcm: Remove DMI quirk for the MINIX Z83-4 sh_eth: kill useless check in __sh_eth_get_regs() sh_eth: add sh_eth_cpu_data::no_xdfar flag ipv6: factorize sk_wmem_alloc updates done by __ip6_append_data() ipv4: factorize sk_wmem_alloc updates done by __ip_append_data() ...
This commit is contained in:
commit
5bb053bef8
@ -539,6 +539,18 @@ A: Although LLVM IR generation and optimization try to stay architecture
|
||||
The clang option "-fno-jump-tables" can be used to disable
|
||||
switch table generation.
|
||||
|
||||
- For clang -target bpf, it is guaranteed that pointer or long /
|
||||
unsigned long types will always have a width of 64 bit, no matter
|
||||
whether underlying clang binary or default target (or kernel) is
|
||||
32 bit. However, when native clang target is used, then it will
|
||||
compile these types based on the underlying architecture's conventions,
|
||||
meaning in case of 32 bit architecture, pointer or long / unsigned
|
||||
long types e.g. in BPF context structure will have width of 32 bit
|
||||
while the BPF LLVM back end still operates in 64 bit. The native
|
||||
target is mostly needed in tracing for the case of walking pt_regs
|
||||
or other kernel structures where CPU's register width matters.
|
||||
Otherwise, clang -target bpf is generally recommended.
|
||||
|
||||
You should use default target when:
|
||||
|
||||
- Your program includes a header file, e.g., ptrace.h, which eventually
|
||||
|
@ -13,9 +13,18 @@ placed as a child node of an mdio device.
|
||||
The properties described here are those specific to Marvell devices.
|
||||
Additional required and optional properties can be found in dsa.txt.
|
||||
|
||||
The compatibility string is used only to find an identification register,
|
||||
which is at a different MDIO base address in different switch families.
|
||||
- "marvell,mv88e6085" : Switch has base address 0x10. Use with models:
|
||||
6085, 6095, 6097, 6123, 6131, 6141, 6161, 6165,
|
||||
6171, 6172, 6175, 6176, 6185, 6240, 6320, 6321,
|
||||
6341, 6350, 6351, 6352
|
||||
- "marvell,mv88e6190" : Switch has base address 0x00. Use with models:
|
||||
6190, 6190X, 6191, 6290, 6390, 6390X
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be one of "marvell,mv88e6085" or
|
||||
"marvell,mv88e6190"
|
||||
"marvell,mv88e6190" as indicated above
|
||||
- reg : Address on the MII bus for the switch.
|
||||
|
||||
Optional properties:
|
||||
|
@ -10,6 +10,8 @@ Documentation/devicetree/bindings/phy/phy-bindings.txt.
|
||||
the boot program; should be used in cases where the MAC address assigned to
|
||||
the device by the boot program is different from the "local-mac-address"
|
||||
property;
|
||||
- nvmem-cells: phandle, reference to an nvmem node for the MAC address;
|
||||
- nvmem-cell-names: string, should be "mac-address" if nvmem is to be used;
|
||||
- max-speed: number, specifies maximum speed in Mbit/s supported by the device;
|
||||
- max-frame-size: number, maximum transfer unit (IEEE defined MTU), rather than
|
||||
the maximum frame size (there's contradiction in the Devicetree
|
||||
|
23
Documentation/devicetree/bindings/net/ieee802154/mcr20a.txt
Normal file
23
Documentation/devicetree/bindings/net/ieee802154/mcr20a.txt
Normal file
@ -0,0 +1,23 @@
|
||||
* MCR20A IEEE 802.15.4 *
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "nxp,mcr20a"
|
||||
- spi-max-frequency: maximal bus speed, should be set to a frequency
|
||||
lower than 9000000 depends sync or async operation mode
|
||||
- reg: the chipselect index
|
||||
- interrupts: the interrupt generated by the device. Non high-level
|
||||
can occur deadlocks while handling isr.
|
||||
|
||||
Optional properties:
|
||||
- rst_b-gpio: GPIO spec for the RST_B pin
|
||||
|
||||
Example:
|
||||
|
||||
mcr20a@0 {
|
||||
compatible = "nxp,mcr20a";
|
||||
spi-max-frequency = <9000000>;
|
||||
reg = <0>;
|
||||
interrupts = <17 2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
rst_b-gpio = <&gpio 27 1>
|
||||
};
|
@ -29,6 +29,7 @@ Optional properties for PHY child node:
|
||||
- reset-gpios : Should specify the gpio for phy reset
|
||||
- magic-packet : If present, indicates that the hardware supports waking
|
||||
up via magic packet.
|
||||
- phy-handle : see ethernet.txt file in the same directory
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -9,6 +9,7 @@ Required properties on all platforms:
|
||||
- compatible: Depending on the platform this should be one of:
|
||||
- "amlogic,meson6-dwmac"
|
||||
- "amlogic,meson8b-dwmac"
|
||||
- "amlogic,meson8m2-dwmac"
|
||||
- "amlogic,meson-gxbb-dwmac"
|
||||
Additionally "snps,dwmac" and any applicable more
|
||||
detailed version number described in net/stmmac.txt
|
||||
@ -19,13 +20,13 @@ Required properties on all platforms:
|
||||
configuration (for example the PRG_ETHERNET register range
|
||||
on Meson8b and newer)
|
||||
|
||||
Required properties on Meson8b and newer:
|
||||
Required properties on Meson8b, Meson8m2, GXBB and newer:
|
||||
- clock-names: Should contain the following:
|
||||
- "stmmaceth" - see stmmac.txt
|
||||
- "clkin0" - first parent clock of the internal mux
|
||||
- "clkin1" - second parent clock of the internal mux
|
||||
|
||||
Optional properties on Meson8b and newer:
|
||||
Optional properties on Meson8b, Meson8m2, GXBB and newer:
|
||||
- amlogic,tx-delay-ns: The internal RGMII TX clock delay (provided
|
||||
by this driver) in nanoseconds. Allowed values
|
||||
are: 0ns, 2ns, 4ns, 6ns.
|
||||
|
32
Documentation/devicetree/bindings/net/nixge.txt
Normal file
32
Documentation/devicetree/bindings/net/nixge.txt
Normal file
@ -0,0 +1,32 @@
|
||||
* NI XGE Ethernet controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ni,xge-enet-2.00"
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain tx and rx interrupt
|
||||
- interrupt-names: Should be "rx" and "tx"
|
||||
- phy-mode: See ethernet.txt file in the same directory.
|
||||
- phy-handle: See ethernet.txt file in the same directory.
|
||||
- nvmem-cells: Phandle of nvmem cell containing the MAC address
|
||||
- nvmem-cell-names: Should be "address"
|
||||
|
||||
Examples (10G generic PHY):
|
||||
nixge0: ethernet@40000000 {
|
||||
compatible = "ni,xge-enet-2.00";
|
||||
reg = <0x40000000 0x6000>;
|
||||
|
||||
nvmem-cells = <ð1_addr>;
|
||||
nvmem-cell-names = "address";
|
||||
|
||||
interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>, <0 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "rx", "tx";
|
||||
interrupt-parent = <&intc>;
|
||||
|
||||
phy-mode = "xgmii";
|
||||
phy-handle = <ðernet_phy1>;
|
||||
|
||||
ethernet_phy1: ethernet-phy@4 {
|
||||
compatible = "ethernet-phy-ieee802.3-c45";
|
||||
reg = <4>;
|
||||
};
|
||||
};
|
@ -7,6 +7,7 @@ Required properties:
|
||||
- compatible: Must contain one or more of the following:
|
||||
- "renesas,etheravb-r8a7743" for the R8A7743 SoC.
|
||||
- "renesas,etheravb-r8a7745" for the R8A7745 SoC.
|
||||
- "renesas,etheravb-r8a77470" for the R8A77470 SoC.
|
||||
- "renesas,etheravb-r8a7790" for the R8A7790 SoC.
|
||||
- "renesas,etheravb-r8a7791" for the R8A7791 SoC.
|
||||
- "renesas,etheravb-r8a7792" for the R8A7792 SoC.
|
||||
|
@ -33,6 +33,10 @@ Optional Properties:
|
||||
Select (AKA RS1) output gpio signal (SFP+ only), low: low Tx rate, high:
|
||||
high Tx rate. Must not be present for SFF modules
|
||||
|
||||
- maximum-power-milliwatt : Maximum module power consumption
|
||||
Specifies the maximum power consumption allowable by a module in the
|
||||
slot, in milli-Watts. Presently, modules can be up to 1W, 1.5W or 2W.
|
||||
|
||||
Example #1: Direct serdes to SFP connection
|
||||
|
||||
sfp_eth3: sfp-eth3 {
|
||||
@ -40,6 +44,7 @@ sfp_eth3: sfp-eth3 {
|
||||
i2c-bus = <&sfp_1g_i2c>;
|
||||
los-gpios = <&cpm_gpio2 22 GPIO_ACTIVE_HIGH>;
|
||||
mod-def0-gpios = <&cpm_gpio2 21 GPIO_ACTIVE_LOW>;
|
||||
maximum-power-milliwatt = <1000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&cpm_sfp_1g_pins &cps_sfp_1g_pins>;
|
||||
tx-disable-gpios = <&cps_gpio1 24 GPIO_ACTIVE_HIGH>;
|
||||
|
@ -9,6 +9,7 @@ Required properties:
|
||||
- "socionext,uniphier-pxs2-ave4" : for PXs2 SoC
|
||||
- "socionext,uniphier-ld11-ave4" : for LD11 SoC
|
||||
- "socionext,uniphier-ld20-ave4" : for LD20 SoC
|
||||
- "socionext,uniphier-pxs3-ave4" : for PXs3 SoC
|
||||
- reg: Address where registers are mapped and size of region.
|
||||
- interrupts: Should contain the MAC interrupt.
|
||||
- phy-mode: See ethernet.txt in the same directory. Allow to choose
|
||||
|
@ -25,6 +25,8 @@ Optional property:
|
||||
software needs to take when this pin is
|
||||
strapped in these modes. See data manual
|
||||
for details.
|
||||
- ti,clk-output-sel - Muxing option for CLK_OUT pin - see dt-bindings/net/ti-dp83867.h
|
||||
for applicable values.
|
||||
|
||||
Note: ti,min-output-impedance and ti,max-output-impedance are mutually
|
||||
exclusive. When both properties are present ti,max-output-impedance
|
||||
|
39
Documentation/networking/ice.txt
Normal file
39
Documentation/networking/ice.txt
Normal file
@ -0,0 +1,39 @@
|
||||
Intel(R) Ethernet Connection E800 Series Linux Driver
|
||||
===================================================================
|
||||
|
||||
Intel ice Linux driver.
|
||||
Copyright(c) 2018 Intel Corporation.
|
||||
|
||||
Contents
|
||||
========
|
||||
- Enabling the driver
|
||||
- Support
|
||||
|
||||
The driver in this release supports Intel's E800 Series of products. For
|
||||
more information, visit Intel's support page at http://support.intel.com.
|
||||
|
||||
Enabling the driver
|
||||
===================
|
||||
|
||||
The driver is enabled via the standard kernel configuration system,
|
||||
using the make command:
|
||||
|
||||
Make oldconfig/silentoldconfig/menuconfig/etc.
|
||||
|
||||
The driver is located in the menu structure at:
|
||||
|
||||
-> Device Drivers
|
||||
-> Network device support (NETDEVICES [=y])
|
||||
-> Ethernet driver support
|
||||
-> Intel devices
|
||||
-> Intel(R) Ethernet Connection E800 Series Support
|
||||
|
||||
Support
|
||||
=======
|
||||
|
||||
For general information, go to the Intel support website at:
|
||||
|
||||
http://support.intel.com
|
||||
|
||||
If an issue is identified with the released source code, please email
|
||||
the maintainer listed in the MAINTAINERS file.
|
@ -133,14 +133,11 @@ min_adv_mss - INTEGER
|
||||
|
||||
IP Fragmentation:
|
||||
|
||||
ipfrag_high_thresh - INTEGER
|
||||
Maximum memory used to reassemble IP fragments. When
|
||||
ipfrag_high_thresh bytes of memory is allocated for this purpose,
|
||||
the fragment handler will toss packets until ipfrag_low_thresh
|
||||
is reached. This also serves as a maximum limit to namespaces
|
||||
different from the initial one.
|
||||
ipfrag_high_thresh - LONG INTEGER
|
||||
Maximum memory used to reassemble IP fragments.
|
||||
|
||||
ipfrag_low_thresh - INTEGER
|
||||
ipfrag_low_thresh - LONG INTEGER
|
||||
(Obsolete since linux-4.17)
|
||||
Maximum memory used to reassemble IP fragments before the kernel
|
||||
begins to remove incomplete fragment queues to free up resources.
|
||||
The kernel still accepts new fragments for defragmentation.
|
||||
@ -755,13 +752,13 @@ udp_rmem_min - INTEGER
|
||||
Minimal size of receive buffer used by UDP sockets in moderation.
|
||||
Each UDP socket is able to use the size for receiving data, even if
|
||||
total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
|
||||
Default: 1 page
|
||||
Default: 4K
|
||||
|
||||
udp_wmem_min - INTEGER
|
||||
Minimal size of send buffer used by UDP sockets in moderation.
|
||||
Each UDP socket is able to use the size for sending data, even if
|
||||
total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
|
||||
Default: 1 page
|
||||
Default: 4K
|
||||
|
||||
CIPSOv4 Variables:
|
||||
|
||||
@ -1363,6 +1360,13 @@ flowlabel_reflect - BOOLEAN
|
||||
FALSE: disabled
|
||||
Default: FALSE
|
||||
|
||||
fib_multipath_hash_policy - INTEGER
|
||||
Controls which hash policy to use for multipath routes.
|
||||
Default: 0 (Layer 3)
|
||||
Possible values:
|
||||
0 - Layer 3 (source and destination addresses plus flow label)
|
||||
1 - Layer 4 (standard 5-tuple)
|
||||
|
||||
anycast_src_echo_reply - BOOLEAN
|
||||
Controls the use of anycast addresses as source addresses for ICMPv6
|
||||
echo reply
|
||||
@ -1696,7 +1700,9 @@ disable_ipv6 - BOOLEAN
|
||||
interface and start Duplicate Address Detection, if necessary.
|
||||
|
||||
When this value is changed from 0 to 1 (IPv6 is being disabled),
|
||||
it will dynamically delete all address on the given interface.
|
||||
it will dynamically delete all addresses and routes on the given
|
||||
interface. From now on it will not possible to add addresses/routes
|
||||
to the selected interface.
|
||||
|
||||
accept_dad - INTEGER
|
||||
Whether to accept DAD (Duplicate Address Detection).
|
||||
@ -2094,7 +2100,7 @@ sctp_rmem - vector of 3 INTEGERs: min, default, max
|
||||
It is guaranteed to each SCTP socket (but not association) even
|
||||
under moderate memory pressure.
|
||||
|
||||
Default: 1 page
|
||||
Default: 4K
|
||||
|
||||
sctp_wmem - vector of 3 INTEGERs: min, default, max
|
||||
Currently this tunable has no effect.
|
||||
|
@ -72,11 +72,6 @@ this flag, a process must first signal intent by setting a socket option:
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one)))
|
||||
error(1, errno, "setsockopt zerocopy");
|
||||
|
||||
Setting the socket option only works when the socket is in its initial
|
||||
(TCP_CLOSED) state. Trying to set the option for a socket returned by accept(),
|
||||
for example, will lead to an EBUSY error. In this case, the option should be set
|
||||
to the listening socket and it will be inherited by the accepted sockets.
|
||||
|
||||
Transmission
|
||||
------------
|
||||
|
||||
|
174
Documentation/networking/net_dim.txt
Normal file
174
Documentation/networking/net_dim.txt
Normal file
@ -0,0 +1,174 @@
|
||||
Net DIM - Generic Network Dynamic Interrupt Moderation
|
||||
======================================================
|
||||
|
||||
Author:
|
||||
Tal Gilboa <talgi@mellanox.com>
|
||||
|
||||
|
||||
Contents
|
||||
=========
|
||||
|
||||
- Assumptions
|
||||
- Introduction
|
||||
- The Net DIM Algorithm
|
||||
- Registering a Network Device to DIM
|
||||
- Example
|
||||
|
||||
Part 0: Assumptions
|
||||
======================
|
||||
|
||||
This document assumes the reader has basic knowledge in network drivers
|
||||
and in general interrupt moderation.
|
||||
|
||||
|
||||
Part I: Introduction
|
||||
======================
|
||||
|
||||
Dynamic Interrupt Moderation (DIM) (in networking) refers to changing the
|
||||
interrupt moderation configuration of a channel in order to optimize packet
|
||||
processing. The mechanism includes an algorithm which decides if and how to
|
||||
change moderation parameters for a channel, usually by performing an analysis on
|
||||
runtime data sampled from the system. Net DIM is such a mechanism. In each
|
||||
iteration of the algorithm, it analyses a given sample of the data, compares it
|
||||
to the previous sample and if required, it can decide to change some of the
|
||||
interrupt moderation configuration fields. The data sample is composed of data
|
||||
bandwidth, the number of packets and the number of events. The time between
|
||||
samples is also measured. Net DIM compares the current and the previous data and
|
||||
returns an adjusted interrupt moderation configuration object. In some cases,
|
||||
the algorithm might decide not to change anything. The configuration fields are
|
||||
the minimum duration (microseconds) allowed between events and the maximum
|
||||
number of wanted packets per event. The Net DIM algorithm ascribes importance to
|
||||
increase bandwidth over reducing interrupt rate.
|
||||
|
||||
|
||||
Part II: The Net DIM Algorithm
|
||||
===============================
|
||||
|
||||
Each iteration of the Net DIM algorithm follows these steps:
|
||||
1. Calculates new data sample.
|
||||
2. Compares it to previous sample.
|
||||
3. Makes a decision - suggests interrupt moderation configuration fields.
|
||||
4. Applies a schedule work function, which applies suggested configuration.
|
||||
|
||||
The first two steps are straightforward, both the new and the previous data are
|
||||
supplied by the driver registered to Net DIM. The previous data is the new data
|
||||
supplied to the previous iteration. The comparison step checks the difference
|
||||
between the new and previous data and decides on the result of the last step.
|
||||
A step would result as "better" if bandwidth increases and as "worse" if
|
||||
bandwidth reduces. If there is no change in bandwidth, the packet rate is
|
||||
compared in a similar fashion - increase == "better" and decrease == "worse".
|
||||
In case there is no change in the packet rate as well, the interrupt rate is
|
||||
compared. Here the algorithm tries to optimize for lower interrupt rate so an
|
||||
increase in the interrupt rate is considered "worse" and a decrease is
|
||||
considered "better". Step #2 has an optimization for avoiding false results: it
|
||||
only considers a difference between samples as valid if it is greater than a
|
||||
certain percentage. Also, since Net DIM does not measure anything by itself, it
|
||||
assumes the data provided by the driver is valid.
|
||||
|
||||
Step #3 decides on the suggested configuration based on the result from step #2
|
||||
and the internal state of the algorithm. The states reflect the "direction" of
|
||||
the algorithm: is it going left (reducing moderation), right (increasing
|
||||
moderation) or standing still. Another optimization is that if a decision
|
||||
to stay still is made multiple times, the interval between iterations of the
|
||||
algorithm would increase in order to reduce calculation overhead. Also, after
|
||||
"parking" on one of the most left or most right decisions, the algorithm may
|
||||
decide to verify this decision by taking a step in the other direction. This is
|
||||
done in order to avoid getting stuck in a "deep sleep" scenario. Once a
|
||||
decision is made, an interrupt moderation configuration is selected from
|
||||
the predefined profiles.
|
||||
|
||||
The last step is to notify the registered driver that it should apply the
|
||||
suggested configuration. This is done by scheduling a work function, defined by
|
||||
the Net DIM API and provided by the registered driver.
|
||||
|
||||
As you can see, Net DIM itself does not actively interact with the system. It
|
||||
would have trouble making the correct decisions if the wrong data is supplied to
|
||||
it and it would be useless if the work function would not apply the suggested
|
||||
configuration. This does, however, allow the registered driver some room for
|
||||
manoeuvre as it may provide partial data or ignore the algorithm suggestion
|
||||
under some conditions.
|
||||
|
||||
|
||||
Part III: Registering a Network Device to DIM
|
||||
==============================================
|
||||
|
||||
Net DIM API exposes the main function net_dim(struct net_dim *dim,
|
||||
struct net_dim_sample end_sample). This function is the entry point to the Net
|
||||
DIM algorithm and has to be called every time the driver would like to check if
|
||||
it should change interrupt moderation parameters. The driver should provide two
|
||||
data structures: struct net_dim and struct net_dim_sample. Struct net_dim
|
||||
describes the state of DIM for a specific object (RX queue, TX queue,
|
||||
other queues, etc.). This includes the current selected profile, previous data
|
||||
samples, the callback function provided by the driver and more.
|
||||
Struct net_dim_sample describes a data sample, which will be compared to the
|
||||
data sample stored in struct net_dim in order to decide on the algorithm's next
|
||||
step. The sample should include bytes, packets and interrupts, measured by
|
||||
the driver.
|
||||
|
||||
In order to use Net DIM from a networking driver, the driver needs to call the
|
||||
main net_dim() function. The recommended method is to call net_dim() on each
|
||||
interrupt. Since Net DIM has a built-in moderation and it might decide to skip
|
||||
iterations under certain conditions, there is no need to moderate the net_dim()
|
||||
calls as well. As mentioned above, the driver needs to provide an object of type
|
||||
struct net_dim to the net_dim() function call. It is advised for each entity
|
||||
using Net DIM to hold a struct net_dim as part of its data structure and use it
|
||||
as the main Net DIM API object. The struct net_dim_sample should hold the latest
|
||||
bytes, packets and interrupts count. No need to perform any calculations, just
|
||||
include the raw data.
|
||||
|
||||
The net_dim() call itself does not return anything. Instead Net DIM relies on
|
||||
the driver to provide a callback function, which is called when the algorithm
|
||||
decides to make a change in the interrupt moderation parameters. This callback
|
||||
will be scheduled and run in a separate thread in order not to add overhead to
|
||||
the data flow. After the work is done, Net DIM algorithm needs to be set to
|
||||
the proper state in order to move to the next iteration.
|
||||
|
||||
|
||||
Part IV: Example
|
||||
=================
|
||||
|
||||
The following code demonstrates how to register a driver to Net DIM. The actual
|
||||
usage is not complete but it should make the outline of the usage clear.
|
||||
|
||||
my_driver.c:
|
||||
|
||||
#include <linux/net_dim.h>
|
||||
|
||||
/* Callback for net DIM to schedule on a decision to change moderation */
|
||||
void my_driver_do_dim_work(struct work_struct *work)
|
||||
{
|
||||
/* Get struct net_dim from struct work_struct */
|
||||
struct net_dim *dim = container_of(work, struct net_dim,
|
||||
work);
|
||||
/* Do interrupt moderation related stuff */
|
||||
...
|
||||
|
||||
/* Signal net DIM work is done and it should move to next iteration */
|
||||
dim->state = NET_DIM_START_MEASURE;
|
||||
}
|
||||
|
||||
/* My driver's interrupt handler */
|
||||
int my_driver_handle_interrupt(struct my_driver_entity *my_entity, ...)
|
||||
{
|
||||
...
|
||||
/* A struct to hold current measured data */
|
||||
struct net_dim_sample dim_sample;
|
||||
...
|
||||
/* Initiate data sample struct with current data */
|
||||
net_dim_sample(my_entity->events,
|
||||
my_entity->packets,
|
||||
my_entity->bytes,
|
||||
&dim_sample);
|
||||
/* Call net DIM */
|
||||
net_dim(&my_entity->dim, dim_sample);
|
||||
...
|
||||
}
|
||||
|
||||
/* My entity's initialization function (my_entity was already allocated) */
|
||||
int my_driver_init_my_entity(struct my_driver_entity *my_entity, ...)
|
||||
{
|
||||
...
|
||||
/* Initiate struct work_struct with my driver's callback function */
|
||||
INIT_WORK(&my_entity->dim.work, my_driver_do_dim_work);
|
||||
...
|
||||
}
|
112
Documentation/networking/nf_flowtable.txt
Normal file
112
Documentation/networking/nf_flowtable.txt
Normal file
@ -0,0 +1,112 @@
|
||||
Netfilter's flowtable infrastructure
|
||||
====================================
|
||||
|
||||
This documentation describes the software flowtable infrastructure available in
|
||||
Netfilter since Linux kernel 4.16.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Initial packets follow the classic forwarding path, once the flow enters the
|
||||
established state according to the conntrack semantics (ie. we have seen traffic
|
||||
in both directions), then you can decide to offload the flow to the flowtable
|
||||
from the forward chain via the 'flow offload' action available in nftables.
|
||||
|
||||
Packets that find an entry in the flowtable (ie. flowtable hit) are sent to the
|
||||
output netdevice via neigh_xmit(), hence, they bypass the classic forwarding
|
||||
path (the visible effect is that you do not see these packets from any of the
|
||||
netfilter hooks coming after the ingress). In case of flowtable miss, the packet
|
||||
follows the classic forward path.
|
||||
|
||||
The flowtable uses a resizable hashtable, lookups are based on the following
|
||||
7-tuple selectors: source, destination, layer 3 and layer 4 protocols, source
|
||||
and destination ports and the input interface (useful in case there are several
|
||||
conntrack zones in place).
|
||||
|
||||
Flowtables are populated via the 'flow offload' nftables action, so the user can
|
||||
selectively specify what flows are placed into the flow table. Hence, packets
|
||||
follow the classic forwarding path unless the user explicitly instruct packets
|
||||
to use this new alternative forwarding path via nftables policy.
|
||||
|
||||
This is represented in Fig.1, which describes the classic forwarding path
|
||||
including the Netfilter hooks and the flowtable fastpath bypass.
|
||||
|
||||
userspace process
|
||||
^ |
|
||||
| |
|
||||
_____|____ ____\/___
|
||||
/ \ / \
|
||||
| input | | output |
|
||||
\__________/ \_________/
|
||||
^ |
|
||||
| |
|
||||
_________ __________ --------- _____\/_____
|
||||
/ \ / \ |Routing | / \
|
||||
--> ingress ---> prerouting ---> |decision| | postrouting |--> neigh_xmit
|
||||
\_________/ \__________/ ---------- \____________/ ^
|
||||
| ^ | | ^ |
|
||||
flowtable | | ____\/___ | |
|
||||
| | | / \ | |
|
||||
__\/___ | --------->| forward |------------ |
|
||||
|-----| | \_________/ |
|
||||
|-----| | 'flow offload' rule |
|
||||
|-----| | adds entry to |
|
||||
|_____| | flowtable |
|
||||
| | |
|
||||
/ \ | |
|
||||
/hit\_no_| |
|
||||
\ ? / |
|
||||
\ / |
|
||||
|__yes_________________fastpath bypass ____________________________|
|
||||
|
||||
Fig.1 Netfilter hooks and flowtable interactions
|
||||
|
||||
The flowtable entry also stores the NAT configuration, so all packets are
|
||||
mangled according to the NAT policy that matches the initial packets that went
|
||||
through the classic forwarding path. The TTL is decremented before calling
|
||||
neigh_xmit(). Fragmented traffic is passed up to follow the classic forwarding
|
||||
path given that the transport selectors are missing, therefore flowtable lookup
|
||||
is not possible.
|
||||
|
||||
Example configuration
|
||||
---------------------
|
||||
|
||||
Enabling the flowtable bypass is relatively easy, you only need to create a
|
||||
flowtable and add one rule to your forward chain.
|
||||
|
||||
table inet x {
|
||||
flowtable f {
|
||||
hook ingress priority 0 devices = { eth0, eth1 };
|
||||
}
|
||||
chain y {
|
||||
type filter hook forward priority 0; policy accept;
|
||||
ip protocol tcp flow offload @f
|
||||
counter packets 0 bytes 0
|
||||
}
|
||||
}
|
||||
|
||||
This example adds the flowtable 'f' to the ingress hook of the eth0 and eth1
|
||||
netdevices. You can create as many flowtables as you want in case you need to
|
||||
perform resource partitioning. The flowtable priority defines the order in which
|
||||
hooks are run in the pipeline, this is convenient in case you already have a
|
||||
nftables ingress chain (make sure the flowtable priority is smaller than the
|
||||
nftables ingress chain hence the flowtable runs before in the pipeline).
|
||||
|
||||
The 'flow offload' action from the forward chain 'y' adds an entry to the
|
||||
flowtable for the TCP syn-ack packet coming in the reply direction. Once the
|
||||
flow is offloaded, you will observe that the counter rule in the example above
|
||||
does not get updated for the packets that are being forwarded through the
|
||||
forwarding bypass.
|
||||
|
||||
More reading
|
||||
------------
|
||||
|
||||
This documentation is based on the LWN.net articles [1][2]. Rafal Milecki also
|
||||
made a very complete and comprehensive summary called "A state of network
|
||||
acceleration" that describes how things were before this infrastructure was
|
||||
mailined [3] and it also makes a rough summary of this work [4].
|
||||
|
||||
[1] https://lwn.net/Articles/738214/
|
||||
[2] https://lwn.net/Articles/742164/
|
||||
[3] http://lists.infradead.org/pipermail/lede-dev/2018-January/010830.html
|
||||
[4] http://lists.infradead.org/pipermail/lede-dev/2018-January/010829.html
|
@ -7,15 +7,12 @@ socket interface on 2.4/2.6/3.x kernels. This type of sockets is used for
|
||||
i) capture network traffic with utilities like tcpdump, ii) transmit network
|
||||
traffic, or any other that needs raw access to network interface.
|
||||
|
||||
You can find the latest version of this document at:
|
||||
http://wiki.ipxwarzone.com/index.php5?title=Linux_packet_mmap
|
||||
|
||||
Howto can be found at:
|
||||
http://wiki.gnu-log.net (packet_mmap)
|
||||
https://sites.google.com/site/packetmmap/
|
||||
|
||||
Please send your comments to
|
||||
Ulisses Alonso Camaró <uaca@i.hate.spam.alumni.uv.es>
|
||||
Johann Baudy <johann.baudy@gnu-log.net>
|
||||
Johann Baudy
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
+ Why use PACKET_MMAP
|
||||
@ -51,17 +48,8 @@ From the user standpoint, you should use the higher level libpcap library, which
|
||||
is a de facto standard, portable across nearly all operating systems
|
||||
including Win32.
|
||||
|
||||
Said that, at time of this writing, official libpcap 0.8.1 is out and doesn't include
|
||||
support for PACKET_MMAP, and also probably the libpcap included in your distribution.
|
||||
|
||||
I'm aware of two implementations of PACKET_MMAP in libpcap:
|
||||
|
||||
http://wiki.ipxwarzone.com/ (by Simon Patarin, based on libpcap 0.6.2)
|
||||
http://public.lanl.gov/cpw/ (by Phil Wood, based on lastest libpcap)
|
||||
|
||||
The rest of this document is intended for people who want to understand
|
||||
the low level details or want to improve libpcap by including PACKET_MMAP
|
||||
support.
|
||||
Packet MMAP support was integrated into libpcap around the time of version 1.3.0;
|
||||
TPACKET_V3 support was added in version 1.5.0
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
+ How to use mmap() directly to improve capture process
|
||||
@ -174,7 +162,7 @@ As capture, each frame contains two parts:
|
||||
/* bind socket to eth0 */
|
||||
bind(this->socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll));
|
||||
|
||||
A complete tutorial is available at: http://wiki.gnu-log.net/
|
||||
A complete tutorial is available at: https://sites.google.com/site/packetmmap/
|
||||
|
||||
By default, the user should put data at :
|
||||
frame base + TPACKET_HDRLEN - sizeof(struct sockaddr_ll)
|
||||
|
@ -48,6 +48,9 @@ the transmit and the receive into the kernel.
|
||||
|
||||
setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info));
|
||||
|
||||
Transmit and receive are set separately, but the setup is the same, using either
|
||||
TLS_TX or TLS_RX.
|
||||
|
||||
Sending TLS application data
|
||||
----------------------------
|
||||
|
||||
@ -79,6 +82,28 @@ for memory), or the encryption will always succeed. If send() returns
|
||||
-ENOMEM and some data was left on the socket buffer from a previous
|
||||
call using MSG_MORE, the MSG_MORE data is left on the socket buffer.
|
||||
|
||||
Receiving TLS application data
|
||||
------------------------------
|
||||
|
||||
After setting the TLS_RX socket option, all recv family socket calls
|
||||
are decrypted using TLS parameters provided. A full TLS record must
|
||||
be received before decryption can happen.
|
||||
|
||||
char buffer[16384];
|
||||
recv(sock, buffer, 16384);
|
||||
|
||||
Received data is decrypted directly in to the user buffer if it is
|
||||
large enough, and no additional allocations occur. If the userspace
|
||||
buffer is too small, data is decrypted in the kernel and copied to
|
||||
userspace.
|
||||
|
||||
EINVAL is returned if the TLS version in the received message does not
|
||||
match the version passed in setsockopt.
|
||||
|
||||
EMSGSIZE is returned if the received message is too big.
|
||||
|
||||
EBADMSG is returned if decryption failed for any other reason.
|
||||
|
||||
Send TLS control messages
|
||||
-------------------------
|
||||
|
||||
@ -118,6 +143,43 @@ using a record of type @record_type.
|
||||
Control message data should be provided unencrypted, and will be
|
||||
encrypted by the kernel.
|
||||
|
||||
Receiving TLS control messages
|
||||
------------------------------
|
||||
|
||||
TLS control messages are passed in the userspace buffer, with message
|
||||
type passed via cmsg. If no cmsg buffer is provided, an error is
|
||||
returned if a control message is received. Data messages may be
|
||||
received without a cmsg buffer set.
|
||||
|
||||
char buffer[16384];
|
||||
char cmsg[CMSG_SPACE(sizeof(unsigned char))];
|
||||
struct msghdr msg = {0};
|
||||
msg.msg_control = cmsg;
|
||||
msg.msg_controllen = sizeof(cmsg);
|
||||
|
||||
struct iovec msg_iov;
|
||||
msg_iov.iov_base = buffer;
|
||||
msg_iov.iov_len = 16384;
|
||||
|
||||
msg.msg_iov = &msg_iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
int ret = recvmsg(sock, &msg, 0 /* flags */);
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg->cmsg_level == SOL_TLS &&
|
||||
cmsg->cmsg_type == TLS_GET_RECORD_TYPE) {
|
||||
int record_type = *((unsigned char *)CMSG_DATA(cmsg));
|
||||
// Do something with record_type, and control message data in
|
||||
// buffer.
|
||||
//
|
||||
// Note that record_type may be == to application data (23).
|
||||
} else {
|
||||
// Buffer contains application data.
|
||||
}
|
||||
|
||||
recv will never return data from mixed types of TLS records.
|
||||
|
||||
Integrating in to userspace TLS library
|
||||
---------------------------------------
|
||||
|
||||
@ -126,10 +188,10 @@ layer of a userspace TLS library.
|
||||
|
||||
A patchset to OpenSSL to use ktls as the record layer is here:
|
||||
|
||||
https://github.com/Mellanox/tls-openssl
|
||||
https://github.com/Mellanox/openssl/commits/tls_rx2
|
||||
|
||||
An example of calling send directly after a handshake using
|
||||
gnutls. Since it doesn't implement a full record layer, control
|
||||
messages are not supported:
|
||||
|
||||
https://github.com/Mellanox/tls-af_ktls_tool
|
||||
https://github.com/ktls/af_ktls-tool/commits/RX
|
||||
|
@ -18,7 +18,6 @@
|
||||
- Adjust clock frequency
|
||||
|
||||
+ Ancillary clock features
|
||||
- One short or periodic alarms, with signal delivery to user program
|
||||
- Time stamp external events
|
||||
- Period output signals configurable from user space
|
||||
- Synchronization of the Linux system time via the PPS subsystem
|
||||
@ -48,9 +47,7 @@
|
||||
User space programs may control the clock using standardized
|
||||
ioctls. A program may query, enable, configure, and disable the
|
||||
ancillary clock features. User space can receive time stamped
|
||||
events via blocking read() and poll(). One shot and periodic
|
||||
signals may be configured via the POSIX timer_settime() system
|
||||
call.
|
||||
events via blocking read() and poll().
|
||||
|
||||
** Writing clock drivers
|
||||
|
||||
|
@ -270,6 +270,18 @@ optmem_max
|
||||
Maximum ancillary buffer size allowed per socket. Ancillary data is a sequence
|
||||
of struct cmsghdr structures with appended data.
|
||||
|
||||
fb_tunnels_only_for_init_net
|
||||
----------------------------
|
||||
|
||||
Controls if fallback tunnels (like tunl0, gre0, gretap0, erspan0,
|
||||
sit0, ip6tnl0, ip6gre0) are automatically created when a new
|
||||
network namespace is created, if corresponding tunnel is present
|
||||
in initial network namespace.
|
||||
If set to 1, these devices are not automatically created, and
|
||||
user space is responsible for creating them if needed.
|
||||
|
||||
Default : 0 (for compatibility reasons)
|
||||
|
||||
2. /proc/sys/net/unix - Parameters for Unix domain sockets
|
||||
-------------------------------------------------------
|
||||
|
||||
|
21
MAINTAINERS
21
MAINTAINERS
@ -5603,7 +5603,7 @@ S: Maintained
|
||||
F: drivers/dma/fsldma.*
|
||||
|
||||
FREESCALE eTSEC ETHERNET DRIVER (GIANFAR)
|
||||
M: Claudiu Manoil <claudiu.manoil@freescale.com>
|
||||
M: Claudiu Manoil <claudiu.manoil@nxp.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/freescale/gianfar*
|
||||
@ -7025,6 +7025,7 @@ F: Documentation/networking/ixgbe.txt
|
||||
F: Documentation/networking/ixgbevf.txt
|
||||
F: Documentation/networking/i40e.txt
|
||||
F: Documentation/networking/i40evf.txt
|
||||
F: Documentation/networking/ice.txt
|
||||
F: drivers/net/ethernet/intel/
|
||||
F: drivers/net/ethernet/intel/*/
|
||||
F: include/linux/avf/virtchnl.h
|
||||
@ -8578,6 +8579,15 @@ S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
|
||||
F: drivers/iio/potentiometer/mcp4531.c
|
||||
|
||||
MCR20A IEEE-802.15.4 RADIO DRIVER
|
||||
M: Xue Liu <liuxuenetmail@gmail.com>
|
||||
L: linux-wpan@vger.kernel.org
|
||||
W: https://github.com/xueliu/mcr20a-linux
|
||||
S: Maintained
|
||||
F: drivers/net/ieee802154/mcr20a.c
|
||||
F: drivers/net/ieee802154/mcr20a.h
|
||||
F: Documentation/devicetree/bindings/net/ieee802154/mcr20a.txt
|
||||
|
||||
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@ -9121,6 +9131,13 @@ F: drivers/net/dsa/microchip/*
|
||||
F: include/linux/platform_data/microchip-ksz.h
|
||||
F: Documentation/devicetree/bindings/net/dsa/ksz.txt
|
||||
|
||||
MICROCHIP LAN743X ETHERNET DRIVER
|
||||
M: Bryan Whitehead <bryan.whitehead@microchip.com>
|
||||
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/microchip/lan743x_*
|
||||
|
||||
MICROCHIP USB251XB DRIVER
|
||||
M: Richard Leitner <richard.leitner@skidata.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
@ -14878,7 +14895,7 @@ F: drivers/input/mouse/vmmouse.c
|
||||
F: drivers/input/mouse/vmmouse.h
|
||||
|
||||
VMWARE VMXNET3 ETHERNET DRIVER
|
||||
M: Shrikrishna Khare <skhare@vmware.com>
|
||||
M: Ronak Doshi <doshir@vmware.com>
|
||||
M: "VMware, Inc." <pv-drivers@vmware.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include "armada-370.dtsi"
|
||||
|
||||
@ -243,6 +244,8 @@ switch: switch@10 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
@ -278,6 +281,35 @@ fixed-link {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
switchphy0: switchphy@0 {
|
||||
reg = <0>;
|
||||
interrupt-parent = <&switch>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
switchphy1: switchphy@1 {
|
||||
reg = <1>;
|
||||
interrupt-parent = <&switch>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
switchphy2: switchphy@2 {
|
||||
reg = <2>;
|
||||
interrupt-parent = <&switch>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
switchphy3: switchphy@3 {
|
||||
reg = <3>;
|
||||
interrupt-parent = <&switch>;
|
||||
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1090,6 +1090,10 @@ int __init mac_platform_init(void)
|
||||
macintosh_config->expansion_type == MAC_EXP_PDS_COMM)
|
||||
platform_device_register_simple("macsonic", -1, NULL, 0);
|
||||
|
||||
if (macintosh_config->expansion_type == MAC_EXP_PDS ||
|
||||
macintosh_config->expansion_type == MAC_EXP_PDS_COMM)
|
||||
platform_device_register_simple("mac89x0", -1, NULL, 0);
|
||||
|
||||
if (macintosh_config->ether_type == MAC_ETHER_MACE)
|
||||
platform_device_register_simple("macmace", -1, NULL, 0);
|
||||
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <asm/set_memory.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
/*
|
||||
* assembly code in arch/x86/net/bpf_jit.S
|
||||
@ -61,7 +61,12 @@ static bool is_imm8(int value)
|
||||
|
||||
static bool is_simm32(s64 value)
|
||||
{
|
||||
return value == (s64) (s32) value;
|
||||
return value == (s64)(s32)value;
|
||||
}
|
||||
|
||||
static bool is_uimm32(u64 value)
|
||||
{
|
||||
return value == (u64)(u32)value;
|
||||
}
|
||||
|
||||
/* mov dst, src */
|
||||
@ -98,16 +103,6 @@ static int bpf_size_to_x86_bytes(int bpf_size)
|
||||
#define X86_JLE 0x7E
|
||||
#define X86_JG 0x7F
|
||||
|
||||
static void bpf_flush_icache(void *start, void *end)
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
smp_wmb();
|
||||
flush_icache_range((unsigned long)start, (unsigned long)end);
|
||||
set_fs(old_fs);
|
||||
}
|
||||
|
||||
#define CHOOSE_LOAD_FUNC(K, func) \
|
||||
((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
|
||||
|
||||
@ -212,7 +207,7 @@ struct jit_context {
|
||||
/* emit x64 prologue code for BPF program and check it's size.
|
||||
* bpf_tail_call helper will skip it while jumping into another program
|
||||
*/
|
||||
static void emit_prologue(u8 **pprog, u32 stack_depth)
|
||||
static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
@ -247,18 +242,21 @@ static void emit_prologue(u8 **pprog, u32 stack_depth)
|
||||
/* mov qword ptr [rbp+24],r15 */
|
||||
EMIT4(0x4C, 0x89, 0x7D, 24);
|
||||
|
||||
/* Clear the tail call counter (tail_call_cnt): for eBPF tail calls
|
||||
* we need to reset the counter to 0. It's done in two instructions,
|
||||
* resetting rax register to 0 (xor on eax gets 0 extended), and
|
||||
* moving it to the counter location.
|
||||
*/
|
||||
if (!ebpf_from_cbpf) {
|
||||
/* Clear the tail call counter (tail_call_cnt): for eBPF tail
|
||||
* calls we need to reset the counter to 0. It's done in two
|
||||
* instructions, resetting rax register to 0, and moving it
|
||||
* to the counter location.
|
||||
*/
|
||||
|
||||
/* xor eax, eax */
|
||||
EMIT2(0x31, 0xc0);
|
||||
/* mov qword ptr [rbp+32], rax */
|
||||
EMIT4(0x48, 0x89, 0x45, 32);
|
||||
/* xor eax, eax */
|
||||
EMIT2(0x31, 0xc0);
|
||||
/* mov qword ptr [rbp+32], rax */
|
||||
EMIT4(0x48, 0x89, 0x45, 32);
|
||||
|
||||
BUILD_BUG_ON(cnt != PROLOGUE_SIZE);
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(cnt != PROLOGUE_SIZE);
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
@ -356,6 +354,86 @@ static void emit_load_skb_data_hlen(u8 **pprog)
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_mov_imm32(u8 **pprog, bool sign_propagate,
|
||||
u32 dst_reg, const u32 imm32)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
u8 b1, b2, b3;
|
||||
int cnt = 0;
|
||||
|
||||
/* optimization: if imm32 is positive, use 'mov %eax, imm32'
|
||||
* (which zero-extends imm32) to save 2 bytes.
|
||||
*/
|
||||
if (sign_propagate && (s32)imm32 < 0) {
|
||||
/* 'mov %rax, imm32' sign extends imm32 */
|
||||
b1 = add_1mod(0x48, dst_reg);
|
||||
b2 = 0xC7;
|
||||
b3 = 0xC0;
|
||||
EMIT3_off32(b1, b2, add_1reg(b3, dst_reg), imm32);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* optimization: if imm32 is zero, use 'xor %eax, %eax'
|
||||
* to save 3 bytes.
|
||||
*/
|
||||
if (imm32 == 0) {
|
||||
if (is_ereg(dst_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, dst_reg));
|
||||
b2 = 0x31; /* xor */
|
||||
b3 = 0xC0;
|
||||
EMIT2(b2, add_2reg(b3, dst_reg, dst_reg));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* mov %eax, imm32 */
|
||||
if (is_ereg(dst_reg))
|
||||
EMIT1(add_1mod(0x40, dst_reg));
|
||||
EMIT1_off32(add_1reg(0xB8, dst_reg), imm32);
|
||||
done:
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_mov_imm64(u8 **pprog, u32 dst_reg,
|
||||
const u32 imm32_hi, const u32 imm32_lo)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
if (is_uimm32(((u64)imm32_hi << 32) | (u32)imm32_lo)) {
|
||||
/* For emitting plain u32, where sign bit must not be
|
||||
* propagated LLVM tends to load imm64 over mov32
|
||||
* directly, so save couple of bytes by just doing
|
||||
* 'mov %eax, imm32' instead.
|
||||
*/
|
||||
emit_mov_imm32(&prog, false, dst_reg, imm32_lo);
|
||||
} else {
|
||||
/* movabsq %rax, imm64 */
|
||||
EMIT2(add_1mod(0x48, dst_reg), add_1reg(0xB8, dst_reg));
|
||||
EMIT(imm32_lo, 4);
|
||||
EMIT(imm32_hi, 4);
|
||||
}
|
||||
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
if (is64) {
|
||||
/* mov dst, src */
|
||||
EMIT_mov(dst_reg, src_reg);
|
||||
} else {
|
||||
/* mov32 dst, src */
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, src_reg));
|
||||
EMIT2(0x89, add_2reg(0xC0, dst_reg, src_reg));
|
||||
}
|
||||
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
int oldproglen, struct jit_context *ctx)
|
||||
{
|
||||
@ -369,7 +447,8 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
int proglen = 0;
|
||||
u8 *prog = temp;
|
||||
|
||||
emit_prologue(&prog, bpf_prog->aux->stack_depth);
|
||||
emit_prologue(&prog, bpf_prog->aux->stack_depth,
|
||||
bpf_prog_was_classic(bpf_prog));
|
||||
|
||||
if (seen_ld_abs)
|
||||
emit_load_skb_data_hlen(&prog);
|
||||
@ -378,7 +457,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
const s32 imm32 = insn->imm;
|
||||
u32 dst_reg = insn->dst_reg;
|
||||
u32 src_reg = insn->src_reg;
|
||||
u8 b1 = 0, b2 = 0, b3 = 0;
|
||||
u8 b2 = 0, b3 = 0;
|
||||
s64 jmp_offset;
|
||||
u8 jmp_cond;
|
||||
bool reload_skb_data;
|
||||
@ -414,16 +493,11 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
EMIT2(b2, add_2reg(0xC0, dst_reg, src_reg));
|
||||
break;
|
||||
|
||||
/* mov dst, src */
|
||||
case BPF_ALU64 | BPF_MOV | BPF_X:
|
||||
EMIT_mov(dst_reg, src_reg);
|
||||
break;
|
||||
|
||||
/* mov32 dst, src */
|
||||
case BPF_ALU | BPF_MOV | BPF_X:
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, src_reg));
|
||||
EMIT2(0x89, add_2reg(0xC0, dst_reg, src_reg));
|
||||
emit_mov_reg(&prog,
|
||||
BPF_CLASS(insn->code) == BPF_ALU64,
|
||||
dst_reg, src_reg);
|
||||
break;
|
||||
|
||||
/* neg dst */
|
||||
@ -486,58 +560,13 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
break;
|
||||
|
||||
case BPF_ALU64 | BPF_MOV | BPF_K:
|
||||
/* optimization: if imm32 is positive,
|
||||
* use 'mov eax, imm32' (which zero-extends imm32)
|
||||
* to save 2 bytes
|
||||
*/
|
||||
if (imm32 < 0) {
|
||||
/* 'mov rax, imm32' sign extends imm32 */
|
||||
b1 = add_1mod(0x48, dst_reg);
|
||||
b2 = 0xC7;
|
||||
b3 = 0xC0;
|
||||
EMIT3_off32(b1, b2, add_1reg(b3, dst_reg), imm32);
|
||||
break;
|
||||
}
|
||||
|
||||
case BPF_ALU | BPF_MOV | BPF_K:
|
||||
/* optimization: if imm32 is zero, use 'xor <dst>,<dst>'
|
||||
* to save 3 bytes.
|
||||
*/
|
||||
if (imm32 == 0) {
|
||||
if (is_ereg(dst_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, dst_reg));
|
||||
b2 = 0x31; /* xor */
|
||||
b3 = 0xC0;
|
||||
EMIT2(b2, add_2reg(b3, dst_reg, dst_reg));
|
||||
break;
|
||||
}
|
||||
|
||||
/* mov %eax, imm32 */
|
||||
if (is_ereg(dst_reg))
|
||||
EMIT1(add_1mod(0x40, dst_reg));
|
||||
EMIT1_off32(add_1reg(0xB8, dst_reg), imm32);
|
||||
emit_mov_imm32(&prog, BPF_CLASS(insn->code) == BPF_ALU64,
|
||||
dst_reg, imm32);
|
||||
break;
|
||||
|
||||
case BPF_LD | BPF_IMM | BPF_DW:
|
||||
/* optimization: if imm64 is zero, use 'xor <dst>,<dst>'
|
||||
* to save 7 bytes.
|
||||
*/
|
||||
if (insn[0].imm == 0 && insn[1].imm == 0) {
|
||||
b1 = add_2mod(0x48, dst_reg, dst_reg);
|
||||
b2 = 0x31; /* xor */
|
||||
b3 = 0xC0;
|
||||
EMIT3(b1, b2, add_2reg(b3, dst_reg, dst_reg));
|
||||
|
||||
insn++;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* movabsq %rax, imm64 */
|
||||
EMIT2(add_1mod(0x48, dst_reg), add_1reg(0xB8, dst_reg));
|
||||
EMIT(insn[0].imm, 4);
|
||||
EMIT(insn[1].imm, 4);
|
||||
|
||||
emit_mov_imm64(&prog, dst_reg, insn[1].imm, insn[0].imm);
|
||||
insn++;
|
||||
i++;
|
||||
break;
|
||||
@ -594,36 +623,38 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
case BPF_ALU | BPF_MUL | BPF_X:
|
||||
case BPF_ALU64 | BPF_MUL | BPF_K:
|
||||
case BPF_ALU64 | BPF_MUL | BPF_X:
|
||||
EMIT1(0x50); /* push rax */
|
||||
EMIT1(0x52); /* push rdx */
|
||||
{
|
||||
bool is64 = BPF_CLASS(insn->code) == BPF_ALU64;
|
||||
|
||||
if (dst_reg != BPF_REG_0)
|
||||
EMIT1(0x50); /* push rax */
|
||||
if (dst_reg != BPF_REG_3)
|
||||
EMIT1(0x52); /* push rdx */
|
||||
|
||||
/* mov r11, dst_reg */
|
||||
EMIT_mov(AUX_REG, dst_reg);
|
||||
|
||||
if (BPF_SRC(insn->code) == BPF_X)
|
||||
/* mov rax, src_reg */
|
||||
EMIT_mov(BPF_REG_0, src_reg);
|
||||
emit_mov_reg(&prog, is64, BPF_REG_0, src_reg);
|
||||
else
|
||||
/* mov rax, imm32 */
|
||||
EMIT3_off32(0x48, 0xC7, 0xC0, imm32);
|
||||
emit_mov_imm32(&prog, is64, BPF_REG_0, imm32);
|
||||
|
||||
if (BPF_CLASS(insn->code) == BPF_ALU64)
|
||||
if (is64)
|
||||
EMIT1(add_1mod(0x48, AUX_REG));
|
||||
else if (is_ereg(AUX_REG))
|
||||
EMIT1(add_1mod(0x40, AUX_REG));
|
||||
/* mul(q) r11 */
|
||||
EMIT2(0xF7, add_1reg(0xE0, AUX_REG));
|
||||
|
||||
/* mov r11, rax */
|
||||
EMIT_mov(AUX_REG, BPF_REG_0);
|
||||
|
||||
EMIT1(0x5A); /* pop rdx */
|
||||
EMIT1(0x58); /* pop rax */
|
||||
|
||||
/* mov dst_reg, r11 */
|
||||
EMIT_mov(dst_reg, AUX_REG);
|
||||
if (dst_reg != BPF_REG_3)
|
||||
EMIT1(0x5A); /* pop rdx */
|
||||
if (dst_reg != BPF_REG_0) {
|
||||
/* mov dst_reg, rax */
|
||||
EMIT_mov(dst_reg, BPF_REG_0);
|
||||
EMIT1(0x58); /* pop rax */
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
/* shifts */
|
||||
case BPF_ALU | BPF_LSH | BPF_K:
|
||||
case BPF_ALU | BPF_RSH | BPF_K:
|
||||
@ -641,7 +672,11 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
case BPF_RSH: b3 = 0xE8; break;
|
||||
case BPF_ARSH: b3 = 0xF8; break;
|
||||
}
|
||||
EMIT3(0xC1, add_1reg(b3, dst_reg), imm32);
|
||||
|
||||
if (imm32 == 1)
|
||||
EMIT2(0xD1, add_1reg(b3, dst_reg));
|
||||
else
|
||||
EMIT3(0xC1, add_1reg(b3, dst_reg), imm32);
|
||||
break;
|
||||
|
||||
case BPF_ALU | BPF_LSH | BPF_X:
|
||||
@ -1222,7 +1257,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
bpf_jit_dump(prog->len, proglen, pass + 1, image);
|
||||
|
||||
if (image) {
|
||||
bpf_flush_icache(header, image + proglen);
|
||||
if (!prog->is_func || extra_pass) {
|
||||
bpf_jit_binary_lock_ro(header);
|
||||
} else {
|
||||
|
@ -3173,14 +3173,10 @@ static void init_sram(struct idt77252_dev *card)
|
||||
(u32) 0xffffffff);
|
||||
}
|
||||
|
||||
writel((SAR_FBQ0_LOW << 28) | 0x00000000 | 0x00000000 |
|
||||
(SAR_FB_SIZE_0 / 48), SAR_REG_FBQS0);
|
||||
writel((SAR_FBQ1_LOW << 28) | 0x00000000 | 0x00000000 |
|
||||
(SAR_FB_SIZE_1 / 48), SAR_REG_FBQS1);
|
||||
writel((SAR_FBQ2_LOW << 28) | 0x00000000 | 0x00000000 |
|
||||
(SAR_FB_SIZE_2 / 48), SAR_REG_FBQS2);
|
||||
writel((SAR_FBQ3_LOW << 28) | 0x00000000 | 0x00000000 |
|
||||
(SAR_FB_SIZE_3 / 48), SAR_REG_FBQS3);
|
||||
writel((SAR_FBQ0_LOW << 28) | (SAR_FB_SIZE_0 / 48), SAR_REG_FBQS0);
|
||||
writel((SAR_FBQ1_LOW << 28) | (SAR_FB_SIZE_1 / 48), SAR_REG_FBQS1);
|
||||
writel((SAR_FBQ2_LOW << 28) | (SAR_FB_SIZE_2 / 48), SAR_REG_FBQS2);
|
||||
writel((SAR_FBQ3_LOW << 28) | (SAR_FB_SIZE_3 / 48), SAR_REG_FBQS3);
|
||||
|
||||
/* Initialize rate table */
|
||||
for (i = 0; i < 256; i++) {
|
||||
|
@ -55,7 +55,7 @@ config BCMA_DRIVER_PCI
|
||||
|
||||
config BCMA_DRIVER_PCI_HOSTMODE
|
||||
bool "Driver for PCI core working in hostmode"
|
||||
depends on MIPS && BCMA_DRIVER_PCI && PCI_DRIVERS_LEGACY
|
||||
depends on MIPS && BCMA_DRIVER_PCI && PCI_DRIVERS_LEGACY && BCMA = y
|
||||
help
|
||||
PCI core hostmode operation (external PCI bus).
|
||||
|
||||
|
@ -203,7 +203,7 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
|
||||
* Add some delay; allow resources to come up and settle.
|
||||
* Delay is required for SoC (early init).
|
||||
*/
|
||||
mdelay(2);
|
||||
usleep_range(2000, 2500);
|
||||
}
|
||||
|
||||
/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
|
||||
|
@ -297,6 +297,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0016) },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0018) },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_FOXCONN, 0xe092) },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_HP, 0x804a) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
|
||||
|
@ -147,6 +147,7 @@ config BT_HCIUART_ATH3K
|
||||
config BT_HCIUART_LL
|
||||
bool "HCILL protocol support"
|
||||
depends on BT_HCIUART_SERDEV
|
||||
select BT_HCIUART_H4
|
||||
help
|
||||
HCILL (HCI Low Level) is a serial protocol for communication
|
||||
between Bluetooth device and host. This protocol is required for
|
||||
@ -242,8 +243,7 @@ config BT_HCIBCM203X
|
||||
|
||||
config BT_HCIBPA10X
|
||||
tristate "HCI BPA10x USB driver"
|
||||
depends on USB && BT_HCIUART
|
||||
select BT_HCIUART_H4
|
||||
depends on USB
|
||||
help
|
||||
Bluetooth HCI BPA10x USB driver.
|
||||
This driver provides support for the Digianswer BPA 100/105 Bluetooth
|
||||
@ -305,22 +305,6 @@ config BT_HCIBLUECARD
|
||||
Say Y here to compile support for HCI BlueCard devices into the
|
||||
kernel or say M to compile it as module (bluecard_cs).
|
||||
|
||||
config BT_HCIBTUART
|
||||
tristate "HCI UART (PC Card) device driver"
|
||||
depends on PCMCIA
|
||||
help
|
||||
Bluetooth HCI UART (PC Card) driver.
|
||||
This driver provides support for Bluetooth PCMCIA devices with
|
||||
an UART interface:
|
||||
Xircom CreditCard Bluetooth Adapter
|
||||
Xircom RealPort2 Bluetooth Adapter
|
||||
Sphinx PICO Card
|
||||
H-Soft blue+Card
|
||||
Cyber-blue Compact Flash Card
|
||||
|
||||
Say Y here to compile support for HCI UART devices into the
|
||||
kernel or say M to compile it as module (btuart_cs).
|
||||
|
||||
config BT_HCIVHCI
|
||||
tristate "HCI VHCI (Virtual HCI device) driver"
|
||||
help
|
||||
@ -392,4 +376,14 @@ config BT_QCOMSMD
|
||||
Say Y here to compile support for HCI over Qualcomm SMD into the
|
||||
kernel or say M to compile as a module.
|
||||
|
||||
config BT_HCIRSI
|
||||
tristate
|
||||
help
|
||||
Redpine BT driver.
|
||||
This driver handles BT traffic from upper layers and pass
|
||||
to the RSI_91x coex module for further scheduling to device
|
||||
|
||||
Say Y here to compile support for HCI over Redpine into the
|
||||
kernel or say M to compile as a module.
|
||||
|
||||
endmenu
|
||||
|
@ -11,7 +11,6 @@ obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
|
||||
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
|
||||
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
|
||||
obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
|
||||
obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
|
||||
|
||||
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
|
||||
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
|
||||
@ -28,6 +27,8 @@ obj-$(CONFIG_BT_QCA) += btqca.o
|
||||
|
||||
obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
|
||||
|
||||
obj-$(CONFIG_BT_HCIRSI) += btrsi.o
|
||||
|
||||
btmrvl-y := btmrvl_main.o
|
||||
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
|
||||
|
||||
|
@ -203,6 +203,12 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
static inline void ath3k_log_failed_loading(int err, int len, int size)
|
||||
{
|
||||
BT_ERR("Error in firmware loading err = %d, len = %d, size = %d",
|
||||
err, len, size);
|
||||
}
|
||||
|
||||
#define USB_REQ_DFU_DNLOAD 1
|
||||
#define BULK_SIZE 4096
|
||||
#define FW_HDR_SIZE 20
|
||||
@ -227,15 +233,16 @@ static int ath3k_load_firmware(struct usb_device *udev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(send_buf, firmware->data, 20);
|
||||
memcpy(send_buf, firmware->data, FW_HDR_SIZE);
|
||||
err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR,
|
||||
0, 0, send_buf, 20, USB_CTRL_SET_TIMEOUT);
|
||||
0, 0, send_buf, FW_HDR_SIZE,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (err < 0) {
|
||||
BT_ERR("Can't change to loading configuration err");
|
||||
goto error;
|
||||
}
|
||||
sent += 20;
|
||||
count -= 20;
|
||||
sent += FW_HDR_SIZE;
|
||||
count -= FW_HDR_SIZE;
|
||||
|
||||
pipe = usb_sndbulkpipe(udev, 0x02);
|
||||
|
||||
@ -250,8 +257,7 @@ static int ath3k_load_firmware(struct usb_device *udev,
|
||||
&len, 3000);
|
||||
|
||||
if (err || (len != size)) {
|
||||
BT_ERR("Error in firmware loading err = %d,"
|
||||
"len = %d, size = %d", err, len, size);
|
||||
ath3k_log_failed_loading(err, len, size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -350,8 +356,7 @@ static int ath3k_load_fwfile(struct usb_device *udev,
|
||||
err = usb_bulk_msg(udev, pipe, send_buf, size,
|
||||
&len, 3000);
|
||||
if (err || (len != size)) {
|
||||
BT_ERR("Error in firmware loading err = %d,"
|
||||
"len = %d, size = %d", err, len, size);
|
||||
ath3k_log_failed_loading(err, len, size);
|
||||
kfree(send_buf);
|
||||
return err;
|
||||
}
|
||||
@ -398,7 +403,7 @@ static int ath3k_set_normal_mode(struct usb_device *udev)
|
||||
static int ath3k_load_patch(struct usb_device *udev)
|
||||
{
|
||||
unsigned char fw_state;
|
||||
char filename[ATH3K_NAME_LEN] = {0};
|
||||
char filename[ATH3K_NAME_LEN];
|
||||
const struct firmware *firmware;
|
||||
struct ath3k_version fw_version;
|
||||
__u32 pt_rom_version, pt_build_version;
|
||||
@ -451,7 +456,7 @@ static int ath3k_load_patch(struct usb_device *udev)
|
||||
static int ath3k_load_syscfg(struct usb_device *udev)
|
||||
{
|
||||
unsigned char fw_state;
|
||||
char filename[ATH3K_NAME_LEN] = {0};
|
||||
char filename[ATH3K_NAME_LEN];
|
||||
const struct firmware *firmware;
|
||||
struct ath3k_version fw_version;
|
||||
int clk_value, ret;
|
||||
@ -522,7 +527,6 @@ static int ath3k_probe(struct usb_interface *intf,
|
||||
|
||||
/* load patch and sysconfig files for AR3012 */
|
||||
if (id->driver_info & BTUSB_ATH3012) {
|
||||
|
||||
/* New firmware with patch and sysconfig files already loaded */
|
||||
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
|
||||
return -ENODEV;
|
||||
@ -565,7 +569,7 @@ static int ath3k_probe(struct usb_interface *intf,
|
||||
|
||||
static void ath3k_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
BT_DBG("ath3k_disconnect intf %p", intf);
|
||||
BT_DBG("%s intf %p", __func__, intf);
|
||||
}
|
||||
|
||||
static struct usb_driver ath3k_driver = {
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "hci_uart.h"
|
||||
#include "h4_recv.h"
|
||||
|
||||
#define VERSION "0.11"
|
||||
|
||||
|
@ -183,7 +183,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
BT_ERR("No free skb");
|
||||
return -ENOMEM;
|
||||
|
@ -689,7 +689,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
||||
int ret, num_blocks, blksz;
|
||||
struct sk_buff *skb = NULL;
|
||||
u32 type;
|
||||
u8 *payload = NULL;
|
||||
u8 *payload;
|
||||
struct hci_dev *hdev = priv->btmrvl_dev.hcidev;
|
||||
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
|
||||
|
||||
@ -920,7 +920,7 @@ static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
|
||||
{
|
||||
struct sdio_func *func;
|
||||
u8 reg;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!card || !card->func) {
|
||||
BT_ERR("Error: card or function is NULL!");
|
||||
|
187
drivers/bluetooth/btrsi.c
Normal file
187
drivers/bluetooth/btrsi.c
Normal file
@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright (c) 2017 Redpine Signals Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <net/rsi_91x.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#define RSI_HEADROOM_FOR_BT_HAL 16
|
||||
#define RSI_FRAME_DESC_SIZE 16
|
||||
|
||||
struct rsi_hci_adapter {
|
||||
void *priv;
|
||||
struct rsi_proto_ops *proto_ops;
|
||||
struct hci_dev *hdev;
|
||||
};
|
||||
|
||||
static int rsi_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsi_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsi_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct rsi_hci_adapter *h_adapter = hci_get_drvdata(hdev);
|
||||
struct sk_buff *new_skb = NULL;
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (skb_headroom(skb) < RSI_HEADROOM_FOR_BT_HAL) {
|
||||
/* Insufficient skb headroom - allocate a new skb */
|
||||
new_skb = skb_realloc_headroom(skb, RSI_HEADROOM_FOR_BT_HAL);
|
||||
if (unlikely(!new_skb))
|
||||
return -ENOMEM;
|
||||
bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb);
|
||||
kfree_skb(skb);
|
||||
skb = new_skb;
|
||||
}
|
||||
|
||||
return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb,
|
||||
RSI_BT_Q);
|
||||
}
|
||||
|
||||
static int rsi_hci_recv_pkt(void *priv, const u8 *pkt)
|
||||
{
|
||||
struct rsi_hci_adapter *h_adapter = priv;
|
||||
struct hci_dev *hdev = h_adapter->hdev;
|
||||
struct sk_buff *skb;
|
||||
int pkt_len = get_unaligned_le16(pkt) & 0x0fff;
|
||||
|
||||
skb = dev_alloc_skb(pkt_len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len);
|
||||
skb_put(skb, pkt_len);
|
||||
h_adapter->hdev->stat.byte_rx += skb->len;
|
||||
|
||||
hci_skb_pkt_type(skb) = pkt[14];
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
|
||||
{
|
||||
struct rsi_hci_adapter *h_adapter = NULL;
|
||||
struct hci_dev *hdev;
|
||||
int err = 0;
|
||||
|
||||
h_adapter = kzalloc(sizeof(*h_adapter), GFP_KERNEL);
|
||||
if (!h_adapter)
|
||||
return -ENOMEM;
|
||||
|
||||
h_adapter->priv = priv;
|
||||
ops->set_bt_context(priv, h_adapter);
|
||||
h_adapter->proto_ops = ops;
|
||||
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
BT_ERR("Failed to alloc HCI device");
|
||||
goto err;
|
||||
}
|
||||
|
||||
h_adapter->hdev = hdev;
|
||||
|
||||
if (ops->get_host_intf(priv) == RSI_HOST_INTF_SDIO)
|
||||
hdev->bus = HCI_SDIO;
|
||||
else
|
||||
hdev->bus = HCI_USB;
|
||||
|
||||
hci_set_drvdata(hdev, h_adapter);
|
||||
hdev->dev_type = HCI_PRIMARY;
|
||||
hdev->open = rsi_hci_open;
|
||||
hdev->close = rsi_hci_close;
|
||||
hdev->flush = rsi_hci_flush;
|
||||
hdev->send = rsi_hci_send_pkt;
|
||||
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
BT_ERR("HCI registration failed with errcode %d", err);
|
||||
hci_free_dev(hdev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
h_adapter->hdev = NULL;
|
||||
kfree(h_adapter);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void rsi_hci_detach(void *priv)
|
||||
{
|
||||
struct rsi_hci_adapter *h_adapter = priv;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
if (!h_adapter)
|
||||
return;
|
||||
|
||||
hdev = h_adapter->hdev;
|
||||
if (hdev) {
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
h_adapter->hdev = NULL;
|
||||
}
|
||||
|
||||
kfree(h_adapter);
|
||||
}
|
||||
|
||||
const struct rsi_mod_ops rsi_bt_ops = {
|
||||
.attach = rsi_hci_attach,
|
||||
.detach = rsi_hci_detach,
|
||||
.recv_pkt = rsi_hci_recv_pkt,
|
||||
};
|
||||
EXPORT_SYMBOL(rsi_bt_ops);
|
||||
|
||||
static int rsi_91x_bt_module_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rsi_91x_bt_module_exit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(rsi_91x_bt_module_init);
|
||||
module_exit(rsi_91x_bt_module_exit);
|
||||
MODULE_AUTHOR("Redpine Signals Inc");
|
||||
MODULE_DESCRIPTION("RSI BT driver");
|
||||
MODULE_SUPPORTED_DEVICE("RSI-BT");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
@ -35,6 +35,60 @@
|
||||
#define RTL_ROM_LMP_8761A 0x8761
|
||||
#define RTL_ROM_LMP_8822B 0x8822
|
||||
|
||||
#define IC_MATCH_FL_LMPSUBV (1 << 0)
|
||||
#define IC_MATCH_FL_HCIREV (1 << 1)
|
||||
#define IC_INFO(lmps, hcir) \
|
||||
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \
|
||||
.lmp_subver = (lmps), \
|
||||
.hci_rev = (hcir)
|
||||
|
||||
struct id_table {
|
||||
__u16 match_flags;
|
||||
__u16 lmp_subver;
|
||||
__u16 hci_rev;
|
||||
bool config_needed;
|
||||
char *fw_name;
|
||||
char *cfg_name;
|
||||
};
|
||||
|
||||
static const struct id_table ic_id_table[] = {
|
||||
/* 8723B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb),
|
||||
.config_needed = false,
|
||||
.fw_name = "rtl_bt/rtl8723b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723b_config.bin" },
|
||||
|
||||
/* 8723D */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd),
|
||||
.config_needed = true,
|
||||
.fw_name = "rtl_bt/rtl8723d_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config.bin" },
|
||||
|
||||
/* 8821A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa),
|
||||
.config_needed = false,
|
||||
.fw_name = "rtl_bt/rtl8821a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821a_config.bin" },
|
||||
|
||||
/* 8821C */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc),
|
||||
.config_needed = false,
|
||||
.fw_name = "rtl_bt/rtl8821c_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821c_config.bin" },
|
||||
|
||||
/* 8761A */
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0,
|
||||
.config_needed = false,
|
||||
.fw_name = "rtl_bt/rtl8761a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761a_config.bin" },
|
||||
|
||||
/* 8822B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb),
|
||||
.config_needed = true,
|
||||
.fw_name = "rtl_bt/rtl8822b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822b_config.bin" },
|
||||
};
|
||||
|
||||
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
|
||||
{
|
||||
struct rtl_rom_version_evt *rom_version;
|
||||
@ -64,9 +118,9 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
const struct firmware *fw,
|
||||
unsigned char **_buf)
|
||||
static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
const struct firmware *fw,
|
||||
unsigned char **_buf)
|
||||
{
|
||||
const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
|
||||
struct rtl_epatch_header *epatch_info;
|
||||
@ -88,6 +142,8 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
{ RTL_ROM_LMP_8821A, 2 },
|
||||
{ RTL_ROM_LMP_8761A, 3 },
|
||||
{ RTL_ROM_LMP_8822B, 8 },
|
||||
{ RTL_ROM_LMP_8723B, 9 }, /* 8723D */
|
||||
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */
|
||||
};
|
||||
|
||||
ret = rtl_read_rom_version(hdev, &rom_version);
|
||||
@ -320,8 +376,8 @@ static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
|
||||
const char *fw_name)
|
||||
static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 hci_rev,
|
||||
u16 lmp_subver)
|
||||
{
|
||||
unsigned char *fw_data = NULL;
|
||||
const struct firmware *fw;
|
||||
@ -330,39 +386,40 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
|
||||
u8 *cfg_buff = NULL;
|
||||
u8 *tbuff;
|
||||
char *cfg_name = NULL;
|
||||
bool config_needed = false;
|
||||
char *fw_name = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
|
||||
(ic_id_table[i].lmp_subver != lmp_subver))
|
||||
continue;
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
|
||||
(ic_id_table[i].hci_rev != hci_rev))
|
||||
continue;
|
||||
|
||||
switch (lmp_subver) {
|
||||
case RTL_ROM_LMP_8723B:
|
||||
cfg_name = "rtl_bt/rtl8723b_config.bin";
|
||||
break;
|
||||
case RTL_ROM_LMP_8821A:
|
||||
cfg_name = "rtl_bt/rtl8821a_config.bin";
|
||||
break;
|
||||
case RTL_ROM_LMP_8761A:
|
||||
cfg_name = "rtl_bt/rtl8761a_config.bin";
|
||||
break;
|
||||
case RTL_ROM_LMP_8822B:
|
||||
cfg_name = "rtl_bt/rtl8822b_config.bin";
|
||||
config_needed = true;
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: rtl: no config according to lmp_subver %04x",
|
||||
hdev->name, lmp_subver);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(ic_id_table)) {
|
||||
BT_ERR("%s: unknown IC info, lmp subver %04x, hci rev %04x",
|
||||
hdev->name, lmp_subver, hci_rev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg_name = ic_id_table[i].cfg_name;
|
||||
|
||||
if (cfg_name) {
|
||||
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
|
||||
if (cfg_sz < 0) {
|
||||
cfg_sz = 0;
|
||||
if (config_needed)
|
||||
if (ic_id_table[i].config_needed)
|
||||
BT_ERR("Necessary config file %s not found\n",
|
||||
cfg_name);
|
||||
}
|
||||
} else
|
||||
cfg_sz = 0;
|
||||
|
||||
fw_name = ic_id_table[i].fw_name;
|
||||
bt_dev_info(hdev, "rtl: loading %s", fw_name);
|
||||
ret = request_firmware(&fw, fw_name, &hdev->dev);
|
||||
if (ret < 0) {
|
||||
@ -370,7 +427,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
|
||||
goto err_req_fw;
|
||||
}
|
||||
|
||||
ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
|
||||
ret = rtlbt_parse_firmware(hdev, lmp_subver, fw, &fw_data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
@ -429,7 +486,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *resp;
|
||||
u16 lmp_subver;
|
||||
u16 hci_rev, lmp_subver;
|
||||
|
||||
skb = btrtl_read_local_version(hdev);
|
||||
if (IS_ERR(skb))
|
||||
@ -441,6 +498,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
resp->hci_ver, resp->hci_rev,
|
||||
resp->lmp_ver, resp->lmp_subver);
|
||||
|
||||
hci_rev = le16_to_cpu(resp->hci_rev);
|
||||
lmp_subver = le16_to_cpu(resp->lmp_subver);
|
||||
kfree_skb(skb);
|
||||
|
||||
@ -455,17 +513,10 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
case RTL_ROM_LMP_3499:
|
||||
return btrtl_setup_rtl8723a(hdev);
|
||||
case RTL_ROM_LMP_8723B:
|
||||
return btrtl_setup_rtl8723b(hdev, lmp_subver,
|
||||
"rtl_bt/rtl8723b_fw.bin");
|
||||
case RTL_ROM_LMP_8821A:
|
||||
return btrtl_setup_rtl8723b(hdev, lmp_subver,
|
||||
"rtl_bt/rtl8821a_fw.bin");
|
||||
case RTL_ROM_LMP_8761A:
|
||||
return btrtl_setup_rtl8723b(hdev, lmp_subver,
|
||||
"rtl_bt/rtl8761a_fw.bin");
|
||||
case RTL_ROM_LMP_8822B:
|
||||
return btrtl_setup_rtl8723b(hdev, lmp_subver,
|
||||
"rtl_bt/rtl8822b_fw.bin");
|
||||
return btrtl_setup_rtl8723b(hdev, hci_rev, lmp_subver);
|
||||
default:
|
||||
bt_dev_info(hdev, "rtl: assuming no firmware upload needed");
|
||||
return 0;
|
||||
|
@ -1,675 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Driver for Bluetooth PCMCIA cards with HCI UART interface
|
||||
*
|
||||
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation;
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
|
||||
|
||||
/* ======================== Module parameters ======================== */
|
||||
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
|
||||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
struct btuart_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
struct sk_buff_head txq;
|
||||
unsigned long tx_state;
|
||||
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
};
|
||||
|
||||
|
||||
static int btuart_config(struct pcmcia_device *link);
|
||||
static void btuart_release(struct pcmcia_device *link);
|
||||
|
||||
static void btuart_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
|
||||
/* Maximum baud rate */
|
||||
#define SPEED_MAX 115200
|
||||
|
||||
/* Default baud rate: 57600, 115200, 230400 or 460800 */
|
||||
#define DEFAULT_BAUD_RATE 115200
|
||||
|
||||
|
||||
/* Transmit states */
|
||||
#define XMIT_SENDING 1
|
||||
#define XMIT_WAKEUP 2
|
||||
#define XMIT_WAITING 8
|
||||
|
||||
/* Receiver states */
|
||||
#define RECV_WAIT_PACKET_TYPE 0
|
||||
#define RECV_WAIT_EVENT_HEADER 1
|
||||
#define RECV_WAIT_ACL_HEADER 2
|
||||
#define RECV_WAIT_SCO_HEADER 3
|
||||
#define RECV_WAIT_DATA 4
|
||||
|
||||
|
||||
|
||||
/* ======================== Interrupt handling ======================== */
|
||||
|
||||
|
||||
static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
|
||||
{
|
||||
int actual = 0;
|
||||
|
||||
/* Tx FIFO should be empty */
|
||||
if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
|
||||
return 0;
|
||||
|
||||
/* Fill FIFO with current frame */
|
||||
while ((fifo_size-- > 0) && (actual < len)) {
|
||||
/* Transmit next byte */
|
||||
outb(buf[actual], iobase + UART_TX);
|
||||
actual++;
|
||||
}
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
static void btuart_write_wakeup(struct btuart_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
|
||||
set_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register struct sk_buff *skb;
|
||||
int len;
|
||||
|
||||
clear_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
if (!pcmcia_dev_present(info->p_dev))
|
||||
return;
|
||||
|
||||
skb = skb_dequeue(&(info->txq));
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
/* Send frame */
|
||||
len = btuart_write(iobase, 16, skb->data, skb->len);
|
||||
set_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
if (len == skb->len) {
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
skb_pull(skb, len);
|
||||
skb_queue_head(&(info->txq), skb);
|
||||
}
|
||||
|
||||
info->hdev->stat.byte_tx += len;
|
||||
|
||||
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
|
||||
|
||||
clear_bit(XMIT_SENDING, &(info->tx_state));
|
||||
}
|
||||
|
||||
|
||||
static void btuart_receive(struct btuart_info *info)
|
||||
{
|
||||
unsigned int iobase;
|
||||
int boguscount = 0;
|
||||
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
do {
|
||||
info->hdev->stat.byte_rx++;
|
||||
|
||||
/* Allocate packet */
|
||||
if (!info->rx_skb) {
|
||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||
info->rx_count = 0;
|
||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!info->rx_skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
|
||||
|
||||
hci_skb_pkt_type(info->rx_skb) = inb(iobase + UART_RX);
|
||||
|
||||
switch (hci_skb_pkt_type(info->rx_skb)) {
|
||||
|
||||
case HCI_EVENT_PKT:
|
||||
info->rx_state = RECV_WAIT_EVENT_HEADER;
|
||||
info->rx_count = HCI_EVENT_HDR_SIZE;
|
||||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
info->rx_state = RECV_WAIT_ACL_HEADER;
|
||||
info->rx_count = HCI_ACL_HDR_SIZE;
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
info->rx_state = RECV_WAIT_SCO_HEADER;
|
||||
info->rx_count = HCI_SCO_HDR_SIZE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown packet */
|
||||
BT_ERR("Unknown HCI packet with type 0x%02x received",
|
||||
hci_skb_pkt_type(info->rx_skb));
|
||||
info->hdev->stat.err_rx++;
|
||||
|
||||
kfree_skb(info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
skb_put_u8(info->rx_skb, inb(iobase + UART_RX));
|
||||
info->rx_count--;
|
||||
|
||||
if (info->rx_count == 0) {
|
||||
|
||||
int dlen;
|
||||
struct hci_event_hdr *eh;
|
||||
struct hci_acl_hdr *ah;
|
||||
struct hci_sco_hdr *sh;
|
||||
|
||||
|
||||
switch (info->rx_state) {
|
||||
|
||||
case RECV_WAIT_EVENT_HEADER:
|
||||
eh = hci_event_hdr(info->rx_skb);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = eh->plen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_ACL_HEADER:
|
||||
ah = hci_acl_hdr(info->rx_skb);
|
||||
dlen = __le16_to_cpu(ah->dlen);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = dlen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_SCO_HEADER:
|
||||
sh = hci_sco_hdr(info->rx_skb);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = sh->dlen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_DATA:
|
||||
hci_recv_frame(info->hdev, info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Make sure we don't stay here too long */
|
||||
if (boguscount++ > 16)
|
||||
break;
|
||||
|
||||
} while (inb(iobase + UART_LSR) & UART_LSR_DR);
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
struct btuart_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
int boguscount = 0;
|
||||
int iir, lsr;
|
||||
irqreturn_t r = IRQ_NONE;
|
||||
|
||||
if (!info || !info->hdev)
|
||||
/* our irq handler is shared */
|
||||
return IRQ_NONE;
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
spin_lock(&(info->lock));
|
||||
|
||||
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
|
||||
while (iir) {
|
||||
r = IRQ_HANDLED;
|
||||
|
||||
/* Clear interrupt */
|
||||
lsr = inb(iobase + UART_LSR);
|
||||
|
||||
switch (iir) {
|
||||
case UART_IIR_RLSI:
|
||||
BT_ERR("RLSI");
|
||||
break;
|
||||
case UART_IIR_RDI:
|
||||
/* Receive interrupt */
|
||||
btuart_receive(info);
|
||||
break;
|
||||
case UART_IIR_THRI:
|
||||
if (lsr & UART_LSR_THRE) {
|
||||
/* Transmitter ready for data */
|
||||
btuart_write_wakeup(info);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unhandled IIR=%#x", iir);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make sure we don't stay here too long */
|
||||
if (boguscount++ > 100)
|
||||
break;
|
||||
|
||||
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
|
||||
|
||||
}
|
||||
|
||||
spin_unlock(&(info->lock));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void btuart_change_speed(struct btuart_info *info,
|
||||
unsigned int speed)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase;
|
||||
int fcr; /* FIFO control reg */
|
||||
int lcr; /* Line control reg */
|
||||
int divisor;
|
||||
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
spin_lock_irqsave(&(info->lock), flags);
|
||||
|
||||
/* Turn off interrupts */
|
||||
outb(0, iobase + UART_IER);
|
||||
|
||||
divisor = SPEED_MAX / speed;
|
||||
|
||||
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
|
||||
|
||||
/*
|
||||
* Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
|
||||
* almost 1,7 ms at 19200 bps. At speeds above that we can just forget
|
||||
* about this timeout since it will always be fast enough.
|
||||
*/
|
||||
|
||||
if (speed < 38400)
|
||||
fcr |= UART_FCR_TRIGGER_1;
|
||||
else
|
||||
fcr |= UART_FCR_TRIGGER_14;
|
||||
|
||||
/* Bluetooth cards use 8N1 */
|
||||
lcr = UART_LCR_WLEN8;
|
||||
|
||||
outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
|
||||
outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
|
||||
outb(divisor >> 8, iobase + UART_DLM);
|
||||
outb(lcr, iobase + UART_LCR); /* Set 8N1 */
|
||||
outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
|
||||
|
||||
/* Turn on interrupts */
|
||||
outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
|
||||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== HCI interface ======================== */
|
||||
|
||||
|
||||
static int btuart_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
struct btuart_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int btuart_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int btuart_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
btuart_hci_flush(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btuart_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
|
||||
skb_queue_tail(&(info->txq), skb);
|
||||
|
||||
btuart_write_wakeup(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int btuart_open(struct btuart_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spin_lock_init(&(info->lock));
|
||||
|
||||
skb_queue_head_init(&(info->txq));
|
||||
|
||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||
info->rx_count = 0;
|
||||
info->rx_skb = NULL;
|
||||
|
||||
/* Initialize HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
BT_ERR("Can't allocate HCI device");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->hdev = hdev;
|
||||
|
||||
hdev->bus = HCI_PCCARD;
|
||||
hci_set_drvdata(hdev, info);
|
||||
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
|
||||
|
||||
hdev->open = btuart_hci_open;
|
||||
hdev->close = btuart_hci_close;
|
||||
hdev->flush = btuart_hci_flush;
|
||||
hdev->send = btuart_hci_send_frame;
|
||||
|
||||
spin_lock_irqsave(&(info->lock), flags);
|
||||
|
||||
/* Reset UART */
|
||||
outb(0, iobase + UART_MCR);
|
||||
|
||||
/* Turn off interrupts */
|
||||
outb(0, iobase + UART_IER);
|
||||
|
||||
/* Initialize UART */
|
||||
outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
|
||||
outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
|
||||
|
||||
/* Turn on interrupts */
|
||||
// outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
|
||||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
btuart_change_speed(info, DEFAULT_BAUD_RATE);
|
||||
|
||||
/* Timeout before it is safe to send the first HCI packet */
|
||||
msleep(1000);
|
||||
|
||||
/* Register HCI device */
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
info->hdev = NULL;
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int btuart_close(struct btuart_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev = info->hdev;
|
||||
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
btuart_hci_close(hdev);
|
||||
|
||||
spin_lock_irqsave(&(info->lock), flags);
|
||||
|
||||
/* Reset UART */
|
||||
outb(0, iobase + UART_MCR);
|
||||
|
||||
/* Turn off interrupts */
|
||||
outb(0, iobase + UART_IER);
|
||||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btuart_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct btuart_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->p_dev = link;
|
||||
link->priv = info;
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
|
||||
CONF_AUTO_SET_IO;
|
||||
|
||||
return btuart_config(link);
|
||||
}
|
||||
|
||||
|
||||
static void btuart_detach(struct pcmcia_device *link)
|
||||
{
|
||||
btuart_release(link);
|
||||
}
|
||||
|
||||
static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
int *try = priv_data;
|
||||
|
||||
if (!try)
|
||||
p_dev->io_lines = 16;
|
||||
|
||||
if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
|
||||
return -EINVAL;
|
||||
|
||||
p_dev->resource[0]->end = 8;
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
}
|
||||
|
||||
static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
|
||||
void *priv_data)
|
||||
{
|
||||
static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
|
||||
int j;
|
||||
|
||||
if (p_dev->io_lines > 3)
|
||||
return -ENODEV;
|
||||
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
p_dev->resource[0]->end = 8;
|
||||
|
||||
for (j = 0; j < 5; j++) {
|
||||
p_dev->resource[0]->start = base[j];
|
||||
p_dev->io_lines = base[j] ? 16 : 3;
|
||||
if (!pcmcia_request_io(p_dev))
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int btuart_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct btuart_info *info = link->priv;
|
||||
int i;
|
||||
int try;
|
||||
|
||||
/* First pass: look for a config entry that looks normal.
|
||||
* Two tries: without IO aliases, then with aliases
|
||||
*/
|
||||
for (try = 0; try < 2; try++)
|
||||
if (!pcmcia_loop_config(link, btuart_check_config, &try))
|
||||
goto found_port;
|
||||
|
||||
/* Second pass: try to find an entry that isn't picky about
|
||||
* its base address, then try to grab any standard serial port
|
||||
* address, and finally try to get any free port.
|
||||
*/
|
||||
if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
|
||||
goto found_port;
|
||||
|
||||
BT_ERR("No usable port range found");
|
||||
goto failed;
|
||||
|
||||
found_port:
|
||||
i = pcmcia_request_irq(link, btuart_interrupt);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_enable_device(link);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
if (btuart_open(info) != 0)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
btuart_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void btuart_release(struct pcmcia_device *link)
|
||||
{
|
||||
struct btuart_info *info = link->priv;
|
||||
|
||||
btuart_close(info);
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
}
|
||||
|
||||
static const struct pcmcia_device_id btuart_ids[] = {
|
||||
/* don't use this driver. Use serial_cs + hci_uart instead */
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, btuart_ids);
|
||||
|
||||
static struct pcmcia_driver btuart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "btuart_cs",
|
||||
.probe = btuart_probe,
|
||||
.remove = btuart_detach,
|
||||
.id_table = btuart_ids,
|
||||
};
|
||||
module_pcmcia_driver(btuart_driver);
|
@ -340,6 +340,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
|
||||
/* Intel Bluetooth devices */
|
||||
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_NEW },
|
||||
{ USB_DEVICE(0x8087, 0x0026), .driver_info = BTUSB_INTEL_NEW },
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
|
||||
@ -367,6 +368,9 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8723BU Bluetooth devices */
|
||||
{ USB_DEVICE(0x7392, 0xa611), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8821AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
|
||||
@ -374,6 +378,9 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8822BE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Silicon Wave based devices */
|
||||
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
|
||||
|
||||
@ -2073,6 +2080,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
case 0x0c: /* WsP */
|
||||
case 0x11: /* JfP */
|
||||
case 0x12: /* ThP */
|
||||
case 0x13: /* HrP */
|
||||
case 0x14: /* QnJ, IcP */
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
|
||||
@ -2165,6 +2174,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
break;
|
||||
case 0x11: /* JfP */
|
||||
case 0x12: /* ThP */
|
||||
case 0x13: /* HrP */
|
||||
case 0x14: /* QnJ, IcP */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.sfi",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(ver.hw_revision),
|
||||
@ -2196,6 +2207,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
break;
|
||||
case 0x11: /* JfP */
|
||||
case 0x12: /* ThP */
|
||||
case 0x13: /* HrP */
|
||||
case 0x14: /* QnJ, IcP */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.ddc",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(ver.hw_revision),
|
||||
@ -3050,6 +3063,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
if (id->driver_info & BTUSB_QCA_ROME) {
|
||||
data->setup_on_usb = btusb_setup_qca;
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_RTL
|
||||
|
160
drivers/bluetooth/h4_recv.h
Normal file
160
drivers/bluetooth/h4_recv.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
*
|
||||
* Generic Bluetooth HCI UART driver
|
||||
*
|
||||
* Copyright (C) 2015-2018 Intel Corporation
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
struct h4_recv_pkt {
|
||||
u8 type; /* Packet type */
|
||||
u8 hlen; /* Header length */
|
||||
u8 loff; /* Data length offset in header */
|
||||
u8 lsize; /* Data length field size */
|
||||
u16 maxlen; /* Max overall packet length */
|
||||
int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
};
|
||||
|
||||
#define H4_RECV_ACL \
|
||||
.type = HCI_ACLDATA_PKT, \
|
||||
.hlen = HCI_ACL_HDR_SIZE, \
|
||||
.loff = 2, \
|
||||
.lsize = 2, \
|
||||
.maxlen = HCI_MAX_FRAME_SIZE \
|
||||
|
||||
#define H4_RECV_SCO \
|
||||
.type = HCI_SCODATA_PKT, \
|
||||
.hlen = HCI_SCO_HDR_SIZE, \
|
||||
.loff = 2, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_MAX_SCO_SIZE
|
||||
|
||||
#define H4_RECV_EVENT \
|
||||
.type = HCI_EVENT_PKT, \
|
||||
.hlen = HCI_EVENT_HDR_SIZE, \
|
||||
.loff = 1, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_MAX_EVENT_SIZE
|
||||
|
||||
static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
|
||||
struct sk_buff *skb,
|
||||
const unsigned char *buffer,
|
||||
int count,
|
||||
const struct h4_recv_pkt *pkts,
|
||||
int pkts_count)
|
||||
{
|
||||
while (count) {
|
||||
int i, len;
|
||||
|
||||
if (!count)
|
||||
break;
|
||||
|
||||
if (!skb) {
|
||||
for (i = 0; i < pkts_count; i++) {
|
||||
if (buffer[0] != (&pkts[i])->type)
|
||||
continue;
|
||||
|
||||
skb = bt_skb_alloc((&pkts[i])->maxlen,
|
||||
GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hci_skb_pkt_type(skb) = (&pkts[i])->type;
|
||||
hci_skb_expect(skb) = (&pkts[i])->hlen;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for invalid packet type */
|
||||
if (!skb)
|
||||
return ERR_PTR(-EILSEQ);
|
||||
|
||||
count -= 1;
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
|
||||
skb_put_data(skb, buffer, len);
|
||||
|
||||
count -= len;
|
||||
buffer += len;
|
||||
|
||||
/* Check for partial packet */
|
||||
if (skb->len < hci_skb_expect(skb))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < pkts_count; i++) {
|
||||
if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= pkts_count) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EILSEQ);
|
||||
}
|
||||
|
||||
if (skb->len == (&pkts[i])->hlen) {
|
||||
u16 dlen;
|
||||
|
||||
switch ((&pkts[i])->lsize) {
|
||||
case 0:
|
||||
/* No variable data length */
|
||||
dlen = 0;
|
||||
break;
|
||||
case 1:
|
||||
/* Single octet variable length */
|
||||
dlen = skb->data[(&pkts[i])->loff];
|
||||
hci_skb_expect(skb) += dlen;
|
||||
|
||||
if (skb_tailroom(skb) < dlen) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EMSGSIZE);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* Double octet variable length */
|
||||
dlen = get_unaligned_le16(skb->data +
|
||||
(&pkts[i])->loff);
|
||||
hci_skb_expect(skb) += dlen;
|
||||
|
||||
if (skb_tailroom(skb) < dlen) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EMSGSIZE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Unsupported variable length */
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EILSEQ);
|
||||
}
|
||||
|
||||
if (!dlen) {
|
||||
/* No more data, complete frame */
|
||||
(&pkts[i])->recv(hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
} else {
|
||||
/* Complete frame */
|
||||
(&pkts[i])->recv(hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
@ -71,12 +71,12 @@ static int ath_wakeup_ar3k(struct tty_struct *tty)
|
||||
/* Clear RTS first */
|
||||
tty->driver->ops->tiocmget(tty);
|
||||
tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
|
||||
mdelay(20);
|
||||
msleep(20);
|
||||
|
||||
/* Set RTS, wake up board */
|
||||
tty->driver->ops->tiocmget(tty);
|
||||
tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
|
||||
mdelay(20);
|
||||
msleep(20);
|
||||
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
return status;
|
||||
|
@ -98,6 +98,8 @@ struct bcm_device {
|
||||
int (*set_shutdown)(struct bcm_device *, bool);
|
||||
#ifdef CONFIG_ACPI
|
||||
acpi_handle btlp, btpu, btpd;
|
||||
int gpio_count;
|
||||
int gpio_int_idx;
|
||||
#endif
|
||||
|
||||
struct clk *clk;
|
||||
@ -126,6 +128,10 @@ struct bcm_data {
|
||||
static DEFINE_MUTEX(bcm_device_lock);
|
||||
static LIST_HEAD(bcm_device_list);
|
||||
|
||||
static int irq_polarity = -1;
|
||||
module_param(irq_polarity, int, 0444);
|
||||
MODULE_PARM_DESC(irq_polarity, "IRQ polarity 0: active-high 1: active-low");
|
||||
|
||||
static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||
{
|
||||
if (hu->serdev)
|
||||
@ -770,47 +776,27 @@ static int bcm_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct acpi_gpio_params int_last_device_wakeup_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params int_last_shutdown_gpios = { 1, 0, false };
|
||||
static const struct acpi_gpio_params int_last_host_wakeup_gpios = { 2, 0, false };
|
||||
static const struct acpi_gpio_params first_gpio = { 0, 0, false };
|
||||
static const struct acpi_gpio_params second_gpio = { 1, 0, false };
|
||||
static const struct acpi_gpio_params third_gpio = { 2, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_bcm_int_last_gpios[] = {
|
||||
{ "device-wakeup-gpios", &int_last_device_wakeup_gpios, 1 },
|
||||
{ "shutdown-gpios", &int_last_shutdown_gpios, 1 },
|
||||
{ "host-wakeup-gpios", &int_last_host_wakeup_gpios, 1 },
|
||||
{ "device-wakeup-gpios", &first_gpio, 1 },
|
||||
{ "shutdown-gpios", &second_gpio, 1 },
|
||||
{ "host-wakeup-gpios", &third_gpio, 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params int_first_host_wakeup_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params int_first_device_wakeup_gpios = { 1, 0, false };
|
||||
static const struct acpi_gpio_params int_first_shutdown_gpios = { 2, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios[] = {
|
||||
{ "device-wakeup-gpios", &int_first_device_wakeup_gpios, 1 },
|
||||
{ "shutdown-gpios", &int_first_shutdown_gpios, 1 },
|
||||
{ "host-wakeup-gpios", &int_first_host_wakeup_gpios, 1 },
|
||||
{ "host-wakeup-gpios", &first_gpio, 1 },
|
||||
{ "device-wakeup-gpios", &second_gpio, 1 },
|
||||
{ "shutdown-gpios", &third_gpio, 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
|
||||
static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
|
||||
{
|
||||
.ident = "Asus T100TA",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR,
|
||||
"ASUSTeK COMPUTER INC."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Asus T100CHI",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR,
|
||||
"ASUSTeK COMPUTER INC."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100CHI"),
|
||||
},
|
||||
},
|
||||
{ /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
|
||||
.ident = "Lenovo ThinkPad 8",
|
||||
.matches = {
|
||||
@ -818,13 +804,6 @@ static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "MINIX Z83-4",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -838,13 +817,18 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
|
||||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||
irq = &ares->data.extended_irq;
|
||||
dev->irq_active_low = irq->polarity == ACPI_ACTIVE_LOW;
|
||||
if (irq->polarity != ACPI_ACTIVE_LOW)
|
||||
dev_info(dev->dev, "ACPI Interrupt resource is active-high, this is usually wrong, treating the IRQ as active-low\n");
|
||||
dev->irq_active_low = true;
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_GPIO:
|
||||
gpio = &ares->data.gpio;
|
||||
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
|
||||
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) {
|
||||
dev->gpio_int_idx = dev->gpio_count;
|
||||
dev->irq_active_low = gpio->polarity == ACPI_ACTIVE_LOW;
|
||||
}
|
||||
dev->gpio_count++;
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_SERIAL_BUS:
|
||||
@ -908,13 +892,13 @@ static inline int bcm_apple_get_resources(struct bcm_device *dev)
|
||||
|
||||
static int bcm_gpio_set_device_wakeup(struct bcm_device *dev, bool awake)
|
||||
{
|
||||
gpiod_set_value(dev->device_wakeup, awake);
|
||||
gpiod_set_value_cansleep(dev->device_wakeup, awake);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_gpio_set_shutdown(struct bcm_device *dev, bool powered)
|
||||
{
|
||||
gpiod_set_value(dev->shutdown, powered);
|
||||
gpiod_set_value_cansleep(dev->shutdown, powered);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -962,20 +946,11 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
||||
LIST_HEAD(resources);
|
||||
const struct dmi_system_id *dmi_id;
|
||||
const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios;
|
||||
const struct acpi_device_id *id;
|
||||
struct resource_entry *entry;
|
||||
int ret;
|
||||
|
||||
/* Retrieve GPIO data */
|
||||
id = acpi_match_device(dev->dev->driver->acpi_match_table, dev->dev);
|
||||
if (id)
|
||||
gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data;
|
||||
|
||||
ret = devm_acpi_dev_add_driver_gpios(dev->dev, gpio_mapping);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Retrieve UART ACPI info */
|
||||
dev->gpio_int_idx = -1;
|
||||
ret = acpi_dev_get_resources(ACPI_COMPANION(dev->dev),
|
||||
&resources, bcm_resource, dev);
|
||||
if (ret < 0)
|
||||
@ -989,11 +964,40 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
||||
}
|
||||
acpi_dev_free_resource_list(&resources);
|
||||
|
||||
dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
|
||||
if (dmi_id) {
|
||||
dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
|
||||
dmi_id->ident);
|
||||
dev->irq_active_low = true;
|
||||
/* If the DSDT uses an Interrupt resource for the IRQ, then there are
|
||||
* only 2 GPIO resources, we use the irq-last mapping for this, since
|
||||
* we already have an irq the 3th / last mapping will not be used.
|
||||
*/
|
||||
if (dev->irq)
|
||||
gpio_mapping = acpi_bcm_int_last_gpios;
|
||||
else if (dev->gpio_int_idx == 0)
|
||||
gpio_mapping = acpi_bcm_int_first_gpios;
|
||||
else if (dev->gpio_int_idx == 2)
|
||||
gpio_mapping = acpi_bcm_int_last_gpios;
|
||||
else
|
||||
dev_warn(dev->dev, "Unexpected ACPI gpio_int_idx: %d\n",
|
||||
dev->gpio_int_idx);
|
||||
|
||||
/* Warn if our expectations are not met. */
|
||||
if (dev->gpio_count != (dev->irq ? 2 : 3))
|
||||
dev_warn(dev->dev, "Unexpected number of ACPI GPIOs: %d\n",
|
||||
dev->gpio_count);
|
||||
|
||||
ret = devm_acpi_dev_add_driver_gpios(dev->dev, gpio_mapping);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (irq_polarity != -1) {
|
||||
dev->irq_active_low = irq_polarity;
|
||||
dev_warn(dev->dev, "Overwriting IRQ polarity to active %s by module-param\n",
|
||||
dev->irq_active_low ? "low" : "high");
|
||||
} else {
|
||||
dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
|
||||
if (dmi_id) {
|
||||
dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
|
||||
dmi_id->ident);
|
||||
dev->irq_active_low = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1079,25 +1083,172 @@ static const struct hci_uart_proto bcm_proto = {
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id bcm_acpi_match[] = {
|
||||
{ "BCM2E1A", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E39", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E3A", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E3D", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E3F", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E40", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E54", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E55", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E64", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E65", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E67", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E71", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E72", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E7B", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E7C", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E7E", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2E95", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2E96", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2EA4", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2E00" },
|
||||
{ "BCM2E01" },
|
||||
{ "BCM2E02" },
|
||||
{ "BCM2E03" },
|
||||
{ "BCM2E04" },
|
||||
{ "BCM2E05" },
|
||||
{ "BCM2E06" },
|
||||
{ "BCM2E07" },
|
||||
{ "BCM2E08" },
|
||||
{ "BCM2E09" },
|
||||
{ "BCM2E0A" },
|
||||
{ "BCM2E0B" },
|
||||
{ "BCM2E0C" },
|
||||
{ "BCM2E0D" },
|
||||
{ "BCM2E0E" },
|
||||
{ "BCM2E0F" },
|
||||
{ "BCM2E10" },
|
||||
{ "BCM2E11" },
|
||||
{ "BCM2E12" },
|
||||
{ "BCM2E13" },
|
||||
{ "BCM2E14" },
|
||||
{ "BCM2E15" },
|
||||
{ "BCM2E16" },
|
||||
{ "BCM2E17" },
|
||||
{ "BCM2E18" },
|
||||
{ "BCM2E19" },
|
||||
{ "BCM2E1A" },
|
||||
{ "BCM2E1B" },
|
||||
{ "BCM2E1C" },
|
||||
{ "BCM2E1D" },
|
||||
{ "BCM2E1F" },
|
||||
{ "BCM2E20" },
|
||||
{ "BCM2E21" },
|
||||
{ "BCM2E22" },
|
||||
{ "BCM2E23" },
|
||||
{ "BCM2E24" },
|
||||
{ "BCM2E25" },
|
||||
{ "BCM2E26" },
|
||||
{ "BCM2E27" },
|
||||
{ "BCM2E28" },
|
||||
{ "BCM2E29" },
|
||||
{ "BCM2E2A" },
|
||||
{ "BCM2E2B" },
|
||||
{ "BCM2E2C" },
|
||||
{ "BCM2E2D" },
|
||||
{ "BCM2E2E" },
|
||||
{ "BCM2E2F" },
|
||||
{ "BCM2E30" },
|
||||
{ "BCM2E31" },
|
||||
{ "BCM2E32" },
|
||||
{ "BCM2E33" },
|
||||
{ "BCM2E34" },
|
||||
{ "BCM2E35" },
|
||||
{ "BCM2E36" },
|
||||
{ "BCM2E37" },
|
||||
{ "BCM2E38" },
|
||||
{ "BCM2E39" },
|
||||
{ "BCM2E3A" },
|
||||
{ "BCM2E3B" },
|
||||
{ "BCM2E3C" },
|
||||
{ "BCM2E3D" },
|
||||
{ "BCM2E3E" },
|
||||
{ "BCM2E3F" },
|
||||
{ "BCM2E40" },
|
||||
{ "BCM2E41" },
|
||||
{ "BCM2E42" },
|
||||
{ "BCM2E43" },
|
||||
{ "BCM2E44" },
|
||||
{ "BCM2E45" },
|
||||
{ "BCM2E46" },
|
||||
{ "BCM2E47" },
|
||||
{ "BCM2E48" },
|
||||
{ "BCM2E49" },
|
||||
{ "BCM2E4A" },
|
||||
{ "BCM2E4B" },
|
||||
{ "BCM2E4C" },
|
||||
{ "BCM2E4D" },
|
||||
{ "BCM2E4E" },
|
||||
{ "BCM2E4F" },
|
||||
{ "BCM2E50" },
|
||||
{ "BCM2E51" },
|
||||
{ "BCM2E52" },
|
||||
{ "BCM2E53" },
|
||||
{ "BCM2E54" },
|
||||
{ "BCM2E55" },
|
||||
{ "BCM2E56" },
|
||||
{ "BCM2E57" },
|
||||
{ "BCM2E58" },
|
||||
{ "BCM2E59" },
|
||||
{ "BCM2E5A" },
|
||||
{ "BCM2E5B" },
|
||||
{ "BCM2E5C" },
|
||||
{ "BCM2E5D" },
|
||||
{ "BCM2E5E" },
|
||||
{ "BCM2E5F" },
|
||||
{ "BCM2E60" },
|
||||
{ "BCM2E61" },
|
||||
{ "BCM2E62" },
|
||||
{ "BCM2E63" },
|
||||
{ "BCM2E64" },
|
||||
{ "BCM2E65" },
|
||||
{ "BCM2E66" },
|
||||
{ "BCM2E67" },
|
||||
{ "BCM2E68" },
|
||||
{ "BCM2E69" },
|
||||
{ "BCM2E6B" },
|
||||
{ "BCM2E6D" },
|
||||
{ "BCM2E6E" },
|
||||
{ "BCM2E6F" },
|
||||
{ "BCM2E70" },
|
||||
{ "BCM2E71" },
|
||||
{ "BCM2E72" },
|
||||
{ "BCM2E73" },
|
||||
{ "BCM2E74" },
|
||||
{ "BCM2E75" },
|
||||
{ "BCM2E76" },
|
||||
{ "BCM2E77" },
|
||||
{ "BCM2E78" },
|
||||
{ "BCM2E79" },
|
||||
{ "BCM2E7A" },
|
||||
{ "BCM2E7B" },
|
||||
{ "BCM2E7C" },
|
||||
{ "BCM2E7D" },
|
||||
{ "BCM2E7E" },
|
||||
{ "BCM2E7F" },
|
||||
{ "BCM2E80" },
|
||||
{ "BCM2E81" },
|
||||
{ "BCM2E82" },
|
||||
{ "BCM2E83" },
|
||||
{ "BCM2E84" },
|
||||
{ "BCM2E85" },
|
||||
{ "BCM2E86" },
|
||||
{ "BCM2E87" },
|
||||
{ "BCM2E88" },
|
||||
{ "BCM2E89" },
|
||||
{ "BCM2E8A" },
|
||||
{ "BCM2E8B" },
|
||||
{ "BCM2E8C" },
|
||||
{ "BCM2E8D" },
|
||||
{ "BCM2E8E" },
|
||||
{ "BCM2E90" },
|
||||
{ "BCM2E92" },
|
||||
{ "BCM2E93" },
|
||||
{ "BCM2E94" },
|
||||
{ "BCM2E95" },
|
||||
{ "BCM2E96" },
|
||||
{ "BCM2E97" },
|
||||
{ "BCM2E98" },
|
||||
{ "BCM2E99" },
|
||||
{ "BCM2E9A" },
|
||||
{ "BCM2E9B" },
|
||||
{ "BCM2E9C" },
|
||||
{ "BCM2E9D" },
|
||||
{ "BCM2EA0" },
|
||||
{ "BCM2EA1" },
|
||||
{ "BCM2EA2" },
|
||||
{ "BCM2EA3" },
|
||||
{ "BCM2EA4" },
|
||||
{ "BCM2EA5" },
|
||||
{ "BCM2EA6" },
|
||||
{ "BCM2EA7" },
|
||||
{ "BCM2EA8" },
|
||||
{ "BCM2EA9" },
|
||||
{ "BCM2EAA" },
|
||||
{ "BCM2EAB" },
|
||||
{ "BCM2EAC" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
|
||||
@ -1146,6 +1297,12 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!bcmdev->shutdown) {
|
||||
dev_warn(&serdev->dev,
|
||||
"No reset resource, using default baud rate\n");
|
||||
bcmdev->oper_speed = bcmdev->init_speed;
|
||||
}
|
||||
|
||||
err = bcm_gpio_set_power(bcmdev, false);
|
||||
if (err)
|
||||
dev_err(&serdev->dev, "Failed to power down\n");
|
||||
|
@ -67,13 +67,6 @@
|
||||
#define HCILL_WAKE_UP_IND 0x32
|
||||
#define HCILL_WAKE_UP_ACK 0x33
|
||||
|
||||
/* HCILL receiver States */
|
||||
#define HCILL_W4_PACKET_TYPE 0
|
||||
#define HCILL_W4_EVENT_HDR 1
|
||||
#define HCILL_W4_ACL_HDR 2
|
||||
#define HCILL_W4_SCO_HDR 3
|
||||
#define HCILL_W4_DATA 4
|
||||
|
||||
/* HCILL states */
|
||||
enum hcill_states_e {
|
||||
HCILL_ASLEEP,
|
||||
@ -82,10 +75,6 @@ enum hcill_states_e {
|
||||
HCILL_AWAKE_TO_ASLEEP
|
||||
};
|
||||
|
||||
struct hcill_cmd {
|
||||
u8 cmd;
|
||||
} __packed;
|
||||
|
||||
struct ll_device {
|
||||
struct hci_uart hu;
|
||||
struct serdev_device *serdev;
|
||||
@ -95,8 +84,6 @@ struct ll_device {
|
||||
};
|
||||
|
||||
struct ll_struct {
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
struct sk_buff_head txq;
|
||||
spinlock_t hcill_lock; /* HCILL state lock */
|
||||
@ -113,7 +100,6 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
|
||||
int err = 0;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct ll_struct *ll = hu->priv;
|
||||
struct hcill_cmd *hcill_packet;
|
||||
|
||||
BT_DBG("hu %p cmd 0x%x", hu, cmd);
|
||||
|
||||
@ -126,8 +112,7 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
|
||||
}
|
||||
|
||||
/* prepare packet */
|
||||
hcill_packet = skb_put(skb, 1);
|
||||
hcill_packet->cmd = cmd;
|
||||
skb_put_u8(skb, cmd);
|
||||
|
||||
/* send packet */
|
||||
skb_queue_tail(&ll->txq, skb);
|
||||
@ -379,155 +364,88 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, int len)
|
||||
static int ll_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
int room = skb_tailroom(ll->rx_skb);
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct ll_struct *ll = hu->priv;
|
||||
|
||||
BT_DBG("len %d room %d", len, room);
|
||||
|
||||
if (!len) {
|
||||
hci_recv_frame(hdev, ll->rx_skb);
|
||||
} else if (len > room) {
|
||||
BT_ERR("Data length is too large");
|
||||
kfree_skb(ll->rx_skb);
|
||||
} else {
|
||||
ll->rx_state = HCILL_W4_DATA;
|
||||
ll->rx_count = len;
|
||||
return len;
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCILL_GO_TO_SLEEP_IND:
|
||||
BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
|
||||
ll_device_want_to_sleep(hu);
|
||||
break;
|
||||
case HCILL_GO_TO_SLEEP_ACK:
|
||||
/* shouldn't happen */
|
||||
bt_dev_err(hdev, "received HCILL_GO_TO_SLEEP_ACK in state %ld",
|
||||
ll->hcill_state);
|
||||
break;
|
||||
case HCILL_WAKE_UP_IND:
|
||||
BT_DBG("HCILL_WAKE_UP_IND packet");
|
||||
ll_device_want_to_wakeup(hu);
|
||||
break;
|
||||
case HCILL_WAKE_UP_ACK:
|
||||
BT_DBG("HCILL_WAKE_UP_ACK packet");
|
||||
ll_device_woke_up(hu);
|
||||
break;
|
||||
}
|
||||
|
||||
ll->rx_state = HCILL_W4_PACKET_TYPE;
|
||||
ll->rx_skb = NULL;
|
||||
ll->rx_count = 0;
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LL_RECV_SLEEP_IND \
|
||||
.type = HCILL_GO_TO_SLEEP_IND, \
|
||||
.hlen = 0, \
|
||||
.loff = 0, \
|
||||
.lsize = 0, \
|
||||
.maxlen = 0
|
||||
|
||||
#define LL_RECV_SLEEP_ACK \
|
||||
.type = HCILL_GO_TO_SLEEP_ACK, \
|
||||
.hlen = 0, \
|
||||
.loff = 0, \
|
||||
.lsize = 0, \
|
||||
.maxlen = 0
|
||||
|
||||
#define LL_RECV_WAKE_IND \
|
||||
.type = HCILL_WAKE_UP_IND, \
|
||||
.hlen = 0, \
|
||||
.loff = 0, \
|
||||
.lsize = 0, \
|
||||
.maxlen = 0
|
||||
|
||||
#define LL_RECV_WAKE_ACK \
|
||||
.type = HCILL_WAKE_UP_ACK, \
|
||||
.hlen = 0, \
|
||||
.loff = 0, \
|
||||
.lsize = 0, \
|
||||
.maxlen = 0
|
||||
|
||||
static const struct h4_recv_pkt ll_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||
{ LL_RECV_SLEEP_IND, .recv = ll_recv_frame },
|
||||
{ LL_RECV_SLEEP_ACK, .recv = ll_recv_frame },
|
||||
{ LL_RECV_WAKE_IND, .recv = ll_recv_frame },
|
||||
{ LL_RECV_WAKE_ACK, .recv = ll_recv_frame },
|
||||
};
|
||||
|
||||
/* Recv data */
|
||||
static int ll_recv(struct hci_uart *hu, const void *data, int count)
|
||||
{
|
||||
struct ll_struct *ll = hu->priv;
|
||||
const char *ptr;
|
||||
struct hci_event_hdr *eh;
|
||||
struct hci_acl_hdr *ah;
|
||||
struct hci_sco_hdr *sh;
|
||||
int len, type, dlen;
|
||||
|
||||
BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
|
||||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
ptr = data;
|
||||
while (count) {
|
||||
if (ll->rx_count) {
|
||||
len = min_t(unsigned int, ll->rx_count, count);
|
||||
skb_put_data(ll->rx_skb, ptr, len);
|
||||
ll->rx_count -= len; count -= len; ptr += len;
|
||||
|
||||
if (ll->rx_count)
|
||||
continue;
|
||||
|
||||
switch (ll->rx_state) {
|
||||
case HCILL_W4_DATA:
|
||||
BT_DBG("Complete data");
|
||||
hci_recv_frame(hu->hdev, ll->rx_skb);
|
||||
|
||||
ll->rx_state = HCILL_W4_PACKET_TYPE;
|
||||
ll->rx_skb = NULL;
|
||||
continue;
|
||||
|
||||
case HCILL_W4_EVENT_HDR:
|
||||
eh = hci_event_hdr(ll->rx_skb);
|
||||
|
||||
BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
|
||||
|
||||
ll_check_data_len(hu->hdev, ll, eh->plen);
|
||||
continue;
|
||||
|
||||
case HCILL_W4_ACL_HDR:
|
||||
ah = hci_acl_hdr(ll->rx_skb);
|
||||
dlen = __le16_to_cpu(ah->dlen);
|
||||
|
||||
BT_DBG("ACL header: dlen %d", dlen);
|
||||
|
||||
ll_check_data_len(hu->hdev, ll, dlen);
|
||||
continue;
|
||||
|
||||
case HCILL_W4_SCO_HDR:
|
||||
sh = hci_sco_hdr(ll->rx_skb);
|
||||
|
||||
BT_DBG("SCO header: dlen %d", sh->dlen);
|
||||
|
||||
ll_check_data_len(hu->hdev, ll, sh->dlen);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* HCILL_W4_PACKET_TYPE */
|
||||
switch (*ptr) {
|
||||
case HCI_EVENT_PKT:
|
||||
BT_DBG("Event packet");
|
||||
ll->rx_state = HCILL_W4_EVENT_HDR;
|
||||
ll->rx_count = HCI_EVENT_HDR_SIZE;
|
||||
type = HCI_EVENT_PKT;
|
||||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
BT_DBG("ACL packet");
|
||||
ll->rx_state = HCILL_W4_ACL_HDR;
|
||||
ll->rx_count = HCI_ACL_HDR_SIZE;
|
||||
type = HCI_ACLDATA_PKT;
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
BT_DBG("SCO packet");
|
||||
ll->rx_state = HCILL_W4_SCO_HDR;
|
||||
ll->rx_count = HCI_SCO_HDR_SIZE;
|
||||
type = HCI_SCODATA_PKT;
|
||||
break;
|
||||
|
||||
/* HCILL signals */
|
||||
case HCILL_GO_TO_SLEEP_IND:
|
||||
BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
|
||||
ll_device_want_to_sleep(hu);
|
||||
ptr++; count--;
|
||||
continue;
|
||||
|
||||
case HCILL_GO_TO_SLEEP_ACK:
|
||||
/* shouldn't happen */
|
||||
BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state);
|
||||
ptr++; count--;
|
||||
continue;
|
||||
|
||||
case HCILL_WAKE_UP_IND:
|
||||
BT_DBG("HCILL_WAKE_UP_IND packet");
|
||||
ll_device_want_to_wakeup(hu);
|
||||
ptr++; count--;
|
||||
continue;
|
||||
|
||||
case HCILL_WAKE_UP_ACK:
|
||||
BT_DBG("HCILL_WAKE_UP_ACK packet");
|
||||
ll_device_woke_up(hu);
|
||||
ptr++; count--;
|
||||
continue;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
|
||||
hu->hdev->stat.err_rx++;
|
||||
ptr++; count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr++; count--;
|
||||
|
||||
/* Allocate packet */
|
||||
ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!ll->rx_skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
ll->rx_state = HCILL_W4_PACKET_TYPE;
|
||||
ll->rx_count = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hci_skb_pkt_type(ll->rx_skb) = type;
|
||||
ll->rx_skb = h4_recv_buf(hu->hdev, ll->rx_skb, data, count,
|
||||
ll_recv_pkts, ARRAY_SIZE(ll_recv_pkts));
|
||||
if (IS_ERR(ll->rx_skb)) {
|
||||
int err = PTR_ERR(ll->rx_skb);
|
||||
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
|
||||
ll->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -650,7 +568,7 @@ static int download_firmware(struct ll_device *lldev)
|
||||
break;
|
||||
case ACTION_DELAY: /* sleep */
|
||||
bt_dev_info(lldev->hu.hdev, "sleep command in scr");
|
||||
mdelay(((struct bts_action_delay *)action_ptr)->msec);
|
||||
msleep(((struct bts_action_delay *)action_ptr)->msec);
|
||||
break;
|
||||
}
|
||||
len -= (sizeof(struct bts_action) +
|
||||
|
@ -29,3 +29,14 @@ config CHELSIO_IPSEC_INLINE
|
||||
default n
|
||||
---help---
|
||||
Enable support for IPSec Tx Inline.
|
||||
|
||||
config CRYPTO_DEV_CHELSIO_TLS
|
||||
tristate "Chelsio Crypto Inline TLS Driver"
|
||||
depends on CHELSIO_T4
|
||||
depends on TLS
|
||||
select CRYPTO_DEV_CHELSIO
|
||||
---help---
|
||||
Support Chelsio Inline TLS with Chelsio crypto accelerator.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called chtls.
|
||||
|
@ -3,3 +3,4 @@ ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4
|
||||
obj-$(CONFIG_CRYPTO_DEV_CHELSIO) += chcr.o
|
||||
chcr-objs := chcr_core.o chcr_algo.o
|
||||
chcr-$(CONFIG_CHELSIO_IPSEC_INLINE) += chcr_ipsec.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_CHELSIO_TLS) += chtls/
|
||||
|
@ -86,6 +86,39 @@
|
||||
KEY_CONTEXT_OPAD_PRESENT_M)
|
||||
#define KEY_CONTEXT_OPAD_PRESENT_F KEY_CONTEXT_OPAD_PRESENT_V(1U)
|
||||
|
||||
#define TLS_KEYCTX_RXFLIT_CNT_S 24
|
||||
#define TLS_KEYCTX_RXFLIT_CNT_V(x) ((x) << TLS_KEYCTX_RXFLIT_CNT_S)
|
||||
|
||||
#define TLS_KEYCTX_RXPROT_VER_S 20
|
||||
#define TLS_KEYCTX_RXPROT_VER_M 0xf
|
||||
#define TLS_KEYCTX_RXPROT_VER_V(x) ((x) << TLS_KEYCTX_RXPROT_VER_S)
|
||||
|
||||
#define TLS_KEYCTX_RXCIPH_MODE_S 16
|
||||
#define TLS_KEYCTX_RXCIPH_MODE_M 0xf
|
||||
#define TLS_KEYCTX_RXCIPH_MODE_V(x) ((x) << TLS_KEYCTX_RXCIPH_MODE_S)
|
||||
|
||||
#define TLS_KEYCTX_RXAUTH_MODE_S 12
|
||||
#define TLS_KEYCTX_RXAUTH_MODE_M 0xf
|
||||
#define TLS_KEYCTX_RXAUTH_MODE_V(x) ((x) << TLS_KEYCTX_RXAUTH_MODE_S)
|
||||
|
||||
#define TLS_KEYCTX_RXCIAU_CTRL_S 11
|
||||
#define TLS_KEYCTX_RXCIAU_CTRL_V(x) ((x) << TLS_KEYCTX_RXCIAU_CTRL_S)
|
||||
|
||||
#define TLS_KEYCTX_RX_SEQCTR_S 9
|
||||
#define TLS_KEYCTX_RX_SEQCTR_M 0x3
|
||||
#define TLS_KEYCTX_RX_SEQCTR_V(x) ((x) << TLS_KEYCTX_RX_SEQCTR_S)
|
||||
|
||||
#define TLS_KEYCTX_RX_VALID_S 8
|
||||
#define TLS_KEYCTX_RX_VALID_V(x) ((x) << TLS_KEYCTX_RX_VALID_S)
|
||||
|
||||
#define TLS_KEYCTX_RXCK_SIZE_S 3
|
||||
#define TLS_KEYCTX_RXCK_SIZE_M 0x7
|
||||
#define TLS_KEYCTX_RXCK_SIZE_V(x) ((x) << TLS_KEYCTX_RXCK_SIZE_S)
|
||||
|
||||
#define TLS_KEYCTX_RXMK_SIZE_S 0
|
||||
#define TLS_KEYCTX_RXMK_SIZE_M 0x7
|
||||
#define TLS_KEYCTX_RXMK_SIZE_V(x) ((x) << TLS_KEYCTX_RXMK_SIZE_S)
|
||||
|
||||
#define CHCR_HASH_MAX_DIGEST_SIZE 64
|
||||
#define CHCR_MAX_SHA_DIGEST_SIZE 64
|
||||
|
||||
@ -176,6 +209,15 @@
|
||||
KEY_CONTEXT_SALT_PRESENT_V(1) | \
|
||||
KEY_CONTEXT_CTX_LEN_V((ctx_len)))
|
||||
|
||||
#define FILL_KEY_CRX_HDR(ck_size, mk_size, d_ck, opad, ctx_len) \
|
||||
htonl(TLS_KEYCTX_RXMK_SIZE_V(mk_size) | \
|
||||
TLS_KEYCTX_RXCK_SIZE_V(ck_size) | \
|
||||
TLS_KEYCTX_RX_VALID_V(1) | \
|
||||
TLS_KEYCTX_RX_SEQCTR_V(3) | \
|
||||
TLS_KEYCTX_RXAUTH_MODE_V(4) | \
|
||||
TLS_KEYCTX_RXCIPH_MODE_V(2) | \
|
||||
TLS_KEYCTX_RXFLIT_CNT_V((ctx_len)))
|
||||
|
||||
#define FILL_WR_OP_CCTX_SIZE \
|
||||
htonl( \
|
||||
FW_CRYPTO_LOOKASIDE_WR_OPCODE_V( \
|
||||
|
@ -65,10 +65,58 @@ struct uld_ctx;
|
||||
struct _key_ctx {
|
||||
__be32 ctx_hdr;
|
||||
u8 salt[MAX_SALT];
|
||||
__be64 reserverd;
|
||||
__be64 iv_to_auth;
|
||||
unsigned char key[0];
|
||||
};
|
||||
|
||||
#define KEYCTX_TX_WR_IV_S 55
|
||||
#define KEYCTX_TX_WR_IV_M 0x1ffULL
|
||||
#define KEYCTX_TX_WR_IV_V(x) ((x) << KEYCTX_TX_WR_IV_S)
|
||||
#define KEYCTX_TX_WR_IV_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_IV_S) & KEYCTX_TX_WR_IV_M)
|
||||
|
||||
#define KEYCTX_TX_WR_AAD_S 47
|
||||
#define KEYCTX_TX_WR_AAD_M 0xffULL
|
||||
#define KEYCTX_TX_WR_AAD_V(x) ((x) << KEYCTX_TX_WR_AAD_S)
|
||||
#define KEYCTX_TX_WR_AAD_G(x) (((x) >> KEYCTX_TX_WR_AAD_S) & \
|
||||
KEYCTX_TX_WR_AAD_M)
|
||||
|
||||
#define KEYCTX_TX_WR_AADST_S 39
|
||||
#define KEYCTX_TX_WR_AADST_M 0xffULL
|
||||
#define KEYCTX_TX_WR_AADST_V(x) ((x) << KEYCTX_TX_WR_AADST_S)
|
||||
#define KEYCTX_TX_WR_AADST_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_AADST_S) & KEYCTX_TX_WR_AADST_M)
|
||||
|
||||
#define KEYCTX_TX_WR_CIPHER_S 30
|
||||
#define KEYCTX_TX_WR_CIPHER_M 0x1ffULL
|
||||
#define KEYCTX_TX_WR_CIPHER_V(x) ((x) << KEYCTX_TX_WR_CIPHER_S)
|
||||
#define KEYCTX_TX_WR_CIPHER_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_CIPHER_S) & KEYCTX_TX_WR_CIPHER_M)
|
||||
|
||||
#define KEYCTX_TX_WR_CIPHERST_S 23
|
||||
#define KEYCTX_TX_WR_CIPHERST_M 0x7f
|
||||
#define KEYCTX_TX_WR_CIPHERST_V(x) ((x) << KEYCTX_TX_WR_CIPHERST_S)
|
||||
#define KEYCTX_TX_WR_CIPHERST_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_CIPHERST_S) & KEYCTX_TX_WR_CIPHERST_M)
|
||||
|
||||
#define KEYCTX_TX_WR_AUTH_S 14
|
||||
#define KEYCTX_TX_WR_AUTH_M 0x1ff
|
||||
#define KEYCTX_TX_WR_AUTH_V(x) ((x) << KEYCTX_TX_WR_AUTH_S)
|
||||
#define KEYCTX_TX_WR_AUTH_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_AUTH_S) & KEYCTX_TX_WR_AUTH_M)
|
||||
|
||||
#define KEYCTX_TX_WR_AUTHST_S 7
|
||||
#define KEYCTX_TX_WR_AUTHST_M 0x7f
|
||||
#define KEYCTX_TX_WR_AUTHST_V(x) ((x) << KEYCTX_TX_WR_AUTHST_S)
|
||||
#define KEYCTX_TX_WR_AUTHST_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_AUTHST_S) & KEYCTX_TX_WR_AUTHST_M)
|
||||
|
||||
#define KEYCTX_TX_WR_AUTHIN_S 0
|
||||
#define KEYCTX_TX_WR_AUTHIN_M 0x7f
|
||||
#define KEYCTX_TX_WR_AUTHIN_V(x) ((x) << KEYCTX_TX_WR_AUTHIN_S)
|
||||
#define KEYCTX_TX_WR_AUTHIN_G(x) \
|
||||
(((x) >> KEYCTX_TX_WR_AUTHIN_S) & KEYCTX_TX_WR_AUTHIN_M)
|
||||
|
||||
struct chcr_wr {
|
||||
struct fw_crypto_lookaside_wr wreq;
|
||||
struct ulp_txpkt ulptx;
|
||||
@ -90,6 +138,11 @@ struct uld_ctx {
|
||||
struct chcr_dev *dev;
|
||||
};
|
||||
|
||||
struct sge_opaque_hdr {
|
||||
void *dev;
|
||||
dma_addr_t addr[MAX_SKB_FRAGS + 1];
|
||||
};
|
||||
|
||||
struct chcr_ipsec_req {
|
||||
struct ulp_txpkt ulptx;
|
||||
struct ulptx_idata sc_imm;
|
||||
|
4
drivers/crypto/chelsio/chtls/Makefile
Normal file
4
drivers/crypto/chelsio/chtls/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4 -Idrivers/crypto/chelsio/
|
||||
|
||||
obj-$(CONFIG_CRYPTO_DEV_CHELSIO_TLS) += chtls.o
|
||||
chtls-objs := chtls_main.o chtls_cm.o chtls_io.o chtls_hw.o
|
482
drivers/crypto/chelsio/chtls/chtls.h
Normal file
482
drivers/crypto/chelsio/chtls/chtls.h
Normal file
@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Chelsio Communications, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __CHTLS_H__
|
||||
#define __CHTLS_H__
|
||||
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/authenc.h>
|
||||
#include <crypto/ctr.h>
|
||||
#include <crypto/gf128mul.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/null.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <linux/tls.h>
|
||||
#include <net/tls.h>
|
||||
|
||||
#include "t4fw_api.h"
|
||||
#include "t4_msg.h"
|
||||
#include "cxgb4.h"
|
||||
#include "cxgb4_uld.h"
|
||||
#include "l2t.h"
|
||||
#include "chcr_algo.h"
|
||||
#include "chcr_core.h"
|
||||
#include "chcr_crypto.h"
|
||||
|
||||
#define MAX_IVS_PAGE 256
|
||||
#define TLS_KEY_CONTEXT_SZ 64
|
||||
#define CIPHER_BLOCK_SIZE 16
|
||||
#define GCM_TAG_SIZE 16
|
||||
#define KEY_ON_MEM_SZ 16
|
||||
#define AEAD_EXPLICIT_DATA_SIZE 8
|
||||
#define TLS_HEADER_LENGTH 5
|
||||
#define SCMD_CIPH_MODE_AES_GCM 2
|
||||
/* Any MFS size should work and come from openssl */
|
||||
#define TLS_MFS 16384
|
||||
|
||||
#define RSS_HDR sizeof(struct rss_header)
|
||||
#define TLS_WR_CPL_LEN \
|
||||
(sizeof(struct fw_tlstx_data_wr) + sizeof(struct cpl_tx_tls_sfo))
|
||||
|
||||
enum {
|
||||
CHTLS_KEY_CONTEXT_DSGL,
|
||||
CHTLS_KEY_CONTEXT_IMM,
|
||||
CHTLS_KEY_CONTEXT_DDR,
|
||||
};
|
||||
|
||||
enum {
|
||||
CHTLS_LISTEN_START,
|
||||
CHTLS_LISTEN_STOP,
|
||||
};
|
||||
|
||||
/* Flags for return value of CPL message handlers */
|
||||
enum {
|
||||
CPL_RET_BUF_DONE = 1, /* buffer processing done */
|
||||
CPL_RET_BAD_MSG = 2, /* bad CPL message */
|
||||
CPL_RET_UNKNOWN_TID = 4 /* unexpected unknown TID */
|
||||
};
|
||||
|
||||
#define TLS_RCV_ST_READ_HEADER 0xF0
|
||||
#define TLS_RCV_ST_READ_BODY 0xF1
|
||||
#define TLS_RCV_ST_READ_DONE 0xF2
|
||||
#define TLS_RCV_ST_READ_NB 0xF3
|
||||
|
||||
#define LISTEN_INFO_HASH_SIZE 32
|
||||
#define RSPQ_HASH_BITS 5
|
||||
struct listen_info {
|
||||
struct listen_info *next; /* Link to next entry */
|
||||
struct sock *sk; /* The listening socket */
|
||||
unsigned int stid; /* The server TID */
|
||||
};
|
||||
|
||||
enum {
|
||||
T4_LISTEN_START_PENDING,
|
||||
T4_LISTEN_STARTED
|
||||
};
|
||||
|
||||
enum csk_flags {
|
||||
CSK_CALLBACKS_CHKD, /* socket callbacks have been sanitized */
|
||||
CSK_ABORT_REQ_RCVD, /* received one ABORT_REQ_RSS message */
|
||||
CSK_TX_MORE_DATA, /* sending ULP data; don't set SHOVE bit */
|
||||
CSK_TX_WAIT_IDLE, /* suspend Tx until in-flight data is ACKed */
|
||||
CSK_ABORT_SHUTDOWN, /* shouldn't send more abort requests */
|
||||
CSK_ABORT_RPL_PENDING, /* expecting an abort reply */
|
||||
CSK_CLOSE_CON_REQUESTED,/* we've sent a close_conn_req */
|
||||
CSK_TX_DATA_SENT, /* sent a TX_DATA WR on this connection */
|
||||
CSK_TX_FAILOVER, /* Tx traffic failing over */
|
||||
CSK_UPDATE_RCV_WND, /* Need to update rcv window */
|
||||
CSK_RST_ABORTED, /* outgoing RST was aborted */
|
||||
CSK_TLS_HANDSHK, /* TLS Handshake */
|
||||
CSK_CONN_INLINE, /* Connection on HW */
|
||||
};
|
||||
|
||||
struct listen_ctx {
|
||||
struct sock *lsk;
|
||||
struct chtls_dev *cdev;
|
||||
struct sk_buff_head synq;
|
||||
u32 state;
|
||||
};
|
||||
|
||||
struct key_map {
|
||||
unsigned long *addr;
|
||||
unsigned int start;
|
||||
unsigned int available;
|
||||
unsigned int size;
|
||||
spinlock_t lock; /* lock for key id request from map */
|
||||
} __packed;
|
||||
|
||||
struct tls_scmd {
|
||||
u32 seqno_numivs;
|
||||
u32 ivgen_hdrlen;
|
||||
};
|
||||
|
||||
struct chtls_dev {
|
||||
struct tls_device tlsdev;
|
||||
struct list_head list;
|
||||
struct cxgb4_lld_info *lldi;
|
||||
struct pci_dev *pdev;
|
||||
struct listen_info *listen_hash_tab[LISTEN_INFO_HASH_SIZE];
|
||||
spinlock_t listen_lock; /* lock for listen list */
|
||||
struct net_device **ports;
|
||||
struct tid_info *tids;
|
||||
unsigned int pfvf;
|
||||
const unsigned short *mtus;
|
||||
|
||||
struct idr hwtid_idr;
|
||||
struct idr stid_idr;
|
||||
|
||||
spinlock_t idr_lock ____cacheline_aligned_in_smp;
|
||||
|
||||
struct net_device *egr_dev[NCHAN * 2];
|
||||
struct sk_buff *rspq_skb_cache[1 << RSPQ_HASH_BITS];
|
||||
struct sk_buff *askb;
|
||||
|
||||
struct sk_buff_head deferq;
|
||||
struct work_struct deferq_task;
|
||||
|
||||
struct list_head list_node;
|
||||
struct list_head rcu_node;
|
||||
struct list_head na_node;
|
||||
unsigned int send_page_order;
|
||||
struct key_map kmap;
|
||||
};
|
||||
|
||||
struct chtls_hws {
|
||||
struct sk_buff_head sk_recv_queue;
|
||||
u8 txqid;
|
||||
u8 ofld;
|
||||
u16 type;
|
||||
u16 rstate;
|
||||
u16 keyrpl;
|
||||
u16 pldlen;
|
||||
u16 rcvpld;
|
||||
u16 compute;
|
||||
u16 expansion;
|
||||
u16 keylen;
|
||||
u16 pdus;
|
||||
u16 adjustlen;
|
||||
u16 ivsize;
|
||||
u16 txleft;
|
||||
u32 mfs;
|
||||
s32 txkey;
|
||||
s32 rxkey;
|
||||
u32 fcplenmax;
|
||||
u32 copied_seq;
|
||||
u64 tx_seq_no;
|
||||
struct tls_scmd scmd;
|
||||
struct tls12_crypto_info_aes_gcm_128 crypto_info;
|
||||
};
|
||||
|
||||
struct chtls_sock {
|
||||
struct sock *sk;
|
||||
struct chtls_dev *cdev;
|
||||
struct l2t_entry *l2t_entry; /* pointer to the L2T entry */
|
||||
struct net_device *egress_dev; /* TX_CHAN for act open retry */
|
||||
|
||||
struct sk_buff_head txq;
|
||||
struct sk_buff *wr_skb_head;
|
||||
struct sk_buff *wr_skb_tail;
|
||||
struct sk_buff *ctrl_skb_cache;
|
||||
struct sk_buff *txdata_skb_cache; /* abort path messages */
|
||||
struct kref kref;
|
||||
unsigned long flags;
|
||||
u32 opt2;
|
||||
u32 wr_credits;
|
||||
u32 wr_unacked;
|
||||
u32 wr_max_credits;
|
||||
u32 wr_nondata;
|
||||
u32 hwtid; /* TCP Control Block ID */
|
||||
u32 txq_idx;
|
||||
u32 rss_qid;
|
||||
u32 tid;
|
||||
u32 idr;
|
||||
u32 mss;
|
||||
u32 ulp_mode;
|
||||
u32 tx_chan;
|
||||
u32 rx_chan;
|
||||
u32 sndbuf;
|
||||
u32 txplen_max;
|
||||
u32 mtu_idx; /* MTU table index */
|
||||
u32 smac_idx;
|
||||
u8 port_id;
|
||||
u8 tos;
|
||||
u16 resv2;
|
||||
u32 delack_mode;
|
||||
u32 delack_seq;
|
||||
|
||||
void *passive_reap_next; /* placeholder for passive */
|
||||
struct chtls_hws tlshws;
|
||||
struct synq {
|
||||
struct sk_buff *next;
|
||||
struct sk_buff *prev;
|
||||
} synq;
|
||||
struct listen_ctx *listen_ctx;
|
||||
};
|
||||
|
||||
struct tls_hdr {
|
||||
u8 type;
|
||||
u16 version;
|
||||
u16 length;
|
||||
} __packed;
|
||||
|
||||
struct tlsrx_cmp_hdr {
|
||||
u8 type;
|
||||
u16 version;
|
||||
u16 length;
|
||||
|
||||
u64 tls_seq;
|
||||
u16 reserved1;
|
||||
u8 res_to_mac_error;
|
||||
} __packed;
|
||||
|
||||
/* res_to_mac_error fields */
|
||||
#define TLSRX_HDR_PKT_INT_ERROR_S 4
|
||||
#define TLSRX_HDR_PKT_INT_ERROR_M 0x1
|
||||
#define TLSRX_HDR_PKT_INT_ERROR_V(x) \
|
||||
((x) << TLSRX_HDR_PKT_INT_ERROR_S)
|
||||
#define TLSRX_HDR_PKT_INT_ERROR_G(x) \
|
||||
(((x) >> TLSRX_HDR_PKT_INT_ERROR_S) & TLSRX_HDR_PKT_INT_ERROR_M)
|
||||
#define TLSRX_HDR_PKT_INT_ERROR_F TLSRX_HDR_PKT_INT_ERROR_V(1U)
|
||||
|
||||
#define TLSRX_HDR_PKT_SPP_ERROR_S 3
|
||||
#define TLSRX_HDR_PKT_SPP_ERROR_M 0x1
|
||||
#define TLSRX_HDR_PKT_SPP_ERROR_V(x) ((x) << TLSRX_HDR_PKT_SPP_ERROR)
|
||||
#define TLSRX_HDR_PKT_SPP_ERROR_G(x) \
|
||||
(((x) >> TLSRX_HDR_PKT_SPP_ERROR_S) & TLSRX_HDR_PKT_SPP_ERROR_M)
|
||||
#define TLSRX_HDR_PKT_SPP_ERROR_F TLSRX_HDR_PKT_SPP_ERROR_V(1U)
|
||||
|
||||
#define TLSRX_HDR_PKT_CCDX_ERROR_S 2
|
||||
#define TLSRX_HDR_PKT_CCDX_ERROR_M 0x1
|
||||
#define TLSRX_HDR_PKT_CCDX_ERROR_V(x) ((x) << TLSRX_HDR_PKT_CCDX_ERROR_S)
|
||||
#define TLSRX_HDR_PKT_CCDX_ERROR_G(x) \
|
||||
(((x) >> TLSRX_HDR_PKT_CCDX_ERROR_S) & TLSRX_HDR_PKT_CCDX_ERROR_M)
|
||||
#define TLSRX_HDR_PKT_CCDX_ERROR_F TLSRX_HDR_PKT_CCDX_ERROR_V(1U)
|
||||
|
||||
#define TLSRX_HDR_PKT_PAD_ERROR_S 1
|
||||
#define TLSRX_HDR_PKT_PAD_ERROR_M 0x1
|
||||
#define TLSRX_HDR_PKT_PAD_ERROR_V(x) ((x) << TLSRX_HDR_PKT_PAD_ERROR_S)
|
||||
#define TLSRX_HDR_PKT_PAD_ERROR_G(x) \
|
||||
(((x) >> TLSRX_HDR_PKT_PAD_ERROR_S) & TLSRX_HDR_PKT_PAD_ERROR_M)
|
||||
#define TLSRX_HDR_PKT_PAD_ERROR_F TLSRX_HDR_PKT_PAD_ERROR_V(1U)
|
||||
|
||||
#define TLSRX_HDR_PKT_MAC_ERROR_S 0
|
||||
#define TLSRX_HDR_PKT_MAC_ERROR_M 0x1
|
||||
#define TLSRX_HDR_PKT_MAC_ERROR_V(x) ((x) << TLSRX_HDR_PKT_MAC_ERROR)
|
||||
#define TLSRX_HDR_PKT_MAC_ERROR_G(x) \
|
||||
(((x) >> S_TLSRX_HDR_PKT_MAC_ERROR_S) & TLSRX_HDR_PKT_MAC_ERROR_M)
|
||||
#define TLSRX_HDR_PKT_MAC_ERROR_F TLSRX_HDR_PKT_MAC_ERROR_V(1U)
|
||||
|
||||
#define TLSRX_HDR_PKT_ERROR_M 0x1F
|
||||
|
||||
struct ulp_mem_rw {
|
||||
__be32 cmd;
|
||||
__be32 len16; /* command length */
|
||||
__be32 dlen; /* data length in 32-byte units */
|
||||
__be32 lock_addr;
|
||||
};
|
||||
|
||||
struct tls_key_wr {
|
||||
__be32 op_to_compl;
|
||||
__be32 flowid_len16;
|
||||
__be32 ftid;
|
||||
u8 reneg_to_write_rx;
|
||||
u8 protocol;
|
||||
__be16 mfs;
|
||||
};
|
||||
|
||||
struct tls_key_req {
|
||||
struct tls_key_wr wr;
|
||||
struct ulp_mem_rw req;
|
||||
struct ulptx_idata sc_imm;
|
||||
};
|
||||
|
||||
/*
|
||||
* This lives in skb->cb and is used to chain WRs in a linked list.
|
||||
*/
|
||||
struct wr_skb_cb {
|
||||
struct l2t_skb_cb l2t; /* reserve space for l2t CB */
|
||||
struct sk_buff *next_wr; /* next write request */
|
||||
};
|
||||
|
||||
/* Per-skb backlog handler. Run when a socket's backlog is processed. */
|
||||
struct blog_skb_cb {
|
||||
void (*backlog_rcv)(struct sock *sk, struct sk_buff *skb);
|
||||
struct chtls_dev *cdev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Similar to tcp_skb_cb but with ULP elements added to support TLS,
|
||||
* etc.
|
||||
*/
|
||||
struct ulp_skb_cb {
|
||||
struct wr_skb_cb wr; /* reserve space for write request */
|
||||
u16 flags; /* TCP-like flags */
|
||||
u8 psh;
|
||||
u8 ulp_mode; /* ULP mode/submode of sk_buff */
|
||||
u32 seq; /* TCP sequence number */
|
||||
union { /* ULP-specific fields */
|
||||
struct {
|
||||
u8 type;
|
||||
u8 ofld;
|
||||
u8 iv;
|
||||
} tls;
|
||||
} ulp;
|
||||
};
|
||||
|
||||
#define ULP_SKB_CB(skb) ((struct ulp_skb_cb *)&((skb)->cb[0]))
|
||||
#define BLOG_SKB_CB(skb) ((struct blog_skb_cb *)(skb)->cb)
|
||||
|
||||
/*
|
||||
* Flags for ulp_skb_cb.flags.
|
||||
*/
|
||||
enum {
|
||||
ULPCB_FLAG_NEED_HDR = 1 << 0, /* packet needs a TX_DATA_WR header */
|
||||
ULPCB_FLAG_NO_APPEND = 1 << 1, /* don't grow this skb */
|
||||
ULPCB_FLAG_BARRIER = 1 << 2, /* set TX_WAIT_IDLE after sending */
|
||||
ULPCB_FLAG_HOLD = 1 << 3, /* skb not ready for Tx yet */
|
||||
ULPCB_FLAG_COMPL = 1 << 4, /* request WR completion */
|
||||
ULPCB_FLAG_URG = 1 << 5, /* urgent data */
|
||||
ULPCB_FLAG_TLS_ND = 1 << 6, /* payload of zero length */
|
||||
ULPCB_FLAG_NO_HDR = 1 << 7, /* not a ofld wr */
|
||||
};
|
||||
|
||||
/* The ULP mode/submode of an skbuff */
|
||||
#define skb_ulp_mode(skb) (ULP_SKB_CB(skb)->ulp_mode)
|
||||
#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)
|
||||
{
|
||||
return container_of(tlsdev, struct chtls_dev, tlsdev);
|
||||
}
|
||||
|
||||
static inline void csk_set_flag(struct chtls_sock *csk,
|
||||
enum csk_flags flag)
|
||||
{
|
||||
__set_bit(flag, &csk->flags);
|
||||
}
|
||||
|
||||
static inline void csk_reset_flag(struct chtls_sock *csk,
|
||||
enum csk_flags flag)
|
||||
{
|
||||
__clear_bit(flag, &csk->flags);
|
||||
}
|
||||
|
||||
static inline bool csk_conn_inline(const struct chtls_sock *csk)
|
||||
{
|
||||
return test_bit(CSK_CONN_INLINE, &csk->flags);
|
||||
}
|
||||
|
||||
static inline int csk_flag(const struct sock *sk, enum csk_flags flag)
|
||||
{
|
||||
struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
|
||||
|
||||
if (!csk_conn_inline(csk))
|
||||
return 0;
|
||||
return test_bit(flag, &csk->flags);
|
||||
}
|
||||
|
||||
static inline int csk_flag_nochk(const struct chtls_sock *csk,
|
||||
enum csk_flags flag)
|
||||
{
|
||||
return test_bit(flag, &csk->flags);
|
||||
}
|
||||
|
||||
static inline void *cplhdr(struct sk_buff *skb)
|
||||
{
|
||||
return skb->data;
|
||||
}
|
||||
|
||||
static inline int is_neg_adv(unsigned int status)
|
||||
{
|
||||
return status == CPL_ERR_RTX_NEG_ADVICE ||
|
||||
status == CPL_ERR_KEEPALV_NEG_ADVICE ||
|
||||
status == CPL_ERR_PERSIST_NEG_ADVICE;
|
||||
}
|
||||
|
||||
static inline void process_cpl_msg(void (*fn)(struct sock *, struct sk_buff *),
|
||||
struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
if (unlikely(sock_owned_by_user(sk))) {
|
||||
BLOG_SKB_CB(skb)->backlog_rcv = fn;
|
||||
__sk_add_backlog(sk, skb);
|
||||
} else {
|
||||
fn(sk, skb);
|
||||
}
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
|
||||
static inline void chtls_sock_free(struct kref *ref)
|
||||
{
|
||||
struct chtls_sock *csk = container_of(ref, struct chtls_sock,
|
||||
kref);
|
||||
kfree(csk);
|
||||
}
|
||||
|
||||
static inline void __chtls_sock_put(const char *fn, struct chtls_sock *csk)
|
||||
{
|
||||
kref_put(&csk->kref, chtls_sock_free);
|
||||
}
|
||||
|
||||
static inline void __chtls_sock_get(const char *fn,
|
||||
struct chtls_sock *csk)
|
||||
{
|
||||
kref_get(&csk->kref);
|
||||
}
|
||||
|
||||
static inline void send_or_defer(struct sock *sk, struct tcp_sock *tp,
|
||||
struct sk_buff *skb, int through_l2t)
|
||||
{
|
||||
struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
|
||||
|
||||
if (through_l2t) {
|
||||
/* send through L2T */
|
||||
cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry);
|
||||
} else {
|
||||
/* send directly */
|
||||
cxgb4_ofld_send(csk->egress_dev, skb);
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*chtls_handler_func)(struct chtls_dev *, struct sk_buff *);
|
||||
extern chtls_handler_func chtls_handlers[NUM_CPL_CMDS];
|
||||
void chtls_install_cpl_ops(struct sock *sk);
|
||||
int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi);
|
||||
void chtls_listen_stop(struct chtls_dev *cdev, struct sock *sk);
|
||||
int chtls_listen_start(struct chtls_dev *cdev, struct sock *sk);
|
||||
void chtls_close(struct sock *sk, long timeout);
|
||||
int chtls_disconnect(struct sock *sk, int flags);
|
||||
void chtls_shutdown(struct sock *sk, int how);
|
||||
void chtls_destroy_sock(struct sock *sk);
|
||||
int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
|
||||
int chtls_recvmsg(struct sock *sk, struct msghdr *msg,
|
||||
size_t len, int nonblock, int flags, int *addr_len);
|
||||
int chtls_sendpage(struct sock *sk, struct page *page,
|
||||
int offset, size_t size, int flags);
|
||||
int send_tx_flowc_wr(struct sock *sk, int compl,
|
||||
u32 snd_nxt, u32 rcv_nxt);
|
||||
void chtls_tcp_push(struct sock *sk, int flags);
|
||||
int chtls_push_frames(struct chtls_sock *csk, int comp);
|
||||
int chtls_set_tcb_tflag(struct sock *sk, unsigned int bit_pos, int val);
|
||||
int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 mode);
|
||||
void skb_entail(struct sock *sk, struct sk_buff *skb, int flags);
|
||||
unsigned int keyid_to_addr(int start_addr, int keyid);
|
||||
void free_tls_keyid(struct sock *sk);
|
||||
#endif
|
2126
drivers/crypto/chelsio/chtls/chtls_cm.c
Normal file
2126
drivers/crypto/chelsio/chtls/chtls_cm.c
Normal file
File diff suppressed because it is too large
Load Diff
203
drivers/crypto/chelsio/chtls/chtls_cm.h
Normal file
203
drivers/crypto/chelsio/chtls/chtls_cm.h
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Chelsio Communications, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __CHTLS_CM_H__
|
||||
#define __CHTLS_CM_H__
|
||||
|
||||
/*
|
||||
* TCB settings
|
||||
*/
|
||||
/* 3:0 */
|
||||
#define TCB_ULP_TYPE_W 0
|
||||
#define TCB_ULP_TYPE_S 0
|
||||
#define TCB_ULP_TYPE_M 0xfULL
|
||||
#define TCB_ULP_TYPE_V(x) ((x) << TCB_ULP_TYPE_S)
|
||||
|
||||
/* 11:4 */
|
||||
#define TCB_ULP_RAW_W 0
|
||||
#define TCB_ULP_RAW_S 4
|
||||
#define TCB_ULP_RAW_M 0xffULL
|
||||
#define TCB_ULP_RAW_V(x) ((x) << TCB_ULP_RAW_S)
|
||||
|
||||
#define TF_TLS_KEY_SIZE_S 7
|
||||
#define TF_TLS_KEY_SIZE_V(x) ((x) << TF_TLS_KEY_SIZE_S)
|
||||
|
||||
#define TF_TLS_CONTROL_S 2
|
||||
#define TF_TLS_CONTROL_V(x) ((x) << TF_TLS_CONTROL_S)
|
||||
|
||||
#define TF_TLS_ACTIVE_S 1
|
||||
#define TF_TLS_ACTIVE_V(x) ((x) << TF_TLS_ACTIVE_S)
|
||||
|
||||
#define TF_TLS_ENABLE_S 0
|
||||
#define TF_TLS_ENABLE_V(x) ((x) << TF_TLS_ENABLE_S)
|
||||
|
||||
#define TF_RX_QUIESCE_S 15
|
||||
#define TF_RX_QUIESCE_V(x) ((x) << TF_RX_QUIESCE_S)
|
||||
|
||||
/*
|
||||
* Max receive window supported by HW in bytes. Only a small part of it can
|
||||
* be set through option0, the rest needs to be set through RX_DATA_ACK.
|
||||
*/
|
||||
#define MAX_RCV_WND ((1U << 27) - 1)
|
||||
#define MAX_MSS 65536
|
||||
|
||||
/*
|
||||
* Min receive window. We want it to be large enough to accommodate receive
|
||||
* coalescing, handle jumbo frames, and not trigger sender SWS avoidance.
|
||||
*/
|
||||
#define MIN_RCV_WND (24 * 1024U)
|
||||
#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000))
|
||||
|
||||
/* ulp_mem_io + ulptx_idata + payload + padding */
|
||||
#define MAX_IMM_ULPTX_WR_LEN (32 + 8 + 256 + 8)
|
||||
|
||||
/* for TX: a skb must have a headroom of at least TX_HEADER_LEN bytes */
|
||||
#define TX_HEADER_LEN \
|
||||
(sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr))
|
||||
#define TX_TLSHDR_LEN \
|
||||
(sizeof(struct fw_tlstx_data_wr) + sizeof(struct cpl_tx_tls_sfo) + \
|
||||
sizeof(struct sge_opaque_hdr))
|
||||
#define TXDATA_SKB_LEN 128
|
||||
|
||||
enum {
|
||||
CPL_TX_TLS_SFO_TYPE_CCS,
|
||||
CPL_TX_TLS_SFO_TYPE_ALERT,
|
||||
CPL_TX_TLS_SFO_TYPE_HANDSHAKE,
|
||||
CPL_TX_TLS_SFO_TYPE_DATA,
|
||||
CPL_TX_TLS_SFO_TYPE_HEARTBEAT,
|
||||
};
|
||||
|
||||
enum {
|
||||
TLS_HDR_TYPE_CCS = 20,
|
||||
TLS_HDR_TYPE_ALERT,
|
||||
TLS_HDR_TYPE_HANDSHAKE,
|
||||
TLS_HDR_TYPE_RECORD,
|
||||
TLS_HDR_TYPE_HEARTBEAT,
|
||||
};
|
||||
|
||||
typedef void (*defer_handler_t)(struct chtls_dev *dev, struct sk_buff *skb);
|
||||
extern struct request_sock_ops chtls_rsk_ops;
|
||||
|
||||
struct deferred_skb_cb {
|
||||
defer_handler_t handler;
|
||||
struct chtls_dev *dev;
|
||||
};
|
||||
|
||||
#define DEFERRED_SKB_CB(skb) ((struct deferred_skb_cb *)(skb)->cb)
|
||||
#define failover_flowc_wr_len offsetof(struct fw_flowc_wr, mnemval[3])
|
||||
#define WR_SKB_CB(skb) ((struct wr_skb_cb *)(skb)->cb)
|
||||
#define ACCEPT_QUEUE(sk) (&inet_csk(sk)->icsk_accept_queue.rskq_accept_head)
|
||||
|
||||
#define SND_WSCALE(tp) ((tp)->rx_opt.snd_wscale)
|
||||
#define RCV_WSCALE(tp) ((tp)->rx_opt.rcv_wscale)
|
||||
#define USER_MSS(tp) ((tp)->rx_opt.user_mss)
|
||||
#define TS_RECENT_STAMP(tp) ((tp)->rx_opt.ts_recent_stamp)
|
||||
#define WSCALE_OK(tp) ((tp)->rx_opt.wscale_ok)
|
||||
#define TSTAMP_OK(tp) ((tp)->rx_opt.tstamp_ok)
|
||||
#define SACK_OK(tp) ((tp)->rx_opt.sack_ok)
|
||||
#define INC_ORPHAN_COUNT(sk) percpu_counter_inc((sk)->sk_prot->orphan_count)
|
||||
|
||||
/* TLS SKB */
|
||||
#define skb_ulp_tls_inline(skb) (ULP_SKB_CB(skb)->ulp.tls.ofld)
|
||||
#define skb_ulp_tls_iv_imm(skb) (ULP_SKB_CB(skb)->ulp.tls.iv)
|
||||
|
||||
void chtls_defer_reply(struct sk_buff *skb, struct chtls_dev *dev,
|
||||
defer_handler_t handler);
|
||||
|
||||
/*
|
||||
* Returns true if the socket is in one of the supplied states.
|
||||
*/
|
||||
static inline unsigned int sk_in_state(const struct sock *sk,
|
||||
unsigned int states)
|
||||
{
|
||||
return states & (1 << sk->sk_state);
|
||||
}
|
||||
|
||||
static void chtls_rsk_destructor(struct request_sock *req)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
static inline void chtls_init_rsk_ops(struct proto *chtls_tcp_prot,
|
||||
struct request_sock_ops *chtls_tcp_ops,
|
||||
struct proto *tcp_prot, int family)
|
||||
{
|
||||
memset(chtls_tcp_ops, 0, sizeof(*chtls_tcp_ops));
|
||||
chtls_tcp_ops->family = family;
|
||||
chtls_tcp_ops->obj_size = sizeof(struct tcp_request_sock);
|
||||
chtls_tcp_ops->destructor = chtls_rsk_destructor;
|
||||
chtls_tcp_ops->slab = tcp_prot->rsk_prot->slab;
|
||||
chtls_tcp_prot->rsk_prot = chtls_tcp_ops;
|
||||
}
|
||||
|
||||
static inline void chtls_reqsk_free(struct request_sock *req)
|
||||
{
|
||||
if (req->rsk_listener)
|
||||
sock_put(req->rsk_listener);
|
||||
kmem_cache_free(req->rsk_ops->slab, req);
|
||||
}
|
||||
|
||||
#define DECLARE_TASK_FUNC(task, task_param) \
|
||||
static void task(struct work_struct *task_param)
|
||||
|
||||
static inline void sk_wakeup_sleepers(struct sock *sk, bool interruptable)
|
||||
{
|
||||
struct socket_wq *wq;
|
||||
|
||||
rcu_read_lock();
|
||||
wq = rcu_dereference(sk->sk_wq);
|
||||
if (skwq_has_sleeper(wq)) {
|
||||
if (interruptable)
|
||||
wake_up_interruptible(sk_sleep(sk));
|
||||
else
|
||||
wake_up_all(sk_sleep(sk));
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static inline void chtls_set_req_port(struct request_sock *oreq,
|
||||
__be16 source, __be16 dest)
|
||||
{
|
||||
inet_rsk(oreq)->ir_rmt_port = source;
|
||||
inet_rsk(oreq)->ir_num = ntohs(dest);
|
||||
}
|
||||
|
||||
static inline void chtls_set_req_addr(struct request_sock *oreq,
|
||||
__be32 local_ip, __be32 peer_ip)
|
||||
{
|
||||
inet_rsk(oreq)->ir_loc_addr = local_ip;
|
||||
inet_rsk(oreq)->ir_rmt_addr = peer_ip;
|
||||
}
|
||||
|
||||
static inline void chtls_free_skb(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
skb_dst_set(skb, NULL);
|
||||
__skb_unlink(skb, &sk->sk_receive_queue);
|
||||
__kfree_skb(skb);
|
||||
}
|
||||
|
||||
static inline void chtls_kfree_skb(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
skb_dst_set(skb, NULL);
|
||||
__skb_unlink(skb, &sk->sk_receive_queue);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static inline void enqueue_wr(struct chtls_sock *csk, struct sk_buff *skb)
|
||||
{
|
||||
WR_SKB_CB(skb)->next_wr = NULL;
|
||||
|
||||
skb_get(skb);
|
||||
|
||||
if (!csk->wr_skb_head)
|
||||
csk->wr_skb_head = skb;
|
||||
else
|
||||
WR_SKB_CB(csk->wr_skb_tail)->next_wr = skb;
|
||||
csk->wr_skb_tail = skb;
|
||||
}
|
||||
#endif
|
412
drivers/crypto/chelsio/chtls/chtls_hw.c
Normal file
412
drivers/crypto/chelsio/chtls/chtls_hw.c
Normal file
@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Chelsio Communications, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Written by: Atul Gupta (atul.gupta@chelsio.com)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/tls.h>
|
||||
#include <net/tls.h>
|
||||
|
||||
#include "chtls.h"
|
||||
#include "chtls_cm.h"
|
||||
|
||||
static void __set_tcb_field_direct(struct chtls_sock *csk,
|
||||
struct cpl_set_tcb_field *req, u16 word,
|
||||
u64 mask, u64 val, u8 cookie, int no_reply)
|
||||
{
|
||||
struct ulptx_idata *sc;
|
||||
|
||||
INIT_TP_WR_CPL(req, CPL_SET_TCB_FIELD, csk->tid);
|
||||
req->wr.wr_mid |= htonl(FW_WR_FLOWID_V(csk->tid));
|
||||
req->reply_ctrl = htons(NO_REPLY_V(no_reply) |
|
||||
QUEUENO_V(csk->rss_qid));
|
||||
req->word_cookie = htons(TCB_WORD_V(word) | TCB_COOKIE_V(cookie));
|
||||
req->mask = cpu_to_be64(mask);
|
||||
req->val = cpu_to_be64(val);
|
||||
sc = (struct ulptx_idata *)(req + 1);
|
||||
sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP));
|
||||
sc->len = htonl(0);
|
||||
}
|
||||
|
||||
static void __set_tcb_field(struct sock *sk, struct sk_buff *skb, u16 word,
|
||||
u64 mask, u64 val, u8 cookie, int no_reply)
|
||||
{
|
||||
struct cpl_set_tcb_field *req;
|
||||
struct chtls_sock *csk;
|
||||
struct ulptx_idata *sc;
|
||||
unsigned int wrlen;
|
||||
|
||||
wrlen = roundup(sizeof(*req) + sizeof(*sc), 16);
|
||||
csk = rcu_dereference_sk_user_data(sk);
|
||||
|
||||
req = (struct cpl_set_tcb_field *)__skb_put(skb, wrlen);
|
||||
__set_tcb_field_direct(csk, req, word, mask, val, cookie, no_reply);
|
||||
set_wr_txq(skb, CPL_PRIORITY_CONTROL, csk->port_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send control message to HW, message go as immediate data and packet
|
||||
* is freed immediately.
|
||||
*/
|
||||
static int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val)
|
||||
{
|
||||
struct cpl_set_tcb_field *req;
|
||||
unsigned int credits_needed;
|
||||
struct chtls_sock *csk;
|
||||
struct ulptx_idata *sc;
|
||||
struct sk_buff *skb;
|
||||
unsigned int wrlen;
|
||||
int ret;
|
||||
|
||||
wrlen = roundup(sizeof(*req) + sizeof(*sc), 16);
|
||||
|
||||
skb = alloc_skb(wrlen, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
credits_needed = DIV_ROUND_UP(wrlen, 16);
|
||||
csk = rcu_dereference_sk_user_data(sk);
|
||||
|
||||
__set_tcb_field(sk, skb, word, mask, val, 0, 1);
|
||||
skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA);
|
||||
csk->wr_credits -= credits_needed;
|
||||
csk->wr_unacked += credits_needed;
|
||||
enqueue_wr(csk, skb);
|
||||
ret = cxgb4_ofld_send(csk->egress_dev, skb);
|
||||
if (ret < 0)
|
||||
kfree_skb(skb);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set one of the t_flags bits in the TCB.
|
||||
*/
|
||||
int chtls_set_tcb_tflag(struct sock *sk, unsigned int bit_pos, int val)
|
||||
{
|
||||
return chtls_set_tcb_field(sk, 1, 1ULL << bit_pos,
|
||||
val << bit_pos);
|
||||
}
|
||||
|
||||
static int chtls_set_tcb_keyid(struct sock *sk, int keyid)
|
||||
{
|
||||
return chtls_set_tcb_field(sk, 31, 0xFFFFFFFFULL, keyid);
|
||||
}
|
||||
|
||||
static int chtls_set_tcb_seqno(struct sock *sk)
|
||||
{
|
||||
return chtls_set_tcb_field(sk, 28, ~0ULL, 0);
|
||||
}
|
||||
|
||||
static int chtls_set_tcb_quiesce(struct sock *sk, int val)
|
||||
{
|
||||
return chtls_set_tcb_field(sk, 1, (1ULL << TF_RX_QUIESCE_S),
|
||||
TF_RX_QUIESCE_V(val));
|
||||
}
|
||||
|
||||
/* TLS Key bitmap processing */
|
||||
int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi)
|
||||
{
|
||||
unsigned int num_key_ctx, bsize;
|
||||
int ksize;
|
||||
|
||||
num_key_ctx = (lldi->vr->key.size / TLS_KEY_CONTEXT_SZ);
|
||||
bsize = BITS_TO_LONGS(num_key_ctx);
|
||||
|
||||
cdev->kmap.size = num_key_ctx;
|
||||
cdev->kmap.available = bsize;
|
||||
ksize = sizeof(*cdev->kmap.addr) * bsize;
|
||||
cdev->kmap.addr = kvzalloc(ksize, GFP_KERNEL);
|
||||
if (!cdev->kmap.addr)
|
||||
return -ENOMEM;
|
||||
|
||||
cdev->kmap.start = lldi->vr->key.start;
|
||||
spin_lock_init(&cdev->kmap.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_new_keyid(struct chtls_sock *csk, u32 optname)
|
||||
{
|
||||
struct net_device *dev = csk->egress_dev;
|
||||
struct chtls_dev *cdev = csk->cdev;
|
||||
struct chtls_hws *hws;
|
||||
struct adapter *adap;
|
||||
int keyid;
|
||||
|
||||
adap = netdev2adap(dev);
|
||||
hws = &csk->tlshws;
|
||||
|
||||
spin_lock_bh(&cdev->kmap.lock);
|
||||
keyid = find_first_zero_bit(cdev->kmap.addr, cdev->kmap.size);
|
||||
if (keyid < cdev->kmap.size) {
|
||||
__set_bit(keyid, cdev->kmap.addr);
|
||||
if (optname == TLS_RX)
|
||||
hws->rxkey = keyid;
|
||||
else
|
||||
hws->txkey = keyid;
|
||||
atomic_inc(&adap->chcr_stats.tls_key);
|
||||
} else {
|
||||
keyid = -1;
|
||||
}
|
||||
spin_unlock_bh(&cdev->kmap.lock);
|
||||
return keyid;
|
||||
}
|
||||
|
||||
void free_tls_keyid(struct sock *sk)
|
||||
{
|
||||
struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
|
||||
struct net_device *dev = csk->egress_dev;
|
||||
struct chtls_dev *cdev = csk->cdev;
|
||||
struct chtls_hws *hws;
|
||||
struct adapter *adap;
|
||||
|
||||
if (!cdev->kmap.addr)
|
||||
return;
|
||||
|
||||
adap = netdev2adap(dev);
|
||||
hws = &csk->tlshws;
|
||||
|
||||
spin_lock_bh(&cdev->kmap.lock);
|
||||
if (hws->rxkey >= 0) {
|
||||
__clear_bit(hws->rxkey, cdev->kmap.addr);
|
||||
atomic_dec(&adap->chcr_stats.tls_key);
|
||||
hws->rxkey = -1;
|
||||
}
|
||||
if (hws->txkey >= 0) {
|
||||
__clear_bit(hws->txkey, cdev->kmap.addr);
|
||||
atomic_dec(&adap->chcr_stats.tls_key);
|
||||
hws->txkey = -1;
|
||||
}
|
||||
spin_unlock_bh(&cdev->kmap.lock);
|
||||
}
|
||||
|
||||
unsigned int keyid_to_addr(int start_addr, int keyid)
|
||||
{
|
||||
return (start_addr + (keyid * TLS_KEY_CONTEXT_SZ)) >> 5;
|
||||
}
|
||||
|
||||
static void chtls_rxkey_ivauth(struct _key_ctx *kctx)
|
||||
{
|
||||
kctx->iv_to_auth = cpu_to_be64(KEYCTX_TX_WR_IV_V(6ULL) |
|
||||
KEYCTX_TX_WR_AAD_V(1ULL) |
|
||||
KEYCTX_TX_WR_AADST_V(5ULL) |
|
||||
KEYCTX_TX_WR_CIPHER_V(14ULL) |
|
||||
KEYCTX_TX_WR_CIPHERST_V(0ULL) |
|
||||
KEYCTX_TX_WR_AUTH_V(14ULL) |
|
||||
KEYCTX_TX_WR_AUTHST_V(16ULL) |
|
||||
KEYCTX_TX_WR_AUTHIN_V(16ULL));
|
||||
}
|
||||
|
||||
static int chtls_key_info(struct chtls_sock *csk,
|
||||
struct _key_ctx *kctx,
|
||||
u32 keylen, u32 optname)
|
||||
{
|
||||
unsigned char key[CHCR_KEYCTX_CIPHER_KEY_SIZE_256];
|
||||
struct tls12_crypto_info_aes_gcm_128 *gcm_ctx;
|
||||
unsigned char ghash_h[AEAD_H_SIZE];
|
||||
struct crypto_cipher *cipher;
|
||||
int ck_size, key_ctx_size;
|
||||
int ret;
|
||||
|
||||
gcm_ctx = (struct tls12_crypto_info_aes_gcm_128 *)
|
||||
&csk->tlshws.crypto_info;
|
||||
|
||||
key_ctx_size = sizeof(struct _key_ctx) +
|
||||
roundup(keylen, 16) + AEAD_H_SIZE;
|
||||
|
||||
if (keylen == AES_KEYSIZE_128) {
|
||||
ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_128;
|
||||
} else if (keylen == AES_KEYSIZE_192) {
|
||||
ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_192;
|
||||
} else if (keylen == AES_KEYSIZE_256) {
|
||||
ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_256;
|
||||
} else {
|
||||
pr_err("GCM: Invalid key length %d\n", keylen);
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy(key, gcm_ctx->key, keylen);
|
||||
|
||||
/* Calculate the H = CIPH(K, 0 repeated 16 times).
|
||||
* It will go in key context
|
||||
*/
|
||||
cipher = crypto_alloc_cipher("aes", 0, 0);
|
||||
if (IS_ERR(cipher)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = crypto_cipher_setkey(cipher, key, keylen);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
memset(ghash_h, 0, AEAD_H_SIZE);
|
||||
crypto_cipher_encrypt_one(cipher, ghash_h, ghash_h);
|
||||
csk->tlshws.keylen = key_ctx_size;
|
||||
|
||||
/* Copy the Key context */
|
||||
if (optname == TLS_RX) {
|
||||
int key_ctx;
|
||||
|
||||
key_ctx = ((key_ctx_size >> 4) << 3);
|
||||
kctx->ctx_hdr = FILL_KEY_CRX_HDR(ck_size,
|
||||
CHCR_KEYCTX_MAC_KEY_SIZE_128,
|
||||
0, 0, key_ctx);
|
||||
chtls_rxkey_ivauth(kctx);
|
||||
} else {
|
||||
kctx->ctx_hdr = FILL_KEY_CTX_HDR(ck_size,
|
||||
CHCR_KEYCTX_MAC_KEY_SIZE_128,
|
||||
0, 0, key_ctx_size >> 4);
|
||||
}
|
||||
|
||||
memcpy(kctx->salt, gcm_ctx->salt, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
|
||||
memcpy(kctx->key, gcm_ctx->key, keylen);
|
||||
memcpy(kctx->key + keylen, ghash_h, AEAD_H_SIZE);
|
||||
/* erase key info from driver */
|
||||
memset(gcm_ctx->key, 0, keylen);
|
||||
|
||||
out1:
|
||||
crypto_free_cipher(cipher);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void chtls_set_scmd(struct chtls_sock *csk)
|
||||
{
|
||||
struct chtls_hws *hws = &csk->tlshws;
|
||||
|
||||
hws->scmd.seqno_numivs =
|
||||
SCMD_SEQ_NO_CTRL_V(3) |
|
||||
SCMD_PROTO_VERSION_V(0) |
|
||||
SCMD_ENC_DEC_CTRL_V(0) |
|
||||
SCMD_CIPH_AUTH_SEQ_CTRL_V(1) |
|
||||
SCMD_CIPH_MODE_V(2) |
|
||||
SCMD_AUTH_MODE_V(4) |
|
||||
SCMD_HMAC_CTRL_V(0) |
|
||||
SCMD_IV_SIZE_V(4) |
|
||||
SCMD_NUM_IVS_V(1);
|
||||
|
||||
hws->scmd.ivgen_hdrlen =
|
||||
SCMD_IV_GEN_CTRL_V(1) |
|
||||
SCMD_KEY_CTX_INLINE_V(0) |
|
||||
SCMD_TLS_FRAG_ENABLE_V(1);
|
||||
}
|
||||
|
||||
int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 optname)
|
||||
{
|
||||
struct tls_key_req *kwr;
|
||||
struct chtls_dev *cdev;
|
||||
struct _key_ctx *kctx;
|
||||
int wrlen, klen, len;
|
||||
struct sk_buff *skb;
|
||||
struct sock *sk;
|
||||
int keyid;
|
||||
int kaddr;
|
||||
int ret;
|
||||
|
||||
cdev = csk->cdev;
|
||||
sk = csk->sk;
|
||||
|
||||
klen = roundup((keylen + AEAD_H_SIZE) + sizeof(*kctx), 32);
|
||||
wrlen = roundup(sizeof(*kwr), 16);
|
||||
len = klen + wrlen;
|
||||
|
||||
/* Flush out-standing data before new key takes effect */
|
||||
if (optname == TLS_TX) {
|
||||
lock_sock(sk);
|
||||
if (skb_queue_len(&csk->txq))
|
||||
chtls_push_frames(csk, 0);
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
skb = alloc_skb(len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
keyid = get_new_keyid(csk, optname);
|
||||
if (keyid < 0) {
|
||||
ret = -ENOSPC;
|
||||
goto out_nokey;
|
||||
}
|
||||
|
||||
kaddr = keyid_to_addr(cdev->kmap.start, keyid);
|
||||
kwr = (struct tls_key_req *)__skb_put_zero(skb, len);
|
||||
kwr->wr.op_to_compl =
|
||||
cpu_to_be32(FW_WR_OP_V(FW_ULPTX_WR) | FW_WR_COMPL_F |
|
||||
FW_WR_ATOMIC_V(1U));
|
||||
kwr->wr.flowid_len16 =
|
||||
cpu_to_be32(FW_WR_LEN16_V(DIV_ROUND_UP(len, 16) |
|
||||
FW_WR_FLOWID_V(csk->tid)));
|
||||
kwr->wr.protocol = 0;
|
||||
kwr->wr.mfs = htons(TLS_MFS);
|
||||
kwr->wr.reneg_to_write_rx = optname;
|
||||
|
||||
/* ulptx command */
|
||||
kwr->req.cmd = cpu_to_be32(ULPTX_CMD_V(ULP_TX_MEM_WRITE) |
|
||||
T5_ULP_MEMIO_ORDER_V(1) |
|
||||
T5_ULP_MEMIO_IMM_V(1));
|
||||
kwr->req.len16 = cpu_to_be32((csk->tid << 8) |
|
||||
DIV_ROUND_UP(len - sizeof(kwr->wr), 16));
|
||||
kwr->req.dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN_V(klen >> 5));
|
||||
kwr->req.lock_addr = cpu_to_be32(ULP_MEMIO_ADDR_V(kaddr));
|
||||
|
||||
/* sub command */
|
||||
kwr->sc_imm.cmd_more = cpu_to_be32(ULPTX_CMD_V(ULP_TX_SC_IMM));
|
||||
kwr->sc_imm.len = cpu_to_be32(klen);
|
||||
|
||||
/* key info */
|
||||
kctx = (struct _key_ctx *)(kwr + 1);
|
||||
ret = chtls_key_info(csk, kctx, keylen, optname);
|
||||
if (ret)
|
||||
goto out_notcb;
|
||||
|
||||
set_wr_txq(skb, CPL_PRIORITY_DATA, csk->tlshws.txqid);
|
||||
csk->wr_credits -= DIV_ROUND_UP(len, 16);
|
||||
csk->wr_unacked += DIV_ROUND_UP(len, 16);
|
||||
enqueue_wr(csk, skb);
|
||||
cxgb4_ofld_send(csk->egress_dev, skb);
|
||||
|
||||
chtls_set_scmd(csk);
|
||||
/* Clear quiesce for Rx key */
|
||||
if (optname == TLS_RX) {
|
||||
ret = chtls_set_tcb_keyid(sk, keyid);
|
||||
if (ret)
|
||||
goto out_notcb;
|
||||
ret = chtls_set_tcb_field(sk, 0,
|
||||
TCB_ULP_RAW_V(TCB_ULP_RAW_M),
|
||||
TCB_ULP_RAW_V((TF_TLS_KEY_SIZE_V(1) |
|
||||
TF_TLS_CONTROL_V(1) |
|
||||
TF_TLS_ACTIVE_V(1) |
|
||||
TF_TLS_ENABLE_V(1))));
|
||||
if (ret)
|
||||
goto out_notcb;
|
||||
ret = chtls_set_tcb_seqno(sk);
|
||||
if (ret)
|
||||
goto out_notcb;
|
||||
ret = chtls_set_tcb_quiesce(sk, 0);
|
||||
if (ret)
|
||||
goto out_notcb;
|
||||
csk->tlshws.rxkey = keyid;
|
||||
} else {
|
||||
csk->tlshws.tx_seq_no = 0;
|
||||
csk->tlshws.txkey = keyid;
|
||||
}
|
||||
|
||||
return ret;
|
||||
out_notcb:
|
||||
free_tls_keyid(sk);
|
||||
out_nokey:
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
1822
drivers/crypto/chelsio/chtls/chtls_io.c
Normal file
1822
drivers/crypto/chelsio/chtls/chtls_io.c
Normal file
File diff suppressed because it is too large
Load Diff
578
drivers/crypto/chelsio/chtls/chtls_main.c
Normal file
578
drivers/crypto/chelsio/chtls/chtls_main.c
Normal file
@ -0,0 +1,578 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Chelsio Communications, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Written by: Atul Gupta (atul.gupta@chelsio.com)
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/tls.h>
|
||||
|
||||
#include "chtls.h"
|
||||
#include "chtls_cm.h"
|
||||
|
||||
#define DRV_NAME "chtls"
|
||||
|
||||
/*
|
||||
* chtls device management
|
||||
* maintains a list of the chtls devices
|
||||
*/
|
||||
static LIST_HEAD(cdev_list);
|
||||
static DEFINE_MUTEX(cdev_mutex);
|
||||
static DEFINE_MUTEX(cdev_list_lock);
|
||||
|
||||
static DEFINE_MUTEX(notify_mutex);
|
||||
static RAW_NOTIFIER_HEAD(listen_notify_list);
|
||||
static struct proto chtls_cpl_prot;
|
||||
struct request_sock_ops chtls_rsk_ops;
|
||||
static uint send_page_order = (14 - PAGE_SHIFT < 0) ? 0 : 14 - PAGE_SHIFT;
|
||||
|
||||
static void register_listen_notifier(struct notifier_block *nb)
|
||||
{
|
||||
mutex_lock(¬ify_mutex);
|
||||
raw_notifier_chain_register(&listen_notify_list, nb);
|
||||
mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
static void unregister_listen_notifier(struct notifier_block *nb)
|
||||
{
|
||||
mutex_lock(¬ify_mutex);
|
||||
raw_notifier_chain_unregister(&listen_notify_list, nb);
|
||||
mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
static int listen_notify_handler(struct notifier_block *this,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct chtls_dev *cdev;
|
||||
struct sock *sk;
|
||||
int ret;
|
||||
|
||||
sk = data;
|
||||
ret = NOTIFY_DONE;
|
||||
|
||||
switch (event) {
|
||||
case CHTLS_LISTEN_START:
|
||||
case CHTLS_LISTEN_STOP:
|
||||
mutex_lock(&cdev_list_lock);
|
||||
list_for_each_entry(cdev, &cdev_list, list) {
|
||||
if (event == CHTLS_LISTEN_START)
|
||||
ret = chtls_listen_start(cdev, sk);
|
||||
else
|
||||
chtls_listen_stop(cdev, sk);
|
||||
}
|
||||
mutex_unlock(&cdev_list_lock);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block listen_notifier = {
|
||||
.notifier_call = listen_notify_handler
|
||||
};
|
||||
|
||||
static int listen_backlog_rcv(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
if (likely(skb_transport_header(skb) != skb_network_header(skb)))
|
||||
return tcp_v4_do_rcv(sk, skb);
|
||||
BLOG_SKB_CB(skb)->backlog_rcv(sk, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chtls_start_listen(struct sock *sk)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (sk->sk_protocol != IPPROTO_TCP)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if (sk->sk_family == PF_INET &&
|
||||
LOOPBACK(inet_sk(sk)->inet_rcv_saddr))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
sk->sk_backlog_rcv = listen_backlog_rcv;
|
||||
mutex_lock(¬ify_mutex);
|
||||
err = raw_notifier_call_chain(&listen_notify_list,
|
||||
CHTLS_LISTEN_START, sk);
|
||||
mutex_unlock(¬ify_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void chtls_stop_listen(struct sock *sk)
|
||||
{
|
||||
if (sk->sk_protocol != IPPROTO_TCP)
|
||||
return;
|
||||
|
||||
mutex_lock(¬ify_mutex);
|
||||
raw_notifier_call_chain(&listen_notify_list,
|
||||
CHTLS_LISTEN_STOP, sk);
|
||||
mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
static int chtls_inline_feature(struct tls_device *dev)
|
||||
{
|
||||
struct net_device *netdev;
|
||||
struct chtls_dev *cdev;
|
||||
int i;
|
||||
|
||||
cdev = to_chtls_dev(dev);
|
||||
|
||||
for (i = 0; i < cdev->lldi->nports; i++) {
|
||||
netdev = cdev->ports[i];
|
||||
if (netdev->features & NETIF_F_HW_TLS_RECORD)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
|
||||
{
|
||||
if (sk->sk_state == TCP_LISTEN)
|
||||
return chtls_start_listen(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk)
|
||||
{
|
||||
if (sk->sk_state == TCP_LISTEN)
|
||||
chtls_stop_listen(sk);
|
||||
}
|
||||
|
||||
static void chtls_register_dev(struct chtls_dev *cdev)
|
||||
{
|
||||
struct tls_device *tlsdev = &cdev->tlsdev;
|
||||
|
||||
strlcpy(tlsdev->name, "chtls", TLS_DEVICE_NAME_MAX);
|
||||
strlcat(tlsdev->name, cdev->lldi->ports[0]->name,
|
||||
TLS_DEVICE_NAME_MAX);
|
||||
tlsdev->feature = chtls_inline_feature;
|
||||
tlsdev->hash = chtls_create_hash;
|
||||
tlsdev->unhash = chtls_destroy_hash;
|
||||
tls_register_device(&cdev->tlsdev);
|
||||
}
|
||||
|
||||
static void chtls_unregister_dev(struct chtls_dev *cdev)
|
||||
{
|
||||
tls_unregister_device(&cdev->tlsdev);
|
||||
}
|
||||
|
||||
static void process_deferq(struct work_struct *task_param)
|
||||
{
|
||||
struct chtls_dev *cdev = container_of(task_param,
|
||||
struct chtls_dev, deferq_task);
|
||||
struct sk_buff *skb;
|
||||
|
||||
spin_lock_bh(&cdev->deferq.lock);
|
||||
while ((skb = __skb_dequeue(&cdev->deferq)) != NULL) {
|
||||
spin_unlock_bh(&cdev->deferq.lock);
|
||||
DEFERRED_SKB_CB(skb)->handler(cdev, skb);
|
||||
spin_lock_bh(&cdev->deferq.lock);
|
||||
}
|
||||
spin_unlock_bh(&cdev->deferq.lock);
|
||||
}
|
||||
|
||||
static int chtls_get_skb(struct chtls_dev *cdev)
|
||||
{
|
||||
cdev->askb = alloc_skb(sizeof(struct tcphdr), GFP_KERNEL);
|
||||
if (!cdev->askb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_put(cdev->askb, sizeof(struct tcphdr));
|
||||
skb_reset_transport_header(cdev->askb);
|
||||
memset(cdev->askb->data, 0, cdev->askb->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *chtls_uld_add(const struct cxgb4_lld_info *info)
|
||||
{
|
||||
struct cxgb4_lld_info *lldi;
|
||||
struct chtls_dev *cdev;
|
||||
int i, j;
|
||||
|
||||
cdev = kzalloc(sizeof(*cdev) + info->nports *
|
||||
(sizeof(struct net_device *)), GFP_KERNEL);
|
||||
if (!cdev)
|
||||
goto out;
|
||||
|
||||
lldi = kzalloc(sizeof(*lldi), GFP_KERNEL);
|
||||
if (!lldi)
|
||||
goto out_lldi;
|
||||
|
||||
if (chtls_get_skb(cdev))
|
||||
goto out_skb;
|
||||
|
||||
*lldi = *info;
|
||||
cdev->lldi = lldi;
|
||||
cdev->pdev = lldi->pdev;
|
||||
cdev->tids = lldi->tids;
|
||||
cdev->ports = (struct net_device **)(cdev + 1);
|
||||
cdev->ports = lldi->ports;
|
||||
cdev->mtus = lldi->mtus;
|
||||
cdev->tids = lldi->tids;
|
||||
cdev->pfvf = FW_VIID_PFN_G(cxgb4_port_viid(lldi->ports[0]))
|
||||
<< FW_VIID_PFN_S;
|
||||
|
||||
for (i = 0; i < (1 << RSPQ_HASH_BITS); i++) {
|
||||
unsigned int size = 64 - sizeof(struct rsp_ctrl) - 8;
|
||||
|
||||
cdev->rspq_skb_cache[i] = __alloc_skb(size,
|
||||
gfp_any(), 0,
|
||||
lldi->nodeid);
|
||||
if (unlikely(!cdev->rspq_skb_cache[i]))
|
||||
goto out_rspq_skb;
|
||||
}
|
||||
|
||||
idr_init(&cdev->hwtid_idr);
|
||||
INIT_WORK(&cdev->deferq_task, process_deferq);
|
||||
spin_lock_init(&cdev->listen_lock);
|
||||
spin_lock_init(&cdev->idr_lock);
|
||||
cdev->send_page_order = min_t(uint, get_order(32768),
|
||||
send_page_order);
|
||||
|
||||
if (lldi->vr->key.size)
|
||||
if (chtls_init_kmap(cdev, lldi))
|
||||
goto out_rspq_skb;
|
||||
|
||||
mutex_lock(&cdev_mutex);
|
||||
list_add_tail(&cdev->list, &cdev_list);
|
||||
mutex_unlock(&cdev_mutex);
|
||||
|
||||
return cdev;
|
||||
out_rspq_skb:
|
||||
for (j = 0; j <= i; j++)
|
||||
kfree_skb(cdev->rspq_skb_cache[j]);
|
||||
kfree_skb(cdev->askb);
|
||||
out_skb:
|
||||
kfree(lldi);
|
||||
out_lldi:
|
||||
kfree(cdev);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void chtls_free_uld(struct chtls_dev *cdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
chtls_unregister_dev(cdev);
|
||||
kvfree(cdev->kmap.addr);
|
||||
idr_destroy(&cdev->hwtid_idr);
|
||||
for (i = 0; i < (1 << RSPQ_HASH_BITS); i++)
|
||||
kfree_skb(cdev->rspq_skb_cache[i]);
|
||||
kfree(cdev->lldi);
|
||||
if (cdev->askb)
|
||||
kfree_skb(cdev->askb);
|
||||
kfree(cdev);
|
||||
}
|
||||
|
||||
static void chtls_free_all_uld(void)
|
||||
{
|
||||
struct chtls_dev *cdev, *tmp;
|
||||
|
||||
mutex_lock(&cdev_mutex);
|
||||
list_for_each_entry_safe(cdev, tmp, &cdev_list, list)
|
||||
chtls_free_uld(cdev);
|
||||
mutex_unlock(&cdev_mutex);
|
||||
}
|
||||
|
||||
static int chtls_uld_state_change(void *handle, enum cxgb4_state new_state)
|
||||
{
|
||||
struct chtls_dev *cdev = handle;
|
||||
|
||||
switch (new_state) {
|
||||
case CXGB4_STATE_UP:
|
||||
chtls_register_dev(cdev);
|
||||
break;
|
||||
case CXGB4_STATE_DOWN:
|
||||
break;
|
||||
case CXGB4_STATE_START_RECOVERY:
|
||||
break;
|
||||
case CXGB4_STATE_DETACH:
|
||||
mutex_lock(&cdev_mutex);
|
||||
list_del(&cdev->list);
|
||||
mutex_unlock(&cdev_mutex);
|
||||
chtls_free_uld(cdev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *copy_gl_to_skb_pkt(const struct pkt_gl *gl,
|
||||
const __be64 *rsp,
|
||||
u32 pktshift)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Allocate space for cpl_pass_accpet_req which will be synthesized by
|
||||
* driver. Once driver synthesizes cpl_pass_accpet_req the skb will go
|
||||
* through the regular cpl_pass_accept_req processing in TOM.
|
||||
*/
|
||||
skb = alloc_skb(gl->tot_len + sizeof(struct cpl_pass_accept_req)
|
||||
- pktshift, GFP_ATOMIC);
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
__skb_put(skb, gl->tot_len + sizeof(struct cpl_pass_accept_req)
|
||||
- pktshift);
|
||||
/* For now we will copy cpl_rx_pkt in the skb */
|
||||
skb_copy_to_linear_data(skb, rsp, sizeof(struct cpl_rx_pkt));
|
||||
skb_copy_to_linear_data_offset(skb, sizeof(struct cpl_pass_accept_req)
|
||||
, gl->va + pktshift,
|
||||
gl->tot_len - pktshift);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int chtls_recv_packet(struct chtls_dev *cdev,
|
||||
const struct pkt_gl *gl, const __be64 *rsp)
|
||||
{
|
||||
unsigned int opcode = *(u8 *)rsp;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
skb = copy_gl_to_skb_pkt(gl, rsp, cdev->lldi->sge_pktshift);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = chtls_handlers[opcode](cdev, skb);
|
||||
if (ret & CPL_RET_BUF_DONE)
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chtls_recv_rsp(struct chtls_dev *cdev, const __be64 *rsp)
|
||||
{
|
||||
unsigned long rspq_bin;
|
||||
unsigned int opcode;
|
||||
struct sk_buff *skb;
|
||||
unsigned int len;
|
||||
int ret;
|
||||
|
||||
len = 64 - sizeof(struct rsp_ctrl) - 8;
|
||||
opcode = *(u8 *)rsp;
|
||||
|
||||
rspq_bin = hash_ptr((void *)rsp, RSPQ_HASH_BITS);
|
||||
skb = cdev->rspq_skb_cache[rspq_bin];
|
||||
if (skb && !skb_is_nonlinear(skb) &&
|
||||
!skb_shared(skb) && !skb_cloned(skb)) {
|
||||
refcount_inc(&skb->users);
|
||||
if (refcount_read(&skb->users) == 2) {
|
||||
__skb_trim(skb, 0);
|
||||
if (skb_tailroom(skb) >= len)
|
||||
goto copy_out;
|
||||
}
|
||||
refcount_dec(&skb->users);
|
||||
}
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (unlikely(!skb))
|
||||
return -ENOMEM;
|
||||
|
||||
copy_out:
|
||||
__skb_put(skb, len);
|
||||
skb_copy_to_linear_data(skb, rsp, len);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
ret = chtls_handlers[opcode](cdev, skb);
|
||||
|
||||
if (ret & CPL_RET_BUF_DONE)
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chtls_recv(struct chtls_dev *cdev,
|
||||
struct sk_buff **skbs, const __be64 *rsp)
|
||||
{
|
||||
struct sk_buff *skb = *skbs;
|
||||
unsigned int opcode;
|
||||
int ret;
|
||||
|
||||
opcode = *(u8 *)rsp;
|
||||
|
||||
__skb_push(skb, sizeof(struct rss_header));
|
||||
skb_copy_to_linear_data(skb, rsp, sizeof(struct rss_header));
|
||||
|
||||
ret = chtls_handlers[opcode](cdev, skb);
|
||||
if (ret & CPL_RET_BUF_DONE)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int chtls_uld_rx_handler(void *handle, const __be64 *rsp,
|
||||
const struct pkt_gl *gl)
|
||||
{
|
||||
struct chtls_dev *cdev = handle;
|
||||
unsigned int opcode;
|
||||
struct sk_buff *skb;
|
||||
|
||||
opcode = *(u8 *)rsp;
|
||||
|
||||
if (unlikely(opcode == CPL_RX_PKT)) {
|
||||
if (chtls_recv_packet(cdev, gl, rsp) < 0)
|
||||
goto nomem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!gl)
|
||||
return chtls_recv_rsp(cdev, rsp);
|
||||
|
||||
#define RX_PULL_LEN 128
|
||||
skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN);
|
||||
if (unlikely(!skb))
|
||||
goto nomem;
|
||||
chtls_recv(cdev, &skb, rsp);
|
||||
return 0;
|
||||
|
||||
nomem:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int do_chtls_getsockopt(struct sock *sk, char __user *optval,
|
||||
int __user *optlen)
|
||||
{
|
||||
struct tls_crypto_info crypto_info;
|
||||
|
||||
crypto_info.version = TLS_1_2_VERSION;
|
||||
if (copy_to_user(optval, &crypto_info, sizeof(struct tls_crypto_info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chtls_getsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
|
||||
if (level != SOL_TLS)
|
||||
return ctx->getsockopt(sk, level, optname, optval, optlen);
|
||||
|
||||
return do_chtls_getsockopt(sk, optval, optlen);
|
||||
}
|
||||
|
||||
static int do_chtls_setsockopt(struct sock *sk, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
struct tls_crypto_info *crypto_info, tmp_crypto_info;
|
||||
struct chtls_sock *csk;
|
||||
int keylen;
|
||||
int rc = 0;
|
||||
|
||||
csk = rcu_dereference_sk_user_data(sk);
|
||||
|
||||
if (!optval || optlen < sizeof(*crypto_info)) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = copy_from_user(&tmp_crypto_info, optval, sizeof(*crypto_info));
|
||||
if (rc) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check version */
|
||||
if (tmp_crypto_info.version != TLS_1_2_VERSION) {
|
||||
rc = -ENOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
crypto_info = (struct tls_crypto_info *)&csk->tlshws.crypto_info;
|
||||
|
||||
switch (tmp_crypto_info.cipher_type) {
|
||||
case TLS_CIPHER_AES_GCM_128: {
|
||||
rc = copy_from_user(crypto_info, optval,
|
||||
sizeof(struct
|
||||
tls12_crypto_info_aes_gcm_128));
|
||||
|
||||
if (rc) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
keylen = TLS_CIPHER_AES_GCM_128_KEY_SIZE;
|
||||
rc = chtls_setkey(csk, keylen, optname);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int chtls_setsockopt(struct sock *sk, int level, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
struct tls_context *ctx = tls_get_ctx(sk);
|
||||
|
||||
if (level != SOL_TLS)
|
||||
return ctx->setsockopt(sk, level, optname, optval, optlen);
|
||||
|
||||
return do_chtls_setsockopt(sk, optname, optval, optlen);
|
||||
}
|
||||
|
||||
static struct cxgb4_uld_info chtls_uld_info = {
|
||||
.name = DRV_NAME,
|
||||
.nrxq = MAX_ULD_QSETS,
|
||||
.ntxq = MAX_ULD_QSETS,
|
||||
.rxq_size = 1024,
|
||||
.add = chtls_uld_add,
|
||||
.state_change = chtls_uld_state_change,
|
||||
.rx_handler = chtls_uld_rx_handler,
|
||||
};
|
||||
|
||||
void chtls_install_cpl_ops(struct sock *sk)
|
||||
{
|
||||
sk->sk_prot = &chtls_cpl_prot;
|
||||
}
|
||||
|
||||
static void __init chtls_init_ulp_ops(void)
|
||||
{
|
||||
chtls_cpl_prot = tcp_prot;
|
||||
chtls_init_rsk_ops(&chtls_cpl_prot, &chtls_rsk_ops,
|
||||
&tcp_prot, PF_INET);
|
||||
chtls_cpl_prot.close = chtls_close;
|
||||
chtls_cpl_prot.disconnect = chtls_disconnect;
|
||||
chtls_cpl_prot.destroy = chtls_destroy_sock;
|
||||
chtls_cpl_prot.shutdown = chtls_shutdown;
|
||||
chtls_cpl_prot.sendmsg = chtls_sendmsg;
|
||||
chtls_cpl_prot.sendpage = chtls_sendpage;
|
||||
chtls_cpl_prot.recvmsg = chtls_recvmsg;
|
||||
chtls_cpl_prot.setsockopt = chtls_setsockopt;
|
||||
chtls_cpl_prot.getsockopt = chtls_getsockopt;
|
||||
}
|
||||
|
||||
static int __init chtls_register(void)
|
||||
{
|
||||
chtls_init_ulp_ops();
|
||||
register_listen_notifier(&listen_notifier);
|
||||
cxgb4_register_uld(CXGB4_ULD_TLS, &chtls_uld_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit chtls_unregister(void)
|
||||
{
|
||||
unregister_listen_notifier(&listen_notifier);
|
||||
chtls_free_all_uld();
|
||||
cxgb4_unregister_uld(CXGB4_ULD_TLS);
|
||||
}
|
||||
|
||||
module_init(chtls_register);
|
||||
module_exit(chtls_unregister);
|
||||
|
||||
MODULE_DESCRIPTION("Chelsio TLS Inline driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Chelsio Communications");
|
||||
MODULE_VERSION(DRV_VERSION);
|
@ -1334,7 +1334,7 @@ static bool validate_ipv6_net_dev(struct net_device *net_dev,
|
||||
IPV6_ADDR_LINKLOCAL;
|
||||
struct rt6_info *rt = rt6_lookup(dev_net(net_dev), &dst_addr->sin6_addr,
|
||||
&src_addr->sin6_addr, net_dev->ifindex,
|
||||
strict);
|
||||
NULL, strict);
|
||||
bool ret;
|
||||
|
||||
if (!rt)
|
||||
|
@ -403,10 +403,12 @@ static void enum_all_gids_of_dev_cb(struct ib_device *ib_dev,
|
||||
* our feet
|
||||
*/
|
||||
rtnl_lock();
|
||||
down_read(&net_rwsem);
|
||||
for_each_net(net)
|
||||
for_each_netdev(net, ndev)
|
||||
if (is_eth_port_of_netdev(ib_dev, port, rdma_ndev, ndev))
|
||||
add_netdev_ips(ib_dev, port, rdma_ndev, ndev);
|
||||
up_read(&net_rwsem);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
|
@ -1217,6 +1217,7 @@ static int c4iw_uld_state_change(void *handle, enum cxgb4_state new_state)
|
||||
if (ctx->dev)
|
||||
c4iw_remove(ctx);
|
||||
break;
|
||||
case CXGB4_STATE_FATAL_ERROR:
|
||||
case CXGB4_STATE_START_RECOVERY:
|
||||
pr_info("%s: Fatal Error\n", pci_name(ctx->lldi.pdev));
|
||||
if (ctx->dev) {
|
||||
|
@ -1153,7 +1153,7 @@ static int get_ctxt_info(struct hfi1_filedata *fd, unsigned long arg, u32 len)
|
||||
cinfo.sdma_ring_size = fd->cq->nentries;
|
||||
cinfo.rcvegr_size = uctxt->egrbufs.rcvtid_size;
|
||||
|
||||
trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, fd->subctxt, cinfo);
|
||||
trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, fd->subctxt, &cinfo);
|
||||
if (copy_to_user((void __user *)arg, &cinfo, len))
|
||||
return -EFAULT;
|
||||
|
||||
|
@ -106,7 +106,7 @@ TRACE_EVENT(hfi1_uctxtdata,
|
||||
TRACE_EVENT(hfi1_ctxt_info,
|
||||
TP_PROTO(struct hfi1_devdata *dd, unsigned int ctxt,
|
||||
unsigned int subctxt,
|
||||
struct hfi1_ctxt_info cinfo),
|
||||
struct hfi1_ctxt_info *cinfo),
|
||||
TP_ARGS(dd, ctxt, subctxt, cinfo),
|
||||
TP_STRUCT__entry(DD_DEV_ENTRY(dd)
|
||||
__field(unsigned int, ctxt)
|
||||
@ -120,11 +120,11 @@ TRACE_EVENT(hfi1_ctxt_info,
|
||||
TP_fast_assign(DD_DEV_ASSIGN(dd);
|
||||
__entry->ctxt = ctxt;
|
||||
__entry->subctxt = subctxt;
|
||||
__entry->egrtids = cinfo.egrtids;
|
||||
__entry->rcvhdrq_cnt = cinfo.rcvhdrq_cnt;
|
||||
__entry->rcvhdrq_size = cinfo.rcvhdrq_entsize;
|
||||
__entry->sdma_ring_size = cinfo.sdma_ring_size;
|
||||
__entry->rcvegr_size = cinfo.rcvegr_size;
|
||||
__entry->egrtids = cinfo->egrtids;
|
||||
__entry->rcvhdrq_cnt = cinfo->rcvhdrq_cnt;
|
||||
__entry->rcvhdrq_size = cinfo->rcvhdrq_entsize;
|
||||
__entry->sdma_ring_size = cinfo->sdma_ring_size;
|
||||
__entry->rcvegr_size = cinfo->rcvegr_size;
|
||||
),
|
||||
TP_printk("[%s] ctxt %u:%u " CINFO_FMT,
|
||||
__get_str(dev),
|
||||
|
@ -2,3 +2,4 @@ obj-$(CONFIG_MLX5_INFINIBAND) += mlx5_ib.o
|
||||
|
||||
mlx5_ib-y := main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o gsi.o ib_virt.o cmd.o cong.o
|
||||
mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o
|
||||
mlx5_ib-$(CONFIG_MLX5_ESWITCH) += ib_rep.o
|
||||
|
@ -64,14 +64,9 @@ static void mlx5_ib_cq_event(struct mlx5_core_cq *mcq, enum mlx5_event type)
|
||||
}
|
||||
}
|
||||
|
||||
static void *get_cqe_from_buf(struct mlx5_ib_cq_buf *buf, int n, int size)
|
||||
{
|
||||
return mlx5_buf_offset(&buf->buf, n * size);
|
||||
}
|
||||
|
||||
static void *get_cqe(struct mlx5_ib_cq *cq, int n)
|
||||
{
|
||||
return get_cqe_from_buf(&cq->buf, n, cq->mcq.cqe_sz);
|
||||
return mlx5_frag_buf_get_wqe(&cq->buf.fbc, n);
|
||||
}
|
||||
|
||||
static u8 sw_ownership_bit(int n, int nent)
|
||||
@ -272,14 +267,8 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
|
||||
|
||||
static void dump_cqe(struct mlx5_ib_dev *dev, struct mlx5_err_cqe *cqe)
|
||||
{
|
||||
__be32 *p = (__be32 *)cqe;
|
||||
int i;
|
||||
|
||||
mlx5_ib_warn(dev, "dump error cqe\n");
|
||||
for (i = 0; i < sizeof(*cqe) / 16; i++, p += 4)
|
||||
pr_info("%08x %08x %08x %08x\n", be32_to_cpu(p[0]),
|
||||
be32_to_cpu(p[1]), be32_to_cpu(p[2]),
|
||||
be32_to_cpu(p[3]));
|
||||
mlx5_dump_err_cqe(dev->mdev, cqe);
|
||||
}
|
||||
|
||||
static void mlx5_handle_error_cqe(struct mlx5_ib_dev *dev,
|
||||
@ -404,7 +393,7 @@ static void handle_atomics(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64,
|
||||
|
||||
static void free_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf)
|
||||
{
|
||||
mlx5_buf_free(dev->mdev, &buf->buf);
|
||||
mlx5_frag_buf_free(dev->mdev, &buf->fbc.frag_buf);
|
||||
}
|
||||
|
||||
static void get_sig_err_item(struct mlx5_sig_err_cqe *cqe,
|
||||
@ -725,12 +714,25 @@ int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int alloc_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf,
|
||||
int nent, int cqe_size)
|
||||
static int alloc_cq_frag_buf(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_cq_buf *buf,
|
||||
int nent,
|
||||
int cqe_size)
|
||||
{
|
||||
struct mlx5_frag_buf_ctrl *c = &buf->fbc;
|
||||
struct mlx5_frag_buf *frag_buf = &c->frag_buf;
|
||||
u32 cqc_buff[MLX5_ST_SZ_DW(cqc)] = {0};
|
||||
int err;
|
||||
|
||||
err = mlx5_buf_alloc(dev->mdev, nent * cqe_size, &buf->buf);
|
||||
MLX5_SET(cqc, cqc_buff, log_cq_size, ilog2(cqe_size));
|
||||
MLX5_SET(cqc, cqc_buff, cqe_sz, (cqe_size == 128) ? 1 : 0);
|
||||
|
||||
mlx5_core_init_cq_frag_buf(&buf->fbc, cqc_buff);
|
||||
|
||||
err = mlx5_frag_buf_alloc_node(dev->mdev,
|
||||
nent * cqe_size,
|
||||
frag_buf,
|
||||
dev->mdev->priv.numa_node);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -863,14 +865,15 @@ static void destroy_cq_user(struct mlx5_ib_cq *cq, struct ib_ucontext *context)
|
||||
ib_umem_release(cq->buf.umem);
|
||||
}
|
||||
|
||||
static void init_cq_buf(struct mlx5_ib_cq *cq, struct mlx5_ib_cq_buf *buf)
|
||||
static void init_cq_frag_buf(struct mlx5_ib_cq *cq,
|
||||
struct mlx5_ib_cq_buf *buf)
|
||||
{
|
||||
int i;
|
||||
void *cqe;
|
||||
struct mlx5_cqe64 *cqe64;
|
||||
|
||||
for (i = 0; i < buf->nent; i++) {
|
||||
cqe = get_cqe_from_buf(buf, i, buf->cqe_size);
|
||||
cqe = get_cqe(cq, i);
|
||||
cqe64 = buf->cqe_size == 64 ? cqe : cqe + 64;
|
||||
cqe64->op_own = MLX5_CQE_INVALID << 4;
|
||||
}
|
||||
@ -892,14 +895,15 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
|
||||
cq->mcq.arm_db = cq->db.db + 1;
|
||||
cq->mcq.cqe_sz = cqe_size;
|
||||
|
||||
err = alloc_cq_buf(dev, &cq->buf, entries, cqe_size);
|
||||
err = alloc_cq_frag_buf(dev, &cq->buf, entries, cqe_size);
|
||||
if (err)
|
||||
goto err_db;
|
||||
|
||||
init_cq_buf(cq, &cq->buf);
|
||||
init_cq_frag_buf(cq, &cq->buf);
|
||||
|
||||
*inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
|
||||
MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) * cq->buf.buf.npages;
|
||||
MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) *
|
||||
cq->buf.fbc.frag_buf.npages;
|
||||
*cqb = kvzalloc(*inlen, GFP_KERNEL);
|
||||
if (!*cqb) {
|
||||
err = -ENOMEM;
|
||||
@ -907,11 +911,12 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
|
||||
}
|
||||
|
||||
pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, *cqb, pas);
|
||||
mlx5_fill_page_array(&cq->buf.buf, pas);
|
||||
mlx5_fill_page_frag_array(&cq->buf.fbc.frag_buf, pas);
|
||||
|
||||
cqc = MLX5_ADDR_OF(create_cq_in, *cqb, cq_context);
|
||||
MLX5_SET(cqc, cqc, log_page_size,
|
||||
cq->buf.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
|
||||
cq->buf.fbc.frag_buf.page_shift -
|
||||
MLX5_ADAPTER_PAGE_SHIFT);
|
||||
|
||||
*index = dev->mdev->priv.uar->index;
|
||||
|
||||
@ -1213,11 +1218,11 @@ static int resize_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
|
||||
if (!cq->resize_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = alloc_cq_buf(dev, cq->resize_buf, entries, cqe_size);
|
||||
err = alloc_cq_frag_buf(dev, cq->resize_buf, entries, cqe_size);
|
||||
if (err)
|
||||
goto ex;
|
||||
|
||||
init_cq_buf(cq, cq->resize_buf);
|
||||
init_cq_frag_buf(cq, cq->resize_buf);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1262,9 +1267,8 @@ static int copy_resize_cqes(struct mlx5_ib_cq *cq)
|
||||
}
|
||||
|
||||
while ((scqe64->op_own >> 4) != MLX5_CQE_RESIZE_CQ) {
|
||||
dcqe = get_cqe_from_buf(cq->resize_buf,
|
||||
(i + 1) & (cq->resize_buf->nent),
|
||||
dsize);
|
||||
dcqe = mlx5_frag_buf_get_wqe(&cq->resize_buf->fbc,
|
||||
(i + 1) & cq->resize_buf->nent);
|
||||
dcqe64 = dsize == 64 ? dcqe : dcqe + 64;
|
||||
sw_own = sw_ownership_bit(i + 1, cq->resize_buf->nent);
|
||||
memcpy(dcqe, scqe, dsize);
|
||||
@ -1330,8 +1334,11 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
|
||||
cqe_size = 64;
|
||||
err = resize_kernel(dev, cq, entries, cqe_size);
|
||||
if (!err) {
|
||||
npas = cq->resize_buf->buf.npages;
|
||||
page_shift = cq->resize_buf->buf.page_shift;
|
||||
struct mlx5_frag_buf_ctrl *c;
|
||||
|
||||
c = &cq->resize_buf->fbc;
|
||||
npas = c->frag_buf.npages;
|
||||
page_shift = c->frag_buf.page_shift;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1352,7 +1359,8 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
|
||||
mlx5_ib_populate_pas(dev, cq->resize_umem, page_shift,
|
||||
pas, 0);
|
||||
else
|
||||
mlx5_fill_page_array(&cq->resize_buf->buf, pas);
|
||||
mlx5_fill_page_frag_array(&cq->resize_buf->fbc.frag_buf,
|
||||
pas);
|
||||
|
||||
MLX5_SET(modify_cq_in, in,
|
||||
modify_field_select_resize_field_select.resize_field_select.resize_field_select,
|
||||
|
192
drivers/infiniband/hw/mlx5/ib_rep.c
Normal file
192
drivers/infiniband/hw/mlx5/ib_rep.c
Normal file
@ -0,0 +1,192 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright (c) 2018 Mellanox Technologies. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "ib_rep.h"
|
||||
|
||||
static const struct mlx5_ib_profile rep_profile = {
|
||||
STAGE_CREATE(MLX5_IB_STAGE_INIT,
|
||||
mlx5_ib_stage_init_init,
|
||||
mlx5_ib_stage_init_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_FLOW_DB,
|
||||
mlx5_ib_stage_rep_flow_db_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_CAPS,
|
||||
mlx5_ib_stage_caps_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
|
||||
mlx5_ib_stage_rep_non_default_cb,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_ROCE,
|
||||
mlx5_ib_stage_rep_roce_init,
|
||||
mlx5_ib_stage_rep_roce_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
|
||||
mlx5_ib_stage_dev_res_init,
|
||||
mlx5_ib_stage_dev_res_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_COUNTERS,
|
||||
mlx5_ib_stage_counters_init,
|
||||
mlx5_ib_stage_counters_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_BFREG,
|
||||
mlx5_ib_stage_bfrag_init,
|
||||
mlx5_ib_stage_bfrag_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_PRE_IB_REG_UMR,
|
||||
NULL,
|
||||
mlx5_ib_stage_pre_ib_reg_umr_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
|
||||
mlx5_ib_stage_ib_reg_init,
|
||||
mlx5_ib_stage_ib_reg_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR,
|
||||
mlx5_ib_stage_post_ib_reg_umr_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_CLASS_ATTR,
|
||||
mlx5_ib_stage_class_attr_init,
|
||||
NULL),
|
||||
};
|
||||
|
||||
static int
|
||||
mlx5_ib_nic_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mlx5_ib_nic_rep_unload(struct mlx5_eswitch_rep *rep)
|
||||
{
|
||||
rep->rep_if[REP_IB].priv = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
|
||||
{
|
||||
struct mlx5_ib_dev *ibdev;
|
||||
|
||||
ibdev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*ibdev));
|
||||
if (!ibdev)
|
||||
return -ENOMEM;
|
||||
|
||||
ibdev->rep = rep;
|
||||
ibdev->mdev = dev;
|
||||
ibdev->num_ports = max(MLX5_CAP_GEN(dev, num_ports),
|
||||
MLX5_CAP_GEN(dev, num_vhca_ports));
|
||||
if (!__mlx5_ib_add(ibdev, &rep_profile))
|
||||
return -EINVAL;
|
||||
|
||||
rep->rep_if[REP_IB].priv = ibdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
|
||||
{
|
||||
struct mlx5_ib_dev *dev;
|
||||
|
||||
if (!rep->rep_if[REP_IB].priv)
|
||||
return;
|
||||
|
||||
dev = mlx5_ib_rep_to_dev(rep);
|
||||
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
|
||||
rep->rep_if[REP_IB].priv = NULL;
|
||||
}
|
||||
|
||||
static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep)
|
||||
{
|
||||
return mlx5_ib_rep_to_dev(rep);
|
||||
}
|
||||
|
||||
static void mlx5_ib_rep_register_vf_vports(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
|
||||
int total_vfs = MLX5_TOTAL_VPORTS(dev->mdev);
|
||||
int vport;
|
||||
|
||||
for (vport = 1; vport < total_vfs; vport++) {
|
||||
struct mlx5_eswitch_rep_if rep_if = {};
|
||||
|
||||
rep_if.load = mlx5_ib_vport_rep_load;
|
||||
rep_if.unload = mlx5_ib_vport_rep_unload;
|
||||
rep_if.get_proto_dev = mlx5_ib_vport_get_proto_dev;
|
||||
mlx5_eswitch_register_vport_rep(esw, vport, &rep_if, REP_IB);
|
||||
}
|
||||
}
|
||||
|
||||
static void mlx5_ib_rep_unregister_vf_vports(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
|
||||
int total_vfs = MLX5_TOTAL_VPORTS(dev->mdev);
|
||||
int vport;
|
||||
|
||||
for (vport = 1; vport < total_vfs; vport++)
|
||||
mlx5_eswitch_unregister_vport_rep(esw, vport, REP_IB);
|
||||
}
|
||||
|
||||
void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
|
||||
struct mlx5_eswitch_rep_if rep_if = {};
|
||||
|
||||
rep_if.load = mlx5_ib_nic_rep_load;
|
||||
rep_if.unload = mlx5_ib_nic_rep_unload;
|
||||
rep_if.get_proto_dev = mlx5_ib_vport_get_proto_dev;
|
||||
rep_if.priv = dev;
|
||||
|
||||
mlx5_eswitch_register_vport_rep(esw, 0, &rep_if, REP_IB);
|
||||
|
||||
mlx5_ib_rep_register_vf_vports(dev);
|
||||
}
|
||||
|
||||
void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
|
||||
|
||||
mlx5_ib_rep_unregister_vf_vports(dev); /* VFs vports */
|
||||
mlx5_eswitch_unregister_vport_rep(esw, 0, REP_IB); /* UPLINK PF*/
|
||||
}
|
||||
|
||||
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
|
||||
{
|
||||
return mlx5_eswitch_mode(esw);
|
||||
}
|
||||
|
||||
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
|
||||
int vport_index)
|
||||
{
|
||||
return mlx5_eswitch_get_proto_dev(esw, vport_index, REP_IB);
|
||||
}
|
||||
|
||||
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
|
||||
int vport_index)
|
||||
{
|
||||
return mlx5_eswitch_get_proto_dev(esw, vport_index, REP_ETH);
|
||||
}
|
||||
|
||||
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
|
||||
{
|
||||
return mlx5_eswitch_uplink_get_proto_dev(esw, REP_IB);
|
||||
}
|
||||
|
||||
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw, int vport)
|
||||
{
|
||||
return mlx5_eswitch_vport_rep(esw, vport);
|
||||
}
|
||||
|
||||
int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq)
|
||||
{
|
||||
struct mlx5_flow_handle *flow_rule;
|
||||
struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
|
||||
|
||||
if (!dev->rep)
|
||||
return 0;
|
||||
|
||||
flow_rule =
|
||||
mlx5_eswitch_add_send_to_vport_rule(esw,
|
||||
dev->rep->vport,
|
||||
sq->base.mqp.qpn);
|
||||
if (IS_ERR(flow_rule))
|
||||
return PTR_ERR(flow_rule);
|
||||
sq->flow_rule = flow_rule;
|
||||
|
||||
return 0;
|
||||
}
|
72
drivers/infiniband/hw/mlx5/ib_rep.h
Normal file
72
drivers/infiniband/hw/mlx5/ib_rep.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright (c) 2018 Mellanox Technologies. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MLX5_IB_REP_H__
|
||||
#define __MLX5_IB_REP_H__
|
||||
|
||||
#include <linux/mlx5/eswitch.h>
|
||||
#include "mlx5_ib.h"
|
||||
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw);
|
||||
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
|
||||
int vport_index);
|
||||
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw);
|
||||
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
|
||||
int vport_index);
|
||||
void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev);
|
||||
int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq);
|
||||
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
|
||||
int vport_index);
|
||||
#else /* CONFIG_MLX5_ESWITCH */
|
||||
static inline u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
|
||||
{
|
||||
return SRIOV_NONE;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
|
||||
int vport_index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
|
||||
int vport_index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev) {}
|
||||
static inline void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev) {}
|
||||
static inline int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
|
||||
int vport_index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline
|
||||
struct mlx5_ib_dev *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
|
||||
{
|
||||
return (struct mlx5_ib_dev *)rep->rep_if[REP_IB].priv;
|
||||
}
|
||||
#endif /* __MLX5_IB_REP_H__ */
|
@ -57,7 +57,9 @@
|
||||
#include <linux/in.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include "mlx5_ib.h"
|
||||
#include "ib_rep.h"
|
||||
#include "cmd.h"
|
||||
#include <linux/mlx5/fs_helpers.h>
|
||||
|
||||
#define DRIVER_NAME "mlx5_ib"
|
||||
#define DRIVER_VERSION "5.0-0"
|
||||
@ -130,7 +132,7 @@ static int get_port_state(struct ib_device *ibdev,
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
ret = mlx5_ib_query_port(ibdev, port_num, &attr);
|
||||
ret = ibdev->query_port(ibdev, port_num, &attr);
|
||||
if (!ret)
|
||||
*state = attr.state;
|
||||
return ret;
|
||||
@ -154,10 +156,19 @@ static int mlx5_netdev_event(struct notifier_block *this,
|
||||
case NETDEV_REGISTER:
|
||||
case NETDEV_UNREGISTER:
|
||||
write_lock(&roce->netdev_lock);
|
||||
if (ibdev->rep) {
|
||||
struct mlx5_eswitch *esw = ibdev->mdev->priv.eswitch;
|
||||
struct net_device *rep_ndev;
|
||||
|
||||
if (ndev->dev.parent == &mdev->pdev->dev)
|
||||
roce->netdev = (event == NETDEV_UNREGISTER) ?
|
||||
rep_ndev = mlx5_ib_get_rep_netdev(esw,
|
||||
ibdev->rep->vport);
|
||||
if (rep_ndev == ndev)
|
||||
roce->netdev = (event == NETDEV_UNREGISTER) ?
|
||||
NULL : ndev;
|
||||
} else if (ndev->dev.parent == &ibdev->mdev->pdev->dev) {
|
||||
roce->netdev = (event == NETDEV_UNREGISTER) ?
|
||||
NULL : ndev;
|
||||
}
|
||||
write_unlock(&roce->netdev_lock);
|
||||
break;
|
||||
|
||||
@ -1272,6 +1283,22 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx5_ib_rep_query_port(struct ib_device *ibdev, u8 port,
|
||||
struct ib_port_attr *props)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Only link layer == ethernet is valid for representors */
|
||||
ret = mlx5_query_port_roce(ibdev, port, props);
|
||||
if (ret || !props)
|
||||
return ret;
|
||||
|
||||
/* We don't support GIDS */
|
||||
props->gid_tbl_len = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx5_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
|
||||
union ib_gid *gid)
|
||||
{
|
||||
@ -2290,11 +2317,9 @@ static void set_tos(void *outer_c, void *outer_v, u8 mask, u8 val)
|
||||
offsetof(typeof(filter), field) -\
|
||||
sizeof(filter.field))
|
||||
|
||||
#define IPV4_VERSION 4
|
||||
#define IPV6_VERSION 6
|
||||
static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
|
||||
u32 *match_v, const union ib_flow_spec *ib_spec,
|
||||
u32 *tag_id, bool *is_drop)
|
||||
struct mlx5_flow_act *action)
|
||||
{
|
||||
void *misc_params_c = MLX5_ADDR_OF(fte_match_param, match_c,
|
||||
misc_parameters);
|
||||
@ -2377,7 +2402,7 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
|
||||
ip_version, 0xf);
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_v,
|
||||
ip_version, IPV4_VERSION);
|
||||
ip_version, MLX5_FS_IPV4_VERSION);
|
||||
} else {
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
|
||||
ethertype, 0xffff);
|
||||
@ -2416,7 +2441,7 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
|
||||
ip_version, 0xf);
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_v,
|
||||
ip_version, IPV6_VERSION);
|
||||
ip_version, MLX5_FS_IPV6_VERSION);
|
||||
} else {
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
|
||||
ethertype, 0xffff);
|
||||
@ -2512,13 +2537,14 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
|
||||
if (ib_spec->flow_tag.tag_id >= BIT(24))
|
||||
return -EINVAL;
|
||||
|
||||
*tag_id = ib_spec->flow_tag.tag_id;
|
||||
action->flow_tag = ib_spec->flow_tag.tag_id;
|
||||
action->has_flow_tag = true;
|
||||
break;
|
||||
case IB_FLOW_SPEC_ACTION_DROP:
|
||||
if (FIELDS_NOT_SUPPORTED(ib_spec->drop,
|
||||
LAST_DROP_FIELD))
|
||||
return -EOPNOTSUPP;
|
||||
*is_drop = true;
|
||||
action->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -2635,7 +2661,7 @@ static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
|
||||
ibflow);
|
||||
struct mlx5_ib_flow_handler *iter, *tmp;
|
||||
|
||||
mutex_lock(&dev->flow_db.lock);
|
||||
mutex_lock(&dev->flow_db->lock);
|
||||
|
||||
list_for_each_entry_safe(iter, tmp, &handler->list, list) {
|
||||
mlx5_del_flow_rules(iter->rule);
|
||||
@ -2646,7 +2672,7 @@ static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
|
||||
|
||||
mlx5_del_flow_rules(handler->rule);
|
||||
put_flow_table(dev, handler->prio, true);
|
||||
mutex_unlock(&dev->flow_db.lock);
|
||||
mutex_unlock(&dev->flow_db->lock);
|
||||
|
||||
kfree(handler);
|
||||
|
||||
@ -2695,7 +2721,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
|
||||
MLX5_FLOW_NAMESPACE_BYPASS);
|
||||
num_entries = MLX5_FS_MAX_ENTRIES;
|
||||
num_groups = MLX5_FS_MAX_TYPES;
|
||||
prio = &dev->flow_db.prios[priority];
|
||||
prio = &dev->flow_db->prios[priority];
|
||||
} else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
|
||||
flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) {
|
||||
ns = mlx5_get_flow_namespace(dev->mdev,
|
||||
@ -2703,7 +2729,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
|
||||
build_leftovers_ft_param(&priority,
|
||||
&num_entries,
|
||||
&num_groups);
|
||||
prio = &dev->flow_db.prios[MLX5_IB_FLOW_LEFTOVERS_PRIO];
|
||||
prio = &dev->flow_db->prios[MLX5_IB_FLOW_LEFTOVERS_PRIO];
|
||||
} else if (flow_attr->type == IB_FLOW_ATTR_SNIFFER) {
|
||||
if (!MLX5_CAP_FLOWTABLE(dev->mdev,
|
||||
allow_sniffer_and_nic_rx_shared_tir))
|
||||
@ -2713,7 +2739,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
|
||||
MLX5_FLOW_NAMESPACE_SNIFFER_RX :
|
||||
MLX5_FLOW_NAMESPACE_SNIFFER_TX);
|
||||
|
||||
prio = &dev->flow_db.sniffer[ft_type];
|
||||
prio = &dev->flow_db->sniffer[ft_type];
|
||||
priority = 0;
|
||||
num_entries = 1;
|
||||
num_groups = 1;
|
||||
@ -2771,13 +2797,11 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
|
||||
{
|
||||
struct mlx5_flow_table *ft = ft_prio->flow_table;
|
||||
struct mlx5_ib_flow_handler *handler;
|
||||
struct mlx5_flow_act flow_act = {0};
|
||||
struct mlx5_flow_act flow_act = {.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG};
|
||||
struct mlx5_flow_spec *spec;
|
||||
struct mlx5_flow_destination *rule_dst = dst;
|
||||
const void *ib_flow = (const void *)flow_attr + sizeof(*flow_attr);
|
||||
unsigned int spec_index;
|
||||
u32 flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
|
||||
bool is_drop = false;
|
||||
int err = 0;
|
||||
int dest_num = 1;
|
||||
|
||||
@ -2796,7 +2820,7 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
|
||||
for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
|
||||
err = parse_flow_attr(dev->mdev, spec->match_criteria,
|
||||
spec->match_value,
|
||||
ib_flow, &flow_tag, &is_drop);
|
||||
ib_flow, &flow_act);
|
||||
if (err < 0)
|
||||
goto free;
|
||||
|
||||
@ -2806,9 +2830,20 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
|
||||
if (!flow_is_multicast_only(flow_attr))
|
||||
set_underlay_qp(dev, spec, underlay_qpn);
|
||||
|
||||
if (dev->rep) {
|
||||
void *misc;
|
||||
|
||||
misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
|
||||
misc_parameters);
|
||||
MLX5_SET(fte_match_set_misc, misc, source_port,
|
||||
dev->rep->vport);
|
||||
misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
|
||||
misc_parameters);
|
||||
MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
|
||||
}
|
||||
|
||||
spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria);
|
||||
if (is_drop) {
|
||||
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
|
||||
if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
|
||||
rule_dst = NULL;
|
||||
dest_num = 0;
|
||||
} else {
|
||||
@ -2816,15 +2851,14 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
|
||||
MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO;
|
||||
}
|
||||
|
||||
if (flow_tag != MLX5_FS_DEFAULT_FLOW_TAG &&
|
||||
if (flow_act.has_flow_tag &&
|
||||
(flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
|
||||
flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT)) {
|
||||
mlx5_ib_warn(dev, "Flow tag %u and attribute type %x isn't allowed in leftovers\n",
|
||||
flow_tag, flow_attr->type);
|
||||
flow_act.flow_tag, flow_attr->type);
|
||||
err = -EINVAL;
|
||||
goto free;
|
||||
}
|
||||
flow_act.flow_tag = flow_tag;
|
||||
handler->rule = mlx5_add_flow_rules(ft, spec,
|
||||
&flow_act,
|
||||
rule_dst, dest_num);
|
||||
@ -3003,7 +3037,7 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_lock(&dev->flow_db.lock);
|
||||
mutex_lock(&dev->flow_db->lock);
|
||||
|
||||
ft_prio = get_flow_table(dev, flow_attr, MLX5_IB_FT_RX);
|
||||
if (IS_ERR(ft_prio)) {
|
||||
@ -3052,7 +3086,7 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
|
||||
goto destroy_ft;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->flow_db.lock);
|
||||
mutex_unlock(&dev->flow_db->lock);
|
||||
kfree(dst);
|
||||
|
||||
return &handler->ibflow;
|
||||
@ -3062,7 +3096,7 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
|
||||
if (ft_prio_tx)
|
||||
put_flow_table(dev, ft_prio_tx, false);
|
||||
unlock:
|
||||
mutex_unlock(&dev->flow_db.lock);
|
||||
mutex_unlock(&dev->flow_db->lock);
|
||||
kfree(dst);
|
||||
kfree(handler);
|
||||
return ERR_PTR(err);
|
||||
@ -3775,6 +3809,25 @@ static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx5_port_rep_immutable(struct ib_device *ibdev, u8 port_num,
|
||||
struct ib_port_immutable *immutable)
|
||||
{
|
||||
struct ib_port_attr attr;
|
||||
int err;
|
||||
|
||||
immutable->core_cap_flags = RDMA_CORE_PORT_RAW_PACKET;
|
||||
|
||||
err = ib_query_port(ibdev, port_num, &attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
immutable->pkey_tbl_len = attr.pkey_tbl_len;
|
||||
immutable->gid_tbl_len = attr.gid_tbl_len;
|
||||
immutable->core_cap_flags = RDMA_CORE_PORT_RAW_PACKET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_dev_fw_str(struct ib_device *ibdev, char *str)
|
||||
{
|
||||
struct mlx5_ib_dev *dev =
|
||||
@ -3805,7 +3858,7 @@ static int mlx5_eth_lag_init(struct mlx5_ib_dev *dev)
|
||||
goto err_destroy_vport_lag;
|
||||
}
|
||||
|
||||
dev->flow_db.lag_demux_ft = ft;
|
||||
dev->flow_db->lag_demux_ft = ft;
|
||||
return 0;
|
||||
|
||||
err_destroy_vport_lag:
|
||||
@ -3817,9 +3870,9 @@ static void mlx5_eth_lag_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = dev->mdev;
|
||||
|
||||
if (dev->flow_db.lag_demux_ft) {
|
||||
mlx5_destroy_flow_table(dev->flow_db.lag_demux_ft);
|
||||
dev->flow_db.lag_demux_ft = NULL;
|
||||
if (dev->flow_db->lag_demux_ft) {
|
||||
mlx5_destroy_flow_table(dev->flow_db->lag_demux_ft);
|
||||
dev->flow_db->lag_demux_ft = NULL;
|
||||
|
||||
mlx5_cmd_destroy_vport_lag(mdev);
|
||||
}
|
||||
@ -3851,14 +3904,10 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev, u8 port_num)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mlx5_add_netdev_notifier(dev, port_num);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (MLX5_CAP_GEN(dev->mdev, roce)) {
|
||||
err = mlx5_nic_vport_enable_roce(dev->mdev);
|
||||
if (err)
|
||||
goto err_unregister_netdevice_notifier;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mlx5_eth_lag_init(dev);
|
||||
@ -3871,8 +3920,6 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev, u8 port_num)
|
||||
if (MLX5_CAP_GEN(dev->mdev, roce))
|
||||
mlx5_nic_vport_disable_roce(dev->mdev);
|
||||
|
||||
err_unregister_netdevice_notifier:
|
||||
mlx5_remove_netdev_notifier(dev, port_num);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -4506,7 +4553,7 @@ static void mlx5_ib_cleanup_multiport_master(struct mlx5_ib_dev *dev)
|
||||
mlx5_nic_vport_disable_roce(dev->mdev);
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
|
||||
void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_ib_cleanup_multiport_master(dev);
|
||||
#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
|
||||
@ -4515,7 +4562,7 @@ static void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
|
||||
kfree(dev->port);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = dev->mdev;
|
||||
const char *name;
|
||||
@ -4537,8 +4584,6 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
|
||||
goto err_free_port;
|
||||
|
||||
if (!mlx5_core_mp_enabled(mdev)) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= dev->num_ports; i++) {
|
||||
err = get_port_caps(dev, i);
|
||||
if (err)
|
||||
@ -4567,7 +4612,6 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
|
||||
dev->mdev->priv.eq_table.num_comp_vectors;
|
||||
dev->ib_dev.dev.parent = &mdev->pdev->dev;
|
||||
|
||||
mutex_init(&dev->flow_db.lock);
|
||||
mutex_init(&dev->cap_mask_mutex);
|
||||
INIT_LIST_HEAD(&dev->qp_list);
|
||||
spin_lock_init(&dev->reset_flow_resource_lock);
|
||||
@ -4588,7 +4632,38 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
|
||||
static int mlx5_ib_stage_flow_db_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
dev->flow_db = kzalloc(sizeof(*dev->flow_db), GFP_KERNEL);
|
||||
|
||||
if (!dev->flow_db)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&dev->flow_db->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mlx5_ib_stage_rep_flow_db_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_ib_dev *nic_dev;
|
||||
|
||||
nic_dev = mlx5_ib_get_uplink_ibdev(dev->mdev->priv.eswitch);
|
||||
|
||||
if (!nic_dev)
|
||||
return -EINVAL;
|
||||
|
||||
dev->flow_db = nic_dev->flow_db;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_flow_db_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
kfree(dev->flow_db);
|
||||
}
|
||||
|
||||
int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = dev->mdev;
|
||||
int err;
|
||||
@ -4629,7 +4704,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
|
||||
(1ull << IB_USER_VERBS_EX_CMD_MODIFY_CQ);
|
||||
|
||||
dev->ib_dev.query_device = mlx5_ib_query_device;
|
||||
dev->ib_dev.query_port = mlx5_ib_query_port;
|
||||
dev->ib_dev.get_link_layer = mlx5_ib_port_link_layer;
|
||||
dev->ib_dev.query_gid = mlx5_ib_query_gid;
|
||||
dev->ib_dev.add_gid = mlx5_ib_add_gid;
|
||||
@ -4672,7 +4746,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
|
||||
dev->ib_dev.alloc_mr = mlx5_ib_alloc_mr;
|
||||
dev->ib_dev.map_mr_sg = mlx5_ib_map_mr_sg;
|
||||
dev->ib_dev.check_mr_status = mlx5_ib_check_mr_status;
|
||||
dev->ib_dev.get_port_immutable = mlx5_port_immutable;
|
||||
dev->ib_dev.get_dev_fw_str = get_dev_fw_str;
|
||||
dev->ib_dev.get_vector_affinity = mlx5_ib_get_vector_affinity;
|
||||
if (MLX5_CAP_GEN(mdev, ipoib_enhanced_offloads))
|
||||
@ -4723,6 +4796,80 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_non_default_cb(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
dev->ib_dev.get_port_immutable = mlx5_port_immutable;
|
||||
dev->ib_dev.query_port = mlx5_ib_query_port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mlx5_ib_stage_rep_non_default_cb(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
dev->ib_dev.get_port_immutable = mlx5_port_rep_immutable;
|
||||
dev->ib_dev.query_port = mlx5_ib_rep_query_port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_common_roce_init(struct mlx5_ib_dev *dev,
|
||||
u8 port_num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->num_ports; i++) {
|
||||
dev->roce[i].dev = dev;
|
||||
dev->roce[i].native_port_num = i + 1;
|
||||
dev->roce[i].last_port_state = IB_PORT_DOWN;
|
||||
}
|
||||
|
||||
dev->ib_dev.get_netdev = mlx5_ib_get_netdev;
|
||||
dev->ib_dev.create_wq = mlx5_ib_create_wq;
|
||||
dev->ib_dev.modify_wq = mlx5_ib_modify_wq;
|
||||
dev->ib_dev.destroy_wq = mlx5_ib_destroy_wq;
|
||||
dev->ib_dev.create_rwq_ind_table = mlx5_ib_create_rwq_ind_table;
|
||||
dev->ib_dev.destroy_rwq_ind_table = mlx5_ib_destroy_rwq_ind_table;
|
||||
|
||||
dev->ib_dev.uverbs_ex_cmd_mask |=
|
||||
(1ull << IB_USER_VERBS_EX_CMD_CREATE_WQ) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_MODIFY_WQ) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_DESTROY_WQ) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_DESTROY_RWQ_IND_TBL);
|
||||
|
||||
return mlx5_add_netdev_notifier(dev, port_num);
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_common_roce_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
u8 port_num = mlx5_core_native_port_num(dev->mdev) - 1;
|
||||
|
||||
mlx5_remove_netdev_notifier(dev, port_num);
|
||||
}
|
||||
|
||||
int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = dev->mdev;
|
||||
enum rdma_link_layer ll;
|
||||
int port_type_cap;
|
||||
int err = 0;
|
||||
u8 port_num;
|
||||
|
||||
port_num = mlx5_core_native_port_num(dev->mdev) - 1;
|
||||
port_type_cap = MLX5_CAP_GEN(mdev, port_type);
|
||||
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
|
||||
|
||||
if (ll == IB_LINK_LAYER_ETHERNET)
|
||||
err = mlx5_ib_stage_common_roce_init(dev, port_num);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void mlx5_ib_stage_rep_roce_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_ib_stage_common_roce_cleanup(dev);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_roce_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = dev->mdev;
|
||||
@ -4730,37 +4877,26 @@ static int mlx5_ib_stage_roce_init(struct mlx5_ib_dev *dev)
|
||||
int port_type_cap;
|
||||
u8 port_num;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
port_num = mlx5_core_native_port_num(dev->mdev) - 1;
|
||||
port_type_cap = MLX5_CAP_GEN(mdev, port_type);
|
||||
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
|
||||
|
||||
if (ll == IB_LINK_LAYER_ETHERNET) {
|
||||
for (i = 0; i < dev->num_ports; i++) {
|
||||
dev->roce[i].dev = dev;
|
||||
dev->roce[i].native_port_num = i + 1;
|
||||
dev->roce[i].last_port_state = IB_PORT_DOWN;
|
||||
}
|
||||
|
||||
dev->ib_dev.get_netdev = mlx5_ib_get_netdev;
|
||||
dev->ib_dev.create_wq = mlx5_ib_create_wq;
|
||||
dev->ib_dev.modify_wq = mlx5_ib_modify_wq;
|
||||
dev->ib_dev.destroy_wq = mlx5_ib_destroy_wq;
|
||||
dev->ib_dev.create_rwq_ind_table = mlx5_ib_create_rwq_ind_table;
|
||||
dev->ib_dev.destroy_rwq_ind_table = mlx5_ib_destroy_rwq_ind_table;
|
||||
dev->ib_dev.uverbs_ex_cmd_mask |=
|
||||
(1ull << IB_USER_VERBS_EX_CMD_CREATE_WQ) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_MODIFY_WQ) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_DESTROY_WQ) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL) |
|
||||
(1ull << IB_USER_VERBS_EX_CMD_DESTROY_RWQ_IND_TBL);
|
||||
err = mlx5_enable_eth(dev, port_num);
|
||||
err = mlx5_ib_stage_common_roce_init(dev, port_num);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mlx5_enable_eth(dev, port_num);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
cleanup:
|
||||
mlx5_ib_stage_common_roce_cleanup(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_roce_cleanup(struct mlx5_ib_dev *dev)
|
||||
@ -4776,16 +4912,16 @@ static void mlx5_ib_stage_roce_cleanup(struct mlx5_ib_dev *dev)
|
||||
|
||||
if (ll == IB_LINK_LAYER_ETHERNET) {
|
||||
mlx5_disable_eth(dev);
|
||||
mlx5_remove_netdev_notifier(dev, port_num);
|
||||
mlx5_ib_stage_common_roce_cleanup(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_dev_res_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_dev_res_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
return create_dev_resources(&dev->devr);
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev)
|
||||
void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
destroy_dev_resources(&dev->devr);
|
||||
}
|
||||
@ -4797,7 +4933,7 @@ static int mlx5_ib_stage_odp_init(struct mlx5_ib_dev *dev)
|
||||
return mlx5_ib_odp_init_one(dev);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) {
|
||||
dev->ib_dev.get_hw_stats = mlx5_ib_get_hw_stats;
|
||||
@ -4809,7 +4945,7 @@ static int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev)
|
||||
void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt))
|
||||
mlx5_ib_dealloc_counters(dev);
|
||||
@ -4840,7 +4976,7 @@ static void mlx5_ib_stage_uar_cleanup(struct mlx5_ib_dev *dev)
|
||||
mlx5_put_uars_page(dev->mdev, dev->mdev->priv.uar);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -4855,28 +4991,28 @@ static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev)
|
||||
void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_free_bfreg(dev->mdev, &dev->fp_bfreg);
|
||||
mlx5_free_bfreg(dev->mdev, &dev->bfreg);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
return ib_register_device(&dev->ib_dev, NULL);
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev)
|
||||
void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
destroy_umrc_res(dev);
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_ib_reg_cleanup(struct mlx5_ib_dev *dev)
|
||||
void mlx5_ib_stage_ib_reg_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
ib_unregister_device(&dev->ib_dev);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
return create_umr_res(dev);
|
||||
}
|
||||
@ -4893,7 +5029,7 @@ static void mlx5_ib_stage_delay_drop_cleanup(struct mlx5_ib_dev *dev)
|
||||
cancel_delay_drop(dev);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev)
|
||||
int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
int err;
|
||||
int i;
|
||||
@ -4908,9 +5044,21 @@ static int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
|
||||
const struct mlx5_ib_profile *profile,
|
||||
int stage)
|
||||
static int mlx5_ib_stage_rep_reg_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_ib_register_vport_reps(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_rep_reg_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_ib_unregister_vport_reps(dev);
|
||||
}
|
||||
|
||||
void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
|
||||
const struct mlx5_ib_profile *profile,
|
||||
int stage)
|
||||
{
|
||||
/* Number of stages to cleanup */
|
||||
while (stage) {
|
||||
@ -4924,23 +5072,14 @@ static void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
|
||||
|
||||
static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev, u8 port_num);
|
||||
|
||||
static void *__mlx5_ib_add(struct mlx5_core_dev *mdev,
|
||||
const struct mlx5_ib_profile *profile)
|
||||
void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
|
||||
const struct mlx5_ib_profile *profile)
|
||||
{
|
||||
struct mlx5_ib_dev *dev;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
printk_once(KERN_INFO "%s", mlx5_version);
|
||||
|
||||
dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev));
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
dev->mdev = mdev;
|
||||
dev->num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
|
||||
MLX5_CAP_GEN(mdev, num_vhca_ports));
|
||||
|
||||
for (i = 0; i < MLX5_IB_STAGE_MAX; i++) {
|
||||
if (profile->stage[i].init) {
|
||||
err = profile->stage[i].init(dev);
|
||||
@ -4964,9 +5103,15 @@ static const struct mlx5_ib_profile pf_profile = {
|
||||
STAGE_CREATE(MLX5_IB_STAGE_INIT,
|
||||
mlx5_ib_stage_init_init,
|
||||
mlx5_ib_stage_init_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_FLOW_DB,
|
||||
mlx5_ib_stage_flow_db_init,
|
||||
mlx5_ib_stage_flow_db_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_CAPS,
|
||||
mlx5_ib_stage_caps_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
|
||||
mlx5_ib_stage_non_default_cb,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_ROCE,
|
||||
mlx5_ib_stage_roce_init,
|
||||
mlx5_ib_stage_roce_cleanup),
|
||||
@ -5005,6 +5150,51 @@ static const struct mlx5_ib_profile pf_profile = {
|
||||
NULL),
|
||||
};
|
||||
|
||||
static const struct mlx5_ib_profile nic_rep_profile = {
|
||||
STAGE_CREATE(MLX5_IB_STAGE_INIT,
|
||||
mlx5_ib_stage_init_init,
|
||||
mlx5_ib_stage_init_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_FLOW_DB,
|
||||
mlx5_ib_stage_flow_db_init,
|
||||
mlx5_ib_stage_flow_db_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_CAPS,
|
||||
mlx5_ib_stage_caps_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
|
||||
mlx5_ib_stage_rep_non_default_cb,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_ROCE,
|
||||
mlx5_ib_stage_rep_roce_init,
|
||||
mlx5_ib_stage_rep_roce_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
|
||||
mlx5_ib_stage_dev_res_init,
|
||||
mlx5_ib_stage_dev_res_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_COUNTERS,
|
||||
mlx5_ib_stage_counters_init,
|
||||
mlx5_ib_stage_counters_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_UAR,
|
||||
mlx5_ib_stage_uar_init,
|
||||
mlx5_ib_stage_uar_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_BFREG,
|
||||
mlx5_ib_stage_bfrag_init,
|
||||
mlx5_ib_stage_bfrag_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_PRE_IB_REG_UMR,
|
||||
NULL,
|
||||
mlx5_ib_stage_pre_ib_reg_umr_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
|
||||
mlx5_ib_stage_ib_reg_init,
|
||||
mlx5_ib_stage_ib_reg_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR,
|
||||
mlx5_ib_stage_post_ib_reg_umr_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_CLASS_ATTR,
|
||||
mlx5_ib_stage_class_attr_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_REP_REG,
|
||||
mlx5_ib_stage_rep_reg_init,
|
||||
mlx5_ib_stage_rep_reg_cleanup),
|
||||
};
|
||||
|
||||
static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev, u8 port_num)
|
||||
{
|
||||
struct mlx5_ib_multiport_info *mpi;
|
||||
@ -5050,8 +5240,11 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev, u8 port_num)
|
||||
static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
|
||||
{
|
||||
enum rdma_link_layer ll;
|
||||
struct mlx5_ib_dev *dev;
|
||||
int port_type_cap;
|
||||
|
||||
printk_once(KERN_INFO "%s", mlx5_version);
|
||||
|
||||
port_type_cap = MLX5_CAP_GEN(mdev, port_type);
|
||||
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
|
||||
|
||||
@ -5061,7 +5254,22 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
|
||||
return mlx5_ib_add_slave_port(mdev, port_num);
|
||||
}
|
||||
|
||||
return __mlx5_ib_add(mdev, &pf_profile);
|
||||
dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev));
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
dev->mdev = mdev;
|
||||
dev->num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
|
||||
MLX5_CAP_GEN(mdev, num_vhca_ports));
|
||||
|
||||
if (MLX5_VPORT_MANAGER(mdev) &&
|
||||
mlx5_ib_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) {
|
||||
dev->rep = mlx5_ib_vport_rep(mdev->priv.eswitch, 0);
|
||||
|
||||
return __mlx5_ib_add(dev, &nic_rep_profile);
|
||||
}
|
||||
|
||||
return __mlx5_ib_add(dev, &pf_profile);
|
||||
}
|
||||
|
||||
static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
|
||||
|
@ -343,6 +343,7 @@ struct mlx5_ib_sq {
|
||||
struct mlx5_ib_wq *sq;
|
||||
struct mlx5_ib_ubuffer ubuffer;
|
||||
struct mlx5_db *doorbell;
|
||||
struct mlx5_flow_handle *flow_rule;
|
||||
u32 tisn;
|
||||
u8 state;
|
||||
};
|
||||
@ -371,7 +372,7 @@ struct mlx5_ib_qp {
|
||||
struct mlx5_ib_rss_qp rss_qp;
|
||||
struct mlx5_ib_dct dct;
|
||||
};
|
||||
struct mlx5_buf buf;
|
||||
struct mlx5_frag_buf buf;
|
||||
|
||||
struct mlx5_db db;
|
||||
struct mlx5_ib_wq rq;
|
||||
@ -413,7 +414,7 @@ struct mlx5_ib_qp {
|
||||
};
|
||||
|
||||
struct mlx5_ib_cq_buf {
|
||||
struct mlx5_buf buf;
|
||||
struct mlx5_frag_buf_ctrl fbc;
|
||||
struct ib_umem *umem;
|
||||
int cqe_size;
|
||||
int nent;
|
||||
@ -495,7 +496,7 @@ struct mlx5_ib_wc {
|
||||
struct mlx5_ib_srq {
|
||||
struct ib_srq ibsrq;
|
||||
struct mlx5_core_srq msrq;
|
||||
struct mlx5_buf buf;
|
||||
struct mlx5_frag_buf buf;
|
||||
struct mlx5_db db;
|
||||
u64 *wrid;
|
||||
/* protect SRQ hanlding
|
||||
@ -731,7 +732,9 @@ struct mlx5_ib_delay_drop {
|
||||
|
||||
enum mlx5_ib_stages {
|
||||
MLX5_IB_STAGE_INIT,
|
||||
MLX5_IB_STAGE_FLOW_DB,
|
||||
MLX5_IB_STAGE_CAPS,
|
||||
MLX5_IB_STAGE_NON_DEFAULT_CB,
|
||||
MLX5_IB_STAGE_ROCE,
|
||||
MLX5_IB_STAGE_DEVICE_RESOURCES,
|
||||
MLX5_IB_STAGE_ODP,
|
||||
@ -744,6 +747,7 @@ enum mlx5_ib_stages {
|
||||
MLX5_IB_STAGE_POST_IB_REG_UMR,
|
||||
MLX5_IB_STAGE_DELAY_DROP,
|
||||
MLX5_IB_STAGE_CLASS_ATTR,
|
||||
MLX5_IB_STAGE_REP_REG,
|
||||
MLX5_IB_STAGE_MAX,
|
||||
};
|
||||
|
||||
@ -798,7 +802,7 @@ struct mlx5_ib_dev {
|
||||
struct srcu_struct mr_srcu;
|
||||
u32 null_mkey;
|
||||
#endif
|
||||
struct mlx5_ib_flow_db flow_db;
|
||||
struct mlx5_ib_flow_db *flow_db;
|
||||
/* protect resources needed as part of reset flow */
|
||||
spinlock_t reset_flow_resource_lock;
|
||||
struct list_head qp_list;
|
||||
@ -808,6 +812,7 @@ struct mlx5_ib_dev {
|
||||
struct mlx5_sq_bfreg fp_bfreg;
|
||||
struct mlx5_ib_delay_drop delay_drop;
|
||||
const struct mlx5_ib_profile *profile;
|
||||
struct mlx5_eswitch_rep *rep;
|
||||
|
||||
/* protect the user_td */
|
||||
struct mutex lb_mutex;
|
||||
@ -1050,6 +1055,31 @@ static inline void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset,
|
||||
|
||||
#endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
|
||||
|
||||
/* Needed for rep profile */
|
||||
int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_rep_flow_db_init(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_rep_non_default_cb(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_rep_roce_cleanup(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_dev_res_init(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev);
|
||||
void mlx5_ib_stage_ib_reg_cleanup(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev);
|
||||
int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev);
|
||||
void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
|
||||
const struct mlx5_ib_profile *profile,
|
||||
int stage);
|
||||
void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
|
||||
const struct mlx5_ib_profile *profile);
|
||||
|
||||
int mlx5_ib_get_vf_config(struct ib_device *device, int vf,
|
||||
u8 port, struct ifla_vf_info *info);
|
||||
int mlx5_ib_set_vf_link_state(struct ib_device *device, int vf,
|
||||
|
@ -587,7 +587,7 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c)
|
||||
|
||||
static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
if (!mlx5_debugfs_root)
|
||||
if (!mlx5_debugfs_root || dev->rep)
|
||||
return;
|
||||
|
||||
debugfs_remove_recursive(dev->cache.root);
|
||||
@ -600,7 +600,7 @@ static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev)
|
||||
struct mlx5_cache_ent *ent;
|
||||
int i;
|
||||
|
||||
if (!mlx5_debugfs_root)
|
||||
if (!mlx5_debugfs_root || dev->rep)
|
||||
return 0;
|
||||
|
||||
cache->root = debugfs_create_dir("mr_cache", dev->mdev->priv.dbg_root);
|
||||
@ -690,6 +690,7 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
|
||||
MLX5_IB_UMR_OCTOWORD;
|
||||
ent->access_mode = MLX5_MKC_ACCESS_MODE_MTT;
|
||||
if ((dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE) &&
|
||||
!dev->rep &&
|
||||
mlx5_core_is_pf(dev->mdev))
|
||||
ent->limit = dev->mdev->profile->mr_cache[i].limit;
|
||||
else
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <rdma/ib_user_verbs.h>
|
||||
#include <linux/mlx5/fs.h>
|
||||
#include "mlx5_ib.h"
|
||||
#include "ib_rep.h"
|
||||
|
||||
/* not supported currently */
|
||||
static int wq_signature;
|
||||
@ -1082,6 +1083,13 @@ static void destroy_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
|
||||
mlx5_core_destroy_tis(dev->mdev, sq->tisn);
|
||||
}
|
||||
|
||||
static void destroy_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq)
|
||||
{
|
||||
if (sq->flow_rule)
|
||||
mlx5_del_flow_rules(sq->flow_rule);
|
||||
}
|
||||
|
||||
static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq, void *qpin,
|
||||
struct ib_pd *pd)
|
||||
@ -1145,8 +1153,15 @@ static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
|
||||
if (err)
|
||||
goto err_umem;
|
||||
|
||||
err = create_flow_rule_vport_sq(dev, sq);
|
||||
if (err)
|
||||
goto err_flow;
|
||||
|
||||
return 0;
|
||||
|
||||
err_flow:
|
||||
mlx5_core_destroy_sq_tracked(dev->mdev, &sq->base.mqp);
|
||||
|
||||
err_umem:
|
||||
ib_umem_release(sq->ubuffer.umem);
|
||||
sq->ubuffer.umem = NULL;
|
||||
@ -1157,6 +1172,7 @@ static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
|
||||
static void destroy_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq)
|
||||
{
|
||||
destroy_flow_rule_vport_sq(dev, sq);
|
||||
mlx5_core_destroy_sq_tracked(dev->mdev, &sq->base.mqp);
|
||||
ib_umem_release(sq->ubuffer.umem);
|
||||
}
|
||||
@ -1267,6 +1283,10 @@ static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
|
||||
if (tunnel_offload_en)
|
||||
MLX5_SET(tirc, tirc, tunneled_offload_en, 1);
|
||||
|
||||
if (dev->rep)
|
||||
MLX5_SET(tirc, tirc, self_lb_block,
|
||||
MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST_);
|
||||
|
||||
err = mlx5_core_create_tir(dev->mdev, in, inlen, &rq->tirn);
|
||||
|
||||
kvfree(in);
|
||||
@ -1558,6 +1578,10 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
|
||||
MLX5_SET(rx_hash_field_select, hfso, selected_fields, selected_fields);
|
||||
|
||||
create_tir:
|
||||
if (dev->rep)
|
||||
MLX5_SET(tirc, tirc, self_lb_block,
|
||||
MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST_);
|
||||
|
||||
err = mlx5_core_create_tir(dev->mdev, in, inlen, &qp->rss_qp.tirn);
|
||||
|
||||
if (err)
|
||||
@ -2143,7 +2167,6 @@ static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd,
|
||||
struct ib_qp_init_attr *attr,
|
||||
struct mlx5_ib_create_qp *ucmd)
|
||||
{
|
||||
struct mlx5_ib_dev *dev;
|
||||
struct mlx5_ib_qp *qp;
|
||||
int err = 0;
|
||||
u32 uidx = MLX5_IB_DEFAULT_UIDX;
|
||||
@ -2152,8 +2175,6 @@ static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd,
|
||||
if (!attr->srq || !attr->recv_cq)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
dev = to_mdev(pd->device);
|
||||
|
||||
err = get_qp_user_index(to_mucontext(pd->uobject->context),
|
||||
ucmd, sizeof(*ucmd), &uidx);
|
||||
if (err)
|
||||
@ -4718,26 +4739,14 @@ static int query_raw_packet_qp_sq_state(struct mlx5_ib_dev *dev,
|
||||
struct mlx5_ib_sq *sq,
|
||||
u8 *sq_state)
|
||||
{
|
||||
void *out;
|
||||
void *sqc;
|
||||
int inlen;
|
||||
int err;
|
||||
|
||||
inlen = MLX5_ST_SZ_BYTES(query_sq_out);
|
||||
out = kvzalloc(inlen, GFP_KERNEL);
|
||||
if (!out)
|
||||
return -ENOMEM;
|
||||
|
||||
err = mlx5_core_query_sq(dev->mdev, sq->base.mqp.qpn, out);
|
||||
err = mlx5_core_query_sq_state(dev->mdev, sq->base.mqp.qpn, sq_state);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sqc = MLX5_ADDR_OF(query_sq_out, out, sq_context);
|
||||
*sq_state = MLX5_GET(sqc, sqc, state);
|
||||
sq->state = *sq_state;
|
||||
|
||||
out:
|
||||
kvfree(out);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -90,8 +90,8 @@ static struct net_device *qedr_get_netdev(struct ib_device *dev, u8 port_num)
|
||||
dev_hold(qdev->ndev);
|
||||
|
||||
/* The HW vendor's device driver must guarantee
|
||||
* that this function returns NULL before the net device reaches
|
||||
* NETDEV_UNREGISTER_FINAL state.
|
||||
* that this function returns NULL before the net device has finished
|
||||
* NETDEV_UNREGISTER state.
|
||||
*/
|
||||
return qdev->ndev;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ struct rdma_cqe_responder {
|
||||
__le32 imm_data_or_inv_r_Key;
|
||||
__le32 length;
|
||||
__le32 imm_data_hi;
|
||||
__le16 rq_cons;
|
||||
__le16 rq_cons_or_srq_id;
|
||||
u8 flags;
|
||||
#define RDMA_CQE_RESPONDER_TOGGLE_BIT_MASK 0x1
|
||||
#define RDMA_CQE_RESPONDER_TOGGLE_BIT_SHIFT 0
|
||||
@ -115,6 +115,7 @@ enum rdma_cqe_requester_status_enum {
|
||||
RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR,
|
||||
RDMA_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR,
|
||||
RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR,
|
||||
RDMA_CQE_REQ_STS_XRC_VOILATION_ERR,
|
||||
MAX_RDMA_CQE_REQUESTER_STATUS_ENUM
|
||||
};
|
||||
|
||||
@ -136,6 +137,7 @@ enum rdma_cqe_type {
|
||||
RDMA_CQE_TYPE_REQUESTER,
|
||||
RDMA_CQE_TYPE_RESPONDER_RQ,
|
||||
RDMA_CQE_TYPE_RESPONDER_SRQ,
|
||||
RDMA_CQE_TYPE_RESPONDER_XRC_SRQ,
|
||||
RDMA_CQE_TYPE_INVALID,
|
||||
MAX_RDMA_CQE_TYPE
|
||||
};
|
||||
|
@ -3714,7 +3714,7 @@ static int process_resp_flush(struct qedr_qp *qp, struct qedr_cq *cq,
|
||||
static void try_consume_resp_cqe(struct qedr_cq *cq, struct qedr_qp *qp,
|
||||
struct rdma_cqe_responder *resp, int *update)
|
||||
{
|
||||
if (le16_to_cpu(resp->rq_cons) == qp->rq.wqe_cons) {
|
||||
if (le16_to_cpu(resp->rq_cons_or_srq_id) == qp->rq.wqe_cons) {
|
||||
consume_cqe(cq);
|
||||
*update |= 1;
|
||||
}
|
||||
@ -3729,7 +3729,7 @@ static int qedr_poll_cq_resp(struct qedr_dev *dev, struct qedr_qp *qp,
|
||||
|
||||
if (resp->status == RDMA_CQE_RESP_STS_WORK_REQUEST_FLUSHED_ERR) {
|
||||
cnt = process_resp_flush(qp, cq, num_entries, wc,
|
||||
resp->rq_cons);
|
||||
resp->rq_cons_or_srq_id);
|
||||
try_consume_resp_cqe(cq, qp, resp, update);
|
||||
} else {
|
||||
cnt = process_resp_one(dev, qp, cq, wc, resp);
|
||||
|
@ -95,24 +95,6 @@ void usnic_ib_log_vf(struct usnic_ib_vf *vf)
|
||||
}
|
||||
|
||||
/* Start of netdev section */
|
||||
static inline const char *usnic_ib_netdev_event_to_string(unsigned long event)
|
||||
{
|
||||
const char *event2str[] = {"NETDEV_NONE", "NETDEV_UP", "NETDEV_DOWN",
|
||||
"NETDEV_REBOOT", "NETDEV_CHANGE",
|
||||
"NETDEV_REGISTER", "NETDEV_UNREGISTER", "NETDEV_CHANGEMTU",
|
||||
"NETDEV_CHANGEADDR", "NETDEV_GOING_DOWN", "NETDEV_FEAT_CHANGE",
|
||||
"NETDEV_BONDING_FAILOVER", "NETDEV_PRE_UP",
|
||||
"NETDEV_PRE_TYPE_CHANGE", "NETDEV_POST_TYPE_CHANGE",
|
||||
"NETDEV_POST_INT", "NETDEV_UNREGISTER_FINAL", "NETDEV_RELEASE",
|
||||
"NETDEV_NOTIFY_PEERS", "NETDEV_JOIN"
|
||||
};
|
||||
|
||||
if (event >= ARRAY_SIZE(event2str))
|
||||
return "UNKNOWN_NETDEV_EVENT";
|
||||
else
|
||||
return event2str[event];
|
||||
}
|
||||
|
||||
static void usnic_ib_qp_grp_modify_active_to_err(struct usnic_ib_dev *us_ibdev)
|
||||
{
|
||||
struct usnic_ib_ucontext *ctx;
|
||||
@ -185,7 +167,7 @@ static void usnic_ib_handle_usdev_event(struct usnic_ib_dev *us_ibdev,
|
||||
ib_dispatch_event(&ib_event);
|
||||
} else {
|
||||
usnic_dbg("Ignoring %s on %s\n",
|
||||
usnic_ib_netdev_event_to_string(event),
|
||||
netdev_cmd_to_name(event),
|
||||
us_ibdev->ib_dev.name);
|
||||
}
|
||||
break;
|
||||
@ -222,7 +204,7 @@ static void usnic_ib_handle_usdev_event(struct usnic_ib_dev *us_ibdev,
|
||||
break;
|
||||
default:
|
||||
usnic_dbg("Ignoring event %s on %s",
|
||||
usnic_ib_netdev_event_to_string(event),
|
||||
netdev_cmd_to_name(event),
|
||||
us_ibdev->ib_dev.name);
|
||||
}
|
||||
mutex_unlock(&us_ibdev->usdev_lock);
|
||||
@ -264,7 +246,7 @@ static int usnic_ib_handle_inet_event(struct usnic_ib_dev *us_ibdev,
|
||||
switch (event) {
|
||||
case NETDEV_DOWN:
|
||||
usnic_info("%s via ip notifiers",
|
||||
usnic_ib_netdev_event_to_string(event));
|
||||
netdev_cmd_to_name(event));
|
||||
usnic_fwd_del_ipaddr(us_ibdev->ufdev);
|
||||
usnic_ib_qp_grp_modify_active_to_err(us_ibdev);
|
||||
ib_event.event = IB_EVENT_GID_CHANGE;
|
||||
@ -275,7 +257,7 @@ static int usnic_ib_handle_inet_event(struct usnic_ib_dev *us_ibdev,
|
||||
case NETDEV_UP:
|
||||
usnic_fwd_add_ipaddr(us_ibdev->ufdev, ifa->ifa_address);
|
||||
usnic_info("%s via ip notifiers: ip %pI4",
|
||||
usnic_ib_netdev_event_to_string(event),
|
||||
netdev_cmd_to_name(event),
|
||||
&us_ibdev->ufdev->inaddr);
|
||||
ib_event.event = IB_EVENT_GID_CHANGE;
|
||||
ib_event.device = &us_ibdev->ib_dev;
|
||||
@ -284,7 +266,7 @@ static int usnic_ib_handle_inet_event(struct usnic_ib_dev *us_ibdev,
|
||||
break;
|
||||
default:
|
||||
usnic_info("Ignoring event %s on %s",
|
||||
usnic_ib_netdev_event_to_string(event),
|
||||
netdev_cmd_to_name(event),
|
||||
us_ibdev->ib_dev.name);
|
||||
}
|
||||
mutex_unlock(&us_ibdev->usdev_lock);
|
||||
|
@ -174,14 +174,13 @@ void usnic_transport_put_socket(struct socket *sock)
|
||||
int usnic_transport_sock_get_addr(struct socket *sock, int *proto,
|
||||
uint32_t *addr, uint16_t *port)
|
||||
{
|
||||
int len;
|
||||
int err;
|
||||
struct sockaddr_in sock_addr;
|
||||
|
||||
err = sock->ops->getname(sock,
|
||||
(struct sockaddr *)&sock_addr,
|
||||
&len, 0);
|
||||
if (err)
|
||||
0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (sock_addr.sin_family != AF_INET)
|
||||
|
@ -560,7 +560,7 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
|
||||
|
||||
static int
|
||||
data_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
int *addr_len, int peer)
|
||||
int peer)
|
||||
{
|
||||
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
@ -570,14 +570,13 @@ data_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
*addr_len = sizeof(*maddr);
|
||||
maddr->family = AF_ISDN;
|
||||
maddr->dev = _pms(sk)->dev->id;
|
||||
maddr->channel = _pms(sk)->ch.nr;
|
||||
maddr->sapi = _pms(sk)->ch.addr & 0xff;
|
||||
maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
return sizeof(*maddr);
|
||||
}
|
||||
|
||||
static const struct proto_ops data_sock_ops = {
|
||||
|
@ -149,9 +149,9 @@ config MACVTAP
|
||||
config IPVLAN
|
||||
tristate "IP-VLAN support"
|
||||
depends on INET
|
||||
depends on IPV6
|
||||
depends on IPV6 || !IPV6
|
||||
depends on NETFILTER
|
||||
depends on NET_L3_MASTER_DEV
|
||||
select NET_L3_MASTER_DEV
|
||||
---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)
|
||||
@ -500,6 +500,7 @@ source "drivers/net/hyperv/Kconfig"
|
||||
config NETDEVSIM
|
||||
tristate "Simulated networking device"
|
||||
depends on DEBUG_FS
|
||||
depends on MAY_USE_DEVLINK
|
||||
help
|
||||
This driver is a developer testing tool and software model that can
|
||||
be used to test various control path networking APIs, especially
|
||||
|
@ -113,12 +113,6 @@ static struct devprobe2 m68k_probes[] __initdata = {
|
||||
#endif
|
||||
#ifdef CONFIG_MVME147_NET /* MVME147 internal Ethernet */
|
||||
{mvme147lance_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_MAC8390 /* NuBus NS8390-based cards */
|
||||
{mac8390_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_MAC89x0
|
||||
{mac89x0_probe, 0},
|
||||
#endif
|
||||
{NULL, 0},
|
||||
};
|
||||
|
@ -287,7 +287,7 @@ void bond_create_proc_entry(struct bonding *bond)
|
||||
|
||||
if (bn->proc_dir) {
|
||||
bond->proc_entry = proc_create_data(bond_dev->name,
|
||||
S_IRUGO, bn->proc_dir,
|
||||
0444, bn->proc_dir,
|
||||
&bond_info_fops, bond);
|
||||
if (bond->proc_entry == NULL)
|
||||
netdev_warn(bond_dev, "Cannot create /proc/net/%s/%s\n",
|
||||
|
@ -147,7 +147,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
|
||||
static const struct class_attribute class_attr_bonding_masters = {
|
||||
.attr = {
|
||||
.name = "bonding_masters",
|
||||
.mode = S_IWUSR | S_IRUGO,
|
||||
.mode = 0644,
|
||||
},
|
||||
.show = bonding_show_bonds,
|
||||
.store = bonding_store_bonds,
|
||||
@ -202,7 +202,7 @@ static ssize_t bonding_show_slaves(struct device *d,
|
||||
|
||||
return res;
|
||||
}
|
||||
static DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves,
|
||||
static DEVICE_ATTR(slaves, 0644, bonding_show_slaves,
|
||||
bonding_sysfs_store_option);
|
||||
|
||||
/* Show the bonding mode. */
|
||||
@ -216,8 +216,7 @@ static ssize_t bonding_show_mode(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, BOND_MODE(bond));
|
||||
}
|
||||
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
|
||||
bonding_show_mode, bonding_sysfs_store_option);
|
||||
static DEVICE_ATTR(mode, 0644, bonding_show_mode, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the bonding transmit hash method. */
|
||||
static ssize_t bonding_show_xmit_hash(struct device *d,
|
||||
@ -231,7 +230,7 @@ static ssize_t bonding_show_xmit_hash(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.xmit_policy);
|
||||
}
|
||||
static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(xmit_hash_policy, 0644,
|
||||
bonding_show_xmit_hash, bonding_sysfs_store_option);
|
||||
|
||||
/* Show arp_validate. */
|
||||
@ -247,7 +246,7 @@ static ssize_t bonding_show_arp_validate(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.arp_validate);
|
||||
}
|
||||
static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
|
||||
static DEVICE_ATTR(arp_validate, 0644, bonding_show_arp_validate,
|
||||
bonding_sysfs_store_option);
|
||||
|
||||
/* Show arp_all_targets. */
|
||||
@ -263,7 +262,7 @@ static ssize_t bonding_show_arp_all_targets(struct device *d,
|
||||
return sprintf(buf, "%s %d\n",
|
||||
val->string, bond->params.arp_all_targets);
|
||||
}
|
||||
static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(arp_all_targets, 0644,
|
||||
bonding_show_arp_all_targets, bonding_sysfs_store_option);
|
||||
|
||||
/* Show fail_over_mac. */
|
||||
@ -279,7 +278,7 @@ static ssize_t bonding_show_fail_over_mac(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.fail_over_mac);
|
||||
}
|
||||
static DEVICE_ATTR(fail_over_mac, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(fail_over_mac, 0644,
|
||||
bonding_show_fail_over_mac, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the arp timer interval. */
|
||||
@ -291,7 +290,7 @@ static ssize_t bonding_show_arp_interval(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.arp_interval);
|
||||
}
|
||||
static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(arp_interval, 0644,
|
||||
bonding_show_arp_interval, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the arp targets. */
|
||||
@ -312,7 +311,7 @@ static ssize_t bonding_show_arp_targets(struct device *d,
|
||||
|
||||
return res;
|
||||
}
|
||||
static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(arp_ip_target, 0644,
|
||||
bonding_show_arp_targets, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the up and down delays. */
|
||||
@ -324,7 +323,7 @@ static ssize_t bonding_show_downdelay(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.downdelay * bond->params.miimon);
|
||||
}
|
||||
static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(downdelay, 0644,
|
||||
bonding_show_downdelay, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_updelay(struct device *d,
|
||||
@ -336,7 +335,7 @@ static ssize_t bonding_show_updelay(struct device *d,
|
||||
return sprintf(buf, "%d\n", bond->params.updelay * bond->params.miimon);
|
||||
|
||||
}
|
||||
static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(updelay, 0644,
|
||||
bonding_show_updelay, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the LACP interval. */
|
||||
@ -351,7 +350,7 @@ static ssize_t bonding_show_lacp(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.lacp_fast);
|
||||
}
|
||||
static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(lacp_rate, 0644,
|
||||
bonding_show_lacp, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_min_links(struct device *d,
|
||||
@ -362,7 +361,7 @@ static ssize_t bonding_show_min_links(struct device *d,
|
||||
|
||||
return sprintf(buf, "%u\n", bond->params.min_links);
|
||||
}
|
||||
static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(min_links, 0644,
|
||||
bonding_show_min_links, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_ad_select(struct device *d,
|
||||
@ -376,7 +375,7 @@ static ssize_t bonding_show_ad_select(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.ad_select);
|
||||
}
|
||||
static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(ad_select, 0644,
|
||||
bonding_show_ad_select, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the number of peer notifications to send after a failover event. */
|
||||
@ -387,9 +386,9 @@ static ssize_t bonding_show_num_peer_notif(struct device *d,
|
||||
struct bonding *bond = to_bond(d);
|
||||
return sprintf(buf, "%d\n", bond->params.num_peer_notif);
|
||||
}
|
||||
static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(num_grat_arp, 0644,
|
||||
bonding_show_num_peer_notif, bonding_sysfs_store_option);
|
||||
static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(num_unsol_na, 0644,
|
||||
bonding_show_num_peer_notif, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the MII monitor interval. */
|
||||
@ -401,7 +400,7 @@ static ssize_t bonding_show_miimon(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.miimon);
|
||||
}
|
||||
static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(miimon, 0644,
|
||||
bonding_show_miimon, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the primary slave. */
|
||||
@ -421,7 +420,7 @@ static ssize_t bonding_show_primary(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(primary, 0644,
|
||||
bonding_show_primary, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the primary_reselect flag. */
|
||||
@ -438,7 +437,7 @@ static ssize_t bonding_show_primary_reselect(struct device *d,
|
||||
return sprintf(buf, "%s %d\n",
|
||||
val->string, bond->params.primary_reselect);
|
||||
}
|
||||
static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(primary_reselect, 0644,
|
||||
bonding_show_primary_reselect, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the use_carrier flag. */
|
||||
@ -450,7 +449,7 @@ static ssize_t bonding_show_carrier(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.use_carrier);
|
||||
}
|
||||
static DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(use_carrier, 0644,
|
||||
bonding_show_carrier, bonding_sysfs_store_option);
|
||||
|
||||
|
||||
@ -471,7 +470,7 @@ static ssize_t bonding_show_active_slave(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(active_slave, 0644,
|
||||
bonding_show_active_slave, bonding_sysfs_store_option);
|
||||
|
||||
/* Show link status of the bond interface. */
|
||||
@ -484,7 +483,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
|
||||
|
||||
return sprintf(buf, "%s\n", active ? "up" : "down");
|
||||
}
|
||||
static DEVICE_ATTR(mii_status, S_IRUGO, bonding_show_mii_status, NULL);
|
||||
static DEVICE_ATTR(mii_status, 0444, bonding_show_mii_status, NULL);
|
||||
|
||||
/* Show current 802.3ad aggregator ID. */
|
||||
static ssize_t bonding_show_ad_aggregator(struct device *d,
|
||||
@ -503,7 +502,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(ad_aggregator, S_IRUGO, bonding_show_ad_aggregator, NULL);
|
||||
static DEVICE_ATTR(ad_aggregator, 0444, bonding_show_ad_aggregator, NULL);
|
||||
|
||||
|
||||
/* Show number of active 802.3ad ports. */
|
||||
@ -523,7 +522,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(ad_num_ports, S_IRUGO, bonding_show_ad_num_ports, NULL);
|
||||
static DEVICE_ATTR(ad_num_ports, 0444, bonding_show_ad_num_ports, NULL);
|
||||
|
||||
|
||||
/* Show current 802.3ad actor key. */
|
||||
@ -543,7 +542,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(ad_actor_key, S_IRUGO, bonding_show_ad_actor_key, NULL);
|
||||
static DEVICE_ATTR(ad_actor_key, 0444, bonding_show_ad_actor_key, NULL);
|
||||
|
||||
|
||||
/* Show current 802.3ad partner key. */
|
||||
@ -563,7 +562,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(ad_partner_key, S_IRUGO, bonding_show_ad_partner_key, NULL);
|
||||
static DEVICE_ATTR(ad_partner_key, 0444, bonding_show_ad_partner_key, NULL);
|
||||
|
||||
|
||||
/* Show current 802.3ad partner mac. */
|
||||
@ -582,7 +581,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
|
||||
static DEVICE_ATTR(ad_partner_mac, 0444, bonding_show_ad_partner_mac, NULL);
|
||||
|
||||
/* Show the queue_ids of the slaves in the current bond. */
|
||||
static ssize_t bonding_show_queue_id(struct device *d,
|
||||
@ -615,7 +614,7 @@ static ssize_t bonding_show_queue_id(struct device *d,
|
||||
|
||||
return res;
|
||||
}
|
||||
static DEVICE_ATTR(queue_id, S_IRUGO | S_IWUSR, bonding_show_queue_id,
|
||||
static DEVICE_ATTR(queue_id, 0644, bonding_show_queue_id,
|
||||
bonding_sysfs_store_option);
|
||||
|
||||
|
||||
@ -628,7 +627,7 @@ static ssize_t bonding_show_slaves_active(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.all_slaves_active);
|
||||
}
|
||||
static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(all_slaves_active, 0644,
|
||||
bonding_show_slaves_active, bonding_sysfs_store_option);
|
||||
|
||||
/* Show the number of IGMP membership reports to send on link failure */
|
||||
@ -640,7 +639,7 @@ static ssize_t bonding_show_resend_igmp(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.resend_igmp);
|
||||
}
|
||||
static DEVICE_ATTR(resend_igmp, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(resend_igmp, 0644,
|
||||
bonding_show_resend_igmp, bonding_sysfs_store_option);
|
||||
|
||||
|
||||
@ -652,7 +651,7 @@ static ssize_t bonding_show_lp_interval(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.lp_interval);
|
||||
}
|
||||
static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(lp_interval, 0644,
|
||||
bonding_show_lp_interval, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
|
||||
@ -662,7 +661,7 @@ static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
|
||||
struct bonding *bond = to_bond(d);
|
||||
return sprintf(buf, "%d\n", bond->params.tlb_dynamic_lb);
|
||||
}
|
||||
static DEVICE_ATTR(tlb_dynamic_lb, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(tlb_dynamic_lb, 0644,
|
||||
bonding_show_tlb_dynamic_lb, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_packets_per_slave(struct device *d,
|
||||
@ -674,7 +673,7 @@ static ssize_t bonding_show_packets_per_slave(struct device *d,
|
||||
|
||||
return sprintf(buf, "%u\n", packets_per_slave);
|
||||
}
|
||||
static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(packets_per_slave, 0644,
|
||||
bonding_show_packets_per_slave, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_ad_actor_sys_prio(struct device *d,
|
||||
@ -688,7 +687,7 @@ static ssize_t bonding_show_ad_actor_sys_prio(struct device *d,
|
||||
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(ad_actor_sys_prio, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(ad_actor_sys_prio, 0644,
|
||||
bonding_show_ad_actor_sys_prio, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_ad_actor_system(struct device *d,
|
||||
@ -703,7 +702,7 @@ static ssize_t bonding_show_ad_actor_system(struct device *d,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(ad_actor_system, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(ad_actor_system, 0644,
|
||||
bonding_show_ad_actor_system, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_ad_user_port_key(struct device *d,
|
||||
@ -717,7 +716,7 @@ static ssize_t bonding_show_ad_user_port_key(struct device *d,
|
||||
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(ad_user_port_key, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR(ad_user_port_key, 0644,
|
||||
bonding_show_ad_user_port_key, bonding_sysfs_store_option);
|
||||
|
||||
static struct attribute *per_bond_attrs[] = {
|
||||
|
@ -25,8 +25,8 @@ const struct slave_attribute slave_attr_##_name = { \
|
||||
.mode = _mode }, \
|
||||
.show = _show, \
|
||||
};
|
||||
#define SLAVE_ATTR_RO(_name) \
|
||||
SLAVE_ATTR(_name, S_IRUGO, _name##_show)
|
||||
#define SLAVE_ATTR_RO(_name) \
|
||||
SLAVE_ATTR(_name, 0444, _name##_show)
|
||||
|
||||
static ssize_t state_show(struct slave *slave, char *buf)
|
||||
{
|
||||
|
@ -40,20 +40,20 @@ static LIST_HEAD(ser_list);
|
||||
static LIST_HEAD(ser_release_list);
|
||||
|
||||
static bool ser_loop;
|
||||
module_param(ser_loop, bool, S_IRUGO);
|
||||
module_param(ser_loop, bool, 0444);
|
||||
MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
|
||||
|
||||
static bool ser_use_stx = true;
|
||||
module_param(ser_use_stx, bool, S_IRUGO);
|
||||
module_param(ser_use_stx, bool, 0444);
|
||||
MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
|
||||
|
||||
static bool ser_use_fcs = true;
|
||||
|
||||
module_param(ser_use_fcs, bool, S_IRUGO);
|
||||
module_param(ser_use_fcs, bool, 0444);
|
||||
MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not.");
|
||||
|
||||
static int ser_write_chunk = MAX_WRITE_CHUNK;
|
||||
module_param(ser_write_chunk, int, S_IRUGO);
|
||||
module_param(ser_write_chunk, int, 0444);
|
||||
|
||||
MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART.");
|
||||
|
||||
@ -97,21 +97,21 @@ static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
|
||||
ser->debugfs_tty_dir =
|
||||
debugfs_create_dir(tty->name, debugfsdir);
|
||||
if (!IS_ERR(ser->debugfs_tty_dir)) {
|
||||
debugfs_create_blob("last_tx_msg", S_IRUSR,
|
||||
ser->debugfs_tty_dir,
|
||||
&ser->tx_blob);
|
||||
debugfs_create_blob("last_tx_msg", 0400,
|
||||
ser->debugfs_tty_dir,
|
||||
&ser->tx_blob);
|
||||
|
||||
debugfs_create_blob("last_rx_msg", S_IRUSR,
|
||||
ser->debugfs_tty_dir,
|
||||
&ser->rx_blob);
|
||||
debugfs_create_blob("last_rx_msg", 0400,
|
||||
ser->debugfs_tty_dir,
|
||||
&ser->rx_blob);
|
||||
|
||||
debugfs_create_x32("ser_state", S_IRUSR,
|
||||
ser->debugfs_tty_dir,
|
||||
(u32 *)&ser->state);
|
||||
debugfs_create_x32("ser_state", 0400,
|
||||
ser->debugfs_tty_dir,
|
||||
(u32 *)&ser->state);
|
||||
|
||||
debugfs_create_x8("tty_status", S_IRUSR,
|
||||
ser->debugfs_tty_dir,
|
||||
&ser->tty_status);
|
||||
debugfs_create_x8("tty_status", 0400,
|
||||
ser->debugfs_tty_dir,
|
||||
&ser->tty_status);
|
||||
|
||||
}
|
||||
ser->tx_blob.data = ser->tx_data;
|
||||
|
@ -35,27 +35,27 @@ MODULE_DESCRIPTION("CAIF SPI driver");
|
||||
#define PAD_POW2(x, pow) ((((x)&((pow)-1))==0) ? 0 : (((pow)-((x)&((pow)-1)))))
|
||||
|
||||
static bool spi_loop;
|
||||
module_param(spi_loop, bool, S_IRUGO);
|
||||
module_param(spi_loop, bool, 0444);
|
||||
MODULE_PARM_DESC(spi_loop, "SPI running in loopback mode.");
|
||||
|
||||
/* SPI frame alignment. */
|
||||
module_param(spi_frm_align, int, S_IRUGO);
|
||||
module_param(spi_frm_align, int, 0444);
|
||||
MODULE_PARM_DESC(spi_frm_align, "SPI frame alignment.");
|
||||
|
||||
/*
|
||||
* SPI padding options.
|
||||
* Warning: must be a base of 2 (& operation used) and can not be zero !
|
||||
*/
|
||||
module_param(spi_up_head_align, int, S_IRUGO);
|
||||
module_param(spi_up_head_align, int, 0444);
|
||||
MODULE_PARM_DESC(spi_up_head_align, "SPI uplink head alignment.");
|
||||
|
||||
module_param(spi_up_tail_align, int, S_IRUGO);
|
||||
module_param(spi_up_tail_align, int, 0444);
|
||||
MODULE_PARM_DESC(spi_up_tail_align, "SPI uplink tail alignment.");
|
||||
|
||||
module_param(spi_down_head_align, int, S_IRUGO);
|
||||
module_param(spi_down_head_align, int, 0444);
|
||||
MODULE_PARM_DESC(spi_down_head_align, "SPI downlink head alignment.");
|
||||
|
||||
module_param(spi_down_tail_align, int, S_IRUGO);
|
||||
module_param(spi_down_tail_align, int, 0444);
|
||||
MODULE_PARM_DESC(spi_down_tail_align, "SPI downlink tail alignment.");
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
@ -250,10 +250,10 @@ static const struct file_operations dbgfs_frame_fops = {
|
||||
static inline void dev_debugfs_add(struct cfspi *cfspi)
|
||||
{
|
||||
cfspi->dbgfs_dir = debugfs_create_dir(cfspi->pdev->name, dbgfs_root);
|
||||
cfspi->dbgfs_state = debugfs_create_file("state", S_IRUGO,
|
||||
cfspi->dbgfs_state = debugfs_create_file("state", 0444,
|
||||
cfspi->dbgfs_dir, cfspi,
|
||||
&dbgfs_state_fops);
|
||||
cfspi->dbgfs_frame = debugfs_create_file("frame", S_IRUGO,
|
||||
cfspi->dbgfs_frame = debugfs_create_file("frame", 0444,
|
||||
cfspi->dbgfs_dir, cfspi,
|
||||
&dbgfs_frame_fops);
|
||||
}
|
||||
|
@ -629,21 +629,21 @@ static inline void debugfs_init(struct cfv_info *cfv)
|
||||
if (IS_ERR(cfv->debugfs))
|
||||
return;
|
||||
|
||||
debugfs_create_u32("rx-napi-complete", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs,
|
||||
&cfv->stats.rx_napi_complete);
|
||||
debugfs_create_u32("rx-napi-resched", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs,
|
||||
&cfv->stats.rx_napi_resched);
|
||||
debugfs_create_u32("rx-nomem", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("rx-nomem", 0400, cfv->debugfs,
|
||||
&cfv->stats.rx_nomem);
|
||||
debugfs_create_u32("rx-kicks", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("rx-kicks", 0400, cfv->debugfs,
|
||||
&cfv->stats.rx_kicks);
|
||||
debugfs_create_u32("tx-full-ring", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs,
|
||||
&cfv->stats.tx_full_ring);
|
||||
debugfs_create_u32("tx-no-mem", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs,
|
||||
&cfv->stats.tx_no_mem);
|
||||
debugfs_create_u32("tx-kicks", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("tx-kicks", 0400, cfv->debugfs,
|
||||
&cfv->stats.tx_kicks);
|
||||
debugfs_create_u32("tx-flow-on", S_IRUSR, cfv->debugfs,
|
||||
debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs,
|
||||
&cfv->stats.tx_flow_on);
|
||||
}
|
||||
|
||||
|
@ -1224,8 +1224,7 @@ static ssize_t at91_sysfs_set_mb0_id(struct device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mb0_id, S_IWUSR | S_IRUGO,
|
||||
at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id);
|
||||
static DEVICE_ATTR(mb0_id, 0644, at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id);
|
||||
|
||||
static struct attribute *at91_sysfs_attrs[] = {
|
||||
&dev_attr_mb0_id.attr,
|
||||
|
@ -67,12 +67,12 @@ MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver");
|
||||
* otherwise 11 bit SFF messages.
|
||||
*/
|
||||
static int msgobj15_eff;
|
||||
module_param(msgobj15_eff, int, S_IRUGO);
|
||||
module_param(msgobj15_eff, int, 0444);
|
||||
MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 "
|
||||
"(default: 11-bit standard frames)");
|
||||
|
||||
static int i82527_compat;
|
||||
module_param(i82527_compat, int, S_IRUGO);
|
||||
module_param(i82527_compat, int, 0444);
|
||||
MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode "
|
||||
"without using additional functions");
|
||||
|
||||
|
@ -82,29 +82,29 @@ static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
|
||||
module_param_hw_array(port, ulong, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(port, ulong, ioport, NULL, 0444);
|
||||
MODULE_PARM_DESC(port, "I/O port number");
|
||||
|
||||
module_param_hw_array(mem, ulong, iomem, NULL, S_IRUGO);
|
||||
module_param_hw_array(mem, ulong, iomem, NULL, 0444);
|
||||
MODULE_PARM_DESC(mem, "I/O memory address");
|
||||
|
||||
module_param_hw_array(indirect, int, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(indirect, int, ioport, NULL, 0444);
|
||||
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
|
||||
|
||||
module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0444);
|
||||
MODULE_PARM_DESC(irq, "IRQ number");
|
||||
|
||||
module_param_array(clk, int, NULL, S_IRUGO);
|
||||
module_param_array(clk, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
|
||||
"(default=16000000 [16 MHz])");
|
||||
|
||||
module_param_array(cir, byte, NULL, S_IRUGO);
|
||||
module_param_array(cir, byte, NULL, 0444);
|
||||
MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
|
||||
|
||||
module_param_array(cor, byte, NULL, S_IRUGO);
|
||||
module_param_array(cor, byte, NULL, 0444);
|
||||
MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
|
||||
|
||||
module_param_array(bcr, byte, NULL, S_IRUGO);
|
||||
module_param_array(bcr, byte, NULL, 0444);
|
||||
MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");
|
||||
|
||||
#define CC770_IOSIZE 0x20
|
||||
|
@ -1484,7 +1484,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
|
||||
} \
|
||||
} \
|
||||
module_param_named(name, grcan_module_config.name, \
|
||||
mtype, S_IRUGO); \
|
||||
mtype, 0444); \
|
||||
MODULE_PARM_DESC(name, desc)
|
||||
|
||||
#define GRCAN_CONFIG_ATTR(name, desc) \
|
||||
@ -1513,7 +1513,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
|
||||
struct grcan_priv *priv = netdev_priv(dev); \
|
||||
return sprintf(buf, "%d\n", priv->config.name); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
|
||||
static DEVICE_ATTR(name, 0644, \
|
||||
grcan_show_##name, \
|
||||
grcan_store_##name); \
|
||||
GRCAN_MODULE_PARAM(name, ushort, GRCAN_NOT_BOOL, desc)
|
||||
|
@ -1865,9 +1865,9 @@ static ssize_t ican3_sysfs_show_fwinfo(struct device *dev,
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", mod->fwinfo);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(termination, S_IWUSR | S_IRUGO, ican3_sysfs_show_term,
|
||||
ican3_sysfs_set_term);
|
||||
static DEVICE_ATTR(fwinfo, S_IRUSR | S_IRUGO, ican3_sysfs_show_fwinfo, NULL);
|
||||
static DEVICE_ATTR(termination, 0644, ican3_sysfs_show_term,
|
||||
ican3_sysfs_set_term);
|
||||
static DEVICE_ATTR(fwinfo, 0444, ican3_sysfs_show_fwinfo, NULL);
|
||||
|
||||
static struct attribute *ican3_sysfs_attrs[] = {
|
||||
&dev_attr_termination.attr,
|
||||
|
@ -48,27 +48,27 @@ static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
||||
static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
||||
static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */
|
||||
|
||||
module_param_hw_array(port, ulong, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(port, ulong, ioport, NULL, 0444);
|
||||
MODULE_PARM_DESC(port, "I/O port number");
|
||||
|
||||
module_param_hw_array(mem, ulong, iomem, NULL, S_IRUGO);
|
||||
module_param_hw_array(mem, ulong, iomem, NULL, 0444);
|
||||
MODULE_PARM_DESC(mem, "I/O memory address");
|
||||
|
||||
module_param_hw_array(indirect, int, ioport, NULL, S_IRUGO);
|
||||
module_param_hw_array(indirect, int, ioport, NULL, 0444);
|
||||
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
|
||||
|
||||
module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0444);
|
||||
MODULE_PARM_DESC(irq, "IRQ number");
|
||||
|
||||
module_param_array(clk, int, NULL, S_IRUGO);
|
||||
module_param_array(clk, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
|
||||
"(default=16000000 [16 MHz])");
|
||||
|
||||
module_param_array(cdr, byte, NULL, S_IRUGO);
|
||||
module_param_array(cdr, byte, NULL, 0444);
|
||||
MODULE_PARM_DESC(cdr, "Clock divider register "
|
||||
"(default=0x48 [CDR_CBP | CDR_CLK_OFF])");
|
||||
|
||||
module_param_array(ocr, byte, NULL, S_IRUGO);
|
||||
module_param_array(ocr, byte, NULL, 0444);
|
||||
MODULE_PARM_DESC(ocr, "Output control register "
|
||||
"(default=0x18 [OCR_TX0_PUSHPULL])");
|
||||
|
||||
|
@ -601,8 +601,8 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL);
|
||||
static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
|
||||
static const DEVICE_ATTR(chip, 0444, show_chip, NULL);
|
||||
static const DEVICE_ATTR(output, 0644, show_output, store_output);
|
||||
|
||||
static const struct attribute *const netdev_sysfs_attrs[] = {
|
||||
&dev_attr_chip.attr,
|
||||
|
@ -220,7 +220,7 @@
|
||||
#define DEVICE_NAME "mcp251x"
|
||||
|
||||
static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
|
||||
module_param(mcp251x_enable_dma, int, S_IRUGO);
|
||||
module_param(mcp251x_enable_dma, int, 0444);
|
||||
MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
|
||||
|
||||
static const struct can_bittiming_const mcp251x_bittiming_const = {
|
||||
|
@ -496,7 +496,7 @@ static ssize_t show_firmware(struct device *d,
|
||||
(dev->version >> 8) & 0xf,
|
||||
dev->version & 0xff);
|
||||
}
|
||||
static DEVICE_ATTR(firmware, S_IRUGO, show_firmware, NULL);
|
||||
static DEVICE_ATTR(firmware, 0444, show_firmware, NULL);
|
||||
|
||||
static ssize_t show_hardware(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -509,7 +509,7 @@ static ssize_t show_hardware(struct device *d,
|
||||
(dev->version >> 24) & 0xf,
|
||||
(dev->version >> 16) & 0xff);
|
||||
}
|
||||
static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
|
||||
static DEVICE_ATTR(hardware, 0444, show_hardware, NULL);
|
||||
|
||||
static ssize_t show_nets(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -519,7 +519,7 @@ static ssize_t show_nets(struct device *d,
|
||||
|
||||
return sprintf(buf, "%d", dev->net_count);
|
||||
}
|
||||
static DEVICE_ATTR(nets, S_IRUGO, show_nets, NULL);
|
||||
static DEVICE_ATTR(nets, 0444, show_nets, NULL);
|
||||
|
||||
static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg)
|
||||
{
|
||||
|
@ -65,7 +65,7 @@ MODULE_ALIAS_RTNL_LINK(DRV_NAME);
|
||||
*/
|
||||
|
||||
static bool echo; /* echo testing. Default: 0 (Off) */
|
||||
module_param(echo, bool, S_IRUGO);
|
||||
module_param(echo, bool, 0444);
|
||||
MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
|
||||
|
||||
|
||||
|
@ -852,7 +852,7 @@ void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
|
||||
}
|
||||
EXPORT_SYMBOL(b53_get_ethtool_stats);
|
||||
|
||||
int b53_get_sset_count(struct dsa_switch *ds)
|
||||
int b53_get_sset_count(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct b53_device *dev = ds->priv;
|
||||
|
||||
|
@ -288,7 +288,7 @@ void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port);
|
||||
int b53_configure_vlan(struct dsa_switch *ds);
|
||||
void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
|
||||
void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
|
||||
int b53_get_sset_count(struct dsa_switch *ds);
|
||||
int b53_get_sset_count(struct dsa_switch *ds, int port);
|
||||
int b53_br_join(struct dsa_switch *ds, int port, struct net_device *bridge);
|
||||
void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge);
|
||||
void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state);
|
||||
|
@ -86,7 +86,7 @@ static int dsa_loop_setup(struct dsa_switch *ds)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsa_loop_get_sset_count(struct dsa_switch *ds)
|
||||
static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port)
|
||||
{
|
||||
return __DSA_LOOP_CNT_MAX;
|
||||
}
|
||||
|
@ -1007,7 +1007,7 @@ static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port,
|
||||
}
|
||||
}
|
||||
|
||||
static int lan9303_get_sset_count(struct dsa_switch *ds)
|
||||
static int lan9303_get_sset_count(struct dsa_switch *ds, int port)
|
||||
{
|
||||
return ARRAY_SIZE(lan9303_mib);
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ static void ksz_disable_port(struct dsa_switch *ds, int port,
|
||||
ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true);
|
||||
}
|
||||
|
||||
static int ksz_sset_count(struct dsa_switch *ds)
|
||||
static int ksz_sset_count(struct dsa_switch *ds, int port)
|
||||
{
|
||||
return TOTAL_SWITCH_COUNTER_NUM;
|
||||
}
|
||||
|
@ -604,7 +604,7 @@ mt7530_get_ethtool_stats(struct dsa_switch *ds, int port,
|
||||
}
|
||||
|
||||
static int
|
||||
mt7530_get_sset_count(struct dsa_switch *ds)
|
||||
mt7530_get_sset_count(struct dsa_switch *ds, int port)
|
||||
{
|
||||
return ARRAY_SIZE(mt7530_mib);
|
||||
}
|
||||
|
@ -18,3 +18,13 @@ config NET_DSA_MV88E6XXX_GLOBAL2
|
||||
|
||||
It is required on most chips. If the chip you compile the support for
|
||||
doesn't have such registers set, say N here. In doubt, say Y.
|
||||
|
||||
config NET_DSA_MV88E6XXX_PTP
|
||||
bool "PTP support for Marvell 88E6xxx"
|
||||
default n
|
||||
depends on NET_DSA_MV88E6XXX_GLOBAL2
|
||||
imply NETWORK_PHY_TIMESTAMPING
|
||||
imply PTP_1588_CLOCK
|
||||
help
|
||||
Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
|
||||
chips that support it.
|
||||
|
@ -5,6 +5,10 @@ mv88e6xxx-objs += global1.o
|
||||
mv88e6xxx-objs += global1_atu.o
|
||||
mv88e6xxx-objs += global1_vtu.o
|
||||
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
|
||||
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o
|
||||
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_scratch.o
|
||||
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
|
||||
mv88e6xxx-objs += phy.o
|
||||
mv88e6xxx-objs += port.o
|
||||
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
|
||||
mv88e6xxx-objs += serdes.o
|
||||
|
@ -36,8 +36,10 @@
|
||||
#include "chip.h"
|
||||
#include "global1.h"
|
||||
#include "global2.h"
|
||||
#include "hwtstamp.h"
|
||||
#include "phy.h"
|
||||
#include "port.h"
|
||||
#include "ptp.h"
|
||||
#include "serdes.h"
|
||||
|
||||
static void assert_reg_lock(struct mv88e6xxx_chip *chip)
|
||||
@ -251,9 +253,8 @@ static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
|
||||
chip->g1_irq.masked &= ~(1 << n);
|
||||
}
|
||||
|
||||
static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
|
||||
static irqreturn_t mv88e6xxx_g1_irq_thread_work(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = dev_id;
|
||||
unsigned int nhandled = 0;
|
||||
unsigned int sub_irq;
|
||||
unsigned int n;
|
||||
@ -278,6 +279,13 @@ static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
|
||||
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
|
||||
}
|
||||
|
||||
static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = dev_id;
|
||||
|
||||
return mv88e6xxx_g1_irq_thread_work(chip);
|
||||
}
|
||||
|
||||
static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
||||
@ -333,7 +341,7 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
||||
static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
int irq, virq;
|
||||
u16 mask;
|
||||
@ -342,8 +350,6 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
||||
mask &= ~GENMASK(chip->g1_irq.nirqs, 0);
|
||||
mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, mask);
|
||||
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
for (irq = 0; irq < chip->g1_irq.nirqs; irq++) {
|
||||
virq = irq_find_mapping(chip->g1_irq.domain, irq);
|
||||
irq_dispose_mapping(virq);
|
||||
@ -352,7 +358,14 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
||||
irq_domain_remove(chip->g1_irq.domain);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
|
||||
static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
|
||||
free_irq(chip->irq, chip);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_g1_irq_setup_common(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
int err, irq, virq;
|
||||
u16 reg, mask;
|
||||
@ -385,13 +398,6 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
|
||||
if (err)
|
||||
goto out_disable;
|
||||
|
||||
err = request_threaded_irq(chip->irq, NULL,
|
||||
mv88e6xxx_g1_irq_thread_fn,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
|
||||
dev_name(chip->dev), chip);
|
||||
if (err)
|
||||
goto out_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable:
|
||||
@ -409,6 +415,64 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mv88e6xxx_g1_irq_setup_common(chip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = request_threaded_irq(chip->irq, NULL,
|
||||
mv88e6xxx_g1_irq_thread_fn,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(chip->dev), chip);
|
||||
if (err)
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mv88e6xxx_irq_poll(struct kthread_work *work)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = container_of(work,
|
||||
struct mv88e6xxx_chip,
|
||||
irq_poll_work.work);
|
||||
mv88e6xxx_g1_irq_thread_work(chip);
|
||||
|
||||
kthread_queue_delayed_work(chip->kworker, &chip->irq_poll_work,
|
||||
msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
static int mv88e6xxx_irq_poll_setup(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mv88e6xxx_g1_irq_setup_common(chip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
kthread_init_delayed_work(&chip->irq_poll_work,
|
||||
mv88e6xxx_irq_poll);
|
||||
|
||||
chip->kworker = kthread_create_worker(0, dev_name(chip->dev));
|
||||
if (IS_ERR(chip->kworker))
|
||||
return PTR_ERR(chip->kworker);
|
||||
|
||||
kthread_queue_delayed_work(chip->kworker, &chip->irq_poll_work,
|
||||
msecs_to_jiffies(100));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
|
||||
kthread_cancel_delayed_work_sync(&chip->irq_poll_work);
|
||||
kthread_destroy_worker(chip->kworker);
|
||||
}
|
||||
|
||||
int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
|
||||
{
|
||||
int i;
|
||||
@ -604,7 +668,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
|
||||
return UINT64_MAX;
|
||||
|
||||
low = reg;
|
||||
if (s->sizeof_stat == 4) {
|
||||
if (s->size == 4) {
|
||||
err = mv88e6xxx_port_read(chip, port, s->reg + 1, ®);
|
||||
if (err)
|
||||
return UINT64_MAX;
|
||||
@ -617,7 +681,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
|
||||
case STATS_TYPE_BANK0:
|
||||
reg |= s->reg | histogram;
|
||||
mv88e6xxx_g1_stats_read(chip, reg, &low);
|
||||
if (s->sizeof_stat == 8)
|
||||
if (s->size == 8)
|
||||
mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
|
||||
break;
|
||||
default:
|
||||
@ -627,8 +691,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
|
||||
return value;
|
||||
}
|
||||
|
||||
static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
uint8_t *data, int types)
|
||||
static int mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
uint8_t *data, int types)
|
||||
{
|
||||
struct mv88e6xxx_hw_stat *stat;
|
||||
int i, j;
|
||||
@ -641,29 +705,62 @@ static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
uint8_t *data)
|
||||
static int mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
uint8_t *data)
|
||||
{
|
||||
mv88e6xxx_stats_get_strings(chip, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_PORT);
|
||||
return mv88e6xxx_stats_get_strings(chip, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_PORT);
|
||||
}
|
||||
|
||||
static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
uint8_t *data)
|
||||
static int mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
|
||||
uint8_t *data)
|
||||
{
|
||||
mv88e6xxx_stats_get_strings(chip, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
|
||||
return mv88e6xxx_stats_get_strings(chip, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
|
||||
}
|
||||
|
||||
static const uint8_t *mv88e6xxx_atu_vtu_stats_strings[] = {
|
||||
"atu_member_violation",
|
||||
"atu_miss_violation",
|
||||
"atu_full_violation",
|
||||
"vtu_member_violation",
|
||||
"vtu_miss_violation",
|
||||
};
|
||||
|
||||
static void mv88e6xxx_atu_vtu_get_strings(uint8_t *data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mv88e6xxx_atu_vtu_stats_strings); i++)
|
||||
strlcpy(data + i * ETH_GSTRING_LEN,
|
||||
mv88e6xxx_atu_vtu_stats_strings[i],
|
||||
ETH_GSTRING_LEN);
|
||||
}
|
||||
|
||||
static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
|
||||
uint8_t *data)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = ds->priv;
|
||||
int count = 0;
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
|
||||
if (chip->info->ops->stats_get_strings)
|
||||
chip->info->ops->stats_get_strings(chip, data);
|
||||
count = chip->info->ops->stats_get_strings(chip, data);
|
||||
|
||||
if (chip->info->ops->serdes_get_strings) {
|
||||
data += count * ETH_GSTRING_LEN;
|
||||
count = chip->info->ops->serdes_get_strings(chip, port, data);
|
||||
}
|
||||
|
||||
data += count * ETH_GSTRING_LEN;
|
||||
mv88e6xxx_atu_vtu_get_strings(data);
|
||||
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip,
|
||||
@ -692,19 +789,37 @@ static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip)
|
||||
STATS_TYPE_BANK1);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
|
||||
static int mv88e6xxx_get_sset_count(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = ds->priv;
|
||||
int serdes_count = 0;
|
||||
int count = 0;
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->info->ops->stats_get_sset_count)
|
||||
return chip->info->ops->stats_get_sset_count(chip);
|
||||
count = chip->info->ops->stats_get_sset_count(chip);
|
||||
if (count < 0)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
if (chip->info->ops->serdes_get_sset_count)
|
||||
serdes_count = chip->info->ops->serdes_get_sset_count(chip,
|
||||
port);
|
||||
if (serdes_count < 0) {
|
||||
count = serdes_count;
|
||||
goto out;
|
||||
}
|
||||
count += serdes_count;
|
||||
count += ARRAY_SIZE(mv88e6xxx_atu_vtu_stats_strings);
|
||||
|
||||
out:
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data, int types,
|
||||
u16 bank1_select, u16 histogram)
|
||||
static int mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data, int types,
|
||||
u16 bank1_select, u16 histogram)
|
||||
{
|
||||
struct mv88e6xxx_hw_stat *stat;
|
||||
int i, j;
|
||||
@ -712,24 +827,28 @@ static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
|
||||
stat = &mv88e6xxx_hw_stats[i];
|
||||
if (stat->type & types) {
|
||||
mutex_lock(&chip->reg_lock);
|
||||
data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
|
||||
bank1_select,
|
||||
histogram);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
static int mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
{
|
||||
return mv88e6xxx_stats_get_stats(chip, port, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_PORT,
|
||||
0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
|
||||
}
|
||||
|
||||
static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
static int mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
{
|
||||
return mv88e6xxx_stats_get_stats(chip, port, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
|
||||
@ -737,8 +856,8 @@ static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
|
||||
}
|
||||
|
||||
static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
static int mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
{
|
||||
return mv88e6xxx_stats_get_stats(chip, port, data,
|
||||
STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
|
||||
@ -746,11 +865,32 @@ static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
0);
|
||||
}
|
||||
|
||||
static void mv88e6xxx_atu_vtu_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
{
|
||||
*data++ = chip->ports[port].atu_member_violation;
|
||||
*data++ = chip->ports[port].atu_miss_violation;
|
||||
*data++ = chip->ports[port].atu_full_violation;
|
||||
*data++ = chip->ports[port].vtu_member_violation;
|
||||
*data++ = chip->ports[port].vtu_miss_violation;
|
||||
}
|
||||
|
||||
static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (chip->info->ops->stats_get_stats)
|
||||
chip->info->ops->stats_get_stats(chip, port, data);
|
||||
count = chip->info->ops->stats_get_stats(chip, port, data);
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->info->ops->serdes_get_stats) {
|
||||
data += count;
|
||||
count = chip->info->ops->serdes_get_stats(chip, port, data);
|
||||
}
|
||||
data += count;
|
||||
mv88e6xxx_atu_vtu_get_stats(chip, port, data);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
|
||||
@ -762,14 +902,13 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
|
||||
mutex_lock(&chip->reg_lock);
|
||||
|
||||
ret = mv88e6xxx_stats_snapshot(chip, port);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
if (ret < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
mv88e6xxx_get_stats(chip, port, data);
|
||||
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip)
|
||||
@ -1433,7 +1572,9 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
|
||||
eth_broadcast_addr(addr.mac);
|
||||
|
||||
do {
|
||||
mutex_lock(&chip->reg_lock);
|
||||
err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1466,7 +1607,10 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
|
||||
int err;
|
||||
|
||||
/* Dump port's default Filtering Information Database (VLAN ID 0) */
|
||||
mutex_lock(&chip->reg_lock);
|
||||
err = mv88e6xxx_port_get_fid(chip, port, &fid);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1476,7 +1620,9 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
|
||||
|
||||
/* Dump VLANs' Filtering Information Databases */
|
||||
do {
|
||||
mutex_lock(&chip->reg_lock);
|
||||
err = mv88e6xxx_vtu_getnext(chip, &vlan);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1496,13 +1642,8 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
|
||||
dsa_fdb_dump_cb_t *cb, void *data)
|
||||
{
|
||||
struct mv88e6xxx_chip *chip = ds->priv;
|
||||
int err;
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
err = mv88e6xxx_port_db_dump(chip, port, cb, data);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
return err;
|
||||
return mv88e6xxx_port_db_dump(chip, port, cb, data);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
|
||||
@ -2092,6 +2233,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
/* Setup PTP Hardware Clock and timestamping */
|
||||
if (chip->info->ptp_support) {
|
||||
err = mv88e6xxx_ptp_setup(chip);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
err = mv88e6xxx_hwtstamp_setup(chip);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
@ -2148,6 +2300,15 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
|
||||
struct mii_bus *bus;
|
||||
int err;
|
||||
|
||||
if (external) {
|
||||
mutex_lock(&chip->reg_lock);
|
||||
err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
@ -2170,12 +2331,19 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
|
||||
bus->write = mv88e6xxx_mdio_write;
|
||||
bus->parent = chip->dev;
|
||||
|
||||
if (!external) {
|
||||
err = mv88e6xxx_g2_irq_mdio_setup(chip, bus);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (np)
|
||||
err = of_mdiobus_register(bus, np);
|
||||
else
|
||||
err = mdiobus_register(bus);
|
||||
if (err) {
|
||||
dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
|
||||
mv88e6xxx_g2_irq_mdio_free(chip, bus);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -2202,6 +2370,9 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
|
||||
list_for_each_entry(mdio_bus, &chip->mdios, list) {
|
||||
bus = mdio_bus->bus;
|
||||
|
||||
if (!mdio_bus->external)
|
||||
mv88e6xxx_g2_irq_mdio_free(chip, bus);
|
||||
|
||||
mdiobus_unregister(bus);
|
||||
}
|
||||
}
|
||||
@ -2472,6 +2643,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
|
||||
.reset = mv88e6352_g1_reset,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6161_ops = {
|
||||
@ -2602,6 +2774,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6175_ops = {
|
||||
@ -2673,6 +2846,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6185_ops = {
|
||||
@ -2736,6 +2910,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
|
||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6190x_ops = {
|
||||
@ -2771,6 +2946,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
|
||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6191_ops = {
|
||||
@ -2843,6 +3019,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6290_ops = {
|
||||
@ -2879,6 +3057,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
|
||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6320_ops = {
|
||||
@ -2913,6 +3093,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
|
||||
.reset = mv88e6352_g1_reset,
|
||||
.vtu_getnext = mv88e6185_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6321_ops = {
|
||||
@ -2945,6 +3127,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
|
||||
.reset = mv88e6352_g1_reset,
|
||||
.vtu_getnext = mv88e6185_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6341_ops = {
|
||||
@ -2981,6 +3165,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
|
||||
.reset = mv88e6352_g1_reset,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6350_ops = {
|
||||
@ -3049,6 +3235,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
|
||||
.reset = mv88e6352_g1_reset,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6352_ops = {
|
||||
@ -3086,6 +3273,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
.serdes_get_sset_count = mv88e6352_serdes_get_sset_count,
|
||||
.serdes_get_strings = mv88e6352_serdes_get_strings,
|
||||
.serdes_get_stats = mv88e6352_serdes_get_stats,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6390_ops = {
|
||||
@ -3124,6 +3316,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
|
||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_ops mv88e6390x_ops = {
|
||||
@ -3162,6 +3356,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
|
||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
};
|
||||
|
||||
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
@ -3171,6 +3367,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6085",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 10,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3191,6 +3388,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6095/88E6095F",
|
||||
.num_databases = 256,
|
||||
.num_ports = 11,
|
||||
.num_internal_phys = 0,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3209,6 +3407,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6097/88E6097F",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11,
|
||||
.num_internal_phys = 8,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3229,6 +3428,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6123",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 3,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3249,6 +3449,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6131",
|
||||
.num_databases = 256,
|
||||
.num_ports = 8,
|
||||
.num_internal_phys = 0,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3264,15 +3465,18 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
[MV88E6141] = {
|
||||
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6141,
|
||||
.family = MV88E6XXX_FAMILY_6341,
|
||||
.name = "Marvell 88E6341",
|
||||
.name = "Marvell 88E6141",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 6,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 11,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
.global2_addr = 0x1c,
|
||||
.age_time_coeff = 3750,
|
||||
.atu_move_port_mask = 0x1f,
|
||||
.g1_irqs = 9,
|
||||
.g2_irqs = 10,
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
@ -3286,6 +3490,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6161",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 6,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3306,6 +3511,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6165",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 6,
|
||||
.num_internal_phys = 0,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3326,6 +3532,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6171",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3346,6 +3553,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6172",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 15,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3366,6 +3575,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6175",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3386,6 +3596,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6176",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 15,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3406,6 +3618,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6185",
|
||||
.num_databases = 256,
|
||||
.num_ports = 10,
|
||||
.num_internal_phys = 0,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3424,6 +3637,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6190",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11, /* 10 + Z80 */
|
||||
.num_internal_phys = 11,
|
||||
.num_gpio = 16,
|
||||
.max_vid = 8191,
|
||||
.port_base_addr = 0x0,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3444,6 +3659,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6190X",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11, /* 10 + Z80 */
|
||||
.num_internal_phys = 11,
|
||||
.num_gpio = 16,
|
||||
.max_vid = 8191,
|
||||
.port_base_addr = 0x0,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3464,6 +3681,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6191",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11, /* 10 + Z80 */
|
||||
.num_internal_phys = 11,
|
||||
.max_vid = 8191,
|
||||
.port_base_addr = 0x0,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3475,6 +3693,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_DSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6191_ops,
|
||||
},
|
||||
|
||||
@ -3484,6 +3703,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6240",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 15,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3495,6 +3716,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_EDSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6240_ops,
|
||||
},
|
||||
|
||||
@ -3504,6 +3726,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6290",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11, /* 10 + Z80 */
|
||||
.num_internal_phys = 11,
|
||||
.num_gpio = 16,
|
||||
.max_vid = 8191,
|
||||
.port_base_addr = 0x0,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3515,6 +3739,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_DSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6290_ops,
|
||||
},
|
||||
|
||||
@ -3524,16 +3749,20 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6320",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 15,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
.global2_addr = 0x1c,
|
||||
.age_time_coeff = 15000,
|
||||
.g1_irqs = 8,
|
||||
.g2_irqs = 10,
|
||||
.atu_move_port_mask = 0xf,
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_EDSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6320_ops,
|
||||
},
|
||||
|
||||
@ -3543,15 +3772,19 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6321",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 15,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
.global2_addr = 0x1c,
|
||||
.age_time_coeff = 15000,
|
||||
.g1_irqs = 8,
|
||||
.g2_irqs = 10,
|
||||
.atu_move_port_mask = 0xf,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_EDSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6321_ops,
|
||||
},
|
||||
|
||||
@ -3560,17 +3793,21 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.family = MV88E6XXX_FAMILY_6341,
|
||||
.name = "Marvell 88E6341",
|
||||
.num_databases = 4096,
|
||||
.num_internal_phys = 5,
|
||||
.num_ports = 6,
|
||||
.num_gpio = 11,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
.global2_addr = 0x1c,
|
||||
.age_time_coeff = 3750,
|
||||
.atu_move_port_mask = 0x1f,
|
||||
.g1_irqs = 9,
|
||||
.g2_irqs = 10,
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_EDSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6341_ops,
|
||||
},
|
||||
|
||||
@ -3580,6 +3817,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6350",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3600,6 +3838,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6351",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3620,6 +3859,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6352",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 7,
|
||||
.num_internal_phys = 5,
|
||||
.num_gpio = 15,
|
||||
.max_vid = 4095,
|
||||
.port_base_addr = 0x10,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3631,6 +3872,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_EDSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6352_ops,
|
||||
},
|
||||
[MV88E6390] = {
|
||||
@ -3639,6 +3881,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6390",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11, /* 10 + Z80 */
|
||||
.num_internal_phys = 11,
|
||||
.num_gpio = 16,
|
||||
.max_vid = 8191,
|
||||
.port_base_addr = 0x0,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3650,6 +3894,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_DSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6390_ops,
|
||||
},
|
||||
[MV88E6390X] = {
|
||||
@ -3658,6 +3903,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.name = "Marvell 88E6390X",
|
||||
.num_databases = 4096,
|
||||
.num_ports = 11, /* 10 + Z80 */
|
||||
.num_internal_phys = 11,
|
||||
.num_gpio = 16,
|
||||
.max_vid = 8191,
|
||||
.port_base_addr = 0x0,
|
||||
.global1_addr = 0x1b,
|
||||
@ -3669,6 +3916,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
||||
.pvt = true,
|
||||
.multi_chip = true,
|
||||
.tag_protocol = DSA_TAG_PROTO_DSA,
|
||||
.ptp_support = true,
|
||||
.ops = &mv88e6390x_ops,
|
||||
},
|
||||
};
|
||||
@ -3880,6 +4128,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
|
||||
.port_mdb_del = mv88e6xxx_port_mdb_del,
|
||||
.crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
|
||||
.crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
|
||||
.port_hwtstamp_set = mv88e6xxx_port_hwtstamp_set,
|
||||
.port_hwtstamp_get = mv88e6xxx_port_hwtstamp_get,
|
||||
.port_txtstamp = mv88e6xxx_port_txtstamp,
|
||||
.port_rxtstamp = mv88e6xxx_port_rxtstamp,
|
||||
.get_ts_info = mv88e6xxx_get_ts_info,
|
||||
};
|
||||
|
||||
static struct dsa_switch_driver mv88e6xxx_switch_drv = {
|
||||
@ -3959,33 +4212,34 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chip->irq > 0) {
|
||||
/* Has to be performed before the MDIO bus is created,
|
||||
* because the PHYs will link there interrupts to these
|
||||
* interrupt controllers
|
||||
*/
|
||||
mutex_lock(&chip->reg_lock);
|
||||
/* Has to be performed before the MDIO bus is created, because
|
||||
* the PHYs will link their interrupts to these interrupt
|
||||
* controllers
|
||||
*/
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->irq > 0)
|
||||
err = mv88e6xxx_g1_irq_setup(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
else
|
||||
err = mv88e6xxx_irq_poll_setup(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (chip->info->g2_irqs > 0) {
|
||||
err = mv88e6xxx_g2_irq_setup(chip);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (chip->info->g2_irqs > 0) {
|
||||
err = mv88e6xxx_g2_irq_setup(chip);
|
||||
if (err)
|
||||
goto out_g1_irq;
|
||||
}
|
||||
|
||||
err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
|
||||
if (err)
|
||||
goto out_g2_irq;
|
||||
|
||||
err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
|
||||
if (err)
|
||||
goto out_g1_atu_prob_irq;
|
||||
goto out_g1_irq;
|
||||
}
|
||||
|
||||
err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
|
||||
if (err)
|
||||
goto out_g2_irq;
|
||||
|
||||
err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
|
||||
if (err)
|
||||
goto out_g1_atu_prob_irq;
|
||||
|
||||
err = mv88e6xxx_mdios_register(chip, np);
|
||||
if (err)
|
||||
goto out_g1_vtu_prob_irq;
|
||||
@ -3999,20 +4253,19 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
|
||||
out_mdio:
|
||||
mv88e6xxx_mdios_unregister(chip);
|
||||
out_g1_vtu_prob_irq:
|
||||
if (chip->irq > 0)
|
||||
mv88e6xxx_g1_vtu_prob_irq_free(chip);
|
||||
mv88e6xxx_g1_vtu_prob_irq_free(chip);
|
||||
out_g1_atu_prob_irq:
|
||||
if (chip->irq > 0)
|
||||
mv88e6xxx_g1_atu_prob_irq_free(chip);
|
||||
mv88e6xxx_g1_atu_prob_irq_free(chip);
|
||||
out_g2_irq:
|
||||
if (chip->info->g2_irqs > 0 && chip->irq > 0)
|
||||
if (chip->info->g2_irqs > 0)
|
||||
mv88e6xxx_g2_irq_free(chip);
|
||||
out_g1_irq:
|
||||
if (chip->irq > 0) {
|
||||
mutex_lock(&chip->reg_lock);
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->irq > 0)
|
||||
mv88e6xxx_g1_irq_free(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
else
|
||||
mv88e6xxx_irq_poll_free(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@ -4022,19 +4275,27 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
|
||||
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
|
||||
struct mv88e6xxx_chip *chip = ds->priv;
|
||||
|
||||
if (chip->info->ptp_support) {
|
||||
mv88e6xxx_hwtstamp_free(chip);
|
||||
mv88e6xxx_ptp_free(chip);
|
||||
}
|
||||
|
||||
mv88e6xxx_phy_destroy(chip);
|
||||
mv88e6xxx_unregister_switch(chip);
|
||||
mv88e6xxx_mdios_unregister(chip);
|
||||
|
||||
if (chip->irq > 0) {
|
||||
mv88e6xxx_g1_vtu_prob_irq_free(chip);
|
||||
mv88e6xxx_g1_atu_prob_irq_free(chip);
|
||||
if (chip->info->g2_irqs > 0)
|
||||
mv88e6xxx_g2_irq_free(chip);
|
||||
mutex_lock(&chip->reg_lock);
|
||||
mv88e6xxx_g1_vtu_prob_irq_free(chip);
|
||||
mv88e6xxx_g1_atu_prob_irq_free(chip);
|
||||
|
||||
if (chip->info->g2_irqs > 0)
|
||||
mv88e6xxx_g2_irq_free(chip);
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->irq > 0)
|
||||
mv88e6xxx_g1_irq_free(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
else
|
||||
mv88e6xxx_irq_poll_free(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static const struct of_device_id mv88e6xxx_of_match[] = {
|
||||
|
@ -15,7 +15,10 @@
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/timecounter.h>
|
||||
#include <net/dsa.h>
|
||||
|
||||
#ifndef UINT64_MAX
|
||||
@ -39,6 +42,8 @@
|
||||
#define MV88E6XXX_MAX_PVT_SWITCHES 32
|
||||
#define MV88E6XXX_MAX_PVT_PORTS 16
|
||||
|
||||
#define MV88E6XXX_MAX_GPIO 16
|
||||
|
||||
enum mv88e6xxx_egress_mode {
|
||||
MV88E6XXX_EGRESS_MODE_UNMODIFIED,
|
||||
MV88E6XXX_EGRESS_MODE_UNTAGGED,
|
||||
@ -105,6 +110,8 @@ struct mv88e6xxx_info {
|
||||
const char *name;
|
||||
unsigned int num_databases;
|
||||
unsigned int num_ports;
|
||||
unsigned int num_internal_phys;
|
||||
unsigned int num_gpio;
|
||||
unsigned int max_vid;
|
||||
unsigned int port_base_addr;
|
||||
unsigned int global1_addr;
|
||||
@ -126,6 +133,9 @@ struct mv88e6xxx_info {
|
||||
*/
|
||||
u8 atu_move_port_mask;
|
||||
const struct mv88e6xxx_ops *ops;
|
||||
|
||||
/* Supports PTP */
|
||||
bool ptp_support;
|
||||
};
|
||||
|
||||
struct mv88e6xxx_atu_entry {
|
||||
@ -146,6 +156,8 @@ struct mv88e6xxx_vtu_entry {
|
||||
|
||||
struct mv88e6xxx_bus_ops;
|
||||
struct mv88e6xxx_irq_ops;
|
||||
struct mv88e6xxx_gpio_ops;
|
||||
struct mv88e6xxx_avb_ops;
|
||||
|
||||
struct mv88e6xxx_irq {
|
||||
u16 masked;
|
||||
@ -154,6 +166,41 @@ struct mv88e6xxx_irq {
|
||||
unsigned int nirqs;
|
||||
};
|
||||
|
||||
/* state flags for mv88e6xxx_port_hwtstamp::state */
|
||||
enum {
|
||||
MV88E6XXX_HWTSTAMP_ENABLED,
|
||||
MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS,
|
||||
};
|
||||
|
||||
struct mv88e6xxx_port_hwtstamp {
|
||||
/* Port index */
|
||||
int port_id;
|
||||
|
||||
/* Timestamping state */
|
||||
unsigned long state;
|
||||
|
||||
/* Resources for receive timestamping */
|
||||
struct sk_buff_head rx_queue;
|
||||
struct sk_buff_head rx_queue2;
|
||||
|
||||
/* Resources for transmit timestamping */
|
||||
unsigned long tx_tstamp_start;
|
||||
struct sk_buff *tx_skb;
|
||||
u16 tx_seq_id;
|
||||
|
||||
/* Current timestamp configuration */
|
||||
struct hwtstamp_config tstamp_config;
|
||||
};
|
||||
|
||||
struct mv88e6xxx_port {
|
||||
u64 serdes_stats[2];
|
||||
u64 atu_member_violation;
|
||||
u64 atu_miss_violation;
|
||||
u64 atu_full_violation;
|
||||
u64 vtu_member_violation;
|
||||
u64 vtu_miss_violation;
|
||||
};
|
||||
|
||||
struct mv88e6xxx_chip {
|
||||
const struct mv88e6xxx_info *info;
|
||||
|
||||
@ -207,8 +254,34 @@ struct mv88e6xxx_chip {
|
||||
int irq;
|
||||
int device_irq;
|
||||
int watchdog_irq;
|
||||
|
||||
int atu_prob_irq;
|
||||
int vtu_prob_irq;
|
||||
struct kthread_worker *kworker;
|
||||
struct kthread_delayed_work irq_poll_work;
|
||||
|
||||
/* GPIO resources */
|
||||
u8 gpio_data[2];
|
||||
|
||||
/* This cyclecounter abstracts the switch PTP time.
|
||||
* reg_lock must be held for any operation that read()s.
|
||||
*/
|
||||
struct cyclecounter tstamp_cc;
|
||||
struct timecounter tstamp_tc;
|
||||
struct delayed_work overflow_work;
|
||||
|
||||
struct ptp_clock *ptp_clock;
|
||||
struct ptp_clock_info ptp_clock_info;
|
||||
struct delayed_work tai_event_work;
|
||||
struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO];
|
||||
u16 trig_config;
|
||||
u16 evcap_config;
|
||||
|
||||
/* Per-port timestamping resources. */
|
||||
struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];
|
||||
|
||||
/* Array of port structures. */
|
||||
struct mv88e6xxx_port ports[DSA_MAX_PORTS];
|
||||
};
|
||||
|
||||
struct mv88e6xxx_bus_ops {
|
||||
@ -327,9 +400,9 @@ struct mv88e6xxx_ops {
|
||||
|
||||
/* Return the number of strings describing statistics */
|
||||
int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip);
|
||||
void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data);
|
||||
void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data);
|
||||
int (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data);
|
||||
int (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data);
|
||||
int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port);
|
||||
int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port);
|
||||
const struct mv88e6xxx_irq_ops *watchdog_ops;
|
||||
@ -339,11 +412,24 @@ struct mv88e6xxx_ops {
|
||||
/* Power on/off a SERDES interface */
|
||||
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||
|
||||
/* Statistics from the SERDES interface */
|
||||
int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
|
||||
int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port,
|
||||
uint8_t *data);
|
||||
int (*serdes_get_stats)(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data);
|
||||
|
||||
/* VLAN Translation Unit operations */
|
||||
int (*vtu_getnext)(struct mv88e6xxx_chip *chip,
|
||||
struct mv88e6xxx_vtu_entry *entry);
|
||||
int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip,
|
||||
struct mv88e6xxx_vtu_entry *entry);
|
||||
|
||||
/* GPIO operations */
|
||||
const struct mv88e6xxx_gpio_ops *gpio_ops;
|
||||
|
||||
/* Interface to the AVB/PTP registers */
|
||||
const struct mv88e6xxx_avb_ops *avb_ops;
|
||||
};
|
||||
|
||||
struct mv88e6xxx_irq_ops {
|
||||
@ -355,13 +441,49 @@ struct mv88e6xxx_irq_ops {
|
||||
void (*irq_free)(struct mv88e6xxx_chip *chip);
|
||||
};
|
||||
|
||||
struct mv88e6xxx_gpio_ops {
|
||||
/* Get/set data on GPIO pin */
|
||||
int (*get_data)(struct mv88e6xxx_chip *chip, unsigned int pin);
|
||||
int (*set_data)(struct mv88e6xxx_chip *chip, unsigned int pin,
|
||||
int value);
|
||||
|
||||
/* get/set GPIO direction */
|
||||
int (*get_dir)(struct mv88e6xxx_chip *chip, unsigned int pin);
|
||||
int (*set_dir)(struct mv88e6xxx_chip *chip, unsigned int pin,
|
||||
bool input);
|
||||
|
||||
/* get/set GPIO pin control */
|
||||
int (*get_pctl)(struct mv88e6xxx_chip *chip, unsigned int pin,
|
||||
int *func);
|
||||
int (*set_pctl)(struct mv88e6xxx_chip *chip, unsigned int pin,
|
||||
int func);
|
||||
};
|
||||
|
||||
struct mv88e6xxx_avb_ops {
|
||||
/* Access port-scoped Precision Time Protocol registers */
|
||||
int (*port_ptp_read)(struct mv88e6xxx_chip *chip, int port, int addr,
|
||||
u16 *data, int len);
|
||||
int (*port_ptp_write)(struct mv88e6xxx_chip *chip, int port, int addr,
|
||||
u16 data);
|
||||
|
||||
/* Access global Precision Time Protocol registers */
|
||||
int (*ptp_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
|
||||
int len);
|
||||
int (*ptp_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
|
||||
|
||||
/* Access global Time Application Interface registers */
|
||||
int (*tai_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
|
||||
int len);
|
||||
int (*tai_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
|
||||
};
|
||||
|
||||
#define STATS_TYPE_PORT BIT(0)
|
||||
#define STATS_TYPE_BANK0 BIT(1)
|
||||
#define STATS_TYPE_BANK1 BIT(2)
|
||||
|
||||
struct mv88e6xxx_hw_stat {
|
||||
char string[ETH_GSTRING_LEN];
|
||||
int sizeof_stat;
|
||||
size_t size;
|
||||
int reg;
|
||||
int type;
|
||||
};
|
||||
@ -386,6 +508,11 @@ static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip)
|
||||
return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
|
||||
}
|
||||
|
||||
static inline unsigned int mv88e6xxx_num_gpio(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
return chip->info->num_gpio;
|
||||
}
|
||||
|
||||
int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
|
||||
int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
|
||||
int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user