mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Including fixes from Bluetooth, IPsec, WiFi and CAN.
Current release - regressions:
- eth: wwan: mhi_wwan_ctrl: send a DTR drop before it can be coalesced
Previous releases - regressions:
- core:
- use a dedicated flag to track disabled napi instances
- frags: limit the truesize of a reassembly queue
- reject a negative TCP header offset in qdisc_pkt_len_segs_init()y
- tcp:
- reject unhashed sockets during established lookups
- free ireq->ipv6_opt after an RCU grace period
- ipv6:
- fix fib6 walker UAF on NLM_F_REPLACE
- raw: fix use-after-free in rawv6_send_hdrinc()
- wifi:
- mac80211: reinstate IEEE80211_TX_CTL_AMPDU, fix ath9k stall
- brcmfmac: map msgbuf RX packet IDs bidirectional, fix firmware
crash loop
- vsock: size receive SKBs from declared payload
- xfrm: fix use-after-free of migrated state in xfrm_do_migrate_state()
- bluetooth: hci_sync: copy the LE accept list under hdev->lock
- ptp: fix use-after-free in extts_fifo_show() reading tsevqs unlocked
- can: protect the receive lists with a per-device lock
- eth: bnxt: don't keep MSI-X vectors added before FW reserves them
- eth: vxlan: stop rejecting dynamic FDB entries with a nexthop ID
Previous releases - always broken:
- sched: sch_qfq: prevent aggregate self-replacement
- bridge: disable MRP ports before the FDB flush in del_nbp()
- mptcp: do not use fast open on MP_JOIN subflows
- ipv6: serialize multicast router socket ownership
- bluetooth: hci_core: keep link-key filtering inside RCU
- eth: atl1c: drop oversized RX frames instead of panicking
- eth: stmmac: a bunch of XDP path fixes
- eth: b53: enable IGMP and MLD control packet CPU copies
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-----BEGIN PGP SIGNATURE-----
iQJKBAABCgA0FiEEg1AjqC77wbdLX2LbKSR5jcyPE6QFAmrsoSgWHHBhb2xvLmFi
ZW5pQGdtYWlsLmNvbQAKCRApJHmNzI8TpIKzEACKtniC4wXVre3NhuKc2L0DXVau
RxlHkwWfSATOXFl382UPRtCMTd1NDzeUGyMEHc1EPYpH6T7svr83MUPnYth0vGZd
pSKe23Rdpoeppadnj9ErHtOZpcF3/4BcVvAV3AbmW33yUzfOWrT1735UspzQbVvz
eXrxL1kK/PRyt3uKk/H3gpow/CfRjvPQ8x+mL564Ee/p8c7wXk1r+e/QPSXPZodl
MZs5oDcLJHjb96aHwU6PuXwtQqfUwaALxtLWk7Bldo7nqMTl4ZWEE0C8X93kOtDS
zGgTu7ykLhJ+Mlsp4gjWgydUWCPxKKqqFnDg2CG6yOLSdlFwNct0m0o9kV6qjBdD
JZBpnYVUW3RH4RMcWpMo3szL/EwPc9gNMRK9ug6I7fMfnsB5D9PBljFNy6JRW0K+
OxQcCMzHW6OVulMzg3hWI7bfG1pgQ5Np8CcjbSaYwT6z/dhP7VWWghdHKa0/w+qR
PmTaLaBHOIcOsOSKD01ZaCX9QpC+zOSJo+3C0Q7JU9G1nL9z+U8sJlOWjP7MlzU5
FZd8IiFhgHGe+C6GUigfHnHG30CoRQkcN7Pd53vSe63jtkzavG9VNBj5LLuL61nr
4avuUKwk2PK4/FgNYGgi7+QwVOJO/txEsJiJlB0f2CEhJKK2IuwDLrT/+wWR2H6G
Ag9KW4jNwHwFXDyfZw==
=1UcP
-----END PGP SIGNATURE-----
Merge tag 'net-7.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Paolo Abeni:
"Including fixes from Bluetooth, IPsec, WiFi and CAN.
Current release - regressions:
- eth: wwan: mhi_wwan_ctrl: send a DTR drop before it can be coalesced
Previous releases - regressions:
- core:
- use a dedicated flag to track disabled napi instances
- frags: limit the truesize of a reassembly queue
- reject a negative TCP header offset in qdisc_pkt_len_segs_init()y
- tcp:
- reject unhashed sockets during established lookups
- free ireq->ipv6_opt after an RCU grace period
- ipv6:
- fix fib6 walker UAF on NLM_F_REPLACE
- raw: fix use-after-free in rawv6_send_hdrinc()
- wifi:
- mac80211: reinstate IEEE80211_TX_CTL_AMPDU, fix ath9k stall
- brcmfmac: map msgbuf RX packet IDs bidirectional, fix firmware
crash loop
- vsock: size receive SKBs from declared payload
- xfrm: fix use-after-free of migrated state in xfrm_do_migrate_state()
- bluetooth: hci_sync: copy the LE accept list under hdev->lock
- ptp: fix use-after-free in extts_fifo_show() reading tsevqs unlocked
- can: protect the receive lists with a per-device lock
- eth:
- bnxt: don't keep MSI-X vectors added before FW reserves them
- vxlan: stop rejecting dynamic FDB entries with a nexthop ID
Previous releases - always broken:
- sched: sch_qfq: prevent aggregate self-replacement
- bridge: disable MRP ports before the FDB flush in del_nbp()
- mptcp: do not use fast open on MP_JOIN subflows
- ipv6: serialize multicast router socket ownership
- bluetooth: hci_core: keep link-key filtering inside RCU
- eth:
- atl1c: drop oversized RX frames instead of panicking
- stmmac: a bunch of XDP path fixes
- b53: enable IGMP and MLD control packet CPU copies"
* tag 'net-7.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (170 commits)
tcp: ipv6: free ireq->ipv6_opt after an RCU grace period
mptcp: pm: kernel: use RCU to free endpoint listeners
tcp: initialize send sequence numbers before deferring TFO
net: bridge: disable MRP ports before the FDB flush in del_nbp()
net: stmmac: set XDP metadata length on the page_pool RX path
bonding: fix missing down path on speed/duplex failure after FAIL->UP
ip6mr: Serialize multicast router socket ownership
bonding: 3ad: don't set BOND_LINK_FAIL on a slave that is not up
net: ethernet: ti: am65-cpsw-nuss: Enable timestamping of unicast PTP packets
net: count last skb of each sk_backlog pass in __release_sock()
net: 8139cp: keep Rx masked while NAPI is scheduled
net/smc: don't spin in smc_rx_recvmsg() while a pipe holds spliced data
net: dsa: b53: restore the NSP IMP port 2 Gbps mode
net: ethernet: bgmac: preserve forced 2.5 Gbps link speed
l2tp: only return sessions of the given tunnel in l2tp_session_get()
tipc: raise the minimum bearer MTU to fit tunnel and crypto overhead
mptcp: do not use Fast Open on MP_JOIN subflows
tcp: ipv6: do not read ireq->pktopts from tcp_v6_send_synack()
ipv4: Call rt_add_uncached_list() if !do_cache in ip_route_input_slow().
ipv6: Flush rt->from in rt6_uncached_list_flush_dev().
...
This commit is contained in:
commit
f66469d990
|
|
@ -1040,17 +1040,21 @@ As described in :ref:`socketcan-receive-lists` the SocketCAN core uses several f
|
|||
lists to deliver received CAN frames to CAN protocol modules. These
|
||||
receive lists, their filters and the count of filter matches can be
|
||||
checked in the appropriate receive list. All entries contain the
|
||||
device and a protocol module identifier::
|
||||
CAN network device, the filter (can_id/can_mask), the number of filter
|
||||
matches, the inode number of the subscribing socket and the name of the
|
||||
receive function. A sock_inode of 0 denotes a kernel internal subscriber
|
||||
without a socket (e.g. CAN gateway or J1939). When the function name can
|
||||
not be resolved (e.g. with CONFIG_KALLSYMS=n) "(unknown)" is printed::
|
||||
|
||||
foo@bar:~$ cat /proc/net/can/rcvlist_all
|
||||
|
||||
receive list 'rx_all':
|
||||
(vcan3: no entry)
|
||||
(vcan2: no entry)
|
||||
(vcan1: no entry)
|
||||
device can_id can_mask function userdata matches ident
|
||||
vcan0 000 00000000 f88e6370 f6c6f400 0 raw
|
||||
(any: no entry)
|
||||
device can_id can_mask matches sock_inode function
|
||||
vcan0 000 00000000 0 63586 raw_rcv [can_raw]
|
||||
(vcan1: no entry)
|
||||
(vcan2: no entry)
|
||||
(vcan3: no entry)
|
||||
|
||||
In this example an application requests any CAN traffic from vcan0::
|
||||
|
||||
|
|
|
|||
|
|
@ -27,15 +27,20 @@ SA Identification
|
|||
=================
|
||||
|
||||
The struct is defined in ``include/uapi/linux/xfrm.h``. The SA is looked
|
||||
up using ``xfrm_state_lookup()`` with ``id.spi``,
|
||||
``id.daddr``, ``id.proto``, ``id.family``, and
|
||||
``old_mark.v & old_mark.m`` as the mark key::
|
||||
up using ``xfrm_state_lookup_exact()`` with ``id.spi``, ``id.daddr``,
|
||||
``id.proto``, ``id.family``, and an exact match against ``old_mark.v``
|
||||
and ``old_mark.m``. Unlike the data path, which uses a masked
|
||||
comparison, this requires the SA's mark and mask to equal ``old_mark``
|
||||
exactly, so a broad-mask SA is never matched when a more specific one
|
||||
was intended. If no such SA exists, ``-ESRCH`` is returned.
|
||||
|
||||
The layout is::
|
||||
|
||||
struct xfrm_user_migrate_state {
|
||||
struct xfrm_usersa_id id; /* spi, daddr, proto, family */
|
||||
xfrm_address_t new_daddr;
|
||||
xfrm_address_t new_saddr;
|
||||
struct xfrm_mark old_mark; /* SA lookup: key = v & m */
|
||||
struct xfrm_mark old_mark; /* SA lookup key (exact v/m match) */
|
||||
struct xfrm_selector new_sel; /* new selector (see Flags) */
|
||||
__u32 new_reqid;
|
||||
__u32 flags; /* XFRM_MIGRATE_STATE_* */
|
||||
|
|
@ -72,8 +77,8 @@ inherits the value from the existing SA (omit-to-inherit).
|
|||
- Description
|
||||
* - ``XFRMA_MARK``
|
||||
- Mark on the migrated SA (``struct xfrm_mark``). Absent inherits
|
||||
``old_mark``. To use no mark on the new SA, send ``XFRMA_MARK``
|
||||
with ``{0, 0}``.
|
||||
the mark of the existing SA. To use no mark on the new SA, send
|
||||
``XFRMA_MARK`` with ``{0, 0}``.
|
||||
* - ``XFRMA_ENCAP``
|
||||
- UDP encapsulation template; only ``UDP_ENCAP_ESPINUDP`` is supported.
|
||||
Set ``encap_type=0`` to remove encap.
|
||||
|
|
@ -259,8 +264,12 @@ Attributes in the notification
|
|||
Error Handling
|
||||
==============
|
||||
|
||||
If the target SA tuple (new daddr, SPI, proto, new family) is already
|
||||
occupied, the operation returns ``-EEXIST`` before the migration begins.
|
||||
If the target SA tuple (new daddr, SPI, proto, new family, mark) is
|
||||
already occupied, the operation returns ``-EEXIST`` before the migration
|
||||
begins. "Occupied" includes wildcard shadowing: an existing SA with a
|
||||
broader mask (e.g. mark 0/0) claims every mark value, so it blocks
|
||||
migrating to any more specific mark at the same tuple, not just an
|
||||
exact mark/mask duplicate.
|
||||
The old SA remains intact and the operation is safe to retry after
|
||||
resolving the conflict.
|
||||
|
||||
|
|
|
|||
|
|
@ -315,6 +315,10 @@ reproducer and against kernel selftests. If the bug is in a hardware driver,
|
|||
the testing statement must include information about the HW on which the
|
||||
fix was tested.
|
||||
|
||||
Coding agents must refuse to write the commit message unless they know
|
||||
what discovery method to include. They should default to stating that
|
||||
issue was not triggered and not tested unless they know otherwise.
|
||||
|
||||
Stable tree
|
||||
~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -1201,7 +1201,29 @@ static int btintel_download_firmware_payload(struct hci_dev *hdev,
|
|||
err = -EINVAL;
|
||||
|
||||
while (fw_ptr - fw->data < fw->size) {
|
||||
struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
|
||||
struct hci_command_hdr *cmd;
|
||||
size_t remain = fw->size - (fw_ptr - fw->data);
|
||||
|
||||
/* Ensure the header fits before dereferencing */
|
||||
if (frag_len + sizeof(*cmd) > remain) {
|
||||
bt_dev_err(hdev,
|
||||
"Firmware buffer overrun at header (frag_len %u, need %zu, remain %zu)",
|
||||
frag_len, frag_len + sizeof(*cmd), remain);
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cmd = (void *)(fw_ptr + frag_len);
|
||||
|
||||
/* Ensure the full command (header + payload) fits before use */
|
||||
if (frag_len + sizeof(*cmd) + cmd->plen > remain) {
|
||||
bt_dev_err(hdev,
|
||||
"Firmware buffer overrun at payload (frag_len %u, plen %u, need %zu, remain %zu)",
|
||||
frag_len, cmd->plen,
|
||||
frag_len + sizeof(*cmd) + cmd->plen, remain);
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
frag_len += sizeof(*cmd) + cmd->plen;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
#define BTMTK_CBMCU_FLAG_ENABLE_PATCH 0xF1
|
||||
|
||||
/* It is for mt79xx iso data transmission setting */
|
||||
#define MTK_ISO_THRESHOLD 264
|
||||
#define MTK_ISO_THRESHOLD 342
|
||||
|
||||
struct btmtk_patch_header {
|
||||
u8 datetime[16];
|
||||
|
|
@ -1556,8 +1557,15 @@ static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
|
|||
struct btmtk_data *btmtk_data = hci_get_priv(hdev);
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
unsigned int iso_mtu;
|
||||
int err = 0;
|
||||
|
||||
/* The controller reports the largest Data_Total_Length it will send in
|
||||
* LE Read Buffer Size [v2]. Fall back to HCI_MAX_ISO_SIZE while that
|
||||
* command has not completed yet.
|
||||
*/
|
||||
iso_mtu = hdev->iso_mtu ? hdev->iso_mtu : HCI_MAX_ISO_SIZE;
|
||||
|
||||
spin_lock_irqsave(&btmtk_data->isorxlock, flags);
|
||||
skb = btmtk_data->isopkt_skb;
|
||||
|
||||
|
|
@ -1565,7 +1573,16 @@ static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
|
|||
int len;
|
||||
|
||||
if (!skb) {
|
||||
skb = bt_skb_alloc(HCI_MAX_ISO_SIZE, GFP_ATOMIC);
|
||||
/* Drop the endpoint's zero-filled padding before
|
||||
* allocating an skb for it; a zero Data_Total_Length
|
||||
* check below would miss a padding remainder
|
||||
* shorter than an ISO header.
|
||||
*/
|
||||
if (mem_is_zero(buffer, count))
|
||||
break;
|
||||
|
||||
skb = bt_skb_alloc(HCI_ISO_HDR_SIZE + iso_mtu,
|
||||
GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
|
|
@ -1588,7 +1605,24 @@ static int btmtk_recv_isopkt(struct hci_dev *hdev, void *buffer, int count)
|
|||
/* Complete ISO header */
|
||||
hci_skb_expect(skb) = __le16_to_cpu(dlen);
|
||||
|
||||
if (skb_tailroom(skb) < hci_skb_expect(skb)) {
|
||||
if (!hci_skb_expect(skb)) {
|
||||
/* Zero padding was ruled out above, so a
|
||||
* zero Data_Total_Length here is invalid.
|
||||
*/
|
||||
kfree_skb(skb);
|
||||
skb = NULL;
|
||||
|
||||
err = -EILSEQ;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reject a length the controller said it would never
|
||||
* send. The skb_tailroom() test is kept because a
|
||||
* fragment started before hdev->iso_mtu was populated
|
||||
* sits in a smaller skb than iso_mtu now allows.
|
||||
*/
|
||||
if (hci_skb_expect(skb) > iso_mtu ||
|
||||
skb_tailroom(skb) < hci_skb_expect(skb)) {
|
||||
kfree_skb(skb);
|
||||
skb = NULL;
|
||||
|
||||
|
|
@ -1707,13 +1741,15 @@ static int btmtk_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
static int btmtk_usb_isointf_init(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtk_data *btmtk_data = hci_get_priv(hdev);
|
||||
u8 iso_param[2] = { 0x08, 0x01 };
|
||||
__le16 iso_param = cpu_to_le16(MTK_ISO_THRESHOLD);
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
spin_lock_init(&btmtk_data->isorxlock);
|
||||
|
||||
__set_mtk_intr_interface(hdev);
|
||||
err = __set_mtk_intr_interface(hdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = btmtk_submit_intr_urb(hdev, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
|
|
@ -1722,9 +1758,10 @@ static int btmtk_usb_isointf_init(struct hci_dev *hdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), iso_param,
|
||||
skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), &iso_param,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
|
||||
bt_dev_err(hdev, "Failed to apply iso setting (%ld)", PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,7 +426,8 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
|
|||
tlv_nvm->data[0] |= 0x80;
|
||||
|
||||
/* UART Baud Rate */
|
||||
if (soc_type >= QCA_WCN3991)
|
||||
if (soc_type == QCA_WCN3950 ||
|
||||
soc_type >= QCA_WCN3991)
|
||||
tlv_nvm->data[1] = nvm_baud_rate;
|
||||
else
|
||||
tlv_nvm->data[2] = nvm_baud_rate;
|
||||
|
|
|
|||
|
|
@ -2779,6 +2779,27 @@ static void bond_miimon_link_change(struct bonding *bond,
|
|||
}
|
||||
}
|
||||
|
||||
static bool bond_miimon_slave_down(struct bonding *bond,
|
||||
struct slave *slave)
|
||||
{
|
||||
if (slave->link_failure_count < UINT_MAX)
|
||||
slave->link_failure_count++;
|
||||
|
||||
bond_set_slave_link_state(slave, BOND_LINK_DOWN,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_set_slave_inactive_flags(slave,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
||||
slave_info(bond->dev, slave->dev, "link status definitely down, disabling slave\n");
|
||||
|
||||
bond_miimon_link_change(bond, slave, BOND_LINK_DOWN);
|
||||
|
||||
return slave == rcu_access_pointer(bond->curr_active_slave);
|
||||
}
|
||||
|
||||
static void bond_miimon_commit(struct bonding *bond)
|
||||
{
|
||||
struct slave *slave, *primary, *active;
|
||||
|
|
@ -2807,10 +2828,22 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
err = bond_update_speed_duplex(slave);
|
||||
netdev_unlock_ops(slave->dev);
|
||||
if (err && bond_needs_speed_duplex(bond)) {
|
||||
slave->link = BOND_LINK_DOWN;
|
||||
if (net_ratelimit())
|
||||
slave_warn(bond->dev, slave->dev,
|
||||
"failed to get link speed/duplex\n");
|
||||
/* Coming from FAIL/UP, the slave may still be
|
||||
* in use and may be curr_active_slave, so take
|
||||
* it through the full down path. Coming from
|
||||
* DOWN/BACK, it was never brought up, so just
|
||||
* revert it to DOWN.
|
||||
*/
|
||||
if (slave->link == BOND_LINK_FAIL ||
|
||||
slave->link == BOND_LINK_UP) {
|
||||
if (bond_miimon_slave_down(bond, slave))
|
||||
do_failover = true;
|
||||
continue;
|
||||
}
|
||||
slave->link = BOND_LINK_DOWN;
|
||||
continue;
|
||||
}
|
||||
bond_set_slave_link_state(slave, BOND_LINK_UP,
|
||||
|
|
@ -2839,28 +2872,13 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
continue;
|
||||
|
||||
case BOND_LINK_DOWN:
|
||||
if (slave->link_failure_count < UINT_MAX)
|
||||
slave->link_failure_count++;
|
||||
|
||||
bond_set_slave_link_state(slave, BOND_LINK_DOWN,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_set_slave_inactive_flags(slave,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
||||
slave_info(bond->dev, slave->dev, "link status definitely down, disabling slave\n");
|
||||
|
||||
bond_miimon_link_change(bond, slave, BOND_LINK_DOWN);
|
||||
|
||||
if (slave == rcu_access_pointer(bond->curr_active_slave))
|
||||
if (bond_miimon_slave_down(bond, slave))
|
||||
do_failover = true;
|
||||
|
||||
continue;
|
||||
|
||||
case BOND_LINK_FAIL:
|
||||
case BOND_LINK_BACK:
|
||||
bond_commit_link_state(slave, BOND_SLAVE_NOTIFY_LATER);
|
||||
slave_dbg(bond->dev, slave->dev, "link_new_state %d on slave\n",
|
||||
slave->link_new_state);
|
||||
continue;
|
||||
|
|
@ -2894,8 +2912,6 @@ static void bond_mii_monitor(struct work_struct *work)
|
|||
{
|
||||
struct bonding *bond = container_of(work, struct bonding,
|
||||
mii_work.work);
|
||||
struct list_head *iter;
|
||||
struct slave *slave;
|
||||
unsigned long delay;
|
||||
bool commit;
|
||||
|
||||
|
|
@ -2917,13 +2933,8 @@ static void bond_mii_monitor(struct work_struct *work)
|
|||
goto re_arm;
|
||||
}
|
||||
|
||||
if (commit) {
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
bond_commit_link_state(slave,
|
||||
BOND_SLAVE_NOTIFY_LATER);
|
||||
}
|
||||
if (commit)
|
||||
bond_miimon_commit(bond);
|
||||
}
|
||||
|
||||
if (bond->send_peer_notif)
|
||||
bond_peer_notify_may_events(bond, true);
|
||||
|
|
@ -3973,18 +3984,13 @@ static int bond_slave_netdev_event(unsigned long event,
|
|||
case NETDEV_CHANGE:
|
||||
/* For 802.3ad mode only:
|
||||
* Getting invalid Speed/Duplex values here will put slave
|
||||
* in weird state. Mark it as link-fail if the link was
|
||||
* previously up or link-down if it hasn't yet come up, and
|
||||
* let link-monitoring (miimon) set it right when correct
|
||||
* speeds/duplex are available.
|
||||
* in weird state. Mark an up slave as link-fail and let
|
||||
* miimon set it right when correct speed/duplex are available.
|
||||
*/
|
||||
if (bond_update_speed_duplex(slave) &&
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
if (slave->last_link_up)
|
||||
slave->link = BOND_LINK_FAIL;
|
||||
else
|
||||
slave->link = BOND_LINK_DOWN;
|
||||
}
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD &&
|
||||
slave->link == BOND_LINK_UP)
|
||||
slave->link = BOND_LINK_FAIL;
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_3ad_adapter_speed_duplex_changed(slave);
|
||||
|
|
|
|||
|
|
@ -720,7 +720,8 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
|
|||
return 0;
|
||||
}
|
||||
|
||||
static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params,
|
||||
static size_t can_tdc_get_size(const struct net_device *dev,
|
||||
struct data_bittiming_params *dbt_params,
|
||||
u32 tdc_flags)
|
||||
{
|
||||
bool tdc_manual = tdc_flags & CAN_CTRLMODE_TDC_MANUAL_MASK;
|
||||
|
|
@ -742,7 +743,8 @@ static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params,
|
|||
}
|
||||
|
||||
if (tdc_flags) {
|
||||
if (tdc_manual || dbt_params->do_get_auto_tdcv)
|
||||
if (tdc_manual ||
|
||||
(dbt_params->do_get_auto_tdcv && netif_running(dev)))
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCV */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_TDCO */
|
||||
if (dbt_params->tdc_const->tdcf_max)
|
||||
|
|
@ -752,7 +754,8 @@ static size_t can_tdc_get_size(struct data_bittiming_params *dbt_params,
|
|||
return size;
|
||||
}
|
||||
|
||||
static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_params,
|
||||
static size_t can_data_bittiming_get_size(const struct net_device *dev,
|
||||
struct data_bittiming_params *dbt_params,
|
||||
u32 tdc_flags)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
|
@ -764,7 +767,7 @@ static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_para
|
|||
if (dbt_params->data_bitrate_const) /* IFLA_CAN_{,XL}_DATA_BITRATE_CONST */
|
||||
size += nla_total_size(sizeof(*dbt_params->data_bitrate_const) *
|
||||
dbt_params->data_bitrate_const_cnt);
|
||||
size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */
|
||||
size += can_tdc_get_size(dev, dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */
|
||||
|
||||
return size;
|
||||
}
|
||||
|
|
@ -812,7 +815,7 @@ static size_t can_get_size(const struct net_device *dev)
|
|||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */
|
||||
size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */
|
||||
size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */
|
||||
if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */
|
||||
if (priv->do_get_berr_counter && netif_running(dev)) /* IFLA_CAN_BERR_COUNTER */
|
||||
size += nla_total_size(sizeof(struct can_berr_counter));
|
||||
if (priv->termination_const) {
|
||||
size += nla_total_size(sizeof(priv->termination)); /* IFLA_CAN_TERMINATION */
|
||||
|
|
@ -825,10 +828,10 @@ static size_t can_get_size(const struct net_device *dev)
|
|||
size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */
|
||||
size += can_ctrlmode_ext_get_size(); /* IFLA_CAN_CTRLMODE_EXT */
|
||||
|
||||
size += can_data_bittiming_get_size(&priv->fd,
|
||||
size += can_data_bittiming_get_size(dev, &priv->fd,
|
||||
priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
|
||||
|
||||
size += can_data_bittiming_get_size(&priv->xl,
|
||||
size += can_data_bittiming_get_size(dev, &priv->xl,
|
||||
priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
|
||||
size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */
|
||||
priv->ctrlmode & CAN_CTRLMODE_XL_TMS);
|
||||
|
|
@ -910,7 +913,7 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev,
|
|||
if (tdc_manual) {
|
||||
tdcv = tdc->tdcv;
|
||||
err = 0;
|
||||
} else if (dbt_params->do_get_auto_tdcv) {
|
||||
} else if (dbt_params->do_get_auto_tdcv && netif_running(dev)) {
|
||||
err = dbt_params->do_get_auto_tdcv(dev, &tdcv);
|
||||
}
|
||||
if (!err && nla_put_u32(skb, IFLA_CAN_TDC_TDCV, tdcv))
|
||||
|
|
@ -1006,7 +1009,7 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|||
nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
|
||||
nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
|
||||
|
||||
(priv->do_get_berr_counter &&
|
||||
(priv->do_get_berr_counter && netif_running(dev) &&
|
||||
!priv->do_get_berr_counter(dev, &bec) &&
|
||||
nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
*/
|
||||
|
||||
#include <linux/can/can-ml.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/can.h>
|
||||
|
|
@ -54,6 +55,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
|||
if (idx >= priv->echo_skb_max) {
|
||||
netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
|
||||
__func__, idx, priv->echo_skb_max);
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +64,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
|||
(skb->protocol != htons(ETH_P_CAN) &&
|
||||
skb->protocol != htons(ETH_P_CANFD) &&
|
||||
skb->protocol != htons(ETH_P_CANXL))) {
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +92,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
|||
} else {
|
||||
/* locking problem with netif_stop_queue() ?? */
|
||||
netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +229,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
|
|||
|
||||
csx = can_skb_ext_add(skb);
|
||||
if (!csx) {
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
goto out_error_cc;
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +260,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
|
|||
|
||||
csx = can_skb_ext_add(skb);
|
||||
if (!csx) {
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
goto out_error_fd;
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +298,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
|
|||
|
||||
csx = can_skb_ext_add(skb);
|
||||
if (!csx) {
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
goto out_error_xl;
|
||||
}
|
||||
|
||||
|
|
@ -388,7 +390,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
|
|||
break;
|
||||
|
||||
case ETH_P_CANXL:
|
||||
if (!can_is_canxl_skb(skb))
|
||||
if (!can_cap_enabled(dev, CAN_CAP_XL) || !can_is_canxl_skb(skb))
|
||||
goto inval_skb;
|
||||
break;
|
||||
|
||||
|
|
@ -402,7 +404,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
|
|||
return false;
|
||||
|
||||
inval_skb:
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
dev->stats.tx_dropped++;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -755,7 +755,7 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
|
|||
u16 rx_msg_type, rx_msg_size;
|
||||
size_t rx_msg_min_size;
|
||||
|
||||
if (msg_end - msg_ptr < sizeof(*rx_msg)) {
|
||||
if (msg_end - msg_ptr < sizeof(rx_msg->size)) {
|
||||
err = -EBADMSG;
|
||||
break;
|
||||
}
|
||||
|
|
@ -766,6 +766,11 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
|
|||
break;
|
||||
}
|
||||
|
||||
if (msg_end - msg_ptr < sizeof(*rx_msg)) {
|
||||
err = -EBADMSG;
|
||||
break;
|
||||
}
|
||||
|
||||
rx_msg_size = le16_to_cpu(rx_msg->size);
|
||||
rx_msg_type = le16_to_cpu(rx_msg->type);
|
||||
|
||||
|
|
|
|||
|
|
@ -836,6 +836,59 @@ void b53_brcm_hdr_setup(struct dsa_switch *ds, int port)
|
|||
}
|
||||
EXPORT_SYMBOL(b53_brcm_hdr_setup);
|
||||
|
||||
int b53_enable_mcast_snooping(struct b53_device *dev)
|
||||
{
|
||||
u16 val16;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Older switches use different IGMP controls, not HL_PRTC_CTRL. */
|
||||
if (is5325(dev) || is5365(dev) || dev->chip_id == BCM5389_DEVICE_ID ||
|
||||
is5397_98(dev) || is63xx(dev))
|
||||
return 0;
|
||||
|
||||
/* IGMPv2 and MLDv1 reports use the group's destination MAC. Once
|
||||
* the first listener is offloaded, ordinary multicast forwarding
|
||||
* can keep subsequent reports from reaching the bridge CPU.
|
||||
*/
|
||||
|
||||
/* BCM5395 has a narrower register and a single IGMP classifier.
|
||||
* Its ICMPv6 classifier also covers MLD.
|
||||
*/
|
||||
if (dev->chip_id == BCM5395_DEVICE_ID) {
|
||||
ret = b53_read16(dev, B53_MGMT_PAGE, B53_HL_PRTC_CTRL, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val16 |= HL_PRTC_IGMP_IP_EN_5395 | HL_PRTC_ICMPV6_EN;
|
||||
/* Zero forwarding modes copy control traffic to the CPU
|
||||
* independently of the ARL lookup, retaining L2 forwarding.
|
||||
*/
|
||||
val16 &= ~(HL_PRTC_IGMP_FWD_MODE_5395 |
|
||||
HL_PRTC_ICMPV6_FWD_MODE);
|
||||
|
||||
return b53_write16(dev, B53_MGMT_PAGE, B53_HL_PRTC_CTRL,
|
||||
val16);
|
||||
}
|
||||
|
||||
ret = b53_read32(dev, B53_MGMT_PAGE, B53_HL_PRTC_CTRL, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val |= HL_PRTC_IGMP_RPTLVE_EN | HL_PRTC_IGMP_QRY_EN |
|
||||
HL_PRTC_IGMP_UKN_EN | HL_PRTC_MLD_RPTDONE_EN |
|
||||
HL_PRTC_MLD_QRY_EN;
|
||||
/* Zero forwarding modes copy control traffic to the CPU
|
||||
* independently of the ARL lookup, retaining L2 forwarding.
|
||||
*/
|
||||
val &= ~(HL_PRTC_IGMP_RPTLVE_FWD_MODE | HL_PRTC_IGMP_QRY_FWD_MODE |
|
||||
HL_PRTC_IGMP_UKN_FWD_MODE | HL_PRTC_MLD_RPTDONE_FWD_MODE |
|
||||
HL_PRTC_MLD_QRY_FWD_MODE);
|
||||
|
||||
return b53_write32(dev, B53_MGMT_PAGE, B53_HL_PRTC_CTRL, val);
|
||||
}
|
||||
EXPORT_SYMBOL(b53_enable_mcast_snooping);
|
||||
|
||||
static void b53_enable_cpu_port(struct b53_device *dev, int port)
|
||||
{
|
||||
u8 port_ctrl;
|
||||
|
|
@ -1334,6 +1387,10 @@ static int b53_setup(struct dsa_switch *ds)
|
|||
b53_disable_port(ds, port);
|
||||
}
|
||||
|
||||
ret = b53_enable_mcast_snooping(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return b53_setup_devlink_resources(ds);
|
||||
}
|
||||
|
||||
|
|
@ -1663,9 +1720,15 @@ static void b53_phylink_mac_link_up(struct phylink_config *config,
|
|||
}
|
||||
|
||||
if (mode == MLO_AN_FIXED) {
|
||||
/* Force flow control on BCM5301x's CPU port */
|
||||
if (is5301x(dev) && dsa_is_cpu_port(ds, port))
|
||||
tx_pause = rx_pause = true;
|
||||
/* Force flow control and 2Gbits/sec on BCM5301x and NSP CPU
|
||||
* ports
|
||||
*/
|
||||
if ((is5301x(dev) || dev->chip_id == BCM58XX_DEVICE_ID) &&
|
||||
dsa_is_cpu_port(ds, port)) {
|
||||
tx_pause = true;
|
||||
rx_pause = true;
|
||||
speed = 2000;
|
||||
}
|
||||
|
||||
b53_force_port_config(dev, port, speed, duplex,
|
||||
tx_pause, rx_pause);
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ static inline struct gpio_desc *b53_switch_get_reset_gpio(struct b53_device *dev
|
|||
/* Exported functions towards other drivers */
|
||||
void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port);
|
||||
int b53_configure_vlan(struct dsa_switch *ds);
|
||||
int b53_enable_mcast_snooping(struct b53_device *dev);
|
||||
void b53_get_strings(struct dsa_switch *ds, int port, u32 stringset,
|
||||
uint8_t *data);
|
||||
void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
|
||||
|
|
|
|||
|
|
@ -274,6 +274,23 @@
|
|||
/* Revision ID register (8 bit) */
|
||||
#define B53_REV_ID 0x40
|
||||
|
||||
/* High-level protocol control (16 bit on BCM5395, otherwise 32 bit) */
|
||||
#define B53_HL_PRTC_CTRL 0x50
|
||||
#define HL_PRTC_ICMPV6_EN BIT(4)
|
||||
#define HL_PRTC_ICMPV6_FWD_MODE BIT(5)
|
||||
#define HL_PRTC_IGMP_IP_EN_5395 BIT(6)
|
||||
#define HL_PRTC_IGMP_FWD_MODE_5395 BIT(8)
|
||||
#define HL_PRTC_IGMP_RPTLVE_EN BIT(9)
|
||||
#define HL_PRTC_IGMP_RPTLVE_FWD_MODE BIT(10)
|
||||
#define HL_PRTC_IGMP_QRY_EN BIT(11)
|
||||
#define HL_PRTC_IGMP_QRY_FWD_MODE BIT(12)
|
||||
#define HL_PRTC_IGMP_UKN_EN BIT(13)
|
||||
#define HL_PRTC_IGMP_UKN_FWD_MODE BIT(14)
|
||||
#define HL_PRTC_MLD_RPTDONE_EN BIT(15)
|
||||
#define HL_PRTC_MLD_RPTDONE_FWD_MODE BIT(16)
|
||||
#define HL_PRTC_MLD_QRY_EN BIT(17)
|
||||
#define HL_PRTC_MLD_QRY_FWD_MODE BIT(18)
|
||||
|
||||
/* Broadcom header RX control (16 bit) */
|
||||
#define B53_BRCM_HDR_RX_DIS 0x60
|
||||
|
||||
|
|
|
|||
|
|
@ -1059,6 +1059,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
|
|||
{
|
||||
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
||||
unsigned int port;
|
||||
int ret;
|
||||
|
||||
/* Enable all valid ports and disable those unused */
|
||||
for (port = 0; port < priv->hw_params.num_ports; port++) {
|
||||
|
|
@ -1074,6 +1075,10 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
|
|||
b53_configure_vlan(ds);
|
||||
bcm_sf2_enable_acb(ds);
|
||||
|
||||
ret = b53_enable_mcast_snooping(priv->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return b53_setup_devlink_resources(ds);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1934,6 +1934,39 @@ static int atl1c_clean_rx(struct napi_struct *napi, int budget)
|
|||
break;
|
||||
}
|
||||
atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
|
||||
|
||||
/* A link partner whose l2mtu is larger than ours can hand us a
|
||||
* frame that does not fit the buffer the hardware was given:
|
||||
* the descriptor reports the untruncated length while at most
|
||||
* buffer_info->length bytes were written. skb_put() of such a
|
||||
* length would pull in whatever the tail of the buffer holds
|
||||
* and, past the end of the skb, trips skb_over_panic and takes
|
||||
* the host down. Bound it by what the device actually wrote
|
||||
* rather than by skb_tailroom(): the skb carries 64 bytes of
|
||||
* slack for the DMA erratum, so the two are not the same.
|
||||
*
|
||||
* Frames shorter than an Ethernet header plus its FCS are
|
||||
* rejected by the same test; before this, a length below
|
||||
* ETH_FCS_LEN underflowed in skb_put().
|
||||
*/
|
||||
if (unlikely(length < ETH_HLEN + ETH_FCS_LEN ||
|
||||
length > buffer_info->length)) {
|
||||
dev_core_stats_rx_dropped_inc(netdev);
|
||||
if (netif_msg_rx_err(adapter))
|
||||
dev_warn_ratelimited(&pdev->dev,
|
||||
"dropped rx frame: %u bytes, buffer %u\n",
|
||||
length, buffer_info->length);
|
||||
kfree_skb_reason(skb, SKB_DROP_REASON_PKT_TOO_BIG);
|
||||
/* atl1c_clean_rfd() has already recycled the RFD, so
|
||||
* count the drop as work: the refill at the end of the
|
||||
* poll only runs when work_done is non-zero, and
|
||||
* sustained oversized traffic would otherwise drain the
|
||||
* ring and stop RX for good.
|
||||
*/
|
||||
work_done++;
|
||||
continue;
|
||||
}
|
||||
|
||||
skb_put(skb, length - ETH_FCS_LEN);
|
||||
skb->protocol = eth_type_trans(skb, netdev);
|
||||
atl1c_rx_checksum(adapter, skb, rrs);
|
||||
|
|
|
|||
|
|
@ -243,7 +243,8 @@ static int bgmac_probe(struct platform_device *pdev)
|
|||
bgmac->phy_connect = platform_phy_connect;
|
||||
} else {
|
||||
bgmac->phy_connect = bgmac_phy_connect_direct;
|
||||
bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500;
|
||||
if (of_machine_is_compatible("brcm,nsp"))
|
||||
bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500;
|
||||
}
|
||||
|
||||
return bgmac_enet_probe(bgmac);
|
||||
|
|
|
|||
|
|
@ -1430,11 +1430,15 @@ void bgmac_adjust_link(struct net_device *net_dev)
|
|||
{
|
||||
struct bgmac *bgmac = netdev_priv(net_dev);
|
||||
struct phy_device *phy_dev = net_dev->phydev;
|
||||
int speed = phy_dev->speed;
|
||||
bool update = false;
|
||||
|
||||
if (bgmac->feature_flags & BGMAC_FEAT_FORCE_SPEED_2500)
|
||||
speed = SPEED_2500;
|
||||
|
||||
if (phy_dev->link) {
|
||||
if (phy_dev->speed != bgmac->mac_speed) {
|
||||
bgmac->mac_speed = phy_dev->speed;
|
||||
if (speed != bgmac->mac_speed) {
|
||||
bgmac->mac_speed = speed;
|
||||
update = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11544,6 +11544,7 @@ static int bnxt_change_msix(struct bnxt *bp, int total)
|
|||
map.index = i - 1;
|
||||
map.virq = bp->irq_tbl[i - 1].vector;
|
||||
pci_msix_free_irq(bp->pdev, map);
|
||||
bp->irq_tbl[i - 1].vector = 0;
|
||||
bp->total_irqs--;
|
||||
}
|
||||
return bp->total_irqs;
|
||||
|
|
@ -11721,8 +11722,23 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init)
|
|||
int irqs_required;
|
||||
int rc;
|
||||
|
||||
if (!bnxt_need_reserve_rings(bp))
|
||||
if (!bnxt_need_reserve_rings(bp)) {
|
||||
/* FW may already hold the reservation from an earlier
|
||||
* reservation that was not followed by growing the dynamic
|
||||
* MSI-X vectors, e.g. because the allocation failed.
|
||||
*/
|
||||
if (irq_re_init && BNXT_NEW_RM(bp) && bp->irq_tbl &&
|
||||
pci_msix_can_alloc_dyn(bp->pdev)) {
|
||||
irqs_required = bnxt_get_num_msix(bp);
|
||||
if (irqs_required > bp->total_irqs &&
|
||||
bnxt_change_msix(bp, irqs_required) != irqs_required) {
|
||||
netdev_err(bp->dev, "Unable to allocate %d MSIX vectors, maximum available %d\n",
|
||||
irqs_required, bp->total_irqs);
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BNXT_NEW_RM(bp) && !bnxt_ulp_registered(edev)) {
|
||||
int ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want);
|
||||
|
|
@ -11748,6 +11764,10 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init)
|
|||
rc = bnxt_init_int_mode(bp);
|
||||
bnxt_ulp_irq_restart(bp, rc);
|
||||
} else if (irq_change && !rc) {
|
||||
/* Size the vectors from what FW actually reserved, which
|
||||
* can be less than what was requested.
|
||||
*/
|
||||
irqs_required = bnxt_get_num_msix(bp);
|
||||
if (bnxt_change_msix(bp, irqs_required) != irqs_required)
|
||||
rc = -ENOSPC;
|
||||
}
|
||||
|
|
@ -15113,6 +15133,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
|
|||
hwr.cp = min_t(int, hwr.cp, bnxt_get_max_func_irqs(bp));
|
||||
}
|
||||
if (hwr.cp > bp->total_irqs) {
|
||||
int old_total = bp->total_irqs;
|
||||
int total_msix = bnxt_change_msix(bp, hwr.cp);
|
||||
|
||||
if (total_msix < hwr.cp) {
|
||||
|
|
@ -15120,6 +15141,17 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
|
|||
hwr.cp, total_msix);
|
||||
rc = -ENOSPC;
|
||||
}
|
||||
/* FW has not reserved the new vectors yet, and writes to
|
||||
* MSI-X table entries beyond the reserved IRQs don't
|
||||
* stick (seen on BCM57508). Free them again;
|
||||
* bnxt_reserve_rings() allocates them after
|
||||
* __bnxt_reserve_rings(). Without the new resource
|
||||
* manager there is no IRQ reservation and
|
||||
* bnxt_reserve_rings() does not grow the vectors, so
|
||||
* keep them.
|
||||
*/
|
||||
if (BNXT_NEW_RM(bp))
|
||||
bnxt_change_msix(bp, old_total);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -1502,7 +1502,8 @@ jme_intr_msi(struct jme_adapter *jme, u32 intrstat)
|
|||
* all other events are ignored
|
||||
*/
|
||||
jwrite32(jme, JME_IEVE, intrstat);
|
||||
schedule_work(&jme->linkch_task);
|
||||
if (!test_bit(JME_FLAG_WOL_QUIESCE, &jme->flags))
|
||||
schedule_work(&jme->linkch_task);
|
||||
goto out_reenable;
|
||||
}
|
||||
|
||||
|
|
@ -1546,7 +1547,8 @@ jme_intr_msi(struct jme_adapter *jme, u32 intrstat)
|
|||
/*
|
||||
* Re-enable interrupt
|
||||
*/
|
||||
jwrite32f(jme, JME_IENS, INTR_ENABLE);
|
||||
if (!test_bit(JME_FLAG_WOL_QUIESCE, &jme->flags))
|
||||
jwrite32f(jme, JME_IENS, INTR_ENABLE);
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
|
|
@ -1892,6 +1894,7 @@ jme_powersave_phy(struct jme_adapter *jme)
|
|||
jme_set_100m_half(jme);
|
||||
if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
|
||||
jme_wait_link(jme);
|
||||
jme_mac_rxclk_on(jme);
|
||||
jme_clear_pm_enable_wol(jme);
|
||||
} else {
|
||||
jme_phy_off(jme);
|
||||
|
|
@ -3158,6 +3161,21 @@ jme_shutdown(struct pci_dev *pdev)
|
|||
struct net_device *netdev = pci_get_drvdata(pdev);
|
||||
struct jme_adapter *jme = netdev_priv(netdev);
|
||||
|
||||
if (netif_running(netdev)) {
|
||||
set_bit(JME_FLAG_WOL_QUIESCE, &jme->flags);
|
||||
netif_device_detach(netdev);
|
||||
netif_stop_queue(netdev);
|
||||
jme_stop_irq(jme);
|
||||
synchronize_irq(pdev->irq);
|
||||
jme_stop_irq(jme);
|
||||
cancel_work_sync(&jme->linkch_task);
|
||||
tasklet_disable(&jme->pcc_task);
|
||||
napi_disable(&jme->napi);
|
||||
tasklet_disable(&jme->txclean_task);
|
||||
tasklet_disable(&jme->rxclean_task);
|
||||
tasklet_disable(&jme->rxempty_task);
|
||||
}
|
||||
|
||||
jme_powersave_phy(jme);
|
||||
pci_pme_active(pdev, true);
|
||||
}
|
||||
|
|
@ -3172,11 +3190,16 @@ jme_suspend(struct device *dev)
|
|||
if (!netif_running(netdev))
|
||||
return 0;
|
||||
|
||||
atomic_dec(&jme->link_changing);
|
||||
|
||||
set_bit(JME_FLAG_WOL_QUIESCE, &jme->flags);
|
||||
netif_device_detach(netdev);
|
||||
netif_stop_queue(netdev);
|
||||
jme_stop_irq(jme);
|
||||
synchronize_irq(jme->pdev->irq);
|
||||
jme_stop_irq(jme);
|
||||
cancel_work_sync(&jme->linkch_task);
|
||||
tasklet_disable(&jme->pcc_task);
|
||||
napi_disable(&jme->napi);
|
||||
atomic_dec(&jme->link_changing);
|
||||
|
||||
tasklet_disable(&jme->txclean_task);
|
||||
tasklet_disable(&jme->rxclean_task);
|
||||
|
|
@ -3215,6 +3238,7 @@ jme_resume(struct device *dev)
|
|||
return 0;
|
||||
|
||||
jme_clear_pm_disable_wol(jme);
|
||||
jme_mac_rxclk_off(jme);
|
||||
jme_phy_on(jme);
|
||||
if (test_bit(JME_FLAG_SSET, &jme->flags))
|
||||
jme_set_link_ksettings(netdev, &jme->old_cmd);
|
||||
|
|
@ -3226,8 +3250,11 @@ jme_resume(struct device *dev)
|
|||
|
||||
atomic_inc(&jme->link_changing);
|
||||
|
||||
jme_reset_link(jme);
|
||||
napi_enable(&jme->napi);
|
||||
tasklet_enable(&jme->pcc_task);
|
||||
clear_bit(JME_FLAG_WOL_QUIESCE, &jme->flags);
|
||||
|
||||
jme_reset_link(jme);
|
||||
jme_start_irq(jme);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ enum jme_flags_bits {
|
|||
JME_FLAG_SSET = 2,
|
||||
JME_FLAG_POLL = 5,
|
||||
JME_FLAG_SHUTDOWN = 6,
|
||||
JME_FLAG_WOL_QUIESCE = 7,
|
||||
};
|
||||
|
||||
#define TX_TIMEOUT (5 * HZ)
|
||||
|
|
|
|||
|
|
@ -754,8 +754,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
|
|||
|
||||
hlist_for_each_entry_safe(entry, n, head, list) {
|
||||
if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
|
||||
if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
|
||||
MTK_FOE_STATE_BIND))
|
||||
if (entry->hash != hash)
|
||||
continue;
|
||||
|
||||
entry->hash = 0xffff;
|
||||
|
|
@ -764,7 +763,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
|
|||
}
|
||||
|
||||
if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
|
||||
if (entry->hash != 0xffff)
|
||||
if (entry->hash == hash)
|
||||
entry->hash = 0xffff;
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev)
|
|||
wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) {
|
||||
if (dev->rx_wdma[i].desc)
|
||||
if (!dev->rx_wdma[i].desc)
|
||||
continue;
|
||||
|
||||
wdma_w32(dev,
|
||||
|
|
@ -949,6 +949,14 @@ mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring)
|
|||
static void
|
||||
mtk_wed_free_rx_rings(struct mtk_wed_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->rx_ring); i++)
|
||||
mtk_wed_free_ring(dev, &dev->rx_ring[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
|
||||
mtk_wed_free_ring(dev, &dev->tx_wdma[i]);
|
||||
|
||||
mtk_wed_free_rx_buffer(dev);
|
||||
mtk_wed_free_ring(dev, &dev->rro.ring);
|
||||
}
|
||||
|
|
@ -1938,7 +1946,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
|
|||
wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0);
|
||||
wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0);
|
||||
|
||||
if (reset)
|
||||
if (!mtk_wed_is_v3_or_greater(dev->hw) && reset)
|
||||
mtk_wed_ring_reset(wdma, MTK_WED_WDMA_RING_SIZE, true);
|
||||
|
||||
if (!idx) {
|
||||
|
|
@ -2337,7 +2345,20 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
|
||||
if (!dev->rx_wdma[i].desc)
|
||||
mtk_wed_wdma_rx_ring_setup(dev, i, 16, false);
|
||||
if (mtk_wed_wdma_rx_ring_setup(dev, i, 16, false)) {
|
||||
dev_err(dev->hw->dev, "wdma rx ring %d setup failed\n", i);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* non-DBDC MT7986 allocates only rx_ring[1] and tx_wdma[1] during setup
|
||||
* but tx_wdma[0] is also needed for WED to function.
|
||||
*/
|
||||
if (mtk_wed_is_v2(dev->hw) && !dev->rx_ring[0].desc)
|
||||
if (mtk_wed_wdma_tx_ring_setup(dev, 0, MTK_WED_WDMA_RING_SIZE, !!dev->tx_wdma[0].desc)) {
|
||||
dev_err(dev->hw->dev, "wdma tx ring 0 setup failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->wlan.hw_rro) {
|
||||
for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ struct mlx5_fs_id {
|
|||
refcount_t refcnt;
|
||||
sci_t sci;
|
||||
struct rhash_head hash;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
struct mlx5_macsec_device {
|
||||
|
|
@ -684,7 +685,7 @@ static void macsec_fs_id_del(struct list_head *macsec_devices_list, u32 fs_id,
|
|||
|
||||
xa_erase(fs_id_xa, fs_id);
|
||||
|
||||
kfree(fs_id_found);
|
||||
kfree_rcu(fs_id_found, rcu);
|
||||
|
||||
if (macsec_fs_is_macsec_device_empty(macsec_device)) {
|
||||
list_del(&macsec_device->macsec_devices_list_entry);
|
||||
|
|
|
|||
|
|
@ -2072,7 +2072,7 @@ static int fbnic_alloc_rx_qt_resources(struct fbnic_net *fbn,
|
|||
|
||||
fbnic_bdq_set_page_size(&qt->sub1, rx_page_size);
|
||||
|
||||
err = xdp_rxq_info_reg(&qt->xdp_rxq, fbn->netdev, qt->sub0.q_idx,
|
||||
err = xdp_rxq_info_reg(&qt->xdp_rxq, fbn->netdev, qt->cmpl.q_idx,
|
||||
nv->napi.napi_id);
|
||||
if (err)
|
||||
goto free_page_pools;
|
||||
|
|
|
|||
|
|
@ -353,6 +353,12 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight)
|
|||
if (!fdma_db_is_done(db))
|
||||
continue;
|
||||
|
||||
/* XDP and page pool APIs can't be used with a budget of 0.
|
||||
* Leave these buffers for the next NAPI poll.
|
||||
*/
|
||||
if (!weight && !dcb_buf->use_skb)
|
||||
continue;
|
||||
|
||||
dcb_buf->dev->stats.tx_packets++;
|
||||
dcb_buf->dev->stats.tx_bytes += dcb_buf->len;
|
||||
|
||||
|
|
|
|||
|
|
@ -204,9 +204,10 @@ static void sparx5_ptp_classify(struct sparx5_port *port, struct sk_buff *skb,
|
|||
*rew_op = IFH_REW_OP_TWO_STEP_PTP;
|
||||
}
|
||||
|
||||
static void sparx5_ptp_txtstamp_old_release(struct sparx5_port *port)
|
||||
static unsigned int sparx5_ptp_txtstamp_old_release(struct sparx5_port *port)
|
||||
{
|
||||
struct sk_buff *skb, *skb_tmp;
|
||||
unsigned int purged = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->tx_skbs.lock, flags);
|
||||
|
|
@ -217,8 +218,11 @@ static void sparx5_ptp_txtstamp_old_release(struct sparx5_port *port)
|
|||
|
||||
__skb_unlink(skb, &port->tx_skbs);
|
||||
dev_kfree_skb_any(skb);
|
||||
purged++;
|
||||
}
|
||||
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
|
||||
|
||||
return purged;
|
||||
}
|
||||
|
||||
int sparx5_ptp_txtstamp_request(struct sparx5_port *port,
|
||||
|
|
@ -226,6 +230,7 @@ int sparx5_ptp_txtstamp_request(struct sparx5_port *port,
|
|||
{
|
||||
struct sparx5 *sparx5 = port->sparx5;
|
||||
u8 rew_op, pdu_type, pdu_w16_offset;
|
||||
unsigned int purged;
|
||||
unsigned long flags;
|
||||
|
||||
sparx5_ptp_classify(port, skb, &rew_op, &pdu_type, &pdu_w16_offset);
|
||||
|
|
@ -236,9 +241,10 @@ int sparx5_ptp_txtstamp_request(struct sparx5_port *port,
|
|||
if (rew_op != IFH_REW_OP_TWO_STEP_PTP)
|
||||
return 0;
|
||||
|
||||
sparx5_ptp_txtstamp_old_release(port);
|
||||
purged = sparx5_ptp_txtstamp_old_release(port);
|
||||
|
||||
spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags);
|
||||
sparx5->ptp_skbs -= purged;
|
||||
if (sparx5->ptp_skbs == SPARX5_MAX_PTP_ID) {
|
||||
spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags);
|
||||
return -EBUSY;
|
||||
|
|
|
|||
|
|
@ -757,6 +757,27 @@ void mana_gd_free_service_wq(struct gdma_context *gc)
|
|||
gc->service_wq = NULL;
|
||||
}
|
||||
|
||||
static void mana_gd_schedule_eq_cqs(struct gdma_queue *eq)
|
||||
{
|
||||
struct gdma_context *gc = eq->gdma_dev->gdma_context;
|
||||
struct gdma_queue *cq;
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < GDMA_EQ_MAX_CHILD_CQ; i++) {
|
||||
cq = rcu_dereference(eq->eq.child_cq[i]);
|
||||
|
||||
if (!cq)
|
||||
continue;
|
||||
|
||||
if (cq->id >= READ_ONCE(gc->max_num_cqs) ||
|
||||
!gc->cq_table || gc->cq_table[cq->id] != cq)
|
||||
continue;
|
||||
|
||||
if (cq->cq.callback)
|
||||
cq->cq.callback(cq->cq.context, cq);
|
||||
}
|
||||
}
|
||||
|
||||
static void mana_gd_process_eqe(struct gdma_queue *eq)
|
||||
{
|
||||
u32 head = eq->head % (eq->queue_size / GDMA_EQE_SIZE);
|
||||
|
|
@ -787,6 +808,14 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)
|
|||
|
||||
break;
|
||||
|
||||
case GDMA_EQE_THROTTLE:
|
||||
u64_stats_update_begin(&eq->eq.stats.syncp);
|
||||
u64_stats_inc(&eq->eq.stats.throttle_count);
|
||||
u64_stats_update_end(&eq->eq.stats.syncp);
|
||||
|
||||
mana_gd_schedule_eq_cqs(eq);
|
||||
break;
|
||||
|
||||
case GDMA_EQE_TEST_EVENT:
|
||||
gc->test_event_eq_id = eq->id;
|
||||
complete(&gc->eq_test_event);
|
||||
|
|
@ -1032,6 +1061,8 @@ static int mana_gd_create_eq(struct gdma_dev *gd,
|
|||
queue->eq.msix_index = INVALID_PCI_MSIX_INDEX;
|
||||
queue->id = INVALID_QUEUE_ID;
|
||||
|
||||
u64_stats_init(&queue->eq.stats.syncp);
|
||||
|
||||
log2_num_entries = ilog2(queue->queue_size / GDMA_EQE_SIZE);
|
||||
|
||||
if (spec->eq.log2_throttle_limit > log2_num_entries) {
|
||||
|
|
@ -1073,25 +1104,66 @@ static void mana_gd_create_cq(const struct gdma_queue_spec *spec,
|
|||
struct gdma_queue *queue)
|
||||
{
|
||||
u32 log2_num_entries = ilog2(spec->queue_size / GDMA_CQE_SIZE);
|
||||
struct gdma_queue *parent;
|
||||
u8 i;
|
||||
|
||||
queue->head |= INITIALIZED_OWNER_BIT(log2_num_entries);
|
||||
queue->cq.parent = spec->cq.parent_eq;
|
||||
parent = spec->cq.parent_eq;
|
||||
queue->cq.parent = parent;
|
||||
queue->cq.context = spec->cq.context;
|
||||
queue->cq.callback = spec->cq.callback;
|
||||
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
/* For throttled EQE store the child CQ */
|
||||
for (i = 0; i < GDMA_EQ_MAX_CHILD_CQ; i++) {
|
||||
if (!rcu_access_pointer(parent->eq.child_cq[i])) {
|
||||
rcu_assign_pointer(parent->eq.child_cq[i], queue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WARN_ONCE(1, "mana: EQ %u has no free child CQ slot\n", parent->id);
|
||||
}
|
||||
|
||||
/* Unlink queue from its parent EQ's child_cq[] and from gc->cq_table[].
|
||||
*
|
||||
* Return: true if queue was in the parent EQ's child_cq[]. In that case the
|
||||
* caller must wait for an RCU grace period before freeing queue, because
|
||||
* mana_gd_intr() may still be handling a throttle EQE for it.
|
||||
*/
|
||||
bool mana_gd_detach_cq(struct gdma_context *gc, struct gdma_queue *queue)
|
||||
{
|
||||
struct gdma_queue *eq = queue->cq.parent;
|
||||
bool attached = false;
|
||||
u32 id = queue->id;
|
||||
u8 i;
|
||||
|
||||
if (eq) {
|
||||
for (i = 0; i < GDMA_EQ_MAX_CHILD_CQ; i++) {
|
||||
if (rcu_access_pointer(eq->eq.child_cq[i]) == queue) {
|
||||
RCU_INIT_POINTER(eq->eq.child_cq[i], NULL);
|
||||
attached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if CQ might have been detached already, only clear if
|
||||
* it still points to same queue .
|
||||
*/
|
||||
if (id < gc->max_num_cqs && READ_ONCE(gc->cq_table[id]) == queue)
|
||||
WRITE_ONCE(gc->cq_table[id], NULL);
|
||||
|
||||
return attached;
|
||||
}
|
||||
|
||||
static void mana_gd_destroy_cq(struct gdma_context *gc,
|
||||
struct gdma_queue *queue)
|
||||
{
|
||||
u32 id = queue->id;
|
||||
|
||||
if (id >= gc->max_num_cqs)
|
||||
return;
|
||||
|
||||
if (!gc->cq_table[id])
|
||||
return;
|
||||
|
||||
gc->cq_table[id] = NULL;
|
||||
/* Wait for irq reader holding the cq */
|
||||
if (mana_gd_detach_cq(gc, queue))
|
||||
synchronize_net();
|
||||
}
|
||||
|
||||
int mana_gd_create_hwc_queue(struct gdma_dev *gd,
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ static void mana_hwc_comp_event(void *ctx, struct gdma_queue *q_self)
|
|||
|
||||
completions = hwc_cq->comp_buf;
|
||||
comp_read = mana_gd_poll_cq(q_self, completions, hwc_cq->queue_depth);
|
||||
WARN_ON_ONCE(comp_read <= 0 || comp_read > hwc_cq->queue_depth);
|
||||
WARN_ON_ONCE(comp_read < 0 || comp_read > hwc_cq->queue_depth);
|
||||
|
||||
for (i = 0; i < comp_read; ++i) {
|
||||
comp_data = *(struct hwc_rx_oob *)completions[i].cqe_data;
|
||||
|
|
@ -820,9 +820,14 @@ void mana_hwc_destroy_channel(struct gdma_context *gc)
|
|||
*/
|
||||
if (gc->max_num_cqs > 0) {
|
||||
mana_smc_teardown_hwc(&gc->shm_channel, false);
|
||||
gc->max_num_cqs = 0;
|
||||
WRITE_ONCE(gc->max_num_cqs, 0);
|
||||
}
|
||||
|
||||
if (hwc->cq)
|
||||
mana_gd_detach_cq(gc, hwc->cq->gdma_cq);
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
if (hwc->txq)
|
||||
mana_hwc_destroy_wq(hwc, hwc->txq);
|
||||
|
||||
|
|
|
|||
|
|
@ -2703,6 +2703,7 @@ static void mana_deinit_txq(struct mana_port_context *apc, struct mana_txq *txq)
|
|||
|
||||
static void mana_destroy_txq(struct mana_port_context *apc)
|
||||
{
|
||||
struct gdma_context *gc = apc->ac->gdma_dev->gdma_context;
|
||||
struct napi_struct *napi;
|
||||
int i;
|
||||
|
||||
|
|
@ -2721,8 +2722,6 @@ static void mana_destroy_txq(struct mana_port_context *apc)
|
|||
napi_synchronize(napi);
|
||||
napi_disable_locked(napi);
|
||||
cancel_work_sync(&apc->tx_qp[i]->tx_cq.dim.work);
|
||||
netif_napi_del_locked(napi);
|
||||
apc->tx_qp[i]->txq.napi_initialized = false;
|
||||
}
|
||||
|
||||
/* Forget the SKBs freed without being completed after a drain
|
||||
|
|
@ -2735,6 +2734,18 @@ static void mana_destroy_txq(struct mana_port_context *apc)
|
|||
if (apc->tx_qp[i]->tx_object != INVALID_MANA_HANDLE)
|
||||
mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i]->tx_object);
|
||||
|
||||
if (apc->tx_qp[i]->txq.napi_initialized) {
|
||||
/* Detach the CQ only after the WQ object is destroyed,
|
||||
* so a completion EQE that arrives before then still
|
||||
* finds the CQ in cq_table[] rather than hitting the
|
||||
* WARN in mana_gd_process_eqe(). One grace period then
|
||||
* covers both mana_gd_intr() and NAPI users.
|
||||
*/
|
||||
mana_gd_detach_cq(gc, apc->tx_qp[i]->tx_cq.gdma_cq);
|
||||
netif_napi_del_locked(napi);
|
||||
apc->tx_qp[i]->txq.napi_initialized = false;
|
||||
}
|
||||
|
||||
mana_deinit_cq(apc, &apc->tx_qp[i]->tx_cq);
|
||||
|
||||
mana_deinit_txq(apc, &apc->tx_qp[i]->txq);
|
||||
|
|
@ -2941,7 +2952,6 @@ static void mana_destroy_rxq(struct mana_port_context *apc,
|
|||
|
||||
napi_disable_locked(napi);
|
||||
cancel_work_sync(&rxq->rx_cq.dim.work);
|
||||
netif_napi_del_locked(napi);
|
||||
}
|
||||
|
||||
if (xdp_rxq_info_is_reg(&rxq->xdp_rxq))
|
||||
|
|
@ -2950,6 +2960,12 @@ static void mana_destroy_rxq(struct mana_port_context *apc,
|
|||
if (rxq->rxobj != INVALID_MANA_HANDLE)
|
||||
mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj);
|
||||
|
||||
if (napi_initialized) {
|
||||
/* See the comment in mana_destroy_txq() */
|
||||
mana_gd_detach_cq(gc, rxq->rx_cq.gdma_cq);
|
||||
netif_napi_del_locked(napi);
|
||||
}
|
||||
|
||||
mana_deinit_cq(apc, &rxq->rx_cq);
|
||||
|
||||
if (rxq->xdp_save_va)
|
||||
|
|
|
|||
|
|
@ -147,7 +147,8 @@ static int mana_get_sset_count(struct net_device *ndev, int stringset)
|
|||
return ARRAY_SIZE(mana_eth_stats) +
|
||||
ARRAY_SIZE(mana_phy_stats) +
|
||||
ARRAY_SIZE(mana_hc_stats) +
|
||||
num_queues * (MANA_STATS_RX_COUNT + MANA_STATS_TX_COUNT);
|
||||
num_queues * (MANA_STATS_RX_COUNT + MANA_STATS_TX_COUNT +
|
||||
MANA_STATS_EQ_COUNT);
|
||||
|
||||
case ETH_SS_PRIV_FLAGS:
|
||||
return MANA_PRIV_FLAG_MAX;
|
||||
|
|
@ -198,6 +199,9 @@ static void mana_get_strings_stats(struct mana_port_context *apc, u8 **data)
|
|||
ethtool_sprintf(data, "tx_%d_csum_partial", i);
|
||||
ethtool_sprintf(data, "tx_%d_mana_map_err", i);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_queues; i++)
|
||||
ethtool_sprintf(data, "eq_%d_throttle_events", i);
|
||||
}
|
||||
|
||||
static void mana_get_strings_priv_flags(u8 **data)
|
||||
|
|
@ -234,6 +238,7 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
|
|||
void *phy_stats = &apc->phy_stats;
|
||||
struct mana_stats_rx *rx_stats;
|
||||
struct mana_stats_tx *tx_stats;
|
||||
struct gdma_eq_stats *eq_stats;
|
||||
unsigned int start;
|
||||
u64 packets, bytes;
|
||||
u64 xdp_redirect;
|
||||
|
|
@ -251,6 +256,7 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
|
|||
u64 csum_partial;
|
||||
u64 mana_map_err;
|
||||
int q, i = 0, j;
|
||||
u64 throttled;
|
||||
|
||||
if (!apc->port_is_up)
|
||||
return;
|
||||
|
|
@ -325,6 +331,17 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
|
|||
data[i++] = csum_partial;
|
||||
data[i++] = mana_map_err;
|
||||
}
|
||||
|
||||
for (q = 0; q < num_queues; q++) {
|
||||
eq_stats = &apc->eqs[q].eq->eq.stats;
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&eq_stats->syncp);
|
||||
throttled = u64_stats_read(&eq_stats->throttle_count);
|
||||
} while (u64_stats_fetch_retry(&eq_stats->syncp, start));
|
||||
|
||||
data[i++] = throttled;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 mana_get_rx_ring_count(struct net_device *ndev)
|
||||
|
|
|
|||
|
|
@ -600,11 +600,11 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr))
|
||||
if (napi_schedule_prep(&cp->napi)) {
|
||||
cpw16_f(IntrMask, cp_norx_intr_mask);
|
||||
if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) {
|
||||
cpw16_f(IntrMask, cp_norx_intr_mask);
|
||||
if (napi_schedule_prep(&cp->napi))
|
||||
__napi_schedule(&cp->napi);
|
||||
}
|
||||
}
|
||||
|
||||
if (status & (TxOK | TxErr | TxEmpty | SWInt))
|
||||
cp_tx(cp);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pps_kernel.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include "net_driver.h"
|
||||
|
|
@ -266,11 +267,12 @@ struct efx_ptp_rxfilter {
|
|||
* @evt_frag_idx: Current fragment number
|
||||
* @evt_code: Last event code
|
||||
* @start: Address at which MC indicates ready for synchronisation
|
||||
* @host_time_pps: Host time at last PPS
|
||||
* @adjfreq_ppb_shift: Shift required to convert scaled parts-per-billion
|
||||
* frequency adjustment into a fixed point fractional nanosecond format.
|
||||
* @current_adjfreq: Current ppb adjustment.
|
||||
* @phc_clock: Pointer to registered phc device (if primary function)
|
||||
* @phc_lock: Serializes PHC commands and synchronization, and protects
|
||||
* @current_adjfreq
|
||||
* @phc_clock_info: Registration structure for phc device
|
||||
* @pps_work: pps work task for handling pps events
|
||||
* @pps_workwq: pps work queue
|
||||
|
|
@ -326,10 +328,10 @@ struct efx_ptp_data {
|
|||
int evt_frag_idx;
|
||||
int evt_code;
|
||||
struct efx_buffer start;
|
||||
struct pps_event_time host_time_pps;
|
||||
unsigned int adjfreq_ppb_shift;
|
||||
s64 current_adjfreq;
|
||||
struct ptp_clock *phc_clock;
|
||||
struct mutex phc_lock;
|
||||
struct ptp_clock_info phc_clock_info;
|
||||
struct work_struct pps_work;
|
||||
struct workqueue_struct *pps_workwq;
|
||||
|
|
@ -907,7 +909,8 @@ static void efx_ptp_read_timeset(MCDI_DECLARE_STRUCT_PTR(data),
|
|||
static int
|
||||
efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
|
||||
size_t response_length,
|
||||
const struct pps_event_time *last_time)
|
||||
const struct pps_event_time *last_time,
|
||||
struct pps_event_time *host_time_pps)
|
||||
{
|
||||
unsigned number_readings =
|
||||
MCDI_VAR_ARRAY_LEN(response_length,
|
||||
|
|
@ -985,6 +988,10 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
|
|||
"PTP bad synchronisation seconds\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!host_time_pps)
|
||||
return 0;
|
||||
|
||||
delta.tv_sec = (last_sec - start_sec) & 1;
|
||||
delta.tv_nsec =
|
||||
last_time->ts_real.tv_nsec -
|
||||
|
|
@ -1001,14 +1008,22 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
|
|||
delta.tv_nsec += ktime_to_timespec64(mc_time).tv_nsec;
|
||||
|
||||
/* Set PPS timestamp to match NIC top of second */
|
||||
ptp->host_time_pps = *last_time;
|
||||
pps_sub_ts(&ptp->host_time_pps, delta);
|
||||
*host_time_pps = *last_time;
|
||||
pps_sub_ts(host_time_pps, delta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Synchronize times between the host and the MC */
|
||||
static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
|
||||
/* Synchronize times between the host and the MC.
|
||||
*
|
||||
* The MC serializes PTP requests, so MC_CMD_PTP_OP_ADJUST cannot be applied
|
||||
* while MC_CMD_PTP_OP_SYNCHRONIZE is collecting timesets. This holds across
|
||||
* functions of a controller, which share the clock but adjust it only through
|
||||
* the function that registered it, so a secondary function can capture here
|
||||
* without serializing against the primary.
|
||||
*/
|
||||
static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings,
|
||||
struct pps_event_time *host_time_pps)
|
||||
{
|
||||
struct efx_ptp_data *ptp = efx->ptp_data;
|
||||
MCDI_DECLARE_BUF(synch_buf, MC_CMD_PTP_OUT_SYNCHRONIZE_LENMAX);
|
||||
|
|
@ -1026,11 +1041,14 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
|
|||
MCDI_SET_QWORD(synch_buf, PTP_IN_SYNCHRONIZE_START_ADDR,
|
||||
ptp->start.dma_addr);
|
||||
|
||||
mutex_lock(&ptp->phc_lock);
|
||||
|
||||
/* Clear flag that signals MC ready */
|
||||
WRITE_ONCE(*start, 0);
|
||||
rc = efx_mcdi_rpc_start(efx, MC_CMD_PTP, synch_buf,
|
||||
MC_CMD_PTP_IN_SYNCHRONIZE_LEN);
|
||||
EFX_WARN_ON_ONCE_PARANOID(rc);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Wait for start from MCDI (or timeout) */
|
||||
timeout = jiffies + msecs_to_jiffies(MAX_SYNCHRONISE_WAIT_MS);
|
||||
|
|
@ -1054,18 +1072,20 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)
|
|||
&response_length);
|
||||
if (rc == 0) {
|
||||
rc = efx_ptp_process_times(efx, synch_buf, response_length,
|
||||
&last_time);
|
||||
&last_time, host_time_pps);
|
||||
if (rc == 0)
|
||||
++ptp->good_syncs;
|
||||
else
|
||||
++ptp->no_time_syncs;
|
||||
}
|
||||
|
||||
out:
|
||||
/* Increment the bad syncs counter if the synchronize fails, whatever
|
||||
* the reason.
|
||||
*/
|
||||
if (rc != 0)
|
||||
++ptp->bad_syncs;
|
||||
mutex_unlock(&ptp->phc_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -1445,7 +1465,6 @@ static int efx_ptp_start(struct efx_nic *efx)
|
|||
goto fail;
|
||||
|
||||
ptp->evt_frag_idx = 0;
|
||||
ptp->current_adjfreq = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -1485,14 +1504,15 @@ static void efx_ptp_pps_worker(struct work_struct *work)
|
|||
{
|
||||
struct efx_ptp_data *ptp =
|
||||
container_of(work, struct efx_ptp_data, pps_work);
|
||||
struct pps_event_time host_time_pps = {};
|
||||
struct efx_nic *efx = ptp->efx;
|
||||
struct ptp_clock_event ptp_evt;
|
||||
|
||||
if (efx_ptp_synchronize(efx, PTP_SYNC_ATTEMPTS))
|
||||
if (efx_ptp_synchronize(efx, PTP_SYNC_ATTEMPTS, &host_time_pps))
|
||||
return;
|
||||
|
||||
ptp_evt.type = PTP_CLOCK_PPSUSR;
|
||||
ptp_evt.pps_times = ptp->host_time_pps;
|
||||
ptp_evt.pps_times = host_time_pps;
|
||||
ptp_clock_event(ptp->phc_clock, &ptp_evt);
|
||||
}
|
||||
|
||||
|
|
@ -1569,6 +1589,7 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
|
|||
if (!efx->ptp_data)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ptp->phc_lock);
|
||||
ptp->efx = efx;
|
||||
ptp->channel = channel;
|
||||
|
||||
|
|
@ -1640,6 +1661,7 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
|
|||
efx_nic_free_buffer(efx, &ptp->start);
|
||||
|
||||
fail1:
|
||||
mutex_destroy(&ptp->phc_lock);
|
||||
kfree(efx->ptp_data);
|
||||
efx->ptp_data = NULL;
|
||||
|
||||
|
|
@ -1694,6 +1716,7 @@ void efx_ptp_remove(struct efx_nic *efx)
|
|||
destroy_workqueue(efx->ptp_data->workwq);
|
||||
|
||||
efx_nic_free_buffer(efx, &efx->ptp_data->start);
|
||||
mutex_destroy(&efx->ptp_data->phc_lock);
|
||||
kfree(efx->ptp_data);
|
||||
efx->ptp_data = NULL;
|
||||
}
|
||||
|
|
@ -1825,7 +1848,8 @@ int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
|
|||
rc = efx_ptp_start(efx);
|
||||
if (rc == 0) {
|
||||
rc = efx_ptp_synchronize(efx,
|
||||
PTP_SYNC_ATTEMPTS * 2);
|
||||
PTP_SYNC_ATTEMPTS * 2,
|
||||
NULL);
|
||||
if (rc != 0)
|
||||
efx_ptp_stop(efx);
|
||||
}
|
||||
|
|
@ -2105,23 +2129,23 @@ static int efx_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
MCDI_SET_QWORD(inadj, PTP_IN_ADJUST_FREQ, adjustment_ns);
|
||||
MCDI_SET_DWORD(inadj, PTP_IN_ADJUST_SECONDS, 0);
|
||||
MCDI_SET_DWORD(inadj, PTP_IN_ADJUST_NANOSECONDS, 0);
|
||||
mutex_lock(&ptp_data->phc_lock);
|
||||
rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inadj, sizeof(inadj),
|
||||
NULL, 0, NULL);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
ptp_data->current_adjfreq = adjustment_ns;
|
||||
return 0;
|
||||
if (!rc)
|
||||
ptp_data->current_adjfreq = adjustment_ns;
|
||||
mutex_unlock(&ptp_data->phc_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
static int _efx_phc_adjtime(struct efx_ptp_data *ptp_data, s64 delta)
|
||||
__must_hold(&ptp_data->phc_lock)
|
||||
{
|
||||
u32 nic_major, nic_minor;
|
||||
struct efx_ptp_data *ptp_data = container_of(ptp,
|
||||
struct efx_ptp_data,
|
||||
phc_clock_info);
|
||||
struct efx_nic *efx = ptp_data->efx;
|
||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_ADJUST_LEN);
|
||||
struct efx_nic *efx = ptp_data->efx;
|
||||
u32 nic_major, nic_minor;
|
||||
|
||||
lockdep_assert_held(&ptp_data->phc_lock);
|
||||
|
||||
efx->ptp_data->ns_to_nic_time(delta, &nic_major, &nic_minor);
|
||||
|
||||
|
|
@ -2134,6 +2158,18 @@ static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|||
NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
struct efx_ptp_data *ptp_data = container_of(ptp, struct efx_ptp_data,
|
||||
phc_clock_info);
|
||||
int rc;
|
||||
|
||||
mutex_lock(&ptp_data->phc_lock);
|
||||
rc = _efx_phc_adjtime(ptp_data, delta);
|
||||
mutex_unlock(&ptp_data->phc_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
||||
{
|
||||
struct efx_ptp_data *ptp_data = container_of(ptp,
|
||||
|
|
@ -2165,23 +2201,21 @@ static int efx_phc_settime(struct ptp_clock_info *ptp,
|
|||
{
|
||||
/* Get the current NIC time, efx_phc_gettime.
|
||||
* Subtract from the desired time to get the offset
|
||||
* call efx_phc_adjtime with the offset
|
||||
* call _efx_phc_adjtime with the offset
|
||||
*/
|
||||
struct efx_ptp_data *ptp_data = container_of(ptp, struct efx_ptp_data,
|
||||
phc_clock_info);
|
||||
struct timespec64 time_now, delta;
|
||||
int rc;
|
||||
struct timespec64 time_now;
|
||||
struct timespec64 delta;
|
||||
|
||||
mutex_lock(&ptp_data->phc_lock);
|
||||
rc = efx_phc_gettime(ptp, &time_now);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
delta = timespec64_sub(*e_ts, time_now);
|
||||
|
||||
rc = efx_phc_adjtime(ptp, timespec64_to_ns(&delta));
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
if (!rc) {
|
||||
delta = timespec64_sub(*e_ts, time_now);
|
||||
rc = _efx_phc_adjtime(ptp_data, timespec64_to_ns(&delta));
|
||||
}
|
||||
mutex_unlock(&ptp_data->phc_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int efx_phc_enable(struct ptp_clock_info *ptp,
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ static int enh_desc_get_rx_status(struct stmmac_extra_stats *x,
|
|||
|
||||
if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
|
||||
x->rx_length++;
|
||||
return discard_frame;
|
||||
return discard_frame | rx_not_ls;
|
||||
}
|
||||
|
||||
if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
|
||||
|
|
|
|||
|
|
@ -620,7 +620,7 @@ struct stmmac_mmc_ops {
|
|||
|
||||
struct stmmac_est_ops {
|
||||
int (*configure)(struct stmmac_priv *priv, struct stmmac_est *cfg,
|
||||
unsigned int ptp_rate);
|
||||
unsigned int ptp_rate, bool enable);
|
||||
void (*irq_status)(struct stmmac_priv *priv, struct net_device *dev,
|
||||
struct stmmac_extra_stats *x, u32 txqcnt);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ static int ndesc_get_rx_status(struct stmmac_extra_stats *x,
|
|||
|
||||
if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
|
||||
x->rx_length++;
|
||||
return discard_frame;
|
||||
return discard_frame | rx_not_ls;
|
||||
}
|
||||
|
||||
if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ struct stmmac_est {
|
|||
u32 gcl[EST_GCL];
|
||||
u32 gcl_size;
|
||||
u32 max_sdu[MTL_MAX_TX_QUEUES];
|
||||
u32 pclass;
|
||||
};
|
||||
|
||||
struct stmmac_msi {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ static int est_write(void __iomem *est_addr, u32 reg, u32 val, bool gcl)
|
|||
}
|
||||
|
||||
static int est_configure(struct stmmac_priv *priv, struct stmmac_est *cfg,
|
||||
unsigned int ptp_rate)
|
||||
unsigned int ptp_rate, bool enable)
|
||||
{
|
||||
void __iomem *est_addr = priv->estaddr;
|
||||
int i, ret = 0;
|
||||
|
|
@ -62,7 +62,7 @@ static int est_configure(struct stmmac_priv *priv, struct stmmac_est *cfg,
|
|||
ctrl |= ((NSEC_PER_SEC / ptp_rate) * EST_GMAC5_PTOV_MUL) <<
|
||||
EST_GMAC5_PTOV_SHIFT;
|
||||
}
|
||||
if (cfg->enable)
|
||||
if (enable)
|
||||
ctrl |= EST_EEST | EST_SSWL | EST_DFBS;
|
||||
else
|
||||
ctrl &= ~EST_EEST;
|
||||
|
|
@ -70,7 +70,7 @@ static int est_configure(struct stmmac_priv *priv, struct stmmac_est *cfg,
|
|||
writel(ctrl, est_addr + EST_CONTROL);
|
||||
|
||||
/* Configure EST interrupt */
|
||||
if (cfg->enable)
|
||||
if (enable)
|
||||
ctrl = EST_IECGCE | EST_IEHS | EST_IEHF | EST_IEBE | EST_IECC;
|
||||
else
|
||||
ctrl = 0;
|
||||
|
|
@ -80,6 +80,61 @@ static int est_configure(struct stmmac_priv *priv, struct stmmac_est *cfg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __stmmac_setup_est(struct stmmac_priv *priv)
|
||||
{
|
||||
struct timespec64 current_time, time;
|
||||
ktime_t current_time_ns, basetime;
|
||||
u64 cycle_time;
|
||||
int err;
|
||||
|
||||
lockdep_assert_held(&priv->est_lock);
|
||||
|
||||
priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops, ¤t_time,
|
||||
NULL);
|
||||
current_time_ns = timespec64_to_ktime(current_time);
|
||||
|
||||
time.tv_nsec = priv->est->btr_reserve[0];
|
||||
time.tv_sec = priv->est->btr_reserve[1];
|
||||
basetime = timespec64_to_ktime(time);
|
||||
|
||||
cycle_time = (u64)priv->est->ctr[1] * NSEC_PER_SEC + priv->est->ctr[0];
|
||||
|
||||
time = stmmac_calc_tas_basetime(basetime, current_time_ns, cycle_time);
|
||||
priv->est->btr[0] = (u32)time.tv_nsec;
|
||||
priv->est->btr[1] = (u32)time.tv_sec;
|
||||
|
||||
err = stmmac_est_configure(priv, priv, priv->est,
|
||||
priv->plat->clk_ptp_rate, true);
|
||||
if (err) {
|
||||
netdev_err(priv->dev, "failed to re-configure EST\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stmmac_setup_est(struct stmmac_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!priv->ptp_enabled)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&priv->est_lock);
|
||||
if (priv->est && priv->est->enable) {
|
||||
ret = __stmmac_setup_est(priv);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = stmmac_fpe_map_preemption_class(priv, priv->dev, NULL,
|
||||
priv->est->pclass);
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&priv->est_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void est_irq_status(struct stmmac_priv *priv, struct net_device *dev,
|
||||
struct stmmac_extra_stats *x, u32 txqcnt)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
* stmmac EST(802.3 Qbv) handling
|
||||
*/
|
||||
|
||||
#ifndef __STMMAC_EST_H__
|
||||
#define __STMMAC_EST_H__
|
||||
|
||||
#define EST_GMAC4_OFFSET 0x00000c50
|
||||
#define EST_XGMAC_OFFSET 0x00001050
|
||||
|
||||
|
|
@ -65,3 +68,8 @@
|
|||
#define EST_GCL_DATA 0x00000034
|
||||
|
||||
extern const struct stmmac_est_ops dwmac510_est_ops;
|
||||
|
||||
int __stmmac_setup_est(struct stmmac_priv *priv);
|
||||
int stmmac_setup_est(struct stmmac_priv *priv);
|
||||
|
||||
#endif /* __STMMAC_EST_H__ */
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#include "stmmac_ptp.h"
|
||||
#include "stmmac_fpe.h"
|
||||
#include "stmmac.h"
|
||||
#include "stmmac_est.h"
|
||||
#include "stmmac_pcs.h"
|
||||
#include "stmmac_xdp.h"
|
||||
#include <linux/reset.h>
|
||||
|
|
@ -2182,6 +2183,9 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
|
|||
else
|
||||
dma_free_rx_skbufs(priv, dma_conf, queue);
|
||||
|
||||
dev_kfree_skb_any(rx_q->state.skb);
|
||||
rx_q->state.skb = NULL;
|
||||
rx_q->state_saved = false;
|
||||
rx_q->buf_alloc_num = 0;
|
||||
rx_q->xsk_pool = NULL;
|
||||
|
||||
|
|
@ -4201,6 +4205,10 @@ static int __stmmac_open(struct net_device *dev,
|
|||
if (ret)
|
||||
goto ptp_error;
|
||||
|
||||
ret = stmmac_setup_est(priv);
|
||||
if (ret)
|
||||
goto est_error;
|
||||
|
||||
stmmac_init_coalesce(priv);
|
||||
|
||||
phylink_start(priv->phylink);
|
||||
|
|
@ -4224,7 +4232,7 @@ static int __stmmac_open(struct net_device *dev,
|
|||
|
||||
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
|
||||
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
|
||||
|
||||
est_error:
|
||||
stmmac_release_ptp(priv);
|
||||
ptp_error:
|
||||
stmmac_stop_all_dma(priv);
|
||||
|
|
@ -5099,22 +5107,35 @@ static netdev_features_t stmmac_features_check(struct sk_buff *skb,
|
|||
return vlan_features_check(skb, features);
|
||||
}
|
||||
|
||||
static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
|
||||
static int stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct vlan_ethhdr *veth = skb_vlan_eth_hdr(skb);
|
||||
__be16 vlan_proto = veth->h_vlan_proto;
|
||||
u16 vlanid;
|
||||
struct vlan_ethhdr *veth;
|
||||
|
||||
if ((vlan_proto == htons(ETH_P_8021Q) &&
|
||||
if (!pskb_may_pull(skb, ETH_HLEN))
|
||||
return -EINVAL;
|
||||
|
||||
veth = skb_vlan_eth_hdr(skb);
|
||||
if ((veth->h_vlan_proto == htons(ETH_P_8021Q) &&
|
||||
dev->features & NETIF_F_HW_VLAN_CTAG_RX) ||
|
||||
(vlan_proto == htons(ETH_P_8021AD) &&
|
||||
(veth->h_vlan_proto == htons(ETH_P_8021AD) &&
|
||||
dev->features & NETIF_F_HW_VLAN_STAG_RX)) {
|
||||
__be16 vlan_proto = veth->h_vlan_proto;
|
||||
u8 meta_len = skb_metadata_len(skb);
|
||||
u16 vlanid;
|
||||
|
||||
/* pop the vlan tag */
|
||||
if (!pskb_may_pull(skb, VLAN_ETH_HLEN))
|
||||
return -EINVAL;
|
||||
|
||||
veth = skb_vlan_eth_hdr(skb);
|
||||
vlanid = ntohs(veth->h_vlan_TCI);
|
||||
memmove(skb->data + VLAN_HLEN, veth, ETH_ALEN * 2);
|
||||
memmove(skb->data - meta_len + VLAN_HLEN,
|
||||
skb->data - meta_len, meta_len + ETH_ALEN * 2);
|
||||
skb_pull(skb, VLAN_HLEN);
|
||||
__vlan_hwaccel_put_tag(skb, vlan_proto, vlanid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool stmmac_rx_need_ic(struct stmmac_priv *priv,
|
||||
|
|
@ -5516,7 +5537,8 @@ static struct sk_buff *stmmac_construct_skb_zc(struct stmmac_channel *ch,
|
|||
return NULL;
|
||||
|
||||
skb_reserve(skb, xdp->data - xdp->data_hard_start);
|
||||
memcpy(__skb_put(skb, datasize), xdp->data, datasize);
|
||||
__skb_put(skb, datasize);
|
||||
memcpy(skb->data - metasize, xdp->data_meta, datasize + metasize);
|
||||
if (metasize)
|
||||
skb_metadata_set(skb, metasize);
|
||||
|
||||
|
|
@ -5542,12 +5564,15 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
|
|||
}
|
||||
|
||||
stmmac_get_rx_hwtstamp(priv, p, np, skb);
|
||||
if (priv->hw->hw_vlan_en)
|
||||
if (priv->hw->hw_vlan_en) {
|
||||
/* MAC level stripping. */
|
||||
stmmac_rx_hw_vlan(priv, priv->hw, p, skb);
|
||||
else
|
||||
/* Driver level stripping. */
|
||||
stmmac_rx_vlan(priv->dev, skb);
|
||||
} else if (stmmac_rx_vlan(priv->dev, skb)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
priv->xstats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
skb->protocol = eth_type_trans(skb, priv->dev);
|
||||
|
||||
if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb))
|
||||
|
|
@ -5629,12 +5654,12 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
unsigned int count = 0, error = 0, len = 0;
|
||||
int dirty = stmmac_rx_dirty(priv, queue);
|
||||
unsigned int next_entry = rx_q->cur_rx;
|
||||
bool in_progress = rx_q->state_saved;
|
||||
u32 rx_errors = 0, rx_dropped = 0;
|
||||
unsigned int desc_size;
|
||||
struct bpf_prog *prog;
|
||||
bool failure = false;
|
||||
int xdp_status = 0;
|
||||
int status = 0;
|
||||
|
||||
if (netif_msg_rx_status(priv)) {
|
||||
void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);
|
||||
|
|
@ -5645,23 +5670,25 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
|
||||
rx_q->dma_rx_phy, desc_size);
|
||||
}
|
||||
|
||||
if (rx_q->state_saved) {
|
||||
error = rx_q->state.error;
|
||||
len = rx_q->state.len;
|
||||
rx_q->state_saved = false;
|
||||
}
|
||||
|
||||
while (count < limit) {
|
||||
struct stmmac_rx_buffer *buf;
|
||||
struct stmmac_xdp_buff *ctx;
|
||||
unsigned int buf1_len = 0;
|
||||
struct dma_desc *np, *p;
|
||||
int entry;
|
||||
int entry, status;
|
||||
int res;
|
||||
|
||||
if (!count && rx_q->state_saved) {
|
||||
error = rx_q->state.error;
|
||||
len = rx_q->state.len;
|
||||
} else {
|
||||
rx_q->state_saved = false;
|
||||
if (!in_progress) {
|
||||
error = 0;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
read_again:
|
||||
if (count >= limit)
|
||||
break;
|
||||
|
|
@ -5700,6 +5727,8 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
if (!buf->xdp)
|
||||
break;
|
||||
|
||||
in_progress = status & rx_not_ls;
|
||||
|
||||
if (priv->extend_desc) {
|
||||
/* Read the extended status after the own bit */
|
||||
dma_rmb();
|
||||
|
|
@ -5707,18 +5736,21 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
&rx_q->dma_erx[entry],
|
||||
status);
|
||||
}
|
||||
if (unlikely(status == discard_frame)) {
|
||||
xsk_buff_free(buf->xdp);
|
||||
buf->xdp = NULL;
|
||||
dirty++;
|
||||
|
||||
if (unlikely(!error && (status & discard_frame))) {
|
||||
error = 1;
|
||||
if (!priv->hwts_rx_en)
|
||||
rx_errors++;
|
||||
}
|
||||
|
||||
if (unlikely(error && (status & rx_not_ls)))
|
||||
goto read_again;
|
||||
if (unlikely(error)) {
|
||||
xsk_buff_free(buf->xdp);
|
||||
buf->xdp = NULL;
|
||||
dirty++;
|
||||
|
||||
if (status & rx_not_ls)
|
||||
goto read_again;
|
||||
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -5727,8 +5759,9 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
if (likely(status & rx_not_ls)) {
|
||||
xsk_buff_free(buf->xdp);
|
||||
buf->xdp = NULL;
|
||||
rx_dropped++;
|
||||
error = 1;
|
||||
dirty++;
|
||||
count++;
|
||||
goto read_again;
|
||||
}
|
||||
|
||||
|
|
@ -5776,7 +5809,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
count++;
|
||||
}
|
||||
|
||||
if (status & rx_not_ls) {
|
||||
if (in_progress) {
|
||||
rx_q->state_saved = true;
|
||||
rx_q->state.error = error;
|
||||
rx_q->state.len = len;
|
||||
|
|
@ -5818,9 +5851,10 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
|
||||
struct stmmac_channel *ch = &priv->channel[queue];
|
||||
unsigned int count = 0, error = 0, len = 0;
|
||||
int status = 0, coe = priv->hw->rx_csum;
|
||||
unsigned int next_entry = rx_q->cur_rx;
|
||||
bool in_progress = rx_q->state_saved;
|
||||
enum dma_data_direction dma_dir;
|
||||
int coe = priv->hw->rx_csum;
|
||||
unsigned int desc_size;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct stmmac_xdp_buff ctx;
|
||||
|
|
@ -5840,25 +5874,28 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
|
||||
rx_q->dma_rx_phy, desc_size);
|
||||
}
|
||||
|
||||
if (rx_q->state_saved) {
|
||||
skb = rx_q->state.skb;
|
||||
error = rx_q->state.error;
|
||||
len = rx_q->state.len;
|
||||
rx_q->state.skb = NULL;
|
||||
rx_q->state_saved = false;
|
||||
}
|
||||
|
||||
while (count < limit) {
|
||||
unsigned int buf1_len = 0, buf2_len = 0;
|
||||
enum pkt_hash_types hash_type;
|
||||
struct stmmac_rx_buffer *buf;
|
||||
struct dma_desc *np, *p;
|
||||
int entry;
|
||||
int entry, status;
|
||||
u32 hash;
|
||||
|
||||
if (!count && rx_q->state_saved) {
|
||||
skb = rx_q->state.skb;
|
||||
error = rx_q->state.error;
|
||||
len = rx_q->state.len;
|
||||
} else {
|
||||
rx_q->state_saved = false;
|
||||
if (!in_progress) {
|
||||
skb = NULL;
|
||||
error = 0;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
read_again:
|
||||
if (count >= limit)
|
||||
break;
|
||||
|
|
@ -5887,6 +5924,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
|
||||
prefetch(np);
|
||||
|
||||
in_progress = status & rx_not_ls;
|
||||
|
||||
if (priv->extend_desc) {
|
||||
/* Read the extended status after the own bit */
|
||||
dma_rmb();
|
||||
|
|
@ -5894,21 +5933,26 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
&rx_q->dma_erx[entry],
|
||||
status);
|
||||
}
|
||||
if (unlikely(status == discard_frame)) {
|
||||
page_pool_put_page(rx_q->page_pool, buf->page, 0, true);
|
||||
buf->page = NULL;
|
||||
|
||||
if (unlikely(!error && (status & discard_frame))) {
|
||||
error = 1;
|
||||
if (!priv->hwts_rx_en)
|
||||
rx_errors++;
|
||||
}
|
||||
|
||||
if (unlikely(error && (status & rx_not_ls)))
|
||||
goto read_again;
|
||||
/* We do not support SG in XDP mode */
|
||||
if (unlikely(!error && (status & rx_not_ls) &&
|
||||
stmmac_xdp_is_enabled(priv))) {
|
||||
rx_dropped++;
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if (unlikely(error)) {
|
||||
if (status & rx_not_ls)
|
||||
goto read_again;
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
skb = NULL;
|
||||
count++;
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* Buffer is good. Go on. */
|
||||
|
|
@ -5918,13 +5962,19 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
buf2_len = stmmac_rx_buf2_len(priv, p, status, len);
|
||||
len += buf2_len;
|
||||
|
||||
/* Drop the packet if it is split over buf1 and buf2 on a
|
||||
* single descriptor if the interface is running in XDP mode.
|
||||
*/
|
||||
if (unlikely(stmmac_xdp_is_enabled(priv) && buf2_len)) {
|
||||
rx_dropped++;
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* ACS is disabled; strip manually. */
|
||||
if (likely(!(status & rx_not_ls)))
|
||||
len -= ETH_FCS_LEN;
|
||||
|
||||
if (!skb) {
|
||||
unsigned int pre_len, sync_len;
|
||||
|
||||
/* Each frame starts here: reset the FCS handling */
|
||||
fcs_stripped = false;
|
||||
|
||||
|
|
@ -5942,55 +5992,32 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
xdp_prepare_buff(&ctx.xdp, page_address(buf->page),
|
||||
buf->page_offset, buf1_len, true);
|
||||
|
||||
pre_len = ctx.xdp.data_end - ctx.xdp.data_hard_start -
|
||||
buf->page_offset;
|
||||
|
||||
ctx.priv = priv;
|
||||
ctx.desc = p;
|
||||
ctx.ndesc = np;
|
||||
|
||||
skb = stmmac_xdp_run_prog(priv, &ctx.xdp);
|
||||
/* Due xdp_adjust_tail: DMA sync for_device
|
||||
* cover max len CPU touch
|
||||
*/
|
||||
sync_len = ctx.xdp.data_end - ctx.xdp.data_hard_start -
|
||||
buf->page_offset;
|
||||
sync_len = max(sync_len, pre_len);
|
||||
|
||||
/* For Not XDP_PASS verdict */
|
||||
if (IS_ERR(skb)) {
|
||||
unsigned int xdp_res = -PTR_ERR(skb);
|
||||
|
||||
if (xdp_res & STMMAC_XDP_CONSUMED) {
|
||||
page_pool_put_page(rx_q->page_pool,
|
||||
virt_to_head_page(ctx.xdp.data),
|
||||
sync_len, true);
|
||||
page_pool_recycle_direct(rx_q->page_pool,
|
||||
virt_to_head_page(ctx.xdp.data));
|
||||
buf->page = NULL;
|
||||
rx_dropped++;
|
||||
|
||||
/* Clear skb as it was set as
|
||||
* status by XDP program.
|
||||
*/
|
||||
skb = NULL;
|
||||
|
||||
if (unlikely((status & rx_not_ls)))
|
||||
goto read_again;
|
||||
|
||||
count++;
|
||||
continue;
|
||||
goto next;
|
||||
} else if (xdp_res & (STMMAC_XDP_TX |
|
||||
STMMAC_XDP_REDIRECT)) {
|
||||
xdp_status |= xdp_res;
|
||||
buf->page = NULL;
|
||||
skb = NULL;
|
||||
count++;
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skb) {
|
||||
unsigned int head_pad_len;
|
||||
unsigned int metasize;
|
||||
|
||||
/* XDP program may expand or reduce tail */
|
||||
buf1_len = ctx.xdp.data_end - ctx.xdp.data;
|
||||
|
|
@ -6007,10 +6034,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
}
|
||||
|
||||
/* XDP program may adjust header */
|
||||
head_pad_len = ctx.xdp.data - ctx.xdp.data_hard_start;
|
||||
skb_reserve(skb, head_pad_len);
|
||||
skb_reserve(skb,
|
||||
ctx.xdp.data - ctx.xdp.data_hard_start);
|
||||
skb_put(skb, buf1_len);
|
||||
skb_mark_for_recycle(skb);
|
||||
|
||||
metasize = ctx.xdp.data - ctx.xdp.data_meta;
|
||||
if (metasize)
|
||||
skb_metadata_set(skb, metasize);
|
||||
buf->page = NULL;
|
||||
} else if (buf1_len) {
|
||||
dma_sync_single_for_cpu(priv->device, buf->addr,
|
||||
|
|
@ -6041,20 +6072,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
/* Remove FCS if needed */
|
||||
if (!fcs_stripped && pskb_trim(skb, len)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = NULL;
|
||||
rx_dropped++;
|
||||
count++;
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
|
||||
stmmac_get_rx_hwtstamp(priv, p, np, skb);
|
||||
|
||||
if (priv->hw->hw_vlan_en)
|
||||
if (priv->hw->hw_vlan_en) {
|
||||
/* MAC level stripping. */
|
||||
stmmac_rx_hw_vlan(priv, priv->hw, p, skb);
|
||||
else
|
||||
/* Driver level stripping. */
|
||||
stmmac_rx_vlan(priv->dev, skb);
|
||||
} else if (stmmac_rx_vlan(priv->dev, skb)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
rx_dropped++;
|
||||
goto next;
|
||||
}
|
||||
|
||||
skb->protocol = eth_type_trans(skb, priv->dev);
|
||||
|
||||
|
|
@ -6069,14 +6100,15 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
|||
|
||||
skb_record_rx_queue(skb, queue);
|
||||
napi_gro_receive(&ch->rx_napi, skb);
|
||||
skb = NULL;
|
||||
|
||||
rx_packets++;
|
||||
rx_bytes += len;
|
||||
next:
|
||||
in_progress = false;
|
||||
skb = NULL;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (status & rx_not_ls || skb) {
|
||||
if (in_progress || skb) {
|
||||
rx_q->state_saved = true;
|
||||
rx_q->state.skb = skb;
|
||||
rx_q->state.error = error;
|
||||
|
|
@ -8213,6 +8245,7 @@ static int __stmmac_dvr_probe(struct device *device,
|
|||
stmmac_napi_add(ndev);
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
mutex_init(&priv->est_lock);
|
||||
rwlock_init(&priv->ptp_lock);
|
||||
|
||||
stmmac_fpe_init(priv);
|
||||
|
|
@ -8346,6 +8379,7 @@ void stmmac_dvr_remove(struct device *dev)
|
|||
|
||||
destroy_workqueue(priv->wq);
|
||||
mutex_destroy(&priv->lock);
|
||||
mutex_destroy(&priv->est_lock);
|
||||
bitmap_free(priv->af_xdp_zc_qps);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
|
@ -8421,6 +8455,9 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
|
|||
{
|
||||
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
|
||||
|
||||
dev_kfree_skb_any(rx_q->state.skb);
|
||||
rx_q->state.skb = NULL;
|
||||
rx_q->state_saved = false;
|
||||
rx_q->cur_rx = 0;
|
||||
rx_q->dirty_rx = 0;
|
||||
}
|
||||
|
|
@ -8525,6 +8562,10 @@ int stmmac_resume(struct device *dev)
|
|||
ret = stmmac_init_timestamping(priv);
|
||||
if (ret)
|
||||
goto error_stop_dma;
|
||||
|
||||
ret = stmmac_setup_est(priv);
|
||||
if (ret)
|
||||
goto error_stop_dma;
|
||||
}
|
||||
|
||||
init_coalesce:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
Author: Rayagond Kokatanur <rayagond@vayavyalabs.com>
|
||||
*******************************************************************************/
|
||||
#include "stmmac.h"
|
||||
#include "stmmac_est.h"
|
||||
#include "stmmac_ptp.h"
|
||||
|
||||
#define PTP_SAFE_TIME_OFFSET_NS 500000
|
||||
|
|
@ -54,8 +55,7 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
|
|||
u32 sec, nsec;
|
||||
u32 quotient, reminder;
|
||||
int neg_adj = 0;
|
||||
bool xmac, est_rst = false;
|
||||
int ret;
|
||||
bool xmac;
|
||||
|
||||
xmac = dwmac_is_xmac(priv->plat->core_type);
|
||||
|
||||
|
|
@ -69,47 +69,21 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
|
|||
nsec = reminder;
|
||||
|
||||
/* If EST is enabled, disabled it before adjust ptp time. */
|
||||
if (priv->est && priv->est->enable) {
|
||||
est_rst = true;
|
||||
mutex_lock(&priv->est_lock);
|
||||
priv->est->enable = false;
|
||||
mutex_lock(&priv->est_lock);
|
||||
if (priv->est && priv->est->enable)
|
||||
stmmac_est_configure(priv, priv, priv->est,
|
||||
priv->plat->clk_ptp_rate);
|
||||
mutex_unlock(&priv->est_lock);
|
||||
}
|
||||
priv->plat->clk_ptp_rate, false);
|
||||
mutex_unlock(&priv->est_lock);
|
||||
|
||||
write_lock_irqsave(&priv->ptp_lock, flags);
|
||||
stmmac_adjust_systime(priv, priv->ptpaddr, sec, nsec, neg_adj, xmac);
|
||||
write_unlock_irqrestore(&priv->ptp_lock, flags);
|
||||
|
||||
/* Calculate new basetime and re-configured EST after PTP time adjust. */
|
||||
if (est_rst) {
|
||||
struct timespec64 current_time, time;
|
||||
ktime_t current_time_ns, basetime;
|
||||
u64 cycle_time;
|
||||
|
||||
mutex_lock(&priv->est_lock);
|
||||
priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops,
|
||||
¤t_time, NULL);
|
||||
current_time_ns = timespec64_to_ktime(current_time);
|
||||
time.tv_nsec = priv->est->btr_reserve[0];
|
||||
time.tv_sec = priv->est->btr_reserve[1];
|
||||
basetime = timespec64_to_ktime(time);
|
||||
cycle_time = (u64)priv->est->ctr[1] * NSEC_PER_SEC +
|
||||
priv->est->ctr[0];
|
||||
time = stmmac_calc_tas_basetime(basetime,
|
||||
current_time_ns,
|
||||
cycle_time);
|
||||
|
||||
priv->est->btr[0] = (u32)time.tv_nsec;
|
||||
priv->est->btr[1] = (u32)time.tv_sec;
|
||||
priv->est->enable = true;
|
||||
ret = stmmac_est_configure(priv, priv, priv->est,
|
||||
priv->plat->clk_ptp_rate);
|
||||
mutex_unlock(&priv->est_lock);
|
||||
if (ret)
|
||||
netdev_err(priv->dev, "failed to configure EST\n");
|
||||
}
|
||||
mutex_lock(&priv->est_lock);
|
||||
if (priv->est && priv->est->enable)
|
||||
__stmmac_setup_est(priv);
|
||||
mutex_unlock(&priv->est_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -966,11 +966,12 @@ static void tc_taprio_map_maxsdu_txq(struct stmmac_priv *priv,
|
|||
static int tc_taprio_configure(struct stmmac_priv *priv,
|
||||
struct tc_taprio_qopt_offload *qopt)
|
||||
{
|
||||
u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep;
|
||||
u32 wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep;
|
||||
struct netlink_ext_ack *extack = qopt->mqprio.extack;
|
||||
struct timespec64 time, current_time, qopt_time;
|
||||
ktime_t current_time_ns;
|
||||
int err, i, ret = 0;
|
||||
u32 *gcl = NULL;
|
||||
u64 ctr;
|
||||
|
||||
if (qopt->base_time < 0)
|
||||
|
|
@ -1013,44 +1014,46 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->est_lock);
|
||||
|
||||
if (qopt->cmd == TAPRIO_CMD_DESTROY)
|
||||
goto disable;
|
||||
|
||||
if (qopt->num_entries > dep)
|
||||
return -EINVAL;
|
||||
if (!qopt->cycle_time)
|
||||
return -ERANGE;
|
||||
if (qopt->cycle_time_extension >= BIT(wid + 7))
|
||||
return -ERANGE;
|
||||
|
||||
if (!priv->est) {
|
||||
priv->est = devm_kzalloc(priv->device, sizeof(*priv->est),
|
||||
GFP_KERNEL);
|
||||
if (!priv->est)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->est_lock);
|
||||
} else {
|
||||
mutex_lock(&priv->est_lock);
|
||||
memset(priv->est, 0, sizeof(*priv->est));
|
||||
mutex_unlock(&priv->est_lock);
|
||||
if (qopt->num_entries > dep) {
|
||||
ret = -EINVAL;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
size = qopt->num_entries;
|
||||
if (!qopt->cycle_time ||
|
||||
qopt->cycle_time > (u64)U32_MAX * NSEC_PER_SEC) {
|
||||
ret = -ERANGE;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->est_lock);
|
||||
priv->est->gcl_size = size;
|
||||
priv->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE;
|
||||
mutex_unlock(&priv->est_lock);
|
||||
if (qopt->cycle_time_extension >= BIT(wid + 7)) {
|
||||
ret = -ERANGE;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
gcl = kcalloc(qopt->num_entries, sizeof(*gcl), GFP_KERNEL);
|
||||
if (!gcl) {
|
||||
ret = -ENOMEM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
for (i = 0; i < qopt->num_entries; i++) {
|
||||
s64 delta_ns = qopt->entries[i].interval;
|
||||
u32 gates = qopt->entries[i].gate_mask;
|
||||
|
||||
if (delta_ns > GENMASK(wid - 1, 0))
|
||||
return -ERANGE;
|
||||
if (gates > GENMASK(31 - wid, 0))
|
||||
return -ERANGE;
|
||||
if (delta_ns > GENMASK(wid - 1, 0)) {
|
||||
ret = -ERANGE;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
if (gates > GENMASK(31 - wid, 0)) {
|
||||
ret = -ERANGE;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
switch (qopt->entries[i].command) {
|
||||
case TC_TAPRIO_CMD_SET_GATES:
|
||||
|
|
@ -1062,13 +1065,26 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
|
|||
gates &= ~BIT(0);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
ret = -EOPNOTSUPP;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
priv->est->gcl[i] = delta_ns | (gates << wid);
|
||||
gcl[i] = delta_ns | (gates << wid);
|
||||
}
|
||||
|
||||
mutex_lock(&priv->est_lock);
|
||||
if (!priv->est) {
|
||||
priv->est = devm_kzalloc(priv->device, sizeof(*priv->est),
|
||||
GFP_KERNEL);
|
||||
if (!priv->est) {
|
||||
ret = -ENOMEM;
|
||||
goto error_unlock;
|
||||
}
|
||||
} else {
|
||||
memset(priv->est, 0, sizeof(*priv->est));
|
||||
}
|
||||
memcpy(priv->est->gcl, gcl, sizeof(*gcl) * qopt->num_entries);
|
||||
priv->est->gcl_size = qopt->num_entries;
|
||||
|
||||
/* Adjust for real system time */
|
||||
priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops, ¤t_time,
|
||||
NULL);
|
||||
|
|
@ -1092,8 +1108,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
|
|||
tc_taprio_map_maxsdu_txq(priv, qopt);
|
||||
|
||||
ret = stmmac_est_configure(priv, priv, priv->est,
|
||||
priv->plat->clk_ptp_rate);
|
||||
mutex_unlock(&priv->est_lock);
|
||||
priv->plat->clk_ptp_rate, true);
|
||||
if (ret) {
|
||||
netdev_err(priv->dev, "failed to configure EST\n");
|
||||
goto disable;
|
||||
|
|
@ -1104,24 +1119,31 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
|
|||
if (ret)
|
||||
goto disable;
|
||||
|
||||
priv->est->pclass = qopt->mqprio.preemptible_tcs;
|
||||
priv->est->enable = true;
|
||||
|
||||
mutex_unlock(&priv->est_lock);
|
||||
kfree(gcl);
|
||||
|
||||
return 0;
|
||||
|
||||
disable:
|
||||
if (priv->est) {
|
||||
mutex_lock(&priv->est_lock);
|
||||
priv->est->enable = false;
|
||||
stmmac_est_configure(priv, priv, priv->est,
|
||||
priv->plat->clk_ptp_rate);
|
||||
priv->plat->clk_ptp_rate, false);
|
||||
/* Reset taprio status */
|
||||
for (i = 0; i < priv->plat->tx_queues_to_use; i++) {
|
||||
priv->xstats.max_sdu_txq_drop[i] = 0;
|
||||
priv->xstats.mtl_est_txq_hlbf[i] = 0;
|
||||
priv->xstats.mtl_est_txq_hlbs[i] = 0;
|
||||
}
|
||||
mutex_unlock(&priv->est_lock);
|
||||
}
|
||||
|
||||
err = stmmac_fpe_map_preemption_class(priv, priv->dev, extack, 0);
|
||||
error_unlock:
|
||||
mutex_unlock(&priv->est_lock);
|
||||
kfree(gcl);
|
||||
|
||||
return qopt->cmd == TAPRIO_CMD_DESTROY ? err : ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@
|
|||
#define AM65_CPSW_PN_TS_CTL_LTYPE2_TS_319 BIT(21)
|
||||
#define AM65_CPSW_PN_TS_CTL_LTYPE2_TS_320 BIT(22)
|
||||
#define AM65_CPSW_PN_TS_CTL_LTYPE2_TS_TTL_NONZERO BIT(23)
|
||||
#define AM65_CPSW_PN_TS_CTL_LTYPE2_TS_UNI_EN BIT(24)
|
||||
|
||||
/* The PTP event messages - Sync, Delay_Req, Pdelay_Req, and Pdelay_Resp. */
|
||||
#define AM65_CPSW_TS_EVENT_MSG_TYPE_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
|
||||
|
|
@ -1857,7 +1858,8 @@ static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
|
|||
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_132 |
|
||||
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_319 |
|
||||
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_320 |
|
||||
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_TTL_NONZERO;
|
||||
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_TTL_NONZERO |
|
||||
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_UNI_EN;
|
||||
|
||||
ts_ctrl = AM65_CPSW_TS_EVENT_MSG_TYPE_BITS <<
|
||||
AM65_CPSW_PN_TS_CTL_MSG_TYPE_EN_SHIFT;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,9 @@ struct am65_cpts {
|
|||
struct mutex ptp_clk_lock; /* PHC access sync */
|
||||
u64 timestamp;
|
||||
u32 genf_enable;
|
||||
bool genf_neg_adj;
|
||||
u32 genf_ppm_hi;
|
||||
u32 genf_ppm_low;
|
||||
u32 hw_ts_enable;
|
||||
u32 estf_enable;
|
||||
struct sk_buff_head txq;
|
||||
|
|
@ -436,9 +439,11 @@ static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
{
|
||||
struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
|
||||
u32 estf_ctrl_val = 0, estf_ppm_hi = 0, estf_ppm_low = 0;
|
||||
u32 genf_ctrl_val[AM65_CPTS_GENF_MAX_NUM];
|
||||
u32 genf_ppm_hi, genf_ppm_low;
|
||||
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
|
||||
int pps_index = cpts->pps_genf_idx;
|
||||
u64 adj_period, pps_adj_period;
|
||||
u64 adj_period, genf_adj_period;
|
||||
u32 ctrl_val, ppm_hi, ppm_low;
|
||||
unsigned long flags;
|
||||
int neg_adj = 0, i;
|
||||
|
|
@ -470,20 +475,33 @@ static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
ppm_hi = upper_32_bits(adj_period) & 0x3FF;
|
||||
ppm_low = lower_32_bits(adj_period);
|
||||
|
||||
if (cpts->pps_enabled) {
|
||||
estf_ctrl_val = am65_cpts_read32(cpts, genf[pps_index].control);
|
||||
if (neg_adj)
|
||||
estf_ctrl_val &= ~BIT(1);
|
||||
else
|
||||
estf_ctrl_val |= BIT(1);
|
||||
/* GenF PPM will do correction using cpts refclk tick which is
|
||||
* (cpts->ts_add_val + 1) ns, so GenF length PPM adj period
|
||||
* need to be corrected.
|
||||
*/
|
||||
genf_adj_period = adj_period * (cpts->ts_add_val + 1);
|
||||
genf_ppm_hi = upper_32_bits(genf_adj_period) & 0x3FF;
|
||||
genf_ppm_low = lower_32_bits(genf_adj_period);
|
||||
cpts->genf_neg_adj = neg_adj;
|
||||
cpts->genf_ppm_hi = genf_ppm_hi;
|
||||
cpts->genf_ppm_low = genf_ppm_low;
|
||||
|
||||
/* GenF PPM will do correction using cpts refclk tick which is
|
||||
* (cpts->ts_add_val + 1) ns, so GenF length PPM adj period
|
||||
* need to be corrected.
|
||||
*/
|
||||
pps_adj_period = adj_period * (cpts->ts_add_val + 1);
|
||||
estf_ppm_hi = upper_32_bits(pps_adj_period) & 0x3FF;
|
||||
estf_ppm_low = lower_32_bits(pps_adj_period);
|
||||
for (i = 0; i < AM65_CPTS_GENF_MAX_NUM; i++) {
|
||||
if (cpts->genf_enable & BIT(i)) {
|
||||
genf_ctrl_val[i] = am65_cpts_read32(cpts,
|
||||
genf[i].control);
|
||||
if (neg_adj)
|
||||
genf_ctrl_val[i] &= ~BIT(1);
|
||||
else
|
||||
genf_ctrl_val[i] |= BIT(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpts->pps_enabled) {
|
||||
/* am65_cpts_perout_enable_hw() sets pps_index in genf_enable */
|
||||
estf_ctrl_val = genf_ctrl_val[pps_index];
|
||||
estf_ppm_hi = genf_ppm_hi;
|
||||
estf_ppm_low = genf_ppm_low;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
|
|
@ -500,10 +518,13 @@ static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
am65_cpts_write32(cpts, ppm_hi, ts_ppm_hi);
|
||||
am65_cpts_write32(cpts, ppm_low, ts_ppm_low);
|
||||
|
||||
if (cpts->pps_enabled) {
|
||||
am65_cpts_write32(cpts, estf_ctrl_val, genf[pps_index].control);
|
||||
am65_cpts_write32(cpts, estf_ppm_hi, genf[pps_index].ppm_hi);
|
||||
am65_cpts_write32(cpts, estf_ppm_low, genf[pps_index].ppm_low);
|
||||
for (i = 0; i < AM65_CPTS_GENF_MAX_NUM; i++) {
|
||||
if (cpts->genf_enable & BIT(i)) {
|
||||
am65_cpts_write32(cpts, genf_ctrl_val[i],
|
||||
genf[i].control);
|
||||
am65_cpts_write32(cpts, genf_ppm_hi, genf[i].ppm_hi);
|
||||
am65_cpts_write32(cpts, genf_ppm_low, genf[i].ppm_low);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < AM65_CPTS_ESTF_MAX_NUM; i++) {
|
||||
|
|
@ -513,7 +534,6 @@ static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
am65_cpts_write32(cpts, estf_ppm_low, estf[i].ppm_low);
|
||||
}
|
||||
}
|
||||
/* All GenF/EstF can be updated here the same way */
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
mutex_unlock(&cpts->ptp_clk_lock);
|
||||
|
|
@ -679,9 +699,12 @@ static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts,
|
|||
val = lower_32_bits(cycles);
|
||||
am65_cpts_write32(cpts, val, genf[req->index].length);
|
||||
|
||||
am65_cpts_write32(cpts, 0, genf[req->index].control);
|
||||
am65_cpts_write32(cpts, 0, genf[req->index].ppm_hi);
|
||||
am65_cpts_write32(cpts, 0, genf[req->index].ppm_low);
|
||||
am65_cpts_write32(cpts, cpts->genf_neg_adj ? 0 : BIT(1),
|
||||
genf[req->index].control);
|
||||
am65_cpts_write32(cpts, cpts->genf_ppm_hi,
|
||||
genf[req->index].ppm_hi);
|
||||
am65_cpts_write32(cpts, cpts->genf_ppm_low,
|
||||
genf[req->index].ppm_low);
|
||||
|
||||
cpts->genf_enable |= BIT(req->index);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -260,10 +260,8 @@ int ipa_interrupt_config(struct ipa *ipa)
|
|||
interrupt->enabled = 0;
|
||||
interrupt->suspend_enabled = bitmap_zalloc(ipa->endpoint_count,
|
||||
GFP_KERNEL);
|
||||
if (!interrupt->suspend_enabled) {
|
||||
ret = -ENOMEM;
|
||||
goto err_kfree;
|
||||
}
|
||||
if (!interrupt->suspend_enabled)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Disable IPA interrupt types */
|
||||
reg = ipa_reg(ipa, IPA_IRQ_EN);
|
||||
|
|
@ -289,8 +287,6 @@ int ipa_interrupt_config(struct ipa *ipa)
|
|||
goto err_disable_wakeup;
|
||||
}
|
||||
|
||||
ipa->interrupt = interrupt;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_wakeup:
|
||||
|
|
@ -299,8 +295,6 @@ int ipa_interrupt_config(struct ipa *ipa)
|
|||
free_irq(interrupt->irq, interrupt);
|
||||
err_free_bitmap:
|
||||
bitmap_free(interrupt->suspend_enabled);
|
||||
err_kfree:
|
||||
kfree(interrupt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -311,8 +305,6 @@ void ipa_interrupt_deconfig(struct ipa *ipa)
|
|||
struct ipa_interrupt *interrupt = ipa->interrupt;
|
||||
struct device *dev = ipa->dev;
|
||||
|
||||
ipa->interrupt = NULL;
|
||||
|
||||
dev_pm_clear_wake_irq(dev);
|
||||
(void)device_init_wakeup(dev, false);
|
||||
free_irq(interrupt->irq, interrupt);
|
||||
|
|
|
|||
|
|
@ -643,6 +643,14 @@ static int macvlan_open(struct net_device *dev)
|
|||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
if (dev->flags & IFF_ALLMULTI) {
|
||||
err = dev_set_allmulti(lowerdev, 1);
|
||||
if (err < 0) {
|
||||
if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC))
|
||||
dev_set_promiscuity(lowerdev, -1);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
goto hash_add;
|
||||
}
|
||||
|
||||
|
|
@ -713,6 +721,8 @@ static int macvlan_stop(struct net_device *dev)
|
|||
dev_mc_unsync(lowerdev, dev);
|
||||
|
||||
if (macvlan_passthru(vlan->port)) {
|
||||
if (dev->flags & IFF_ALLMULTI)
|
||||
dev_set_allmulti(lowerdev, -1);
|
||||
if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC))
|
||||
dev_set_promiscuity(lowerdev, -1);
|
||||
goto hash_del;
|
||||
|
|
|
|||
|
|
@ -111,8 +111,7 @@ static int mctp_i3c_read(struct mctp_i3c_device *mi)
|
|||
mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
|
||||
if (!skb) {
|
||||
stats->rx_dropped++;
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb->protocol = htons(ETH_P_MCTP);
|
||||
|
|
|
|||
|
|
@ -1826,9 +1826,13 @@ static void phy_detach_internal(struct phy_device *phydev, bool notify_bus)
|
|||
struct hwtstamp_provider *hwprov;
|
||||
|
||||
/* hwprov may technically be protected by ops lock but
|
||||
* not for devices with a phydev, see phy_link_topo_add_phy()
|
||||
* not for devices with a phydev, see phy_link_topo_add_phy().
|
||||
* Drivers may detach without RTNL while dev is unregistered,
|
||||
* but then nothing else can reach hwprov.
|
||||
*/
|
||||
hwprov = rtnl_dereference(dev->hwprov);
|
||||
hwprov = rcu_dereference_protected(dev->hwprov,
|
||||
lockdep_rtnl_is_held() ||
|
||||
dev->reg_state != NETREG_REGISTERED);
|
||||
/* Disable timestamp if it is the one selected */
|
||||
if (hwprov && hwprov->phydev == phydev) {
|
||||
rcu_assign_pointer(dev->hwprov, NULL);
|
||||
|
|
|
|||
|
|
@ -1377,9 +1377,10 @@ static int veth_set_channels(struct net_device *dev,
|
|||
* to identify the range we have to disable
|
||||
*/
|
||||
veth_disable_range_safe(dev, new_rx_count, old_rx_count);
|
||||
netif_carrier_on(dev);
|
||||
if (peer)
|
||||
if (peer && (peer->flags & IFF_UP)) {
|
||||
netif_carrier_on(dev);
|
||||
netif_carrier_on(peer);
|
||||
}
|
||||
}
|
||||
|
||||
/* update XDP supported features */
|
||||
|
|
|
|||
|
|
@ -1005,12 +1005,6 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (rcu_access_pointer(f->nh) &&
|
||||
!(state & (NUD_PERMANENT | NUD_NOARP))) {
|
||||
NL_SET_ERR_MSG(extack, "Cannot make a nexthop fdb dynamic");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Do not allow an externally learned entry to take over an entry added
|
||||
* by the user.
|
||||
*/
|
||||
|
|
@ -1274,11 +1268,6 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (nhid && !(ndm->ndm_state & (NUD_PERMANENT | NUD_NOARP))) {
|
||||
NL_SET_ERR_MSG(extack, "A nexthop fdb cannot be dynamic");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg = rtnl_dereference(vxlan->cfg);
|
||||
if (cfg->remote_ip.sa.sa_family != ip.sa.sa_family)
|
||||
return -EAFNOSUPPORT;
|
||||
|
|
@ -2947,6 +2936,9 @@ static void vxlan_cleanup(struct timer_list *t)
|
|||
if (f->flags & NTF_EXT_LEARNED)
|
||||
continue;
|
||||
|
||||
if (rcu_access_pointer(f->nh))
|
||||
continue;
|
||||
|
||||
timeout = READ_ONCE(f->updated) + cfg->age_interval * HZ;
|
||||
if (time_before_eq(timeout, jiffies)) {
|
||||
spin_lock(&vxlan->hash_lock);
|
||||
|
|
|
|||
|
|
@ -1639,7 +1639,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
|
|||
if (!msgbuf->tx_pktids)
|
||||
goto fail;
|
||||
msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS,
|
||||
DMA_FROM_DEVICE);
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (!msgbuf->rx_pktids)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
|
|||
tx_params->sta_id = 0;
|
||||
}
|
||||
|
||||
if (rsta) {
|
||||
if (rsta && tid < IEEE80211_NUM_TIDS) {
|
||||
/* Start aggregation if not done for this tid */
|
||||
if (!rsta->start_tx_aggr[tid]) {
|
||||
rsta->start_tx_aggr[tid] = true;
|
||||
|
|
|
|||
|
|
@ -209,9 +209,17 @@ static void mhi_wwan_ctrl_dtr_work(struct work_struct *work)
|
|||
static void mhi_wwan_ctrl_dtr_rts(struct wwan_port *port, unsigned int mdmbits)
|
||||
{
|
||||
struct mhi_wwan_dev *mhiwwan = wwan_port_get_drvdata(port);
|
||||
bool drop = (READ_ONCE(mhiwwan->dtr_mdmbits) & TIOCM_DTR) &&
|
||||
!(mdmbits & TIOCM_DTR);
|
||||
|
||||
WRITE_ONCE(mhiwwan->dtr_mdmbits, mdmbits);
|
||||
schedule_delayed_work(&mhiwwan->dtr_work, 0);
|
||||
|
||||
/* A DTR drop matters on its own, e.g. to hang up a modem set to AT&D2,
|
||||
* so send it now rather than let a following raise coalesce it away
|
||||
*/
|
||||
if (drop)
|
||||
flush_delayed_work(&mhiwwan->dtr_work);
|
||||
}
|
||||
|
||||
static int mhi_wwan_ctrl_start(struct wwan_port *port)
|
||||
|
|
@ -246,6 +254,9 @@ static void mhi_wwan_ctrl_stop(struct wwan_port *port)
|
|||
|
||||
cancel_work_sync(&mhiwwan->rx_refill);
|
||||
|
||||
/* Send any pending DTR/RTS state before the channel stops */
|
||||
flush_delayed_work(&mhiwwan->dtr_work);
|
||||
|
||||
mhi_unprepare_from_transfer(mhiwwan->mhi_dev);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -508,6 +508,11 @@ static int unregister_vclock(struct device *dev, void *data)
|
|||
|
||||
int ptp_clock_unregister(struct ptp_clock *ptp)
|
||||
{
|
||||
/*
|
||||
* Stop and drain virtual-clock creation and deletion before walking the
|
||||
* children. Do not hold n_vclocks_mux while waiting for sysfs callbacks.
|
||||
*/
|
||||
ptp_vclock_remove_sysfs(ptp);
|
||||
if (ptp_vclock_in_use(ptp)) {
|
||||
device_for_each_child(&ptp->dev, NULL, unregister_vclock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,11 @@ struct ptp_vclock {
|
|||
struct hlist_node vclock_hash_node;
|
||||
struct cyclecounter cc;
|
||||
struct timecounter tc;
|
||||
struct mutex lock; /* protects tc/cc */
|
||||
u64 cycles;
|
||||
struct mutex lock; /* protects tc/cc and the cached sample */
|
||||
/* Owned by the auxiliary worker. */
|
||||
unsigned long retry_delay;
|
||||
unsigned long refresh_deadline;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -169,6 +173,7 @@ extern const struct attribute_group *ptp_groups[];
|
|||
|
||||
int ptp_populate_pin_groups(struct ptp_clock *ptp);
|
||||
void ptp_cleanup_pin_groups(struct ptp_clock *ptp);
|
||||
void ptp_vclock_remove_sysfs(struct ptp_clock *ptp);
|
||||
|
||||
struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock);
|
||||
void ptp_vclock_unregister(struct ptp_vclock *vclock);
|
||||
|
|
|
|||
|
|
@ -80,13 +80,17 @@ static ssize_t extts_fifo_show(struct device *dev,
|
|||
size_t qcnt;
|
||||
int cnt = 0;
|
||||
|
||||
spin_lock_irqsave(&ptp->tsevqs_lock, flags);
|
||||
cnt = list_count_nodes(&ptp->tsevqs);
|
||||
if (cnt <= 0)
|
||||
if (cnt <= 0) {
|
||||
spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The sysfs fifo will always draw from the fist queue */
|
||||
queue = list_first_entry(&ptp->tsevqs, struct timestamp_event_queue,
|
||||
qlist);
|
||||
spin_unlock_irqrestore(&ptp->tsevqs_lock, flags);
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
spin_lock_irqsave(&queue->lock, flags);
|
||||
|
|
@ -224,8 +228,10 @@ static ssize_t n_vclocks_store(struct device *dev,
|
|||
if (num > ptp->n_vclocks) {
|
||||
for (i = 0; i < num - ptp->n_vclocks; i++) {
|
||||
vclock = ptp_vclock_register(ptp);
|
||||
if (!vclock)
|
||||
goto out;
|
||||
if (IS_ERR(vclock)) {
|
||||
err = PTR_ERR(vclock);
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
*(ptp->vclock_index + ptp->n_vclocks + i) =
|
||||
vclock->clock->index;
|
||||
|
|
@ -257,12 +263,24 @@ static ssize_t n_vclocks_store(struct device *dev,
|
|||
mutex_unlock(&ptp->n_vclocks_mux);
|
||||
|
||||
return count;
|
||||
err_register:
|
||||
num = i;
|
||||
if (num)
|
||||
device_for_each_child_reverse(dev, &num, unregister_vclock);
|
||||
for (num = 0; num < i; num++)
|
||||
ptp->vclock_index[ptp->n_vclocks + num] = -1;
|
||||
out:
|
||||
mutex_unlock(&ptp->n_vclocks_mux);
|
||||
return err;
|
||||
}
|
||||
static DEVICE_ATTR_RW(n_vclocks);
|
||||
|
||||
void ptp_vclock_remove_sysfs(struct ptp_clock *ptp)
|
||||
{
|
||||
if (!ptp->is_virtual_clock)
|
||||
device_remove_file(&ptp->dev, &dev_attr_n_vclocks);
|
||||
}
|
||||
|
||||
static ssize_t max_vclocks_show(struct device *dev,
|
||||
struct device_attribute *attr, char *page)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Copyright 2021 NXP
|
||||
*/
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include "ptp_private.h"
|
||||
|
|
@ -13,6 +14,7 @@
|
|||
#define PTP_VCLOCK_FADJ_SHIFT 9
|
||||
#define PTP_VCLOCK_FADJ_DENOMINATOR 15625ULL
|
||||
#define PTP_VCLOCK_REFRESH_INTERVAL (HZ * 2)
|
||||
#define PTP_VCLOCK_RETRY_INTERVAL msecs_to_jiffies(10)
|
||||
|
||||
/* protects vclock_hash addition/deletion */
|
||||
static DEFINE_SPINLOCK(vclock_hash_lock);
|
||||
|
|
@ -42,21 +44,39 @@ static void ptp_vclock_hash_del(struct ptp_vclock *vclock)
|
|||
synchronize_srcu(&vclock_srcu);
|
||||
}
|
||||
|
||||
/* The caller holds vclock->lock, or has not published the clock yet. */
|
||||
static int ptp_vclock_sample(struct ptp_vclock *vclock)
|
||||
{
|
||||
struct ptp_clock *ptp = vclock->pclock;
|
||||
struct timespec64 ts;
|
||||
int err;
|
||||
|
||||
err = ptp->info->getcycles64(ptp->info, &ts);
|
||||
if (!err)
|
||||
vclock->cycles = timespec64_to_ns(&ts);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
||||
{
|
||||
struct ptp_vclock *vclock = info_to_vclock(ptp);
|
||||
s64 adj;
|
||||
int err;
|
||||
|
||||
adj = (s64)scaled_ppm << PTP_VCLOCK_FADJ_SHIFT;
|
||||
adj = div_s64(adj, PTP_VCLOCK_FADJ_DENOMINATOR);
|
||||
|
||||
if (mutex_lock_interruptible(&vclock->lock))
|
||||
return -EINTR;
|
||||
timecounter_read(&vclock->tc);
|
||||
vclock->cc.mult = PTP_VCLOCK_CC_MULT + adj;
|
||||
err = ptp_vclock_sample(vclock);
|
||||
if (!err) {
|
||||
timecounter_read(&vclock->tc);
|
||||
vclock->cc.mult = PTP_VCLOCK_CC_MULT + adj;
|
||||
}
|
||||
mutex_unlock(&vclock->lock);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ptp_vclock_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
|
|
@ -75,15 +95,16 @@ static int ptp_vclock_gettime(struct ptp_clock_info *ptp,
|
|||
struct timespec64 *ts)
|
||||
{
|
||||
struct ptp_vclock *vclock = info_to_vclock(ptp);
|
||||
u64 ns;
|
||||
int err;
|
||||
|
||||
if (mutex_lock_interruptible(&vclock->lock))
|
||||
return -EINTR;
|
||||
ns = timecounter_read(&vclock->tc);
|
||||
err = ptp_vclock_sample(vclock);
|
||||
if (!err)
|
||||
*ts = ns_to_timespec64(timecounter_read(&vclock->tc));
|
||||
mutex_unlock(&vclock->lock);
|
||||
*ts = ns_to_timespec64(ns);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ptp_vclock_gettimex(struct ptp_clock_info *ptp,
|
||||
|
|
@ -94,20 +115,18 @@ static int ptp_vclock_gettimex(struct ptp_clock_info *ptp,
|
|||
struct ptp_clock *pptp = vclock->pclock;
|
||||
struct timespec64 pts;
|
||||
int err;
|
||||
u64 ns;
|
||||
|
||||
err = pptp->info->getcyclesx64(pptp->info, &pts, sts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mutex_lock_interruptible(&vclock->lock))
|
||||
return -EINTR;
|
||||
ns = timecounter_cyc2time(&vclock->tc, timespec64_to_ns(&pts));
|
||||
err = pptp->info->getcyclesx64(pptp->info, &pts, sts);
|
||||
if (!err) {
|
||||
u64 ns = timecounter_cyc2time(&vclock->tc, timespec64_to_ns(&pts));
|
||||
|
||||
*ts = ns_to_timespec64(ns);
|
||||
}
|
||||
mutex_unlock(&vclock->lock);
|
||||
|
||||
*ts = ns_to_timespec64(ns);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ptp_vclock_settime(struct ptp_clock_info *ptp,
|
||||
|
|
@ -115,13 +134,16 @@ static int ptp_vclock_settime(struct ptp_clock_info *ptp,
|
|||
{
|
||||
struct ptp_vclock *vclock = info_to_vclock(ptp);
|
||||
u64 ns = timespec64_to_ns(ts);
|
||||
int err;
|
||||
|
||||
if (mutex_lock_interruptible(&vclock->lock))
|
||||
return -EINTR;
|
||||
timecounter_init(&vclock->tc, &vclock->cc, ns);
|
||||
err = ptp_vclock_sample(vclock);
|
||||
if (!err)
|
||||
timecounter_init(&vclock->tc, &vclock->cc, ns);
|
||||
mutex_unlock(&vclock->lock);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ptp_vclock_getcrosststamp(struct ptp_clock_info *ptp,
|
||||
|
|
@ -130,20 +152,18 @@ static int ptp_vclock_getcrosststamp(struct ptp_clock_info *ptp,
|
|||
struct ptp_vclock *vclock = info_to_vclock(ptp);
|
||||
struct ptp_clock *pptp = vclock->pclock;
|
||||
int err;
|
||||
u64 ns;
|
||||
|
||||
err = pptp->info->getcrosscycles(pptp->info, xtstamp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mutex_lock_interruptible(&vclock->lock))
|
||||
return -EINTR;
|
||||
ns = timecounter_cyc2time(&vclock->tc, ktime_to_ns(xtstamp->device));
|
||||
err = pptp->info->getcrosscycles(pptp->info, xtstamp);
|
||||
if (!err) {
|
||||
u64 ns = timecounter_cyc2time(&vclock->tc, ktime_to_ns(xtstamp->device));
|
||||
|
||||
xtstamp->device = ns_to_ktime(ns);
|
||||
}
|
||||
mutex_unlock(&vclock->lock);
|
||||
|
||||
xtstamp->device = ns_to_ktime(ns);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static long ptp_vclock_refresh(struct ptp_clock_info *ptp)
|
||||
|
|
@ -151,8 +171,24 @@ static long ptp_vclock_refresh(struct ptp_clock_info *ptp)
|
|||
struct ptp_vclock *vclock = info_to_vclock(ptp);
|
||||
struct timespec64 ts;
|
||||
|
||||
ptp_vclock_gettime(&vclock->info, &ts);
|
||||
/* Retry transient failures promptly, but back off during long outages. */
|
||||
if (ptp_vclock_gettime(&vclock->info, &ts)) {
|
||||
unsigned long now = jiffies;
|
||||
|
||||
/* Retries must not postpone the next regular refresh attempt. */
|
||||
if (!vclock->retry_delay || time_after_eq(now, vclock->refresh_deadline))
|
||||
vclock->refresh_deadline = now + PTP_VCLOCK_REFRESH_INTERVAL;
|
||||
|
||||
if (!vclock->retry_delay)
|
||||
vclock->retry_delay = PTP_VCLOCK_RETRY_INTERVAL;
|
||||
else
|
||||
vclock->retry_delay = min_t(unsigned long,
|
||||
vclock->retry_delay * 2,
|
||||
PTP_VCLOCK_REFRESH_INTERVAL);
|
||||
return min(vclock->retry_delay, vclock->refresh_deadline - now);
|
||||
}
|
||||
|
||||
vclock->retry_delay = 0;
|
||||
return PTP_VCLOCK_REFRESH_INTERVAL;
|
||||
}
|
||||
|
||||
|
|
@ -174,12 +210,9 @@ static const struct ptp_clock_info ptp_vclock_info = {
|
|||
static u64 ptp_vclock_read(struct cyclecounter *cc)
|
||||
{
|
||||
struct ptp_vclock *vclock = cc_to_vclock(cc);
|
||||
struct ptp_clock *ptp = vclock->pclock;
|
||||
struct timespec64 ts = {};
|
||||
|
||||
ptp->info->getcycles64(ptp->info, &ts);
|
||||
|
||||
return timespec64_to_ns(&ts);
|
||||
/* The cyclecounter callback cannot report a failed PHC read. */
|
||||
return vclock->cycles;
|
||||
}
|
||||
|
||||
static const struct cyclecounter ptp_vclock_cc = {
|
||||
|
|
@ -192,10 +225,11 @@ static const struct cyclecounter ptp_vclock_cc = {
|
|||
struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
|
||||
{
|
||||
struct ptp_vclock *vclock;
|
||||
int err;
|
||||
|
||||
vclock = kzalloc_obj(*vclock);
|
||||
if (!vclock)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
vclock->pclock = pclock;
|
||||
vclock->info = ptp_vclock_info;
|
||||
|
|
@ -214,20 +248,29 @@ struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
|
|||
|
||||
mutex_init(&vclock->lock);
|
||||
|
||||
err = ptp_vclock_sample(vclock);
|
||||
if (err)
|
||||
goto free_vclock;
|
||||
timecounter_init(&vclock->tc, &vclock->cc, 0);
|
||||
|
||||
vclock->clock = ptp_clock_register(&vclock->info, &pclock->dev);
|
||||
if (IS_ERR_OR_NULL(vclock->clock)) {
|
||||
kfree(vclock);
|
||||
return NULL;
|
||||
if (IS_ERR(vclock->clock)) {
|
||||
err = PTR_ERR(vclock->clock);
|
||||
goto free_vclock;
|
||||
}
|
||||
|
||||
ptp_vclock_set_subclass(vclock->clock);
|
||||
|
||||
timecounter_init(&vclock->tc, &vclock->cc, 0);
|
||||
ptp_schedule_worker(vclock->clock, PTP_VCLOCK_REFRESH_INTERVAL);
|
||||
/* The initial sample predates registration; refresh without more delay. */
|
||||
ptp_schedule_worker(vclock->clock, 0);
|
||||
|
||||
ptp_vclock_hash_add(vclock);
|
||||
|
||||
return vclock;
|
||||
|
||||
free_vclock:
|
||||
kfree(vclock);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
void ptp_vclock_unregister(struct ptp_vclock *vclock)
|
||||
|
|
@ -283,6 +326,8 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index)
|
|||
u64 ns;
|
||||
|
||||
ns = ktime_to_ns(*hwtstamp);
|
||||
if (!ns)
|
||||
return 0;
|
||||
|
||||
srcu_idx = srcu_read_lock(&vclock_srcu);
|
||||
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ static struct sk_buff *
|
|||
vhost_vsock_alloc_skb(struct vhost_virtqueue *vq,
|
||||
unsigned int out, unsigned int in)
|
||||
{
|
||||
struct virtio_vsock_hdr *hdr;
|
||||
struct virtio_vsock_hdr hdr;
|
||||
struct iov_iter iov_iter;
|
||||
struct sk_buff *skb;
|
||||
size_t payload_len;
|
||||
|
|
@ -378,34 +378,32 @@ vhost_vsock_alloc_skb(struct vhost_virtqueue *vq,
|
|||
len > VIRTIO_VSOCK_MAX_PKT_BUF_SIZE + VIRTIO_VSOCK_SKB_HEADROOM)
|
||||
return NULL;
|
||||
|
||||
/* len contains both payload and hdr */
|
||||
skb = virtio_vsock_alloc_skb(len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
iov_iter_init(&iov_iter, ITER_SOURCE, vq->iov, out, len);
|
||||
|
||||
hdr = virtio_vsock_hdr(skb);
|
||||
nbytes = copy_from_iter(hdr, sizeof(*hdr), &iov_iter);
|
||||
if (nbytes != sizeof(*hdr)) {
|
||||
nbytes = copy_from_iter(&hdr, sizeof(hdr), &iov_iter);
|
||||
if (nbytes != sizeof(hdr)) {
|
||||
vq_err(vq, "Expected %zu bytes for pkt->hdr, got %zu bytes\n",
|
||||
sizeof(*hdr), nbytes);
|
||||
kfree_skb(skb);
|
||||
sizeof(hdr), nbytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
payload_len = le32_to_cpu(hdr->len);
|
||||
payload_len = le32_to_cpu(hdr.len);
|
||||
|
||||
/* The pkt is too big or the length in the header is invalid */
|
||||
if (payload_len > len - sizeof(hdr))
|
||||
return NULL;
|
||||
|
||||
/* Allocate only for the payload declared in the header. */
|
||||
skb = virtio_vsock_alloc_skb(payload_len + sizeof(hdr), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
memcpy(virtio_vsock_hdr(skb), &hdr, sizeof(hdr));
|
||||
|
||||
/* No payload */
|
||||
if (!payload_len)
|
||||
return skb;
|
||||
|
||||
/* The pkt is too big or the length in the header is invalid */
|
||||
if (payload_len + sizeof(*hdr) > len) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtio_vsock_skb_put(skb, payload_len);
|
||||
|
||||
if (skb_copy_datagram_from_iter(skb, 0, &iov_iter, payload_len)) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <linux/can.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* exposed CAN device capabilities for network layer */
|
||||
#define CAN_CAP_CC BIT(0) /* CAN CC aka Classical CAN */
|
||||
|
|
@ -59,6 +60,7 @@
|
|||
enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
|
||||
|
||||
struct can_dev_rcv_lists {
|
||||
spinlock_t lock; /* protects the receive lists and entries */
|
||||
struct hlist_head rx[RX_MAX];
|
||||
struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
|
||||
struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
|
||||
|
|
@ -81,6 +83,7 @@ static inline struct can_ml_priv *can_get_ml_priv(struct net_device *dev)
|
|||
static inline void can_set_ml_priv(struct net_device *dev,
|
||||
struct can_ml_priv *ml_priv)
|
||||
{
|
||||
spin_lock_init(&ml_priv->dev_rcv_lists.lock);
|
||||
netdev_set_ml_priv(dev, ml_priv, ML_PRIV_CAN);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ extern void can_proto_unregister(const struct can_proto *cp);
|
|||
int can_rx_register(struct net *net, struct net_device *dev,
|
||||
canid_t can_id, canid_t mask,
|
||||
void (*func)(struct sk_buff *, void *),
|
||||
void *data, char *ident, struct sock *sk);
|
||||
void *data, u64 ino, struct sock *sk);
|
||||
|
||||
extern void can_rx_unregister(struct net *net, struct net_device *dev,
|
||||
canid_t can_id, canid_t mask,
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *s
|
|||
return can_dropped_invalid_skb(dev, skb);
|
||||
|
||||
invalid_skb:
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
dev->stats.tx_dropped++;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,12 +78,12 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
|
|||
|
||||
nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (unlikely(!nskb)) {
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
can_skb_set_owner(nskb, skb->sk);
|
||||
consume_skb(skb);
|
||||
dev_consume_skb_any(skb);
|
||||
return nskb;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ enum {
|
|||
* @mfc_flags: entry flags
|
||||
* @expires: unresolved entry expire time
|
||||
* @unresolved: unresolved cached skbs
|
||||
* @nl_queries: queued RTM_GETROUTE requests waiting for resolution
|
||||
* @last_assert: time of last assert
|
||||
* @minvif: minimum VIF id
|
||||
* @maxvif: maximum VIF id
|
||||
|
|
@ -141,6 +142,7 @@ struct mr_mfc {
|
|||
struct {
|
||||
unsigned long expires;
|
||||
struct sk_buff_head unresolved;
|
||||
struct sk_buff_head nl_queries;
|
||||
} unres;
|
||||
struct {
|
||||
unsigned long last_assert;
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ enum {
|
|||
NAPI_STATE_HAS_NOTIFIER, /* Napi has an IRQ notifier */
|
||||
NAPI_STATE_THREADED_BUSY_POLL, /* The threaded NAPI poller will busy poll */
|
||||
NAPI_STATE_PERCPU, /* NAPI handler is run per-CPU - incompatible with threaded mode */
|
||||
NAPI_STATE_DISABLED, /* NAPI is disabled */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -456,6 +457,7 @@ enum {
|
|||
NAPIF_STATE_HAS_NOTIFIER = BIT(NAPI_STATE_HAS_NOTIFIER),
|
||||
NAPIF_STATE_THREADED_BUSY_POLL = BIT(NAPI_STATE_THREADED_BUSY_POLL),
|
||||
NAPIF_STATE_PERCPU = BIT(NAPI_STATE_PERCPU),
|
||||
NAPIF_STATE_DISABLED = BIT(NAPI_STATE_DISABLED),
|
||||
};
|
||||
|
||||
enum gro_result {
|
||||
|
|
|
|||
|
|
@ -496,7 +496,7 @@ int ptp_get_vclocks_index(int pclock_index, int **vclock_index);
|
|||
* @hwtstamp: timestamp
|
||||
* @vclock_index: phc index of ptp vclock.
|
||||
*
|
||||
* Returns: converted timestamp, or 0 on error.
|
||||
* Returns: converted timestamp, or 0 if @hwtstamp is zero or on error.
|
||||
*/
|
||||
ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index);
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -179,6 +179,9 @@ struct tcp_request_sock {
|
|||
u8 ao_rcv_next;
|
||||
bool used_tcp_ao;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
__be32 syn_flowlabel;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define tcp_rsk(ptr) container_of_const(ptr, struct tcp_request_sock, req.req)
|
||||
|
|
|
|||
|
|
@ -680,6 +680,45 @@ enum conn_reasons {
|
|||
CONN_REASON_ISO_CONNECT,
|
||||
};
|
||||
|
||||
enum {
|
||||
HCI_CONN_AUTH_PEND,
|
||||
HCI_CONN_ENCRYPT_PEND,
|
||||
HCI_CONN_RSWITCH_PEND,
|
||||
HCI_CONN_MODE_CHANGE_PEND,
|
||||
HCI_CONN_SCO_SETUP_PEND,
|
||||
HCI_CONN_MGMT_CONNECTED,
|
||||
HCI_CONN_SSP_ENABLED,
|
||||
HCI_CONN_SC_ENABLED,
|
||||
HCI_CONN_AES_CCM,
|
||||
HCI_CONN_POWER_SAVE,
|
||||
HCI_CONN_FLUSH_KEY,
|
||||
HCI_CONN_ENCRYPT,
|
||||
HCI_CONN_AUTH,
|
||||
HCI_CONN_SECURE,
|
||||
HCI_CONN_FIPS,
|
||||
HCI_CONN_STK_ENCRYPT,
|
||||
HCI_CONN_AUTH_INITIATOR,
|
||||
HCI_CONN_DROP,
|
||||
HCI_CONN_CANCEL,
|
||||
HCI_CONN_PARAM_REMOVAL_PEND,
|
||||
HCI_CONN_NEW_LINK_KEY,
|
||||
HCI_CONN_SCANNING,
|
||||
HCI_CONN_AUTH_FAILURE,
|
||||
HCI_CONN_PER_ADV,
|
||||
HCI_CONN_BIG_CREATED,
|
||||
HCI_CONN_CREATE,
|
||||
HCI_CONN_CREATE_CIS,
|
||||
HCI_CONN_CREATE_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC_FAILED,
|
||||
HCI_CONN_CREATE_PA_SYNC,
|
||||
HCI_CONN_PA_SYNC,
|
||||
HCI_CONN_PA_SYNC_FAILED,
|
||||
HCI_CONN_ENC_KEY_SIZE_PEND,
|
||||
|
||||
__HCI_CONN_NUM_FLAGS,
|
||||
};
|
||||
|
||||
struct hci_conn {
|
||||
struct list_head list;
|
||||
|
||||
|
|
@ -750,7 +789,7 @@ struct hci_conn {
|
|||
__u8 num_bis;
|
||||
__u8 bis[HCI_MAX_ISO_BIS];
|
||||
|
||||
unsigned long flags;
|
||||
DECLARE_BITMAP(flags, __HCI_CONN_NUM_FLAGS);
|
||||
|
||||
enum conn_reasons conn_reason;
|
||||
__u8 abort_reason;
|
||||
|
|
@ -980,55 +1019,18 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
|
|||
bool name_known);
|
||||
void hci_inquiry_cache_flush(struct hci_dev *hdev);
|
||||
|
||||
/* ----- HCI Connections ----- */
|
||||
enum {
|
||||
HCI_CONN_AUTH_PEND,
|
||||
HCI_CONN_ENCRYPT_PEND,
|
||||
HCI_CONN_RSWITCH_PEND,
|
||||
HCI_CONN_MODE_CHANGE_PEND,
|
||||
HCI_CONN_SCO_SETUP_PEND,
|
||||
HCI_CONN_MGMT_CONNECTED,
|
||||
HCI_CONN_SSP_ENABLED,
|
||||
HCI_CONN_SC_ENABLED,
|
||||
HCI_CONN_AES_CCM,
|
||||
HCI_CONN_POWER_SAVE,
|
||||
HCI_CONN_FLUSH_KEY,
|
||||
HCI_CONN_ENCRYPT,
|
||||
HCI_CONN_AUTH,
|
||||
HCI_CONN_SECURE,
|
||||
HCI_CONN_FIPS,
|
||||
HCI_CONN_STK_ENCRYPT,
|
||||
HCI_CONN_AUTH_INITIATOR,
|
||||
HCI_CONN_DROP,
|
||||
HCI_CONN_CANCEL,
|
||||
HCI_CONN_PARAM_REMOVAL_PEND,
|
||||
HCI_CONN_NEW_LINK_KEY,
|
||||
HCI_CONN_SCANNING,
|
||||
HCI_CONN_AUTH_FAILURE,
|
||||
HCI_CONN_PER_ADV,
|
||||
HCI_CONN_BIG_CREATED,
|
||||
HCI_CONN_CREATE,
|
||||
HCI_CONN_CREATE_CIS,
|
||||
HCI_CONN_CREATE_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC_FAILED,
|
||||
HCI_CONN_CREATE_PA_SYNC,
|
||||
HCI_CONN_PA_SYNC,
|
||||
HCI_CONN_PA_SYNC_FAILED,
|
||||
};
|
||||
|
||||
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
return hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
|
||||
test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
|
||||
test_bit(HCI_CONN_SSP_ENABLED, conn->flags);
|
||||
}
|
||||
|
||||
static inline bool hci_conn_sc_enabled(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
return hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
|
||||
test_bit(HCI_CONN_SC_ENABLED, &conn->flags);
|
||||
test_bit(HCI_CONN_SC_ENABLED, conn->flags);
|
||||
}
|
||||
|
||||
static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
|
||||
|
|
@ -1219,7 +1221,7 @@ hci_conn_hash_lookup_create_pa_sync(struct hci_dev *hdev)
|
|||
if (c->type != PA_LINK)
|
||||
continue;
|
||||
|
||||
if (!test_bit(HCI_CONN_CREATE_PA_SYNC, &c->flags))
|
||||
if (!test_bit(HCI_CONN_CREATE_PA_SYNC, c->flags))
|
||||
continue;
|
||||
|
||||
rcu_read_unlock();
|
||||
|
|
@ -1245,7 +1247,7 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
|
|||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (bacmp(&c->dst, ba) || c->type != BIS_LINK ||
|
||||
!test_bit(HCI_CONN_PER_ADV, &c->flags))
|
||||
!test_bit(HCI_CONN_PER_ADV, c->flags))
|
||||
continue;
|
||||
|
||||
if (c->iso_qos.bcast.big == big &&
|
||||
|
|
@ -1502,7 +1504,7 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
|
|||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type != BIS_LINK ||
|
||||
!test_bit(HCI_CONN_PA_SYNC, &c->flags))
|
||||
!test_bit(HCI_CONN_PA_SYNC, c->flags))
|
||||
continue;
|
||||
|
||||
if (c->iso_qos.bcast.big == big) {
|
||||
|
|
@ -1580,7 +1582,7 @@ static inline void hci_conn_hash_list_flag(struct hci_dev *hdev,
|
|||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == type && test_bit(flag, &c->flags))
|
||||
if (c->type == type && test_bit(flag, c->flags))
|
||||
func(c, data);
|
||||
}
|
||||
|
||||
|
|
@ -1598,7 +1600,7 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
|
|||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == LE_LINK && c->state == BT_CONNECT &&
|
||||
!test_bit(HCI_CONN_SCANNING, &c->flags)) {
|
||||
!test_bit(HCI_CONN_SCANNING, c->flags)) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
|
|
@ -1619,7 +1621,7 @@ static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev)
|
|||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == LE_LINK && c->state == BT_CONNECT &&
|
||||
test_bit(HCI_CONN_SCANNING, &c->flags)) {
|
||||
test_bit(HCI_CONN_SCANNING, c->flags)) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2239,10 +2241,10 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
|||
struct hci_cb *cb;
|
||||
__u8 encrypt;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags))
|
||||
return;
|
||||
|
||||
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
|
||||
encrypt = test_bit(HCI_CONN_ENCRYPT, conn->flags) ? 0x01 : 0x00;
|
||||
|
||||
mutex_lock(&hci_cb_list_lock);
|
||||
list_for_each_entry(cb, &hci_cb_list, list) {
|
||||
|
|
@ -2271,9 +2273,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, conn->flags))
|
||||
encrypt = 0x00;
|
||||
else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
|
||||
else if (test_bit(HCI_CONN_AES_CCM, conn->flags))
|
||||
encrypt = 0x02;
|
||||
else
|
||||
encrypt = 0x01;
|
||||
|
|
@ -2426,6 +2428,8 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
|
|||
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
|
||||
|
||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
|
||||
void *hci_sent_cmd_data_len(struct hci_dev *hdev, __u16 opcode,
|
||||
size_t *len);
|
||||
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
|
||||
|
||||
u32 hci_conn_get_phy(struct hci_conn *conn);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ static inline struct espintcp_ctx *espintcp_getctx(const struct sock *sk)
|
|||
{
|
||||
const struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
||||
/* RCU is only needed for diag */
|
||||
return (__force void *)icsk->icsk_ulp_data;
|
||||
/* pairs with smp_wmb() in espintcp_init_sk() */
|
||||
smp_rmb();
|
||||
return (__force void *)READ_ONCE(icsk->icsk_ulp_data);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ struct frag_v6_compare_key {
|
|||
* @tstamp_type: stamp has a mono delivery time (EDT)
|
||||
* @flags: fragment queue flags
|
||||
* @max_size: maximum received fragment size
|
||||
* @truesize: sum of the truesize of queued fragments
|
||||
* @fqdir: pointer to struct fqdir
|
||||
* @rcu: rcu head for freeing deferall
|
||||
*/
|
||||
|
|
@ -100,6 +101,7 @@ struct inet_frag_queue {
|
|||
u8 tstamp_type;
|
||||
__u8 flags;
|
||||
u16 max_size;
|
||||
unsigned int truesize;
|
||||
struct fqdir *fqdir;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
|
@ -172,6 +174,13 @@ extern const u8 ip_frag_ecn_table[16];
|
|||
#define IPFRAG_DUP 1
|
||||
#define IPFRAG_OVERLAP 2
|
||||
#define IPFRAG_MIXED_SECPATH 3
|
||||
#define IPFRAG_TOOBIG 4
|
||||
|
||||
/* Max sum of the truesize of fragments in a queue. This keeps the sums
|
||||
* done in inet_frag_reasm_prepare() and inet_frag_reasm_finish() well
|
||||
* below INT_MAX. Legitimate traffic stays far below this limit.
|
||||
*/
|
||||
#define INET_FRAG_MAX_TRUESIZE (INT_MAX / 2)
|
||||
int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb,
|
||||
int offset, int end);
|
||||
void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb,
|
||||
|
|
|
|||
|
|
@ -201,6 +201,8 @@
|
|||
#include <linux/wireless.h> /* IOCTL user space API */
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
struct net;
|
||||
|
||||
/***************************** VERSION *****************************/
|
||||
/*
|
||||
* This constant is used to know which version of the driver API is
|
||||
|
|
@ -411,10 +413,10 @@ struct iw_spy_data {
|
|||
void wireless_send_event(struct net_device *dev, unsigned int cmd,
|
||||
union iwreq_data *wrqu, const char *extra);
|
||||
#ifdef CONFIG_WEXT_CORE
|
||||
/* flush all previous wext events - if work is done from netdev notifiers */
|
||||
void wireless_nlevent_flush(void);
|
||||
/* Flush previous wext events in this netns from netdev notifiers. */
|
||||
void wireless_nlevent_flush(struct net *net);
|
||||
#else
|
||||
static inline void wireless_nlevent_flush(void) {}
|
||||
static inline void wireless_nlevent_flush(struct net *net) {}
|
||||
#endif
|
||||
|
||||
/* We may need a function to send a stream of events to user space.
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ enum gdma_work_request_flags {
|
|||
|
||||
enum gdma_eqe_type {
|
||||
GDMA_EQE_COMPLETION = 3,
|
||||
GDMA_EQE_THROTTLE = 4,
|
||||
GDMA_EQE_TEST_EVENT = 64,
|
||||
GDMA_EQE_HWC_INIT_EQ_ID_DB = 129,
|
||||
GDMA_EQE_HWC_INIT_DATA = 130,
|
||||
|
|
@ -291,6 +292,7 @@ struct gdma_dev {
|
|||
#define GDMA_EQE_SIZE 16
|
||||
#define GDMA_MAX_SQE_SIZE 512
|
||||
#define GDMA_MAX_RQE_SIZE 256
|
||||
#define GDMA_EQ_MAX_CHILD_CQ 2
|
||||
|
||||
#define GDMA_COMP_DATA_SIZE 0x3C
|
||||
|
||||
|
|
@ -318,6 +320,11 @@ struct gdma_event {
|
|||
|
||||
struct gdma_queue;
|
||||
|
||||
struct gdma_eq_stats {
|
||||
u64_stats_t throttle_count;
|
||||
struct u64_stats_sync syncp;
|
||||
};
|
||||
|
||||
struct mana_eq {
|
||||
struct gdma_queue *eq;
|
||||
struct dentry *mana_eq_debugfs;
|
||||
|
|
@ -371,6 +378,10 @@ struct gdma_queue {
|
|||
unsigned int irq;
|
||||
|
||||
u32 log2_throttle_limit;
|
||||
|
||||
struct gdma_eq_stats stats;
|
||||
|
||||
struct gdma_queue __rcu *child_cq[GDMA_EQ_MAX_CHILD_CQ];
|
||||
} eq;
|
||||
|
||||
struct {
|
||||
|
|
@ -601,6 +612,8 @@ void mana_gd_free_service_wq(struct gdma_context *gc);
|
|||
void mana_gd_ring_dim(struct gdma_queue *cq, u32 mod_usec, bool mod_usec_vld,
|
||||
u32 mod_comps, bool mod_comps_vld);
|
||||
|
||||
bool mana_gd_detach_cq(struct gdma_context *gc, struct gdma_queue *queue);
|
||||
|
||||
struct gdma_wqe {
|
||||
u32 reserved :24;
|
||||
u32 last_vbytes :8;
|
||||
|
|
@ -760,6 +773,9 @@ enum {
|
|||
/* Driver supports non-contiguous queue buffers */
|
||||
#define GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS BIT(30)
|
||||
|
||||
/* Driver supports handling throttled EQEs */
|
||||
#define GDMA_DRV_CAP_FLAG_1_THROTTLED_EVENT_QUEUE BIT_ULL(32)
|
||||
|
||||
/* Capabilities in the PCI-only group below rely on dynamic MSI-X allocation
|
||||
* and on the servicing and reset paths reached through
|
||||
* mana_schedule_serv_work(). Transports that provide neither leave
|
||||
|
|
@ -784,7 +800,8 @@ enum {
|
|||
GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY | \
|
||||
GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT | \
|
||||
GDMA_DRV_CAP_FLAG_1_DYN_INTERRUPT_MODERATION | \
|
||||
GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS)
|
||||
GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS | \
|
||||
GDMA_DRV_CAP_FLAG_1_THROTTLED_EVENT_QUEUE)
|
||||
|
||||
#define GDMA_DRV_CAP_FLAGS2 0
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ enum mana_priv_flag_bits {
|
|||
/* Update this count whenever the respective structures are changed */
|
||||
#define MANA_STATS_RX_COUNT (6 + MANA_CQE_COAL_PKTS_8 - 1)
|
||||
#define MANA_STATS_TX_COUNT 11
|
||||
#define MANA_STATS_EQ_COUNT 1
|
||||
|
||||
#define MANA_RX_FRAG_ALIGNMENT 64
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ struct netns_can {
|
|||
|
||||
/* receive filters subscribed for 'all' CAN devices */
|
||||
struct can_dev_rcv_lists *rx_alldev_list;
|
||||
spinlock_t rcvlists_lock;
|
||||
spinlock_t rcvlists_lock; /* protects the rcv_entries(_max) stats */
|
||||
struct timer_list stattimer; /* timer for statistics update */
|
||||
struct can_pkg_stats *pkg_stats;
|
||||
struct can_rcv_lists_stats *rcv_lists_stats;
|
||||
|
|
|
|||
|
|
@ -1765,6 +1765,13 @@ struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
|||
const xfrm_address_t *saddr,
|
||||
u8 proto,
|
||||
unsigned short family);
|
||||
struct xfrm_state *xfrm_state_lookup_exact(struct net *net, const struct xfrm_mark *mark,
|
||||
const xfrm_address_t *daddr, __be32 spi,
|
||||
u8 proto, unsigned short family);
|
||||
bool xfrm_state_mark_collides(struct net *net, u32 mark,
|
||||
const xfrm_address_t *daddr, __be32 spi,
|
||||
u8 proto, unsigned short family,
|
||||
const struct xfrm_state *self);
|
||||
#ifdef CONFIG_XFRM_SUB_POLICY
|
||||
void xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n,
|
||||
unsigned short family);
|
||||
|
|
|
|||
|
|
@ -1410,7 +1410,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
|
|||
return BATADV_NO_DUP;
|
||||
|
||||
orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
|
||||
if (WARN_ON(!orig_ifinfo)) {
|
||||
if (!orig_ifinfo) {
|
||||
batadv_orig_node_put(orig_node);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1909,6 +1909,11 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Mark aggregate as full before forcing emit. */
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
forw_packet->num_packets = BATADV_MAX_AGGREGATION_PACKETS;
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
batadv_iv_ogm_emit(forw_packet);
|
||||
|
||||
/* we have to have at least one packet in the queue to determine the
|
||||
|
|
|
|||
|
|
@ -653,6 +653,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
|
|||
* NOT_FOUND
|
||||
*/
|
||||
cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND;
|
||||
cands[select].orig_node = NULL;
|
||||
|
||||
/* iterate over the originator list and find the node with the closest
|
||||
* dat_address which has not been selected yet
|
||||
|
|
|
|||
|
|
@ -295,6 +295,11 @@ void batadv_mesh_free(struct net_device *mesh_iface)
|
|||
|
||||
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
|
||||
|
||||
/* the originator purge worker accesses (among others) the TT tables
|
||||
* and must therefore be stopped before any of them are freed
|
||||
*/
|
||||
disable_delayed_work_sync(&bat_priv->orig_work);
|
||||
|
||||
batadv_purge_outstanding_packets(bat_priv, NULL);
|
||||
batadv_tp_stop_all(bat_priv);
|
||||
|
||||
|
|
@ -484,8 +489,10 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
|||
* shut down otherwise the packet may trigger de-reference errors
|
||||
* further down in the receive path.
|
||||
*/
|
||||
if (!kref_get_unless_zero(&hard_iface->refcount))
|
||||
goto err_out;
|
||||
if (!kref_get_unless_zero(&hard_iface->refcount)) {
|
||||
kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
|
||||
|
|
@ -541,7 +548,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
|||
kfree_skb(skb);
|
||||
err_put:
|
||||
batadv_hardif_put(hard_iface);
|
||||
err_out:
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -950,8 +950,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
|
|||
|
||||
dest = (u8 *)mcast_tracker + sizeof(*mcast_tracker);
|
||||
|
||||
/* check if full tracker tvlv is within skb length */
|
||||
/* check if full tracker tvlv, including padding, is within skb length */
|
||||
tvlv_len = sizeof(*mcast_tracker) + ETH_ALEN * num_dests;
|
||||
if (!(num_dests % 2))
|
||||
tvlv_len += 2;
|
||||
|
||||
if (tvlv_len > skb_network_header_len(skb))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ static void batadv_tp_batctl_notify(enum batadv_tp_meter_reason reason,
|
|||
unsigned long start_time, u64 total_sent,
|
||||
u32 cookie)
|
||||
{
|
||||
u32 total_bytes;
|
||||
u64 total_bytes;
|
||||
u32 test_time;
|
||||
u8 result;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@
|
|||
#include <linux/jhash.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/netlink.h>
|
||||
|
|
@ -253,7 +255,8 @@ static int batadv_tt_len(int changes_num)
|
|||
*
|
||||
* Return: local translation table size in bytes.
|
||||
*/
|
||||
static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)
|
||||
static int batadv_tt_local_transmit_size(unsigned int num_vlan,
|
||||
unsigned int num_entries)
|
||||
{
|
||||
int hdr_size;
|
||||
|
||||
|
|
@ -266,6 +269,25 @@ static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)
|
|||
return hdr_size + batadv_tt_len(num_entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_tt_local_transmit_size_max() - get the maximum size of a full local table
|
||||
* response
|
||||
* @bat_priv: the bat priv with all the mesh interface information
|
||||
*
|
||||
* The full table response is sent as a single unicast TVLV packet. Its
|
||||
* tvlv_len, which covers the TVLV header and the TT TVLV, is only 16 bit wide.
|
||||
* This limits the response size even when packet_size_max is larger.
|
||||
*
|
||||
* Return: maximum size of a full local table response in bytes
|
||||
*/
|
||||
static int batadv_tt_local_transmit_size_max(struct batadv_priv *bat_priv)
|
||||
{
|
||||
int tvlv_size_max = sizeof(struct batadv_unicast_tvlv_packet) + U16_MAX;
|
||||
int packet_size_max = READ_ONCE(bat_priv->packet_size_max);
|
||||
|
||||
return min(packet_size_max, tvlv_size_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_tt_local_reserve() - reserve room in the transmittable local table
|
||||
* @bat_priv: the bat priv with all the mesh interface information
|
||||
|
|
@ -290,22 +312,23 @@ static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)
|
|||
static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan,
|
||||
u16 num_entries, int *table_size)
|
||||
{
|
||||
int packet_size_max = READ_ONCE(bat_priv->packet_size_max);
|
||||
int size_max = batadv_tt_local_transmit_size_max(bat_priv);
|
||||
unsigned int entries;
|
||||
unsigned int vlans;
|
||||
|
||||
scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
|
||||
bat_priv->tt.reserved_vlans += num_vlan;
|
||||
bat_priv->tt.reserved_entries += num_entries;
|
||||
vlans = bat_priv->tt.reserved_vlans + num_vlan;
|
||||
entries = bat_priv->tt.reserved_entries + num_entries;
|
||||
|
||||
*table_size = batadv_tt_local_transmit_size(bat_priv->tt.reserved_vlans,
|
||||
bat_priv->tt.reserved_entries);
|
||||
if (*table_size <= packet_size_max)
|
||||
return true;
|
||||
*table_size = batadv_tt_local_transmit_size(vlans, entries);
|
||||
if (*table_size > size_max)
|
||||
return false;
|
||||
|
||||
bat_priv->tt.reserved_vlans -= num_vlan;
|
||||
bat_priv->tt.reserved_entries -= num_entries;
|
||||
bat_priv->tt.reserved_vlans = vlans;
|
||||
bat_priv->tt.reserved_entries = entries;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -339,9 +362,10 @@ static bool batadv_tt_local_reserve_entry(struct batadv_priv *bat_priv,
|
|||
return true;
|
||||
|
||||
net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
|
||||
"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
|
||||
"Local translation table size (%i) exceeds maximum table response size (%i); Ignoring new local tt entry: %pM\n",
|
||||
table_size,
|
||||
READ_ONCE(bat_priv->packet_size_max), addr);
|
||||
batadv_tt_local_transmit_size_max(bat_priv),
|
||||
addr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -374,8 +398,9 @@ bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
|
|||
return true;
|
||||
|
||||
net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
|
||||
"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new VLAN: %d\n",
|
||||
table_size, READ_ONCE(bat_priv->packet_size_max),
|
||||
"Local translation table size (%i) exceeds maximum table response size (%i); Ignoring new VLAN: %d\n",
|
||||
table_size,
|
||||
batadv_tt_local_transmit_size_max(bat_priv),
|
||||
batadv_print_vid(vid));
|
||||
|
||||
return false;
|
||||
|
|
@ -622,6 +647,9 @@ static u16 batadv_tt_flags_get(struct batadv_tt_common_entry *common)
|
|||
* @bat_priv: the bat priv with all the mesh interface information
|
||||
* @common: the TT entry involved in the event
|
||||
* @flags: flags of the TT entry combined with the event flags
|
||||
*
|
||||
* Only a single change per client is queued. It describes the latest state of
|
||||
* the client and a new event therefore replaces an already queued one.
|
||||
*/
|
||||
static void __batadv_tt_local_event(struct batadv_priv *bat_priv,
|
||||
const struct batadv_tt_common_entry *common,
|
||||
|
|
@ -631,7 +659,6 @@ static void __batadv_tt_local_event(struct batadv_priv *bat_priv,
|
|||
struct batadv_tt_change_node *entry;
|
||||
struct batadv_tt_change_node *safe;
|
||||
bool del_op_requested;
|
||||
bool del_op_entry;
|
||||
size_t changes;
|
||||
|
||||
tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);
|
||||
|
|
@ -643,9 +670,17 @@ static void __batadv_tt_local_event(struct batadv_priv *bat_priv,
|
|||
ether_addr_copy(tt_change_node->change.addr, common->addr);
|
||||
tt_change_node->change.vid = htons(common->vid);
|
||||
|
||||
tt_change_node->change.flags = flags;
|
||||
del_op_requested = flags & BATADV_TT_CLIENT_DEL;
|
||||
|
||||
/* the ROAM flag is only meaningful for the receiver in combination
|
||||
* with a DEL. An ADD with ROAM would be interpreted as a roaming
|
||||
* advertisement
|
||||
*/
|
||||
if (!del_op_requested)
|
||||
flags &= ~BATADV_TT_CLIENT_ROAM;
|
||||
|
||||
tt_change_node->change.flags = flags;
|
||||
|
||||
/* check for ADD+DEL, DEL+ADD, ADD+ADD or DEL+DEL events */
|
||||
spin_lock_bh(&bat_priv->tt.changes_list_lock);
|
||||
changes = READ_ONCE(bat_priv->tt.local_changes);
|
||||
|
|
@ -657,25 +692,12 @@ static void __batadv_tt_local_event(struct batadv_priv *bat_priv,
|
|||
if (entry->change.vid != tt_change_node->change.vid)
|
||||
continue;
|
||||
|
||||
del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL;
|
||||
if (del_op_requested != del_op_entry) {
|
||||
/* DEL+ADD in the same orig interval have no effect and
|
||||
* can be removed to avoid silly behaviour on the
|
||||
* receiver side. The other way around (ADD+DEL) can
|
||||
* happen in case of roaming of a client still in the
|
||||
* NEW state. Roaming of NEW clients is now possible due
|
||||
* to automatically recognition of "temporary" clients
|
||||
*/
|
||||
list_del(&entry->list);
|
||||
kmem_cache_free(batadv_tt_change_cache, entry);
|
||||
changes--;
|
||||
} else {
|
||||
/* this is a second add or del in the same originator
|
||||
* interval. It could mean that flags have been changed
|
||||
* (e.g. double add): update them
|
||||
*/
|
||||
entry->change.flags = flags;
|
||||
}
|
||||
/* the other nodes may know the client with different
|
||||
* flags. A DEL+ADD must therefore still announce the
|
||||
* current flags and an ADD+DEL must still remove the
|
||||
* client. ADD+ADD and DEL+DEL only update the flags
|
||||
*/
|
||||
entry->change.flags = flags;
|
||||
|
||||
kmem_cache_free(batadv_tt_change_cache, tt_change_node);
|
||||
goto update_changes;
|
||||
|
|
@ -727,9 +749,9 @@ static u16 batadv_tt_entries(u16 tt_len)
|
|||
*/
|
||||
static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
|
||||
{
|
||||
unsigned int tt_local_entries = 0;
|
||||
struct batadv_meshif_vlan *vlan;
|
||||
u16 tt_local_entries = 0;
|
||||
u16 num_vlan = 0;
|
||||
unsigned int num_vlan = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(vlan, &bat_priv->meshif_vlan_list, list) {
|
||||
|
|
@ -811,14 +833,21 @@ static void batadv_tt_local_add_roam(struct batadv_priv *bat_priv,
|
|||
if (!tt_global)
|
||||
return;
|
||||
|
||||
/* Check whether it is a roaming, but don't do anything if the roaming
|
||||
* process has already been handled
|
||||
/* Handle newly roamed client - but not roamed back client.
|
||||
*
|
||||
* A client which roamed back must always be reclaimed from the
|
||||
* originator(s) which sent the ROAMING_ADV. The global entry created by
|
||||
* this ROAMING_ADV might still carry BATADV_TT_CLIENT_ROAM because the
|
||||
* new originator did not yet announce the client.
|
||||
*/
|
||||
scoped_guard(spinlock_bh, &tt_global->common.flags_lock) {
|
||||
if (tt_global->common.flags & BATADV_TT_CLIENT_ROAM)
|
||||
return;
|
||||
if (!roamed_back) {
|
||||
scoped_guard(spinlock_bh, &tt_global->common.flags_lock) {
|
||||
/* Check whether it is a roaming, but don't do anything
|
||||
* if the roaming process has already been handled.
|
||||
*/
|
||||
if (tt_global->common.flags & BATADV_TT_CLIENT_ROAM)
|
||||
return;
|
||||
|
||||
if (!roamed_back) {
|
||||
/* The global entry has to be marked as ROAMING and has to be
|
||||
* kept for consistency purpose.
|
||||
*
|
||||
|
|
@ -886,6 +915,7 @@ static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv,
|
|||
bool *roamed_back)
|
||||
{
|
||||
struct batadv_tt_common_entry *common = &tt_local->common;
|
||||
bool readded = false;
|
||||
|
||||
tt_local->last_seen = jiffies;
|
||||
|
||||
|
|
@ -900,8 +930,7 @@ static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv,
|
|||
* flag can be reset like it was never enqueued
|
||||
*/
|
||||
common->flags &= ~BATADV_TT_CLIENT_PENDING;
|
||||
|
||||
return true;
|
||||
readded = true;
|
||||
}
|
||||
|
||||
if (common->flags & BATADV_TT_CLIENT_ROAM) {
|
||||
|
|
@ -918,7 +947,7 @@ static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv,
|
|||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return readded;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1319,22 +1348,24 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
|
|||
* batadv_tt_tvlv_container_update() - update the translation table tvlv
|
||||
* container after local tt changes have been committed
|
||||
* @bat_priv: the bat priv with all the mesh interface information
|
||||
* @changes_list: list of committed changes which will be consumed
|
||||
* @tt_diff_entries_num: number of entries in @changes_list
|
||||
*/
|
||||
static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
|
||||
static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv,
|
||||
struct list_head *changes_list,
|
||||
int tt_diff_entries_num)
|
||||
{
|
||||
struct batadv_tvlv_tt_change *tt_change;
|
||||
struct batadv_tt_change_node *entry;
|
||||
struct batadv_tvlv_tt_data *tt_data;
|
||||
struct batadv_tt_change_node *safe;
|
||||
int tt_diff_entries_count = 0;
|
||||
int tt_diff_entries_num = 0;
|
||||
bool drop_changes = false;
|
||||
size_t tt_extra_len = 0;
|
||||
int tt_change_len = 0;
|
||||
int tt_diff_len;
|
||||
u16 tvlv_len;
|
||||
|
||||
tt_diff_entries_num = READ_ONCE(bat_priv->tt.local_changes);
|
||||
tt_diff_len = batadv_tt_len(tt_diff_entries_num);
|
||||
|
||||
/* if we have too many changes for one packet don't send any
|
||||
|
|
@ -1352,19 +1383,39 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
|
|||
|
||||
tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data,
|
||||
&tt_change, &tt_diff_len);
|
||||
if (!tvlv_len)
|
||||
if (!tvlv_len) {
|
||||
/* the changes are lost and the other nodes have to request the
|
||||
* full table. Don't answer TT requests for the new TTVN with
|
||||
* the stale changeset of the previous TTVN. Without changes, the
|
||||
* TTVN was not incremented and the last changeset is still valid
|
||||
*/
|
||||
if (drop_changes || tt_diff_entries_num > 0) {
|
||||
list_for_each_entry_safe(entry, safe, changes_list,
|
||||
list) {
|
||||
list_del(&entry->list);
|
||||
kmem_cache_free(batadv_tt_change_cache, entry);
|
||||
}
|
||||
|
||||
spin_lock_bh(&bat_priv->tt.last_changeset_lock);
|
||||
kfree(bat_priv->tt.last_changeset);
|
||||
bat_priv->tt.last_changeset = NULL;
|
||||
bat_priv->tt.last_changeset_len = 0;
|
||||
spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
|
||||
}
|
||||
|
||||
/* announce the already committed TTVN in the next OGM interval
|
||||
* instead of waiting for BATADV_TT_OGM_APPEND_MAX idle rounds
|
||||
*/
|
||||
atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
tt_data->flags = BATADV_TT_OGM_DIFF;
|
||||
|
||||
if (!drop_changes && tt_diff_len == 0)
|
||||
goto container_register;
|
||||
|
||||
spin_lock_bh(&bat_priv->tt.changes_list_lock);
|
||||
WRITE_ONCE(bat_priv->tt.local_changes, 0);
|
||||
|
||||
list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
|
||||
list) {
|
||||
list_for_each_entry_safe(entry, safe, changes_list, list) {
|
||||
if (tt_diff_entries_count < tt_diff_entries_num) {
|
||||
memcpy(tt_change + tt_diff_entries_count,
|
||||
&entry->change,
|
||||
|
|
@ -1374,7 +1425,6 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
|
|||
list_del(&entry->list);
|
||||
kmem_cache_free(batadv_tt_change_cache, entry);
|
||||
}
|
||||
spin_unlock_bh(&bat_priv->tt.changes_list_lock);
|
||||
|
||||
tt_extra_len = batadv_tt_len(tt_diff_entries_num -
|
||||
tt_diff_entries_count);
|
||||
|
|
@ -3342,8 +3392,12 @@ static bool batadv_tt_local_valid(void *entry_ptr,
|
|||
if (tt_flags & BATADV_TT_CLIENT_NEW)
|
||||
return false;
|
||||
|
||||
/* the entry is (still) announced. A ROAM flag of an entry which is
|
||||
* pending to be removed must not be transmitted. Otherwise, the
|
||||
* receiver would handle it like a roaming advertisement
|
||||
*/
|
||||
if (flags)
|
||||
*flags = tt_flags;
|
||||
*flags = tt_flags & ~BATADV_TT_CLIENT_ROAM;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3922,21 +3976,30 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
|
|||
u16 tt_num_changes, u8 ttvn)
|
||||
{
|
||||
int roams;
|
||||
u8 flags;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tt_num_changes; i++) {
|
||||
if ((tt_change + i)->flags & BATADV_TT_CLIENT_DEL) {
|
||||
roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM;
|
||||
flags = (tt_change + i)->flags;
|
||||
|
||||
if (flags & BATADV_TT_CLIENT_DEL) {
|
||||
roams = flags & BATADV_TT_CLIENT_ROAM;
|
||||
batadv_tt_global_del(bat_priv, orig_node,
|
||||
(tt_change + i)->addr,
|
||||
ntohs((tt_change + i)->vid),
|
||||
"tt removed by changes",
|
||||
roams);
|
||||
} else {
|
||||
/* an announced client cannot have roamed away from
|
||||
* orig_node. Only a ROAMING_ADV is allowed to add a
|
||||
* global entry with the ROAM flag
|
||||
*/
|
||||
flags &= ~BATADV_TT_CLIENT_ROAM;
|
||||
|
||||
if (!batadv_tt_global_add(bat_priv, orig_node,
|
||||
(tt_change + i)->addr,
|
||||
ntohs((tt_change + i)->vid),
|
||||
(tt_change + i)->flags, ttvn))
|
||||
flags, ttvn))
|
||||
/* In case of problem while storing a
|
||||
* global_entry, we stop the updating
|
||||
* procedure without committing the
|
||||
|
|
@ -4412,11 +4475,25 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
|
|||
*/
|
||||
static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
|
||||
{
|
||||
LIST_HEAD(changes_list);
|
||||
int changes_num;
|
||||
|
||||
lockdep_assert_held(&bat_priv->tt.commit_lock);
|
||||
|
||||
if (READ_ONCE(bat_priv->tt.local_changes) == 0) {
|
||||
/* atomically get the list of changes. The TTVN decision and the
|
||||
* announced changeset must cover the same events. Events queued
|
||||
* afterwards stay in the tt.changes_list for the next TT round
|
||||
*/
|
||||
spin_lock_bh(&bat_priv->tt.changes_list_lock);
|
||||
list_splice_init(&bat_priv->tt.changes_list, &changes_list);
|
||||
changes_num = READ_ONCE(bat_priv->tt.local_changes);
|
||||
WRITE_ONCE(bat_priv->tt.local_changes, 0);
|
||||
spin_unlock_bh(&bat_priv->tt.changes_list_lock);
|
||||
|
||||
if (changes_num == 0) {
|
||||
if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
|
||||
batadv_tt_tvlv_container_update(bat_priv);
|
||||
batadv_tt_tvlv_container_update(bat_priv, &changes_list,
|
||||
changes_num);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4433,7 +4510,7 @@ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
|
|||
|
||||
/* reset the sending counter */
|
||||
atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
|
||||
batadv_tt_tvlv_container_update(bat_priv);
|
||||
batadv_tt_tvlv_container_update(bat_priv, &changes_list, changes_num);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -4675,16 +4752,16 @@ void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface)
|
|||
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
|
||||
int timeout = BATADV_TT_LOCAL_TIMEOUT / 2;
|
||||
bool reduced = false;
|
||||
int packet_size_max;
|
||||
int table_size;
|
||||
int size_max;
|
||||
|
||||
packet_size_max = READ_ONCE(bat_priv->packet_size_max);
|
||||
size_max = batadv_tt_local_transmit_size_max(bat_priv);
|
||||
|
||||
spin_lock_bh(&bat_priv->tt.commit_lock);
|
||||
|
||||
while (timeout) {
|
||||
table_size = batadv_tt_local_table_transmit_size(bat_priv);
|
||||
if (packet_size_max >= table_size)
|
||||
if (size_max >= table_size)
|
||||
break;
|
||||
|
||||
batadv_tt_local_purge(bat_priv, timeout);
|
||||
|
|
@ -4693,8 +4770,8 @@ void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface)
|
|||
timeout /= 2;
|
||||
reduced = true;
|
||||
net_ratelimited_function(batadv_info, mesh_iface,
|
||||
"Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n",
|
||||
packet_size_max);
|
||||
"Forced to purge local tt entries to fit new maximum table response size (%i)\n",
|
||||
size_max);
|
||||
}
|
||||
|
||||
/* commit these changes immediately, to avoid synchronization problem
|
||||
|
|
|
|||
|
|
@ -2047,7 +2047,13 @@ struct batadv_forw_packet {
|
|||
/** @direct_link_flags: direct link flags for aggregated OGM packets */
|
||||
DECLARE_BITMAP(direct_link_flags, BATADV_MAX_AGGREGATION_PACKETS);
|
||||
|
||||
/** @num_packets: counter for aggregated OGMv1 packets */
|
||||
/**
|
||||
* @num_packets: counter for aggregated OGMv1 packets. Set to
|
||||
* BATADV_MAX_AGGREGATION_PACKETS (independent of the real number of
|
||||
* aggregated packets) by the send worker to prevent further
|
||||
* aggregation while the packet is emitted. It must therefore not be
|
||||
* used to iterate over the aggregated packets
|
||||
*/
|
||||
u8 num_packets;
|
||||
|
||||
/** @delayed_work: work queue callback item for packet sending */
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ static void hci_conn_cleanup(struct hci_conn *conn)
|
|||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
|
||||
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, conn->flags))
|
||||
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
|
||||
hci_chan_list_flush(conn);
|
||||
|
|
@ -685,7 +685,7 @@ static void hci_conn_idle(struct work_struct *work)
|
|||
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags)) {
|
||||
struct hci_cp_sniff_mode cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
|
||||
|
|
@ -814,7 +814,7 @@ static int hci_le_terminate_big(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
|
||||
d->big = conn->iso_qos.bcast.big;
|
||||
d->bis = conn->iso_qos.bcast.bis;
|
||||
d->big_term = test_and_clear_bit(HCI_CONN_BIG_CREATED, &conn->flags);
|
||||
d->big_term = test_and_clear_bit(HCI_CONN_BIG_CREATED, conn->flags);
|
||||
|
||||
ret = hci_cmd_sync_queue(hdev, terminate_big_sync, d,
|
||||
terminate_big_destroy);
|
||||
|
|
@ -867,7 +867,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
d->sync_handle = conn->sync_handle;
|
||||
|
||||
if (conn->type == PA_LINK &&
|
||||
test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
|
||||
test_and_clear_bit(HCI_CONN_PA_SYNC, conn->flags)) {
|
||||
hci_conn_hash_list_flag(hdev, find_bis, PA_LINK,
|
||||
HCI_CONN_PA_SYNC, d);
|
||||
|
||||
|
|
@ -877,7 +877,7 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
d->count = 0;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags)) {
|
||||
if (test_and_clear_bit(HCI_CONN_BIG_SYNC, conn->flags)) {
|
||||
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
|
||||
HCI_CONN_BIG_SYNC, d);
|
||||
|
||||
|
|
@ -912,7 +912,7 @@ static void bis_cleanup(struct hci_conn *conn)
|
|||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
if (conn->role == HCI_ROLE_MASTER) {
|
||||
if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags))
|
||||
if (!test_and_clear_bit(HCI_CONN_PER_ADV, conn->flags))
|
||||
return;
|
||||
|
||||
/* Check if ISO connection is a BIS and terminate advertising
|
||||
|
|
@ -1087,7 +1087,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
|
|||
conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
|
||||
conn->sid = HCI_SID_INVALID;
|
||||
|
||||
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
|
||||
set_bit(HCI_CONN_POWER_SAVE, conn->flags);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
|
||||
/* Set Default Authenticated payload timeout to 30s */
|
||||
|
|
@ -1217,8 +1217,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
|
|||
case BIS_LINK:
|
||||
case PA_LINK:
|
||||
if ((conn->state != BT_CONNECTED &&
|
||||
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
|
||||
test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
|
||||
!test_bit(HCI_CONN_CREATE_CIS, conn->flags)) ||
|
||||
test_bit(HCI_CONN_BIG_CREATED, conn->flags))
|
||||
hci_conn_failed(conn, reason);
|
||||
break;
|
||||
}
|
||||
|
|
@ -1234,8 +1234,8 @@ static void hci_conn_unlink(struct hci_conn *conn)
|
|||
struct hci_link *link, *t;
|
||||
|
||||
conn->sec_level = BT_SECURITY_SDP;
|
||||
clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, conn->flags);
|
||||
/* mgmt_security_level_changed() reports the reset only if
|
||||
* this connection was actually reported to userspace as
|
||||
* connected (it checks HCI_CONN_MGMT_CONNECTED itself), so it
|
||||
|
|
@ -1254,7 +1254,7 @@ static void hci_conn_unlink(struct hci_conn *conn)
|
|||
*/
|
||||
if (test_bit(HCI_UP, &hdev->flags))
|
||||
mgmt_security_level_changed(conn);
|
||||
clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
|
||||
clear_bit(HCI_CONN_MGMT_CONNECTED, conn->flags);
|
||||
|
||||
list_for_each_entry_safe(link, t, &conn->link_list, list) {
|
||||
struct hci_conn *child = link->conn;
|
||||
|
|
@ -1458,8 +1458,8 @@ void hci_conn_failed(struct hci_conn *conn, u8 status)
|
|||
/* In case of BIG/PA sync failed, clear conn flags so that
|
||||
* the conns will be correctly cleaned up by ISO layer
|
||||
*/
|
||||
test_and_clear_bit(HCI_CONN_BIG_SYNC_FAILED, &conn->flags);
|
||||
test_and_clear_bit(HCI_CONN_PA_SYNC_FAILED, &conn->flags);
|
||||
test_and_clear_bit(HCI_CONN_BIG_SYNC_FAILED, conn->flags);
|
||||
test_and_clear_bit(HCI_CONN_PA_SYNC_FAILED, conn->flags);
|
||||
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
|
|
@ -1524,9 +1524,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
* to connect.
|
||||
*/
|
||||
conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
|
||||
if (conn && !test_bit(HCI_CONN_SCANNING, &conn->flags)) {
|
||||
if (conn && !test_bit(HCI_CONN_SCANNING, conn->flags))
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
/* Check if the destination address has been resolved by the controller
|
||||
* since if it did then the identity address shall be used.
|
||||
|
|
@ -1614,8 +1613,9 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev,
|
|||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
/* If we created new params, mark them to be deleted in
|
||||
* hci_connect_le_scan_cleanup. It's different case than
|
||||
/* If we created new params, mark them to be deleted once the
|
||||
* explicit connect is done, see le_conn_complete_evt() and
|
||||
* hci_connect_le_scan_cleanup(). It's different case than
|
||||
* existing disabled params, those will stay after cleanup.
|
||||
*/
|
||||
params->auto_connect = HCI_AUTO_CONN_EXPLICIT;
|
||||
|
|
@ -1797,7 +1797,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
}
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
set_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||
set_bit(HCI_CONN_SCANNING, conn->flags);
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
conn->pending_sec_level = sec_level;
|
||||
conn->conn_timeout = conn_timeout;
|
||||
|
|
@ -1923,12 +1923,12 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
|||
|
||||
if (acl->state == BT_CONNECTED &&
|
||||
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
|
||||
set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
|
||||
set_bit(HCI_CONN_POWER_SAVE, acl->flags);
|
||||
hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
|
||||
|
||||
if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) {
|
||||
if (test_bit(HCI_CONN_MODE_CHANGE_PEND, acl->flags)) {
|
||||
/* defer SCO setup until mode change completed */
|
||||
set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags);
|
||||
set_bit(HCI_CONN_SCO_SETUP_PEND, acl->flags);
|
||||
return sco;
|
||||
}
|
||||
|
||||
|
|
@ -2220,7 +2220,7 @@ int hci_le_create_cis_pending(struct hci_dev *hdev)
|
|||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, conn->flags)) {
|
||||
rcu_read_unlock();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
@ -2448,7 +2448,7 @@ static void bis_mark_per_adv(struct hci_conn *conn, void *data)
|
|||
d->bis != conn->iso_qos.bcast.bis)
|
||||
return;
|
||||
|
||||
set_bit(HCI_CONN_PER_ADV, &conn->flags);
|
||||
set_bit(HCI_CONN_PER_ADV, conn->flags);
|
||||
}
|
||||
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
|
|
@ -2568,7 +2568,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
|
|||
*/
|
||||
if (hci_dev_test_flag(conn->hdev, HCI_SC_ONLY)) {
|
||||
if (!hci_conn_sc_enabled(conn) ||
|
||||
!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
|
||||
!test_bit(HCI_CONN_AES_CCM, conn->flags) ||
|
||||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2584,14 +2584,14 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
|
|||
* not shortened)
|
||||
*/
|
||||
if (conn->sec_level == BT_SECURITY_FIPS &&
|
||||
!test_bit(HCI_CONN_AES_CCM, &conn->flags)) {
|
||||
!test_bit(HCI_CONN_AES_CCM, conn->flags)) {
|
||||
bt_dev_err(conn->hdev,
|
||||
"Invalid security: Missing AES-CCM usage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hci_conn_ssp_enabled(conn) &&
|
||||
!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
!test_bit(HCI_CONN_ENCRYPT, conn->flags))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
|
|
@ -2607,7 +2607,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
|||
|
||||
if (sec_level > conn->sec_level)
|
||||
conn->pending_sec_level = sec_level;
|
||||
else if (test_bit(HCI_CONN_AUTH, &conn->flags))
|
||||
else if (test_bit(HCI_CONN_AUTH, conn->flags))
|
||||
return 1;
|
||||
|
||||
/* Make sure we preserve an existing MITM requirement*/
|
||||
|
|
@ -2615,7 +2615,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
|||
|
||||
conn->auth_type = auth_type;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, conn->flags)) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
|
|
@ -2625,8 +2625,8 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
|||
/* Set the ENCRYPT_PEND to trigger encryption after
|
||||
* authentication.
|
||||
*/
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, conn->flags))
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -2637,7 +2637,7 @@ static void hci_conn_encrypt(struct hci_conn *conn)
|
|||
{
|
||||
BT_DBG("hcon %p", conn);
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, conn->flags)) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.encrypt = 0x01;
|
||||
|
|
@ -2665,7 +2665,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
|
|||
return 1;
|
||||
|
||||
/* For other security levels we need the link key. */
|
||||
if (!test_bit(HCI_CONN_AUTH, &conn->flags))
|
||||
if (!test_bit(HCI_CONN_AUTH, conn->flags))
|
||||
goto auth;
|
||||
|
||||
switch (conn->key_type) {
|
||||
|
|
@ -2705,17 +2705,17 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
|
|||
}
|
||||
|
||||
auth:
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags))
|
||||
return 0;
|
||||
|
||||
if (initiator)
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, conn->flags);
|
||||
|
||||
if (!hci_conn_auth(conn, sec_level, auth_type))
|
||||
return 0;
|
||||
|
||||
encrypt:
|
||||
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_ENCRYPT, conn->flags)) {
|
||||
/* Ensure that the encryption key size has been read,
|
||||
* otherwise stall the upper layer responses.
|
||||
*/
|
||||
|
|
@ -2758,7 +2758,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
|
|||
if (role == conn->role)
|
||||
return 1;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, conn->flags)) {
|
||||
struct hci_cp_switch_role cp;
|
||||
bacpy(&cp.bdaddr, &conn->dst);
|
||||
cp.role = role;
|
||||
|
|
@ -2779,10 +2779,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
|
|||
if (conn->mode != HCI_CM_SNIFF)
|
||||
goto timer;
|
||||
|
||||
if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active)
|
||||
if (!test_bit(HCI_CONN_POWER_SAVE, conn->flags) && !force_active)
|
||||
goto timer;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags)) {
|
||||
struct hci_cp_exit_sniff_mode cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp);
|
||||
|
|
@ -2822,16 +2822,16 @@ static u32 get_link_mode(struct hci_conn *conn)
|
|||
if (conn->role == HCI_ROLE_MASTER)
|
||||
link_mode |= HCI_LM_MASTER;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
if (test_bit(HCI_CONN_ENCRYPT, conn->flags))
|
||||
link_mode |= HCI_LM_ENCRYPT;
|
||||
|
||||
if (test_bit(HCI_CONN_AUTH, &conn->flags))
|
||||
if (test_bit(HCI_CONN_AUTH, conn->flags))
|
||||
link_mode |= HCI_LM_AUTH;
|
||||
|
||||
if (test_bit(HCI_CONN_SECURE, &conn->flags))
|
||||
if (test_bit(HCI_CONN_SECURE, conn->flags))
|
||||
link_mode |= HCI_LM_SECURE;
|
||||
|
||||
if (test_bit(HCI_CONN_FIPS, &conn->flags))
|
||||
if (test_bit(HCI_CONN_FIPS, conn->flags))
|
||||
link_mode |= HCI_LM_FIPS;
|
||||
|
||||
return link_mode;
|
||||
|
|
@ -2945,7 +2945,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
|
|||
|
||||
BT_DBG("%s hcon %p", hdev->name, conn);
|
||||
|
||||
if (test_bit(HCI_CONN_DROP, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_DROP, conn->flags)) {
|
||||
BT_DBG("Refusing to create new hci_chan");
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -2975,7 +2975,7 @@ void hci_chan_del(struct hci_chan *chan)
|
|||
synchronize_rcu();
|
||||
|
||||
/* Prevent new hci_chan's to be created for this hci_conn */
|
||||
set_bit(HCI_CONN_DROP, &conn->flags);
|
||||
set_bit(HCI_CONN_DROP, conn->flags);
|
||||
|
||||
hci_conn_put(conn);
|
||||
|
||||
|
|
|
|||
|
|
@ -1075,17 +1075,16 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(k, &hdev->link_keys, list) {
|
||||
if (bacmp(bdaddr, &k->bdaddr) == 0) {
|
||||
rcu_read_unlock();
|
||||
|
||||
if (hci_is_blocked_key(hdev,
|
||||
HCI_BLOCKED_KEY_TYPE_LINKKEY,
|
||||
k->val)) {
|
||||
bt_dev_warn_ratelimited(hdev,
|
||||
"Link key blocked for %pMR",
|
||||
&k->bdaddr);
|
||||
return NULL;
|
||||
k = NULL;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
|
@ -1252,7 +1251,6 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
key = kzalloc_obj(*key);
|
||||
if (!key)
|
||||
return NULL;
|
||||
list_add_rcu(&key->list, &hdev->link_keys);
|
||||
}
|
||||
|
||||
BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
|
||||
|
|
@ -1276,6 +1274,9 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
else
|
||||
key->type = type;
|
||||
|
||||
if (!old_key)
|
||||
list_add_rcu(&key->list, &hdev->link_keys);
|
||||
|
||||
if (persistent)
|
||||
*persistent = hci_persistent_key(hdev, conn, type,
|
||||
old_key_type);
|
||||
|
|
@ -1297,7 +1298,6 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
key = kzalloc_obj(*key);
|
||||
if (!key)
|
||||
return NULL;
|
||||
list_add_rcu(&key->list, &hdev->long_term_keys);
|
||||
}
|
||||
|
||||
bacpy(&key->bdaddr, bdaddr);
|
||||
|
|
@ -1309,15 +1309,19 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
key->enc_size = enc_size;
|
||||
key->type = type;
|
||||
|
||||
if (!old_key)
|
||||
list_add_rcu(&key->list, &hdev->long_term_keys);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type, u8 val[16], bdaddr_t *rpa)
|
||||
{
|
||||
struct smp_irk *irk;
|
||||
struct smp_irk *irk, *old_irk;
|
||||
|
||||
irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
|
||||
old_irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
|
||||
irk = old_irk;
|
||||
if (!irk) {
|
||||
irk = kzalloc_obj(*irk);
|
||||
if (!irk)
|
||||
|
|
@ -1326,12 +1330,14 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
bacpy(&irk->bdaddr, bdaddr);
|
||||
irk->addr_type = addr_type;
|
||||
|
||||
list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
|
||||
}
|
||||
|
||||
memcpy(irk->val, val, 16);
|
||||
bacpy(&irk->rpa, rpa);
|
||||
|
||||
if (!old_irk)
|
||||
list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
|
||||
|
||||
return irk;
|
||||
}
|
||||
|
||||
|
|
@ -1694,6 +1700,9 @@ struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
|
|||
if (!adv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_DELAYED_WORK(&adv->rpa_expired_cb,
|
||||
adv_instance_rpa_expired);
|
||||
|
||||
adv->pending = true;
|
||||
adv->instance = instance;
|
||||
|
||||
|
|
@ -1730,8 +1739,6 @@ struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
|
|||
else
|
||||
adv->duration = duration;
|
||||
|
||||
INIT_DELAYED_WORK(&adv->rpa_expired_cb, adv_instance_rpa_expired);
|
||||
|
||||
BT_DBG("%s for %dMR", hdev->name, instance);
|
||||
|
||||
return adv;
|
||||
|
|
@ -2897,8 +2904,14 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
{
|
||||
u8 dev_pkt_type;
|
||||
|
||||
/* Pairs with synchronize_rcu() in hci_dev_close_sync(), so rx_work is
|
||||
* not queued once the workqueue is being drained.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
|
||||
if (!hdev || (!test_bit(HCI_UP, &hdev->flags)
|
||||
&& !test_bit(HCI_INIT, &hdev->flags))) {
|
||||
rcu_read_unlock();
|
||||
kfree_skb(skb);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
|
@ -2934,6 +2947,7 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
case HCI_DRV_PKT:
|
||||
break;
|
||||
default:
|
||||
rcu_read_unlock();
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -2947,6 +2961,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
skb_queue_tail(&hdev->rx_q, skb);
|
||||
queue_work(hdev->workqueue, &hdev->rx_work);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_recv_frame);
|
||||
|
|
@ -3159,7 +3175,8 @@ int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
|
|||
EXPORT_SYMBOL(__hci_cmd_send);
|
||||
|
||||
/* Get data from the previously sent command */
|
||||
static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode)
|
||||
static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode,
|
||||
size_t *len)
|
||||
{
|
||||
struct hci_command_hdr *hdr;
|
||||
|
||||
|
|
@ -3171,21 +3188,30 @@ static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode)
|
|||
if (hdr->opcode != cpu_to_le16(opcode))
|
||||
return NULL;
|
||||
|
||||
if (len)
|
||||
*len = skb->len - HCI_COMMAND_HDR_SIZE;
|
||||
|
||||
return skb->data + HCI_COMMAND_HDR_SIZE;
|
||||
}
|
||||
|
||||
/* Get data from the previously sent command */
|
||||
void *hci_sent_cmd_data_len(struct hci_dev *hdev, __u16 opcode, size_t *len)
|
||||
{
|
||||
void *data;
|
||||
|
||||
/* Check if opcode matches last sent command */
|
||||
data = hci_cmd_data(hdev->sent_cmd, opcode, len);
|
||||
if (!data)
|
||||
/* Check if opcode matches last request */
|
||||
data = hci_cmd_data(hdev->req_skb, opcode, len);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Get data from the previously sent command */
|
||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
|
||||
{
|
||||
void *data;
|
||||
|
||||
/* Check if opcode matches last sent command */
|
||||
data = hci_cmd_data(hdev->sent_cmd, opcode);
|
||||
if (!data)
|
||||
/* Check if opcode matches last request */
|
||||
data = hci_cmd_data(hdev->req_skb, opcode);
|
||||
|
||||
return data;
|
||||
return hci_sent_cmd_data_len(hdev, opcode, NULL);
|
||||
}
|
||||
|
||||
/* Get data from last received event */
|
||||
|
|
|
|||
|
|
@ -724,6 +724,8 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
|
|||
goto done;
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->flags);
|
||||
|
||||
/* While unexpected, the read_enc_key_size command may fail. The most
|
||||
* secure approach is to then assume the key size is 0 to force a
|
||||
* disconnection.
|
||||
|
|
@ -732,8 +734,8 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
|
|||
bt_dev_err(hdev, "failed to read key size for handle %u",
|
||||
handle);
|
||||
conn->enc_key_size = 0;
|
||||
clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, conn->flags);
|
||||
/* Downgrade to reflect the connection no longer being
|
||||
* encrypted, instead of advertising a stale elevated
|
||||
* security level alongside MGMT_CONN_SEC_ENCRYPT_NONE.
|
||||
|
|
@ -761,8 +763,8 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
|
|||
* state at a later time.
|
||||
*/
|
||||
status = HCI_ERROR_AUTH_FAILURE;
|
||||
clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, conn->flags);
|
||||
/* Downgrade to reflect the connection no longer being
|
||||
* encrypted, instead of advertising a stale elevated
|
||||
* security level alongside MGMT_CONN_SEC_ENCRYPT_NONE.
|
||||
|
|
@ -1817,6 +1819,8 @@ static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
|
||||
static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
|
||||
{
|
||||
bool timeout_pending;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
switch (enable) {
|
||||
|
|
@ -1846,10 +1850,18 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
|
|||
/* Cancel this timer so that we don't try to disable scanning
|
||||
* when it's already disabled.
|
||||
*/
|
||||
cancel_delayed_work(&hdev->le_scan_disable);
|
||||
timeout_pending = cancel_delayed_work(&hdev->le_scan_disable);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_LE_SCAN);
|
||||
|
||||
/* The scan of a discovery was disabled before its timeout,
|
||||
* for example by a Stop Discovery that timed out and still
|
||||
* completed, so nothing else would end the discovery.
|
||||
*/
|
||||
if (timeout_pending &&
|
||||
hdev->discovery.state == DISCOVERY_FINDING)
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
|
||||
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
|
||||
hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY) &&
|
||||
!test_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
|
|
@ -2595,10 +2607,10 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
|
|||
if (!hci_outgoing_auth_needed(hdev, conn))
|
||||
goto unlock;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, conn->flags)) {
|
||||
struct hci_cp_auth_requested auth_cp;
|
||||
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, conn->flags);
|
||||
|
||||
auth_cp.handle = __cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
|
||||
|
|
@ -2738,9 +2750,9 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
|
|||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
if (conn) {
|
||||
clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags);
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, conn->flags))
|
||||
hci_sco_setup(conn, status);
|
||||
}
|
||||
|
||||
|
|
@ -2765,9 +2777,9 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
|
|||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
if (conn) {
|
||||
clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_MODE_CHANGE_PEND, conn->flags);
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, conn->flags))
|
||||
hci_sco_setup(conn, status);
|
||||
}
|
||||
|
||||
|
|
@ -2824,10 +2836,10 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
|
|||
* still needs it set to know whether to report the security level
|
||||
* reset, so it is consumed there instead of here.
|
||||
*/
|
||||
mgmt_conn = test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
|
||||
mgmt_conn = test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags);
|
||||
|
||||
if (conn->type == ACL_LINK) {
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
}
|
||||
|
||||
|
|
@ -3078,7 +3090,7 @@ static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
|
|||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
||||
if (conn)
|
||||
clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_RSWITCH_PEND, conn->flags);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
|
@ -3190,6 +3202,7 @@ static int hci_read_enc_key_size(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
{
|
||||
struct hci_cp_read_enc_key_size cp;
|
||||
u8 *key_enc_size = hci_conn_key_enc_size(conn);
|
||||
int err;
|
||||
|
||||
if (!read_key_size_capable(hdev)) {
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
|
|
@ -3211,7 +3224,14 @@ static int hci_read_enc_key_size(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
else
|
||||
conn->enc_key_size = hdev->min_enc_key_size;
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
|
||||
/* L2CAP holds incoming data back until the key size is known */
|
||||
set_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->flags);
|
||||
|
||||
err = hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
|
||||
if (err)
|
||||
clear_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
|
||||
|
|
@ -3309,19 +3329,19 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
|
|||
hci_conn_add_sysfs(conn);
|
||||
|
||||
if (test_bit(HCI_AUTH, &hdev->flags))
|
||||
set_bit(HCI_CONN_AUTH, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH, conn->flags);
|
||||
|
||||
if (test_bit(HCI_ENCRYPT, &hdev->flags))
|
||||
set_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
set_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
|
||||
/* "Link key request" completed ahead of "connect request" completes */
|
||||
if (ev->encr_mode == 1 && !test_bit(HCI_CONN_ENCRYPT, &conn->flags) &&
|
||||
if (ev->encr_mode == 1 && !test_bit(HCI_CONN_ENCRYPT, conn->flags) &&
|
||||
ev->link_type == ACL_LINK) {
|
||||
struct link_key *key;
|
||||
|
||||
key = hci_find_link_key(hdev, &ev->bdaddr);
|
||||
if (key) {
|
||||
set_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
set_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
hci_read_enc_key_size(hdev, conn);
|
||||
hci_encrypt_cfm(conn, ev->status);
|
||||
}
|
||||
|
|
@ -3525,9 +3545,9 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
|
|||
* still needs it set to know whether to report the security level
|
||||
* reset, so it is consumed there instead of here.
|
||||
*/
|
||||
mgmt_connected = test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
|
||||
mgmt_connected = test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags);
|
||||
|
||||
if (test_bit(HCI_CONN_AUTH_FAILURE, &conn->flags))
|
||||
if (test_bit(HCI_CONN_AUTH_FAILURE, conn->flags))
|
||||
reason = MGMT_DEV_DISCONN_AUTH_FAILURE;
|
||||
else
|
||||
reason = hci_to_mgmt_reason(ev->reason);
|
||||
|
|
@ -3536,7 +3556,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
|
|||
reason, mgmt_connected);
|
||||
|
||||
if (conn->type == ACL_LINK) {
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
|
||||
hci_update_scan(hdev);
|
||||
|
|
@ -3607,8 +3627,8 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
|
||||
if (!ev->status) {
|
||||
clear_bit(HCI_CONN_AUTH_FAILURE, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH, &conn->flags);
|
||||
clear_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
|
||||
set_bit(HCI_CONN_AUTH, conn->flags);
|
||||
/* Authentication alone does not imply encryption: for SSP
|
||||
* capable devices, encryption is only requested below, and
|
||||
* for legacy devices it is requested by a separate
|
||||
|
|
@ -3621,12 +3641,12 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
|
|||
conn->sec_level = conn->pending_sec_level;
|
||||
} else {
|
||||
if (ev->status == HCI_ERROR_PIN_OR_KEY_MISSING)
|
||||
set_bit(HCI_CONN_AUTH_FAILURE, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
|
||||
|
||||
mgmt_auth_failed(conn, ev->status);
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_AUTH_PEND, conn->flags);
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status && hci_conn_ssp_enabled(conn)) {
|
||||
|
|
@ -3648,7 +3668,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
|
|||
hci_conn_drop(conn);
|
||||
}
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags)) {
|
||||
if (!ev->status) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = ev->handle;
|
||||
|
|
@ -3656,7 +3676,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data,
|
|||
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
|
||||
&cp);
|
||||
} else {
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
|
||||
hci_encrypt_cfm(conn, ev->status);
|
||||
}
|
||||
}
|
||||
|
|
@ -3693,10 +3713,10 @@ static void hci_remote_name_evt(struct hci_dev *hdev, void *data,
|
|||
if (!hci_outgoing_auth_needed(hdev, conn))
|
||||
goto unlock;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, conn->flags)) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, conn->flags);
|
||||
|
||||
cp.handle = __cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
|
||||
|
|
@ -3723,17 +3743,17 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
|||
if (!ev->status) {
|
||||
if (ev->encrypt) {
|
||||
/* Encryption implies authentication */
|
||||
set_bit(HCI_CONN_AUTH, &conn->flags);
|
||||
set_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH, conn->flags);
|
||||
set_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
conn->sec_level = conn->pending_sec_level;
|
||||
|
||||
/* P-256 authentication key implies FIPS */
|
||||
if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256)
|
||||
set_bit(HCI_CONN_FIPS, &conn->flags);
|
||||
set_bit(HCI_CONN_FIPS, conn->flags);
|
||||
|
||||
if ((conn->type == ACL_LINK && ev->encrypt == 0x02) ||
|
||||
conn->type == LE_LINK)
|
||||
set_bit(HCI_CONN_AES_CCM, &conn->flags);
|
||||
set_bit(HCI_CONN_AES_CCM, conn->flags);
|
||||
|
||||
/* For ACL links the encryption key size is still
|
||||
* validated below via hci_read_enc_key_size(). Its
|
||||
|
|
@ -3745,8 +3765,8 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
|||
if (conn->type != ACL_LINK)
|
||||
mgmt_security_level_changed(conn);
|
||||
} else {
|
||||
clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT, conn->flags);
|
||||
clear_bit(HCI_CONN_AES_CCM, conn->flags);
|
||||
/* Elevated security levels (MEDIUM and above) require
|
||||
* encryption, so downgrade to reflect the connection
|
||||
* no longer being encrypted before reporting, instead
|
||||
|
|
@ -3767,7 +3787,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
|||
hci_adv_instances_set_rpa_expired(hdev, true);
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
|
||||
|
||||
/* Check link security requirements are met */
|
||||
if (!hci_conn_check_link_mode(conn))
|
||||
|
|
@ -3775,7 +3795,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
|||
|
||||
if (ev->status && conn->state == BT_CONNECTED) {
|
||||
if (ev->status == HCI_ERROR_PIN_OR_KEY_MISSING)
|
||||
set_bit(HCI_CONN_AUTH_FAILURE, &conn->flags);
|
||||
set_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
|
||||
|
||||
/* Notify upper layers so they can cleanup before
|
||||
* disconnecting.
|
||||
|
|
@ -3813,8 +3833,8 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
|||
* type can be either LE or ACL and controller must support LMP Ping.
|
||||
* Ensure for AES-CCM encryption as well.
|
||||
*/
|
||||
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags) &&
|
||||
test_bit(HCI_CONN_AES_CCM, &conn->flags) &&
|
||||
if (test_bit(HCI_CONN_ENCRYPT, conn->flags) &&
|
||||
test_bit(HCI_CONN_AES_CCM, conn->flags) &&
|
||||
((conn->type == ACL_LINK && lmp_ping_capable(hdev)) ||
|
||||
(conn->type == LE_LINK && (hdev->le_features[0] & HCI_LE_PING)))) {
|
||||
struct hci_cp_write_auth_payload_to cp;
|
||||
|
|
@ -3846,9 +3866,9 @@ static void hci_change_link_key_complete_evt(struct hci_dev *hdev, void *data,
|
|||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
if (!ev->status)
|
||||
set_bit(HCI_CONN_SECURE, &conn->flags);
|
||||
set_bit(HCI_CONN_SECURE, conn->flags);
|
||||
|
||||
clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_AUTH_PEND, conn->flags);
|
||||
|
||||
hci_key_change_cfm(conn, ev->status);
|
||||
}
|
||||
|
|
@ -3975,13 +3995,16 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
|||
struct hci_conn *conn;
|
||||
u8 status = rp->status;
|
||||
bool pending = false;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_CIG_PARAMS);
|
||||
cp = hci_sent_cmd_data_len(hdev, HCI_OP_LE_SET_CIG_PARAMS, &len);
|
||||
if (!rp->status &&
|
||||
(!cp || rp->num_handles != cp->num_cis ||
|
||||
(!cp || len < sizeof(*cp) ||
|
||||
len < struct_size(cp, cis, cp->num_cis) ||
|
||||
rp->num_handles != cp->num_cis ||
|
||||
rp->cig_id != cp->cig_id ||
|
||||
skb->len < array_size(rp->num_handles, sizeof(*rp->handle)))) {
|
||||
bt_dev_err(hdev, "unexpected Set CIG Parameters response data");
|
||||
|
|
@ -4074,7 +4097,7 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
|
|||
hci_connect_cfm(conn, rp->status);
|
||||
|
||||
/* Notify device connected in case it is a BIG Sync */
|
||||
if (!rp->status && test_bit(HCI_CONN_BIG_SYNC, &conn->flags))
|
||||
if (!rp->status && test_bit(HCI_CONN_BIG_SYNC, conn->flags))
|
||||
mgmt_device_connected(hdev, conn, NULL, 0);
|
||||
|
||||
break;
|
||||
|
|
@ -4481,6 +4504,7 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
|
|||
{
|
||||
struct hci_cp_le_create_cis *cp;
|
||||
bool pending = false;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
|
@ -4488,10 +4512,14 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
|
|||
if (!status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CIS);
|
||||
cp = hci_sent_cmd_data_len(hdev, HCI_OP_LE_CREATE_CIS, &len);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
if (len < sizeof(*cp) ||
|
||||
len < struct_size(cp, cis, cp->num_cis))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* Remove connection if command failed */
|
||||
|
|
@ -4504,7 +4532,7 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
|
|||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (conn) {
|
||||
if (test_and_clear_bit(HCI_CONN_CREATE_CIS,
|
||||
&conn->flags))
|
||||
conn->flags))
|
||||
pending = true;
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
|
|
@ -4625,7 +4653,7 @@ static void hci_role_change_evt(struct hci_dev *hdev, void *data,
|
|||
if (!ev->status)
|
||||
conn->role = ev->role;
|
||||
|
||||
clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_RSWITCH_PEND, conn->flags);
|
||||
|
||||
hci_role_switch_cfm(conn, ev->status, ev->role);
|
||||
}
|
||||
|
|
@ -4737,12 +4765,12 @@ static void hci_mode_change_evt(struct hci_dev *hdev, void *data,
|
|||
conn->mode = ev->mode;
|
||||
|
||||
if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND,
|
||||
&conn->flags)) {
|
||||
assign_bit(HCI_CONN_POWER_SAVE, &conn->flags,
|
||||
conn->flags)) {
|
||||
assign_bit(HCI_CONN_POWER_SAVE, conn->flags,
|
||||
conn->mode == HCI_CM_ACTIVE);
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, conn->flags))
|
||||
hci_sco_setup(conn, ev->status);
|
||||
}
|
||||
|
||||
|
|
@ -4770,7 +4798,7 @@ static void hci_pin_code_request_evt(struct hci_dev *hdev, void *data,
|
|||
}
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_BONDABLE) &&
|
||||
!test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) {
|
||||
!test_bit(HCI_CONN_AUTH_INITIATOR, conn->flags)) {
|
||||
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
|
||||
sizeof(ev->bdaddr), &ev->bdaddr);
|
||||
} else if (hci_dev_test_flag(hdev, HCI_MGMT)) {
|
||||
|
|
@ -4845,7 +4873,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, void *data,
|
|||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
if (conn) {
|
||||
clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
|
||||
clear_bit(HCI_CONN_NEW_LINK_KEY, conn->flags);
|
||||
|
||||
if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
|
||||
key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
|
||||
|
|
@ -4908,7 +4936,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, void *data,
|
|||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
hci_conn_drop(conn);
|
||||
|
||||
set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
|
||||
set_bit(HCI_CONN_NEW_LINK_KEY, conn->flags);
|
||||
conn_set_key(conn, ev->key_type, conn->pin_length);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_MGMT))
|
||||
|
|
@ -4939,7 +4967,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
assign_bit(HCI_CONN_FLUSH_KEY, &conn->flags, !persistent);
|
||||
assign_bit(HCI_CONN_FLUSH_KEY, conn->flags, !persistent);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
@ -5117,7 +5145,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, void *data,
|
|||
ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
|
||||
|
||||
if (ev->features[0] & LMP_HOST_SSP) {
|
||||
set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
|
||||
set_bit(HCI_CONN_SSP_ENABLED, conn->flags);
|
||||
} else {
|
||||
/* It is mandatory by the Bluetooth specification that
|
||||
* Extended Inquiry Results are only used when Secure
|
||||
|
|
@ -5127,17 +5155,17 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, void *data,
|
|||
* To make these devices work, the internal SSP
|
||||
* enabled flag needs to be cleared if the remote host
|
||||
* features do not indicate SSP support */
|
||||
clear_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
|
||||
clear_bit(HCI_CONN_SSP_ENABLED, conn->flags);
|
||||
}
|
||||
|
||||
if (ev->features[0] & LMP_HOST_SC)
|
||||
set_bit(HCI_CONN_SC_ENABLED, &conn->flags);
|
||||
set_bit(HCI_CONN_SC_ENABLED, conn->flags);
|
||||
}
|
||||
|
||||
if (conn->state != BT_CONFIG)
|
||||
goto unlock;
|
||||
|
||||
if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
|
||||
if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags)) {
|
||||
struct hci_cp_remote_name_req cp;
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
bacpy(&cp.bdaddr, &conn->dst);
|
||||
|
|
@ -5373,7 +5401,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, void *data,
|
|||
mgmt_security_level_changed(conn);
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
|
||||
|
||||
if (ev->status && conn->state == BT_CONNECTED) {
|
||||
hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
|
||||
|
|
@ -5474,7 +5502,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
|
||||
/* Assume remote supports SSP since it has triggered this event */
|
||||
set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
|
||||
set_bit(HCI_CONN_SSP_ENABLED, conn->flags);
|
||||
|
||||
hci_conn_hold(conn);
|
||||
|
||||
|
|
@ -5485,7 +5513,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, void *data,
|
|||
* pairing or if the remote is not requesting bonding.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_BONDABLE) ||
|
||||
test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) ||
|
||||
test_bit(HCI_CONN_AUTH_INITIATOR, conn->flags) ||
|
||||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
|
||||
struct hci_cp_io_capability_reply cp;
|
||||
|
||||
|
|
@ -5595,7 +5623,7 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, void *data,
|
|||
* local IO capability is not NoInputNoOutput, use JUST_WORKS
|
||||
* method (mgmt_user_confirm with confirm_hint set to 1).
|
||||
*/
|
||||
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) &&
|
||||
if (!test_bit(HCI_CONN_AUTH_PEND, conn->flags) &&
|
||||
conn->io_capability != HCI_IO_NO_INPUT_OUTPUT) {
|
||||
bt_dev_dbg(hdev, "Confirming auto-accept as acceptor");
|
||||
confirm_hint = 1;
|
||||
|
|
@ -5738,7 +5766,7 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev, void *data,
|
|||
* initiated the authentication. A traditional auth_complete
|
||||
* event gets always produced as initiator and is also mapped to
|
||||
* the mgmt_auth_failed event */
|
||||
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
|
||||
if (!test_bit(HCI_CONN_AUTH_PEND, conn->flags) && ev->status)
|
||||
mgmt_auth_failed(conn, ev->status);
|
||||
|
||||
hci_conn_drop(conn);
|
||||
|
|
@ -5984,7 +6012,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||
goto unlock;
|
||||
|
||||
/* Drop the connection if it has been aborted */
|
||||
if (test_bit(HCI_CONN_CANCEL, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_CANCEL, conn->flags)) {
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
|
@ -6041,6 +6069,28 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||
hci_conn_put(params->conn);
|
||||
params->conn = NULL;
|
||||
}
|
||||
|
||||
/* An explicit connect request is complete once connected, so
|
||||
* clean it up here: hci_connect_le_scan_cleanup() no longer
|
||||
* finds the params since they have just been removed from
|
||||
* pend_le_conns.
|
||||
*/
|
||||
if (params->explicit_connect) {
|
||||
params->explicit_connect = false;
|
||||
|
||||
switch (params->auto_connect) {
|
||||
case HCI_AUTO_CONN_EXPLICIT:
|
||||
hci_conn_params_del(hdev, &conn->dst,
|
||||
conn->dst_type);
|
||||
break;
|
||||
case HCI_AUTO_CONN_REPORT:
|
||||
hci_pend_le_list_add(params,
|
||||
&hdev->pend_le_reports);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are central and have subrate parameters stored, queue a
|
||||
|
|
@ -6101,8 +6151,8 @@ static void hci_le_pa_sync_lost_evt(struct hci_dev *hdev, void *data,
|
|||
/* Delete the pa sync connection */
|
||||
conn = hci_conn_hash_lookup_pa_sync_handle(hdev, handle);
|
||||
if (conn) {
|
||||
clear_bit(HCI_CONN_BIG_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_PA_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_BIG_SYNC, conn->flags);
|
||||
clear_bit(HCI_CONN_PA_SYNC, conn->flags);
|
||||
hci_disconn_cfm(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
|
|
@ -6236,7 +6286,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
|
|||
pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
|
||||
|
||||
if (ev->status) {
|
||||
set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
|
||||
set_bit(HCI_CONN_PA_SYNC_FAILED, pa_sync->flags);
|
||||
|
||||
/* Notify iso layer */
|
||||
hci_connect_cfm(pa_sync, ev->status);
|
||||
|
|
@ -6758,7 +6808,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE_PA_SYNC, conn->flags);
|
||||
|
||||
conn->sync_handle = le16_to_cpu(ev->handle);
|
||||
conn->sid = HCI_SID_INVALID;
|
||||
|
|
@ -6783,7 +6833,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
pa_sync->sync_handle = le16_to_cpu(ev->handle);
|
||||
|
||||
if (ev->status) {
|
||||
set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
|
||||
set_bit(HCI_CONN_PA_SYNC_FAILED, pa_sync->flags);
|
||||
|
||||
/* Notify iso layer */
|
||||
hci_connect_cfm(pa_sync, ev->status);
|
||||
|
|
@ -6827,7 +6877,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
|
||||
if (ev->data_status == LE_PA_DATA_COMPLETE &&
|
||||
!test_and_set_bit(HCI_CONN_PA_SYNC, &pa_sync->flags)) {
|
||||
!test_and_set_bit(HCI_CONN_PA_SYNC, pa_sync->flags)) {
|
||||
/* Notify iso layer */
|
||||
hci_connect_cfm(pa_sync, 0);
|
||||
|
||||
|
|
@ -6940,11 +6990,11 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, void *data,
|
|||
* using a distributed LTK.
|
||||
*/
|
||||
if (ltk->type == SMP_STK) {
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, conn->flags);
|
||||
list_del_rcu(<k->list);
|
||||
kfree_rcu(ltk, rcu);
|
||||
} else {
|
||||
clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
|
||||
clear_bit(HCI_CONN_STK_ENCRYPT, conn->flags);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
@ -7140,7 +7190,7 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data,
|
|||
|
||||
qos = &conn->iso_qos;
|
||||
|
||||
pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, &conn->flags);
|
||||
pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, conn->flags);
|
||||
|
||||
/* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 6, Part G
|
||||
* page 3075:
|
||||
|
|
@ -7337,7 +7387,7 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
|||
}
|
||||
|
||||
conn->state = BT_CONNECTED;
|
||||
set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
|
||||
set_bit(HCI_CONN_BIG_CREATED, conn->flags);
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
hci_iso_setup_path(conn);
|
||||
|
|
@ -7382,7 +7432,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, conn->flags);
|
||||
|
||||
conn->num_bis = 0;
|
||||
memset(conn->bis, 0, sizeof(conn->bis));
|
||||
|
|
@ -7405,7 +7455,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
|
||||
if (ev->status != 0x42)
|
||||
/* Mark PA sync as established */
|
||||
set_bit(HCI_CONN_PA_SYNC, &bis->flags);
|
||||
set_bit(HCI_CONN_PA_SYNC, bis->flags);
|
||||
|
||||
bis->sync_handle = conn->sync_handle;
|
||||
bis->iso_qos.bcast.big = ev->handle;
|
||||
|
|
@ -7418,7 +7468,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
|
||||
if (!ev->status) {
|
||||
bis->state = BT_CONNECTED;
|
||||
set_bit(HCI_CONN_BIG_SYNC, &bis->flags);
|
||||
set_bit(HCI_CONN_BIG_SYNC, bis->flags);
|
||||
hci_debugfs_create_conn(bis);
|
||||
hci_conn_add_sysfs(bis);
|
||||
hci_iso_setup_path(bis);
|
||||
|
|
@ -7436,7 +7486,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
if (!bis)
|
||||
continue;
|
||||
|
||||
set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
|
||||
set_bit(HCI_CONN_BIG_SYNC_FAILED, bis->flags);
|
||||
hci_connect_cfm(bis, ev->status);
|
||||
}
|
||||
|
||||
|
|
@ -7466,13 +7516,13 @@ static void hci_le_big_sync_lost_evt(struct hci_dev *hdev, void *data,
|
|||
* it is consumed there instead of here.
|
||||
*/
|
||||
mgmt_conn = test_bit(HCI_CONN_MGMT_CONNECTED,
|
||||
&bis->flags);
|
||||
bis->flags);
|
||||
mgmt_device_disconnected(hdev, &bis->dst, bis->type,
|
||||
bis->dst_type, ev->reason,
|
||||
mgmt_conn);
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_BIG_SYNC, &bis->flags);
|
||||
clear_bit(HCI_CONN_BIG_SYNC, bis->flags);
|
||||
hci_disconn_cfm(bis, ev->reason);
|
||||
hci_conn_del(bis);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -376,8 +376,18 @@ static void le_scan_disable(struct work_struct *work)
|
|||
bt_dev_dbg(hdev, "");
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
||||
/* The scan of a discovery can be disabled while this work
|
||||
* waits for the lock, for example by a Stop Discovery that
|
||||
* timed out and still completed, so end the discovery as
|
||||
* le_set_scan_enable_complete() does when it cancels this
|
||||
* work.
|
||||
*/
|
||||
if (hdev->discovery.state == DISCOVERY_FINDING)
|
||||
goto discov_stopped;
|
||||
|
||||
goto _return;
|
||||
}
|
||||
|
||||
status = hci_cmd_sync_queue(hdev, scan_disable_sync, NULL, NULL);
|
||||
if (status) {
|
||||
|
|
@ -2448,15 +2458,17 @@ static int hci_le_del_resolve_list_sync(struct hci_dev *hdev,
|
|||
bdaddr_t *bdaddr, u8 bdaddr_type)
|
||||
{
|
||||
struct hci_cp_le_del_from_resolv_list cp;
|
||||
struct bdaddr_list_with_irk *entry;
|
||||
bool exists;
|
||||
|
||||
if (!ll_privacy_capable(hdev))
|
||||
return 0;
|
||||
|
||||
/* Check if the IRK has been programmed */
|
||||
entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list, bdaddr,
|
||||
bdaddr_type);
|
||||
if (!entry)
|
||||
hci_dev_lock(hdev);
|
||||
exists = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
|
||||
bdaddr, bdaddr_type);
|
||||
hci_dev_unlock(hdev);
|
||||
if (!exists)
|
||||
return 0;
|
||||
|
||||
cp.bdaddr_type = bdaddr_type;
|
||||
|
|
@ -2512,8 +2524,8 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
|
|||
{
|
||||
struct hci_cp_le_add_to_resolv_list cp;
|
||||
struct smp_irk *irk;
|
||||
struct bdaddr_list_with_irk *entry;
|
||||
struct hci_conn_params *p;
|
||||
bool exists;
|
||||
|
||||
if (!ll_privacy_capable(hdev))
|
||||
return 0;
|
||||
|
|
@ -2531,20 +2543,28 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
|
|||
} else if (!(params->flags & HCI_CONN_FLAG_ADDRESS_RESOLUTION))
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
irk = hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type);
|
||||
if (!irk)
|
||||
if (!irk) {
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(cp.peer_irk, irk->val, 16);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* Check if the IK has _not_ been programmed yet. */
|
||||
entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
|
||||
¶ms->addr,
|
||||
params->addr_type);
|
||||
if (entry)
|
||||
hci_dev_lock(hdev);
|
||||
exists = hci_bdaddr_list_lookup_with_irk(
|
||||
&hdev->le_resolv_list, ¶ms->addr, params->addr_type);
|
||||
hci_dev_unlock(hdev);
|
||||
if (exists)
|
||||
return 0;
|
||||
|
||||
cp.bdaddr_type = params->addr_type;
|
||||
bacpy(&cp.bdaddr, ¶ms->addr);
|
||||
memcpy(cp.peer_irk, irk->val, 16);
|
||||
|
||||
/* Default privacy mode is always Network */
|
||||
params->privacy_mode = HCI_NETWORK_PRIVACY;
|
||||
|
|
@ -2733,7 +2753,7 @@ static int hci_pause_advertising_sync(struct hci_dev *hdev)
|
|||
static int hci_resume_advertising_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct adv_info *adv, *tmp;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
/* If advertising has not been paused there is nothing to do. */
|
||||
if (!hdev->advertising_paused)
|
||||
|
|
@ -2766,13 +2786,19 @@ static int hci_resume_advertising_sync(struct hci_dev *hdev)
|
|||
*/
|
||||
if (hci_dev_test_and_clear_flag(hdev, HCI_LE_ADV_0))
|
||||
err = hci_enable_ext_advertising_sync(hdev, 0x00);
|
||||
} else {
|
||||
} else if (hdev->cur_adv_instance) {
|
||||
/* Schedule for most recent instance to be restarted and begin
|
||||
* the software rotation loop
|
||||
*/
|
||||
err = hci_schedule_adv_instance_sync(hdev,
|
||||
hdev->cur_adv_instance,
|
||||
true);
|
||||
} else if (hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
|
||||
!hci_dev_test_flag(hdev, HCI_LE_ADV)) {
|
||||
/* hci_schedule_adv_instance_sync() rejects instance 0x00 while
|
||||
* HCI_ADVERTISING is set, so enable it directly.
|
||||
*/
|
||||
err = hci_start_adv_sync(hdev, 0x00);
|
||||
}
|
||||
|
||||
hdev->advertising_paused = false;
|
||||
|
|
@ -2879,6 +2905,31 @@ static struct conn_params *conn_params_copy(struct list_head *list, size_t *n)
|
|||
return p;
|
||||
}
|
||||
|
||||
static struct conn_params *le_accept_list_copy(struct hci_dev *hdev,
|
||||
size_t *n)
|
||||
{
|
||||
struct bdaddr_list *entry;
|
||||
struct conn_params *params;
|
||||
size_t i = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
*n = list_count_nodes(&hdev->le_accept_list);
|
||||
params = kvzalloc_objs(struct conn_params, *n);
|
||||
if (!params)
|
||||
goto unlock;
|
||||
|
||||
list_for_each_entry(entry, &hdev->le_accept_list, list) {
|
||||
bacpy(¶ms[i].addr, &entry->bdaddr);
|
||||
params[i].addr_type = entry->bdaddr_type;
|
||||
i++;
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return params;
|
||||
}
|
||||
|
||||
/* Clear LE Accept List */
|
||||
static int hci_le_clear_accept_list_sync(struct hci_dev *hdev)
|
||||
{
|
||||
|
|
@ -2909,7 +2960,6 @@ static int hci_le_clear_accept_list_sync(struct hci_dev *hdev)
|
|||
static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct conn_params *params;
|
||||
struct bdaddr_list *b, *t;
|
||||
u8 num_entries = 0;
|
||||
bool pend_conn, pend_report;
|
||||
u8 filter_policy;
|
||||
|
|
@ -2974,20 +3024,27 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
|
|||
* report. If not present in either list, then remove it from
|
||||
* the controller.
|
||||
*/
|
||||
list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
|
||||
params = le_accept_list_copy(hdev, &n);
|
||||
if (!params) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
rcu_read_lock();
|
||||
|
||||
if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) {
|
||||
if (hci_conn_hash_lookup_le(hdev, ¶ms[i].addr,
|
||||
params[i].addr_type)) {
|
||||
rcu_read_unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
|
||||
&b->bdaddr,
|
||||
b->bdaddr_type);
|
||||
pend_report = hci_pend_le_action_lookup(&hdev->pend_le_reports,
|
||||
&b->bdaddr,
|
||||
b->bdaddr_type);
|
||||
¶ms[i].addr,
|
||||
params[i].addr_type);
|
||||
pend_report = hci_pend_le_action_lookup(
|
||||
&hdev->pend_le_reports, ¶ms[i].addr,
|
||||
params[i].addr_type);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
|
|
@ -2995,14 +3052,16 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
|
|||
* remove it from the acceptlist.
|
||||
*/
|
||||
if (!pend_conn && !pend_report) {
|
||||
hci_le_del_accept_list_sync(hdev, &b->bdaddr,
|
||||
b->bdaddr_type);
|
||||
hci_le_del_accept_list_sync(hdev, ¶ms[i].addr,
|
||||
params[i].addr_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
num_entries++;
|
||||
}
|
||||
|
||||
kvfree(params);
|
||||
|
||||
/* Since all no longer valid accept list entries have been
|
||||
* removed, walk through the list of pending connections
|
||||
* and ensure that any new device gets programmed into
|
||||
|
|
@ -3505,9 +3564,13 @@ int hci_write_ssp_mode_sync(struct hci_dev *hdev, u8 mode)
|
|||
return hci_write_sc_support_sync(hdev, 0x01);
|
||||
}
|
||||
|
||||
static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u16 bit,
|
||||
u8 value);
|
||||
|
||||
int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul)
|
||||
{
|
||||
struct hci_cp_write_le_host_supported cp;
|
||||
int err;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) ||
|
||||
!lmp_bredr_capable(hdev))
|
||||
|
|
@ -3525,8 +3588,16 @@ int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul)
|
|||
cp.le = le;
|
||||
cp.simul = simul;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
if (err || !cis_capable(hdev))
|
||||
return err;
|
||||
|
||||
/* The CIS host feature is set during init based on LE being enabled,
|
||||
* which is not repeated when LE is enabled afterwards, e.g. while the
|
||||
* controller is still auto powered on, so update it here.
|
||||
*/
|
||||
return hci_le_set_host_feature_sync(hdev, 32, iso_enabled(hdev));
|
||||
}
|
||||
|
||||
static int hci_powered_update_adv_sync(struct hci_dev *hdev)
|
||||
|
|
@ -4828,6 +4899,9 @@ static int hci_le_set_def_rate_sync(struct hci_dev *hdev)
|
|||
struct hci_cp_le_set_def_rate cp;
|
||||
u16 interval_min = 0x000a; /* 1.25 ms */
|
||||
u16 interval_max = 0x0078; /* 15 ms */
|
||||
u16 subrate_max = 0x0004;
|
||||
u16 max_latency = 0x0000;
|
||||
u32 supv_timeout;
|
||||
|
||||
if (!le_sci_capable(hdev))
|
||||
return 0;
|
||||
|
|
@ -4852,10 +4926,31 @@ static int hci_le_set_def_rate_sync(struct hci_dev *hdev)
|
|||
cp.interval_min = cpu_to_le16(interval_min);
|
||||
cp.interval_max = cpu_to_le16(interval_max);
|
||||
cp.subrate_min = cpu_to_le16(0x0001);
|
||||
cp.subrate_max = cpu_to_le16(0x0004);
|
||||
cp.max_latency = cpu_to_le16(0x0000);
|
||||
cp.subrate_max = cpu_to_le16(subrate_max);
|
||||
cp.max_latency = cpu_to_le16(max_latency);
|
||||
cp.cont_num = cpu_to_le16(0x0001);
|
||||
cp.supv_timeout = cpu_to_le16(0x000c); /* 120 ms */
|
||||
|
||||
/* HOGP v1.2 recommends a Supervision_Timeout greater than or equal to
|
||||
* (1 + Max_Latency) x Subrate_Max x Connection_Interval_Max x 2, but
|
||||
* the Core spec treats the equality as invalid:
|
||||
*
|
||||
* BLUETOOTH CORE SPECIFICATION Version 6.3 | Vol 4, Part E
|
||||
* 7.8.158. LE Set Default Rate Parameters command
|
||||
*
|
||||
* If Connection_Interval_Max x Subrate_Max x (Max_Latency + 1) is
|
||||
* greater than or equal to Supervision_Timeout x 40, the Controller
|
||||
* shall return the error code Invalid HCI Command Parameters (0x12).
|
||||
*
|
||||
* So default to 2 s, 10x lower than the 20 s used for non-SCI
|
||||
* connections while still well above the minimum required, but make
|
||||
* sure it stays strictly above the minimum in case interval_max has
|
||||
* been raised.
|
||||
*/
|
||||
supv_timeout = (u32)interval_max * subrate_max * (max_latency + 1);
|
||||
supv_timeout = supv_timeout / 40 + 1;
|
||||
supv_timeout = clamp_t(u32, max_t(u32, supv_timeout, 0x00c8),
|
||||
0x000a, 0x0c80);
|
||||
cp.supv_timeout = cpu_to_le16(supv_timeout);
|
||||
|
||||
/* The connection event length recommended in requests by a Peripheral
|
||||
* uses units of 125 us with a valid range of 0x0001 to 0x7CFF
|
||||
|
|
@ -5637,6 +5732,11 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Wait for hci_recv_frame() callers that still saw HCI_UP, so that
|
||||
* none of them queues rx_work while the workqueue is drained below.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
|
||||
hci_leds_update_powered(hdev, false);
|
||||
|
||||
/* Flush RX and TX works */
|
||||
|
|
@ -5830,11 +5930,15 @@ int hci_stop_discovery_sync(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
||||
cancel_delayed_work(&hdev->le_scan_disable);
|
||||
|
||||
err = hci_scan_disable_sync(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Only cancel the LE scan timeout once the scan is
|
||||
* disabled, so that the discovery still ends if
|
||||
* stopping it failed.
|
||||
*/
|
||||
cancel_delayed_work(&hdev->le_scan_disable);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -5913,11 +6017,11 @@ static int hci_le_connect_cancel_sync(struct hci_dev *hdev,
|
|||
/* Return reason if scanning since the connection shall probably be
|
||||
* cleanup directly.
|
||||
*/
|
||||
if (test_bit(HCI_CONN_SCANNING, &conn->flags))
|
||||
if (test_bit(HCI_CONN_SCANNING, conn->flags))
|
||||
return reason;
|
||||
|
||||
if (conn->role == HCI_ROLE_SLAVE ||
|
||||
test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
|
||||
test_and_set_bit(HCI_CONN_CANCEL, conn->flags))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN_CANCEL,
|
||||
|
|
@ -5939,7 +6043,7 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
* then an HCI_LE_CIS_Established event shall also be sent for
|
||||
* this CIS with the Status Operation Cancelled by Host (0x44).
|
||||
*/
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, conn->flags))
|
||||
return hci_disconnect_sync(hdev, conn, reason);
|
||||
|
||||
/* CIS with no Create CIS sent have nothing to cancel */
|
||||
|
|
@ -6310,6 +6414,7 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
|
|||
u8 filter_policy = 0x00;
|
||||
/* Default is to enable duplicates filter */
|
||||
u8 filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
||||
bool paused;
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
|
@ -6333,6 +6438,12 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
|
|||
if (err)
|
||||
goto failed;
|
||||
|
||||
/* LE Set Random Address is disallowed while advertising is enabled. */
|
||||
paused = !hdev->advertising_paused;
|
||||
err = hci_pause_advertising_sync(hdev);
|
||||
if (err)
|
||||
goto failed;
|
||||
|
||||
/* All active scans will be done with either a resolvable private
|
||||
* address (when privacy feature has been enabled) or non-resolvable
|
||||
* private address.
|
||||
|
|
@ -6342,6 +6453,9 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
|
|||
if (err < 0)
|
||||
own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
|
||||
if (paused)
|
||||
hci_resume_advertising_sync(hdev);
|
||||
|
||||
if (hci_is_adv_monitoring(hdev) ||
|
||||
(hci_test_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER) &&
|
||||
hdev->discovery.result_filtering)) {
|
||||
|
|
@ -6365,9 +6479,8 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
|
|||
return err;
|
||||
|
||||
failed:
|
||||
/* Resume advertising if it was paused */
|
||||
if (ll_privacy_capable(hdev))
|
||||
hci_resume_advertising_sync(hdev);
|
||||
/* No-op when advertising was not paused. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
|
||||
/* Resume passive scanning */
|
||||
hci_update_passive_scan_sync(hdev);
|
||||
|
|
@ -6460,6 +6573,12 @@ static int hci_pause_discovery_sync(struct hci_dev *hdev)
|
|||
return 0;
|
||||
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
|
||||
|
||||
/* Cancel the LE scan timeout first, so that it does not run while
|
||||
* suspended if the scan cannot be disabled.
|
||||
*/
|
||||
cancel_delayed_work(&hdev->le_scan_disable);
|
||||
|
||||
err = hci_stop_discovery_sync(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -6928,7 +7047,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||
clear_bit(HCI_CONN_SCANNING, conn->flags);
|
||||
conn->state = BT_CONNECT;
|
||||
|
||||
/* If requested to connect as peripheral use directed advertising */
|
||||
|
|
@ -6995,7 +7114,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
/* Mark create connection in flight so hci_cancel_connect_sync() can
|
||||
* cancel it while blocking on the connection complete event.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
set_bit(HCI_CONN_CREATE, conn->flags);
|
||||
|
||||
/* Send command LE Extended Create Connection if supported */
|
||||
if (use_ext_conn(hdev)) {
|
||||
|
|
@ -7032,7 +7151,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
conn->conn_timeout, NULL);
|
||||
|
||||
done:
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE, conn->flags);
|
||||
|
||||
if (err == -ETIMEDOUT)
|
||||
hci_le_connect_cancel_sync(hdev, conn, 0x00);
|
||||
|
|
@ -7079,7 +7198,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
|
|||
|
||||
/* Wait until previous Create CIS has completed */
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, conn->flags))
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -7115,7 +7234,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
|
|||
conn->iso_qos.ucast.cig != cig)
|
||||
continue;
|
||||
|
||||
set_bit(HCI_CONN_CREATE_CIS, &conn->flags);
|
||||
set_bit(HCI_CONN_CREATE_CIS, conn->flags);
|
||||
cis->acl_handle = cpu_to_le16(conn->parent->handle);
|
||||
cis->cis_handle = cpu_to_le16(conn->handle);
|
||||
timeout = conn->conn_timeout;
|
||||
|
|
@ -7318,14 +7437,14 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
/* Mark create connection in flight so hci_cancel_connect_sync() can
|
||||
* cancel it while blocking on the connection complete event.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
set_bit(HCI_CONN_CREATE, conn->flags);
|
||||
|
||||
err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
conn->conn_timeout, NULL);
|
||||
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE, conn->flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -7414,7 +7533,7 @@ static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
|
|||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
/* In flight: this connection owns the pending request, cancel it. */
|
||||
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_CREATE, conn->flags)) {
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
goto unlock;
|
||||
}
|
||||
|
|
@ -7445,7 +7564,7 @@ static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
|
|||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_CREATE, conn->flags)) {
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
goto unlock;
|
||||
}
|
||||
|
|
@ -7472,7 +7591,7 @@ static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
|
|||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, conn->flags))
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
|
|
@ -7609,7 +7728,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
|||
hci_dev_lock(hdev);
|
||||
|
||||
if (hci_conn_valid(hdev, conn))
|
||||
clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE_PA_SYNC, conn->flags);
|
||||
|
||||
if (!err)
|
||||
goto unlock;
|
||||
|
|
@ -7621,7 +7740,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
|||
if (IS_ERR(pa_sync))
|
||||
goto unlock;
|
||||
|
||||
set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
|
||||
set_bit(HCI_CONN_PA_SYNC_FAILED, pa_sync->flags);
|
||||
|
||||
/* Notify iso layer */
|
||||
hci_connect_cfm(pa_sync, bt_status(err));
|
||||
|
|
@ -7694,7 +7813,7 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
|
|||
* program the address in the allow list so PA advertisements can be
|
||||
* received.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
|
||||
set_bit(HCI_CONN_CREATE_PA_SYNC, conn->flags);
|
||||
|
||||
hci_update_passive_scan_sync(hdev);
|
||||
|
||||
|
|
@ -7797,7 +7916,7 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
|||
if (err == -ECANCELED)
|
||||
goto done;
|
||||
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, conn->flags);
|
||||
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
|
|
@ -7814,7 +7933,7 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
|
|||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
set_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
set_bit(HCI_CONN_CREATE_BIG_SYNC, conn->flags);
|
||||
|
||||
memset(cp, 0, sizeof(*cp));
|
||||
cp->handle = qos->bcast.big;
|
||||
|
|
|
|||
|
|
@ -912,7 +912,7 @@ static void iso_sock_disconn(struct sock *sk)
|
|||
struct sock *bis_sk;
|
||||
struct hci_conn *hcon = iso_pi(sk)->conn->hcon;
|
||||
|
||||
if (test_bit(HCI_CONN_BIG_CREATED, &hcon->flags)) {
|
||||
if (test_bit(HCI_CONN_BIG_CREATED, hcon->flags)) {
|
||||
bis_sk = iso_get_sock_big(sk, &iso_pi(sk)->src,
|
||||
&iso_pi(sk)->dst,
|
||||
iso_pi(sk)->qos.bcast.big);
|
||||
|
|
@ -957,8 +957,8 @@ static void __iso_sock_close(struct sock *sk)
|
|||
|
||||
case BT_CONNECT2:
|
||||
if (iso_pi(sk)->conn->hcon &&
|
||||
(test_bit(HCI_CONN_PA_SYNC, &iso_pi(sk)->conn->hcon->flags) ||
|
||||
test_bit(HCI_CONN_PA_SYNC_FAILED, &iso_pi(sk)->conn->hcon->flags)))
|
||||
(test_bit(HCI_CONN_PA_SYNC, iso_pi(sk)->conn->hcon->flags) ||
|
||||
test_bit(HCI_CONN_PA_SYNC_FAILED, iso_pi(sk)->conn->hcon->flags)))
|
||||
iso_sock_disconn(sk);
|
||||
else
|
||||
iso_chan_del(sk, ECONNRESET);
|
||||
|
|
@ -2289,7 +2289,7 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
|
||||
hdev = hcon->hdev;
|
||||
|
||||
if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags)) {
|
||||
if (test_bit(HCI_CONN_BIG_SYNC, hcon->flags)) {
|
||||
/* A BIS slave hcon is notified to the ISO layer
|
||||
* after the Command Complete for the LE Setup
|
||||
* ISO Data Path command is received. Get the
|
||||
|
|
@ -2298,7 +2298,7 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
|
||||
BT_LISTEN, iso_match_big_hcon,
|
||||
hcon);
|
||||
} else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
|
||||
} else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, hcon->flags)) {
|
||||
ev = hci_recv_event_data(hcon->hdev,
|
||||
HCI_EVT_LE_BIG_SYNC_ESTABLISHED);
|
||||
|
||||
|
|
@ -2312,7 +2312,7 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
&hcon->dst,
|
||||
BT_LISTEN,
|
||||
iso_match_big, ev);
|
||||
} else if (test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
|
||||
} else if (test_bit(HCI_CONN_PA_SYNC_FAILED, hcon->flags)) {
|
||||
ev2 = hci_recv_event_data(hcon->hdev,
|
||||
HCI_EV_LE_PA_SYNC_ESTABLISHED);
|
||||
if (ev2)
|
||||
|
|
@ -2320,7 +2320,7 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
&hcon->dst,
|
||||
BT_LISTEN,
|
||||
iso_match_sid, ev2);
|
||||
} else if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
|
||||
} else if (test_bit(HCI_CONN_PA_SYNC, hcon->flags)) {
|
||||
ev3 = hci_recv_event_data(hcon->hdev,
|
||||
HCI_EV_LE_PER_ADV_REPORT);
|
||||
if (ev3)
|
||||
|
|
@ -2376,7 +2376,7 @@ static void iso_conn_ready(struct iso_conn *conn)
|
|||
hcon->dst_type = le_addr_type(iso_pi(parent)->dst_type);
|
||||
}
|
||||
|
||||
if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
|
||||
if (test_bit(HCI_CONN_PA_SYNC, hcon->flags)) {
|
||||
iso_pi(sk)->qos = iso_pi(parent)->qos;
|
||||
hcon->iso_qos = iso_pi(sk)->qos;
|
||||
iso_pi(sk)->bc_sid = iso_pi(parent)->bc_sid;
|
||||
|
|
@ -2676,8 +2676,8 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
|||
* into the accept queue of the listening socket and wake up
|
||||
* userspace, to inform the user about the event.
|
||||
*/
|
||||
if (!status || test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags) ||
|
||||
test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
|
||||
if (!status || test_bit(HCI_CONN_BIG_SYNC_FAILED, hcon->flags) ||
|
||||
test_bit(HCI_CONN_PA_SYNC_FAILED, hcon->flags)) {
|
||||
struct iso_conn *conn;
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,7 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
|
|||
if (chan->sec_level == BT_SECURITY_FIPS)
|
||||
min_key_size = 16;
|
||||
|
||||
return (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags) ||
|
||||
return (!test_bit(HCI_CONN_ENCRYPT, hcon->flags) ||
|
||||
hcon->enc_key_size >= min_key_size);
|
||||
}
|
||||
|
||||
|
|
@ -4480,11 +4480,11 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
|||
|
||||
lockdep_assert_held(&chan->conn->lock);
|
||||
|
||||
/* Only accept a response to a connection request we sent and that
|
||||
* is still outstanding. A channel waiting in BT_CONNECT for security
|
||||
* or the feature mask has not sent one yet.
|
||||
/* Only accept a response matching a connection request we sent and
|
||||
* that is still outstanding. A channel waiting in BT_CONNECT for
|
||||
* security or the feature mask has not sent one yet.
|
||||
*/
|
||||
if (!l2cap_chan_connect_pending(chan))
|
||||
if (!l2cap_chan_connect_pending(chan) || chan->ident != cmd->ident)
|
||||
goto done;
|
||||
|
||||
switch (result) {
|
||||
|
|
@ -7389,7 +7389,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
u16 cid, len;
|
||||
__le16 psm;
|
||||
|
||||
if (hcon->state != BT_CONNECTED) {
|
||||
/* Also hold data back while the encryption key size is read, as
|
||||
* the security of the link, e.g. for SMP over BR/EDR, depends on it.
|
||||
*/
|
||||
if (hcon->state != BT_CONNECTED ||
|
||||
test_bit(HCI_CONN_ENC_KEY_SIZE_PEND, hcon->flags)) {
|
||||
BT_DBG("queueing pending rx skb");
|
||||
skb_queue_tail(&conn->pending_rx, skb);
|
||||
return;
|
||||
|
|
@ -7452,7 +7456,11 @@ static void process_pending_rx(struct work_struct *work)
|
|||
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
while ((skb = skb_dequeue(&conn->pending_rx)))
|
||||
/* l2cap_recv_frame() would queue it all again while the encryption
|
||||
* key size is read, l2cap_security_cfm() schedules this once known.
|
||||
*/
|
||||
while (!test_bit(HCI_CONN_ENC_KEY_SIZE_PEND, conn->hcon->flags) &&
|
||||
(skb = skb_dequeue(&conn->pending_rx)))
|
||||
l2cap_recv_frame(conn, skb);
|
||||
|
||||
mutex_unlock(&conn->lock);
|
||||
|
|
@ -8041,6 +8049,10 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
}
|
||||
|
||||
mutex_unlock(&conn->lock);
|
||||
|
||||
/* Process data held back while the encryption key size was read */
|
||||
if (!skb_queue_empty(&conn->pending_rx))
|
||||
queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
|
||||
}
|
||||
|
||||
/* Append fragment into frame respecting the maximum len of rx_skb */
|
||||
|
|
|
|||
|
|
@ -304,6 +304,8 @@ static u8 mgmt_errno_status(int err)
|
|||
return MGMT_STATUS_DISCONNECTED;
|
||||
case -ECANCELED:
|
||||
return MGMT_STATUS_CANCELLED;
|
||||
case -ERFKILL:
|
||||
return MGMT_STATUS_RFKILLED;
|
||||
}
|
||||
|
||||
return MGMT_STATUS_FAILED;
|
||||
|
|
@ -3241,7 +3243,7 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
/* Defer clearing up the connection parameters until closing to
|
||||
* give a chance of keeping them if a repairing happens.
|
||||
*/
|
||||
set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
|
||||
set_bit(HCI_CONN_PARAM_REMOVAL_PEND, conn->flags);
|
||||
|
||||
/* Disable auto-connection parameters if present */
|
||||
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, addr_type);
|
||||
|
|
@ -3416,7 +3418,7 @@ static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
i = 0;
|
||||
list_for_each_entry(c, &hdev->conn_hash.list, list) {
|
||||
if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
|
||||
if (test_bit(HCI_CONN_MGMT_CONNECTED, c->flags))
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
@ -3428,7 +3430,7 @@ static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
i = 0;
|
||||
list_for_each_entry(c, &hdev->conn_hash.list, list) {
|
||||
if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
|
||||
if (!test_bit(HCI_CONN_MGMT_CONNECTED, c->flags))
|
||||
continue;
|
||||
bacpy(&rp->addr[i].bdaddr, &c->dst);
|
||||
rp->addr[i].type = link_to_bdaddr(c->type, c->dst_type);
|
||||
|
|
@ -3628,7 +3630,7 @@ static int pairing_complete(struct mgmt_pending_cmd *cmd, u8 status)
|
|||
/* The device is paired so there is no need to remove
|
||||
* its connection parameters anymore.
|
||||
*/
|
||||
clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
|
||||
clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, conn->flags);
|
||||
|
||||
hci_conn_put(conn);
|
||||
|
||||
|
|
@ -6316,12 +6318,29 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)
|
|||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* Change the state before replying, so that a client acting on the
|
||||
* reply sees it, and only while the discovery is still stopping, as
|
||||
* Inquiry Cancel may already have stopped it and a new Start Discovery
|
||||
* may have followed. Go back to DISCOVERY_FINDING when stopping failed
|
||||
* while the inquiry or the active LE scan of the discovery is still
|
||||
* running, so that it can be stopped again.
|
||||
*/
|
||||
if (hdev->discovery.state == DISCOVERY_STOPPING) {
|
||||
if (err && (test_bit(HCI_INQUIRY, &hdev->flags) ||
|
||||
(hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
|
||||
hdev->le_scan_type == LE_SCAN_ACTIVE)))
|
||||
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
|
||||
else
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
|
||||
cmd->param, 1);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
if (!err)
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
}
|
||||
|
||||
static int stop_discovery_sync(struct hci_dev *hdev, void *data)
|
||||
|
|
@ -8173,7 +8192,11 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (param_count > 1)
|
||||
/* A single entry may be an update for one device, so keep the other
|
||||
* disabled parameters then, but clear them otherwise, including when
|
||||
* no entries are loaded.
|
||||
*/
|
||||
if (param_count != 1)
|
||||
hci_conn_params_clear_disabled(hdev);
|
||||
|
||||
for (i = 0; i < param_count; i++) {
|
||||
|
|
@ -10128,7 +10151,7 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
u16 eir_len = 0;
|
||||
u32 flags = 0;
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
if (test_and_set_bit(HCI_CONN_MGMT_CONNECTED, conn->flags))
|
||||
return;
|
||||
|
||||
/* allocate buff for LE or BR/EDR adv */
|
||||
|
|
@ -10177,7 +10200,7 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
* since userspace did not know about them yet, so report it now.
|
||||
* Unencrypted connections have no change to report.
|
||||
*/
|
||||
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
if (test_bit(HCI_CONN_ENCRYPT, conn->flags))
|
||||
mgmt_security_level_changed(conn);
|
||||
}
|
||||
|
||||
|
|
@ -10289,7 +10312,7 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status)
|
|||
* still needs it set to know whether to report the security level
|
||||
* reset, so it is consumed there instead of here.
|
||||
*/
|
||||
if (test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
|
||||
if (test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags)) {
|
||||
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type,
|
||||
hci_to_mgmt_reason(status), true);
|
||||
|
|
@ -10930,7 +10953,7 @@ void mgmt_security_level_changed(struct hci_conn *conn)
|
|||
u8 *tlv_data;
|
||||
u8 tlv_len;
|
||||
|
||||
if (!test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
if (!test_bit(HCI_CONN_MGMT_CONNECTED, conn->flags))
|
||||
return;
|
||||
|
||||
/* Only ACL, LE and BIS links are tracked as mgmt connections, so
|
||||
|
|
@ -10961,9 +10984,9 @@ void mgmt_security_level_changed(struct hci_conn *conn)
|
|||
tlv = (void *)tlv_data;
|
||||
tlv->type = cpu_to_le16(MGMT_SEC_LEVEL_CHANGED_PARAM_ENC_TYPE);
|
||||
tlv->length = sizeof(__u8);
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, conn->flags))
|
||||
tlv->value[0] = MGMT_CONN_SEC_ENCRYPT_NONE;
|
||||
else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
|
||||
else if (test_bit(HCI_CONN_AES_CCM, conn->flags))
|
||||
tlv->value[0] = MGMT_CONN_SEC_ENCRYPT_AES_CCM;
|
||||
else
|
||||
tlv->value[0] = MGMT_CONN_SEC_ENCRYPT_E0;
|
||||
|
|
|
|||
|
|
@ -984,12 +984,12 @@ static u8 smp_random(struct smp_chan *smp)
|
|||
|
||||
smp_s1(smp->tk, smp->rrnd, smp->prnd, stk);
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
hci_le_start_enc(hcon, ediv, rand, stk, smp->enc_key_size);
|
||||
hcon->enc_key_size = smp->enc_key_size;
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, hcon->flags);
|
||||
} else {
|
||||
u8 stk[16], auth;
|
||||
__le64 rand = 0;
|
||||
|
|
@ -1028,7 +1028,7 @@ static void smp_notify_keys(struct l2cap_conn *conn)
|
|||
persistent = false;
|
||||
else
|
||||
persistent = !test_bit(HCI_CONN_FLUSH_KEY,
|
||||
&hcon->flags);
|
||||
hcon->flags);
|
||||
} else {
|
||||
/* The LTKs, IRKs and CSRKs should be persistent only if
|
||||
* both sides had the bonding bit set in their
|
||||
|
|
@ -1186,9 +1186,13 @@ static void sc_generate_ltk(struct smp_chan *smp)
|
|||
struct hci_conn *hcon = smp->conn->hcon;
|
||||
struct hci_dev *hdev = hcon->hdev;
|
||||
struct link_key *key;
|
||||
int err;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
key = hci_find_link_key(hdev, &hcon->dst);
|
||||
if (!key) {
|
||||
rcu_read_unlock();
|
||||
bt_dev_err(hdev, "no Link Key found to generate LTK");
|
||||
return;
|
||||
}
|
||||
|
|
@ -1200,16 +1204,19 @@ static void sc_generate_ltk(struct smp_chan *smp)
|
|||
/* SALT = 0x000000000000000000000000746D7032 */
|
||||
const u8 salt[16] = { 0x32, 0x70, 0x6d, 0x74 };
|
||||
|
||||
if (smp_h7(key->val, salt, smp->tk))
|
||||
return;
|
||||
err = smp_h7(key->val, salt, smp->tk);
|
||||
} else {
|
||||
/* From core spec. Spells out in ASCII as 'tmp2'. */
|
||||
const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
|
||||
|
||||
if (smp_h6(key->val, tmp2, smp->tk))
|
||||
return;
|
||||
err = smp_h6(key->val, tmp2, smp->tk);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (err)
|
||||
return;
|
||||
|
||||
if (smp_h6(smp->tk, brle, smp->tk))
|
||||
return;
|
||||
|
||||
|
|
@ -1753,7 +1760,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
/* SMP over BR/EDR requires special treatment */
|
||||
if (conn->hcon->type == ACL_LINK) {
|
||||
/* We must have a BR/EDR SC link */
|
||||
if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) &&
|
||||
if (!test_bit(HCI_CONN_AES_CCM, conn->hcon->flags) &&
|
||||
!hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
|
||||
return SMP_CROSS_TRANSP_NOT_ALLOWED;
|
||||
|
||||
|
|
@ -2202,24 +2209,30 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
|
|||
{
|
||||
struct smp_ltk *key;
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
bool ret = false;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role);
|
||||
if (!key)
|
||||
return false;
|
||||
goto unlock;
|
||||
|
||||
if (smp_ltk_sec_level(key) < sec_level)
|
||||
return false;
|
||||
goto unlock;
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
|
||||
return true;
|
||||
ret = true;
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags))
|
||||
goto unlock;
|
||||
|
||||
hci_le_start_enc(hcon, key->ediv, key->rand, key->val, key->enc_size);
|
||||
hcon->enc_key_size = key->enc_size;
|
||||
|
||||
/* We never store STKs for initiator role, so clear this flag */
|
||||
clear_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
|
||||
clear_bit(HCI_CONN_STK_ENCRYPT, hcon->flags);
|
||||
|
||||
return true;
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
|
||||
|
|
@ -2235,7 +2248,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
|
|||
* don't have an LTK (e.g. because of key distribution bits).
|
||||
*/
|
||||
if (key_pref == SMP_USE_LTK &&
|
||||
test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
|
||||
test_bit(HCI_CONN_STK_ENCRYPT, hcon->flags) &&
|
||||
hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
|
||||
return false;
|
||||
|
||||
|
|
@ -3084,11 +3097,11 @@ static void bredr_pairing(struct l2cap_chan *chan)
|
|||
bt_dev_dbg(hdev, "chan %p", chan);
|
||||
|
||||
/* Only new pairings are interesting */
|
||||
if (!test_bit(HCI_CONN_NEW_LINK_KEY, &hcon->flags))
|
||||
if (!test_bit(HCI_CONN_NEW_LINK_KEY, hcon->flags))
|
||||
return;
|
||||
|
||||
/* Don't bother if we're not encrypted */
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, hcon->flags))
|
||||
return;
|
||||
|
||||
/* Only initiator may initiate SMP over BR/EDR */
|
||||
|
|
@ -3100,7 +3113,7 @@ static void bredr_pairing(struct l2cap_chan *chan)
|
|||
return;
|
||||
|
||||
/* BR/EDR must use Secure Connections for SMP */
|
||||
if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) &&
|
||||
if (!test_bit(HCI_CONN_AES_CCM, hcon->flags) &&
|
||||
!hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
|
||||
return;
|
||||
|
||||
|
|
@ -3149,7 +3162,7 @@ static void smp_resume_cb(struct l2cap_chan *chan)
|
|||
if (!smp)
|
||||
return;
|
||||
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, hcon->flags))
|
||||
return;
|
||||
|
||||
cancel_delayed_work(&smp->security_timer);
|
||||
|
|
@ -3172,7 +3185,7 @@ static void smp_ready_cb(struct l2cap_chan *chan)
|
|||
*/
|
||||
conn->smp = chan;
|
||||
|
||||
if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
|
||||
if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, hcon->flags))
|
||||
bredr_pairing(chan);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -341,13 +341,13 @@ static void del_nbp(struct net_bridge_port *p)
|
|||
|
||||
nbp_delete_promisc(p);
|
||||
|
||||
br_mrp_port_del(br, p);
|
||||
br_cfm_port_del(br, p);
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br_stp_disable_port(p);
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
br_mrp_port_del(br, p);
|
||||
br_cfm_port_del(br, p);
|
||||
|
||||
br_ifinfo_notify(RTM_DELLINK, NULL, p);
|
||||
|
||||
list_del_rcu(&p->list);
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ int can_send(struct sk_buff *skb, int loop)
|
|||
goto inval_skb;
|
||||
}
|
||||
|
||||
if (unlikely(skb->dev->type != ARPHRD_CAN)) {
|
||||
if (unlikely(!can_get_ml_priv(skb->dev))) {
|
||||
err = -EPERM;
|
||||
goto inval_skb;
|
||||
}
|
||||
|
|
@ -418,7 +418,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
|
|||
* @mask: CAN mask (see description)
|
||||
* @func: callback function on filter match
|
||||
* @data: returned parameter for callback function
|
||||
* @ident: string for calling module identification
|
||||
* @ino: inode number of sock (0 = unknown)
|
||||
* @sk: socket pointer (might be NULL)
|
||||
*
|
||||
* Description:
|
||||
|
|
@ -443,7 +443,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
|
|||
*/
|
||||
int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
|
||||
canid_t mask, void (*func)(struct sk_buff *, void *),
|
||||
void *data, char *ident, struct sock *sk)
|
||||
void *data, u64 ino, struct sock *sk)
|
||||
{
|
||||
struct receiver *rcv;
|
||||
struct hlist_head *rcv_list;
|
||||
|
|
@ -452,7 +452,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
|
|||
|
||||
/* insert new receiver (dev,canid,mask) -> (func,data) */
|
||||
|
||||
if (dev && (dev->type != ARPHRD_CAN || !can_get_ml_priv(dev)))
|
||||
if (dev && !can_get_ml_priv(dev))
|
||||
return -ENODEV;
|
||||
|
||||
if (dev && !net_eq(net, dev_net(dev)))
|
||||
|
|
@ -462,9 +462,10 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
|
|||
if (!rcv)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&net->can.rcvlists_lock);
|
||||
|
||||
dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
|
||||
|
||||
spin_lock_bh(&dev_rcv_lists->lock);
|
||||
|
||||
rcv_list = can_rcv_list_find(&can_id, &mask, dev_rcv_lists);
|
||||
|
||||
rcv->can_id = can_id;
|
||||
|
|
@ -472,12 +473,15 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
|
|||
atomic_long_set(&rcv->matches, 0);
|
||||
rcv->func = func;
|
||||
rcv->data = data;
|
||||
rcv->ident = ident;
|
||||
rcv->ino = ino;
|
||||
rcv->sk = sk;
|
||||
|
||||
hlist_add_head_rcu(&rcv->list, rcv_list);
|
||||
dev_rcv_lists->entries++;
|
||||
|
||||
spin_unlock_bh(&dev_rcv_lists->lock);
|
||||
|
||||
spin_lock_bh(&net->can.rcvlists_lock);
|
||||
rcv_lists_stats->rcv_entries++;
|
||||
rcv_lists_stats->rcv_entries_max = max(rcv_lists_stats->rcv_entries_max,
|
||||
rcv_lists_stats->rcv_entries);
|
||||
|
|
@ -509,6 +513,14 @@ static void can_rx_delete_receiver(struct rcu_head *rp)
|
|||
*
|
||||
* Description:
|
||||
* Removes subscription entry depending on given (subscription) values.
|
||||
* The receive lists of a CAN device are protected by a per-device lock,
|
||||
* so the subscription is found even when the device has been moved to
|
||||
* another network namespace after can_rx_register(). The caller has to
|
||||
* ensure that @dev stays valid, e.g. by holding a reference to @dev or
|
||||
* RTNL. @net should be the namespace that has been used for
|
||||
* can_rx_register(). It selects the 'any' CAN device lists for
|
||||
* @dev == NULL and is otherwise only used for the receive list
|
||||
* statistics, which become imprecise when another namespace is passed.
|
||||
*/
|
||||
void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
|
||||
canid_t mask, void (*func)(struct sk_buff *, void *),
|
||||
|
|
@ -519,15 +531,13 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
|
|||
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
|
||||
struct can_dev_rcv_lists *dev_rcv_lists;
|
||||
|
||||
if (dev && dev->type != ARPHRD_CAN)
|
||||
if (dev && !can_get_ml_priv(dev))
|
||||
return;
|
||||
|
||||
if (dev && !net_eq(net, dev_net(dev)))
|
||||
return;
|
||||
|
||||
spin_lock_bh(&net->can.rcvlists_lock);
|
||||
|
||||
dev_rcv_lists = can_dev_rcv_lists_find(net, dev);
|
||||
|
||||
spin_lock_bh(&dev_rcv_lists->lock);
|
||||
|
||||
rcv_list = can_rcv_list_find(&can_id, &mask, dev_rcv_lists);
|
||||
|
||||
/* Search the receiver list for the item to delete. This should
|
||||
|
|
@ -547,26 +557,26 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
|
|||
* a warning here.
|
||||
*/
|
||||
if (!rcv) {
|
||||
spin_unlock_bh(&dev_rcv_lists->lock);
|
||||
pr_warn("can: receive list entry not found for dev %s, id %03X, mask %03X\n",
|
||||
DNAME(dev), can_id, mask);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
hlist_del_rcu(&rcv->list);
|
||||
dev_rcv_lists->entries--;
|
||||
|
||||
spin_unlock_bh(&dev_rcv_lists->lock);
|
||||
|
||||
spin_lock_bh(&net->can.rcvlists_lock);
|
||||
if (rcv_lists_stats->rcv_entries > 0)
|
||||
rcv_lists_stats->rcv_entries--;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&net->can.rcvlists_lock);
|
||||
|
||||
/* schedule the receiver item for deletion */
|
||||
if (rcv) {
|
||||
if (rcv->sk)
|
||||
sock_hold(rcv->sk);
|
||||
call_rcu(&rcv->rcu, can_rx_delete_receiver);
|
||||
}
|
||||
if (rcv->sk)
|
||||
sock_hold(rcv->sk);
|
||||
call_rcu(&rcv->rcu, can_rx_delete_receiver);
|
||||
}
|
||||
EXPORT_SYMBOL(can_rx_unregister);
|
||||
|
||||
|
|
@ -684,8 +694,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
|
|||
{
|
||||
struct can_skb_ext *csx = can_skb_ext_find(skb);
|
||||
|
||||
if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
|
||||
!csx || !can_is_can_skb(skb))) {
|
||||
if (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_can_skb(skb))) {
|
||||
pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n",
|
||||
dev->type, skb->len);
|
||||
|
||||
|
|
@ -710,8 +719,7 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
|
|||
{
|
||||
struct can_skb_ext *csx = can_skb_ext_find(skb);
|
||||
|
||||
if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
|
||||
!csx || !can_is_canfd_skb(skb))) {
|
||||
if (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_canfd_skb(skb))) {
|
||||
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n",
|
||||
dev->type, skb->len);
|
||||
|
||||
|
|
@ -736,8 +744,7 @@ static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,
|
|||
{
|
||||
struct can_skb_ext *csx = can_skb_ext_find(skb);
|
||||
|
||||
if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) ||
|
||||
!csx || !can_is_canxl_skb(skb))) {
|
||||
if (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_canxl_skb(skb))) {
|
||||
pr_warn_once("PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\n",
|
||||
dev->type, skb->len);
|
||||
|
||||
|
|
@ -826,6 +833,7 @@ static int can_pernet_init(struct net *net)
|
|||
net->can.rx_alldev_list = kzalloc_obj(*net->can.rx_alldev_list);
|
||||
if (!net->can.rx_alldev_list)
|
||||
goto out;
|
||||
spin_lock_init(&net->can.rx_alldev_list->lock);
|
||||
net->can.pkg_stats = kzalloc_obj(*net->can.pkg_stats);
|
||||
if (!net->can.pkg_stats)
|
||||
goto out_free_rx_alldev_list;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ struct receiver {
|
|||
atomic_long_t matches;
|
||||
void (*func)(struct sk_buff *skb, void *data);
|
||||
void *data;
|
||||
char *ident;
|
||||
u64 ino;
|
||||
struct sock *sk;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/can-ml.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/bcm.h>
|
||||
|
|
@ -220,9 +221,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
|
|||
struct bcm_sock *bo = bcm_sk(sk);
|
||||
struct bcm_op *op;
|
||||
|
||||
seq_printf(m, ">>> socket %pK", sk->sk_socket);
|
||||
seq_printf(m, " / sk %pK", sk);
|
||||
seq_printf(m, " / bo %pK", bo);
|
||||
seq_printf(m, ">>> sock inode %s", bo->procname);
|
||||
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
|
||||
seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
|
||||
seq_printf(m, " <<<\n");
|
||||
|
|
@ -963,7 +962,7 @@ static void bcm_remove_op(struct bcm_op *op)
|
|||
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
|
||||
{
|
||||
if (op->rx_reg_dev == dev) {
|
||||
can_rx_unregister(dev_net(dev), dev, op->can_id,
|
||||
can_rx_unregister(sock_net(op->sk), dev, op->can_id,
|
||||
REGMASK(op->can_id), bcm_rx_handler, op);
|
||||
|
||||
/* mark as removed subscription */
|
||||
|
|
@ -989,21 +988,10 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
|
|||
/* disable automatic timer on frame reception */
|
||||
op->flags |= RX_NO_AUTOTIMER;
|
||||
|
||||
/*
|
||||
* Don't care if we're bound or not (due to netdev
|
||||
* problems) can_rx_unregister() is always a save
|
||||
* thing to do here.
|
||||
*/
|
||||
if (op->ifindex) {
|
||||
/*
|
||||
* Only remove subscriptions that had not
|
||||
* been removed due to NETDEV_UNREGISTER
|
||||
* in bcm_notifier()
|
||||
*
|
||||
* op->rx_reg_dev is a tracked reference taken
|
||||
* when the subscription was registered, so it
|
||||
* stays valid here even if a concurrent
|
||||
* NETDEV_UNREGISTER already unlisted the dev.
|
||||
/* Only remove subscriptions that had not been
|
||||
* removed in bcm_notify(). op->rx_reg_dev keeps
|
||||
* the device's receive lists accessible.
|
||||
*/
|
||||
if (op->rx_reg_dev)
|
||||
bcm_rx_unreg(op->rx_reg_dev, op);
|
||||
|
|
@ -1536,7 +1524,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
|||
op->can_id,
|
||||
REGMASK(op->can_id),
|
||||
bcm_rx_handler, op,
|
||||
"bcm", sk);
|
||||
sock_i_ino(sk), sk);
|
||||
|
||||
/* keep a tracked reference so that a later
|
||||
* unregister can safely reach the device even
|
||||
|
|
@ -1560,7 +1548,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
|||
} else {
|
||||
err = can_rx_register(sock_net(sk), NULL, op->can_id,
|
||||
REGMASK(op->can_id),
|
||||
bcm_rx_handler, op, "bcm", sk);
|
||||
bcm_rx_handler, op,
|
||||
sock_i_ino(sk), sk);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
|
|
@ -1719,7 +1708,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
|||
goto out_release;
|
||||
}
|
||||
|
||||
if (dev->type != ARPHRD_CAN) {
|
||||
if (!can_get_ml_priv(dev)) {
|
||||
dev_put(dev);
|
||||
ret = -ENODEV;
|
||||
goto out_release;
|
||||
|
|
@ -1867,7 +1856,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
|
|||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
if (!can_get_ml_priv(dev))
|
||||
return NOTIFY_DONE;
|
||||
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
|
||||
return NOTIFY_DONE;
|
||||
|
|
@ -1935,6 +1924,7 @@ static int bcm_release(struct socket *sock)
|
|||
list_del(&bo->notifier);
|
||||
spin_unlock(&bcm_notifier_lock);
|
||||
|
||||
/* bcm_notify() can not access this socket anymore */
|
||||
lock_sock(sk);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PROC_FS)
|
||||
|
|
@ -1948,29 +1938,20 @@ static int bcm_release(struct socket *sock)
|
|||
bcm_remove_op(op);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
|
||||
/*
|
||||
* Don't care if we're bound or not (due to netdev problems)
|
||||
* can_rx_unregister() is always a save thing to do here.
|
||||
*/
|
||||
list_for_each_entry(op, &bo->rx_ops, list) {
|
||||
if (op->ifindex) {
|
||||
/*
|
||||
* Only remove subscriptions that had not
|
||||
* been removed due to NETDEV_UNREGISTER
|
||||
* in bcm_notifier()
|
||||
*
|
||||
* op->rx_reg_dev is a tracked reference taken
|
||||
* when the subscription was registered, so it
|
||||
* stays valid here even if a concurrent
|
||||
* NETDEV_UNREGISTER already unlisted the device.
|
||||
/* Only remove subscriptions that had not been removed
|
||||
* in bcm_notify(). op->rx_reg_dev keeps the device's
|
||||
* receive lists accessible.
|
||||
*/
|
||||
if (op->rx_reg_dev)
|
||||
bcm_rx_unreg(op->rx_reg_dev, op);
|
||||
} else
|
||||
} else {
|
||||
can_rx_unregister(net, NULL, op->can_id,
|
||||
REGMASK(op->can_id),
|
||||
bcm_rx_handler, op);
|
||||
|
||||
}
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
synchronize_rcu();
|
||||
|
|
@ -2024,7 +2005,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
|
|||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
if (dev->type != ARPHRD_CAN) {
|
||||
if (!can_get_ml_priv(dev)) {
|
||||
dev_put(dev);
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
|
|
@ -2040,8 +2021,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
|
|||
|
||||
#if IS_ENABLED(CONFIG_PROC_FS)
|
||||
if (net->can.bcmproc_dir) {
|
||||
/* unique socket address as filename */
|
||||
sprintf(bo->procname, "%llu", sock_i_ino(sk));
|
||||
/* use unique socket inode number as filename */
|
||||
snprintf(bo->procname, sizeof(bo->procname),
|
||||
"%llu", sock_i_ino(sk));
|
||||
bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644,
|
||||
net->can.bcmproc_dir,
|
||||
bcm_proc_show, sk);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/can-ml.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/gw.h>
|
||||
|
|
@ -579,7 +580,7 @@ static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
|
|||
{
|
||||
return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
|
||||
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
|
||||
gwj, "gw", NULL);
|
||||
gwj, 0, NULL);
|
||||
}
|
||||
|
||||
static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
|
||||
|
|
@ -612,7 +613,7 @@ static int cgw_notifier(struct notifier_block *nb,
|
|||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct net *net = dev_net(dev);
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
if (!can_get_ml_priv(dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (msg == NETDEV_UNREGISTER) {
|
||||
|
|
@ -1163,7 +1164,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
if (!gwj->src.dev)
|
||||
goto out;
|
||||
|
||||
if (gwj->src.dev->type != ARPHRD_CAN)
|
||||
if (!can_get_ml_priv(gwj->src.dev))
|
||||
goto out;
|
||||
|
||||
gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
|
||||
|
|
@ -1171,7 +1172,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
if (!gwj->dst.dev)
|
||||
goto out;
|
||||
|
||||
if (gwj->dst.dev->type != ARPHRD_CAN)
|
||||
if (!can_get_ml_priv(gwj->dst.dev))
|
||||
goto out;
|
||||
|
||||
/* is sending the skb back to the incoming interface intended? */
|
||||
|
|
|
|||
236
net/can/isotp.c
236
net/can/isotp.c
|
|
@ -65,6 +65,7 @@
|
|||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/can-ml.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/isotp.h>
|
||||
|
|
@ -755,6 +756,14 @@ static void isotp_rcv(struct sk_buff *skb, void *data)
|
|||
if (skb->len != so->ll.mtu)
|
||||
return;
|
||||
|
||||
/* check for correct CAN CC/FD frame content */
|
||||
if (so->ll.mtu == CAN_MTU) {
|
||||
if (!can_is_can_skb(skb))
|
||||
return;
|
||||
} else if (!can_is_canfd_skb(skb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cf = (struct canfd_frame *)skb->data;
|
||||
|
||||
/* if enabled: check reception of my configured extended address */
|
||||
|
|
@ -1123,7 +1132,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
|||
int off;
|
||||
int err;
|
||||
|
||||
if (!so->bound || READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN)
|
||||
if (!READ_ONCE(so->bound) || READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
/* claim the socket under so->rx_lock: this serializes the claim
|
||||
|
|
@ -1168,7 +1177,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
|||
/* so->bound is only checked once above - a wakeup may have
|
||||
* unbound/rebound the socket meanwhile => recheck
|
||||
*/
|
||||
if (!so->bound) {
|
||||
if (!READ_ONCE(so->bound)) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
goto err_out_drop;
|
||||
}
|
||||
|
|
@ -1414,7 +1423,7 @@ static int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
|||
if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK | MSG_CMSG_COMPAT))
|
||||
return -EINVAL;
|
||||
|
||||
if (!so->bound)
|
||||
if (!READ_ONCE(so->bound))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
skb = skb_recv_datagram(sk, flags, &ret);
|
||||
|
|
@ -1488,11 +1497,11 @@ static int isotp_release(struct socket *sock)
|
|||
list_del(&so->notifier);
|
||||
spin_unlock(&isotp_notifier_lock);
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
|
||||
/* remove current filters & unregister
|
||||
* tracked reference so->dev is taken at bind() time with rtnl_lock
|
||||
* the tracked reference so->dev keeps the device's receive lists
|
||||
* accessible, even after a namespace change
|
||||
*/
|
||||
if (so->bound && so->dev) {
|
||||
if (isotp_register_rxid(so))
|
||||
|
|
@ -1507,11 +1516,9 @@ static int isotp_release(struct socket *sock)
|
|||
}
|
||||
|
||||
so->ifindex = 0;
|
||||
so->bound = 0;
|
||||
WRITE_ONCE(so->bound, 0);
|
||||
so->dev = NULL;
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
/* Always wait for a grace period before touching the timers below.
|
||||
* A concurrent NETDEV_UNREGISTER may have already unregistered our
|
||||
* filters and cleared so->bound in isotp_notify() without waiting
|
||||
|
|
@ -1541,7 +1548,6 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
|||
struct sock *sk = sock->sk;
|
||||
struct isotp_sock *so = isotp_sk(sk);
|
||||
struct net *net = sock_net(sk);
|
||||
int ifindex;
|
||||
struct net_device *dev;
|
||||
canid_t tx_id = addr->can_addr.tp.tx_id;
|
||||
canid_t rx_id = addr->can_addr.tp.rx_id;
|
||||
|
|
@ -1579,7 +1585,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
|||
if (!addr->can_ifindex)
|
||||
return -ENODEV;
|
||||
|
||||
rtnl_lock();
|
||||
/* lock_sock() serializes the binding with isotp_notify() */
|
||||
lock_sock(sk);
|
||||
|
||||
if (so->bound) {
|
||||
|
|
@ -1605,61 +1611,83 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
|||
goto out;
|
||||
}
|
||||
|
||||
dev = dev_get_by_index(net, addr->can_ifindex);
|
||||
if (!dev) {
|
||||
/* Publish the device inside the RCU section of the lookup: When it
|
||||
* is unlisted concurrently, either isotp_notify() sees so->dev after
|
||||
* synchronize_net() or the lookup fails. Check the device before
|
||||
* publishing it.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
dev = dev_get_by_index_rcu(net, addr->can_ifindex);
|
||||
if (!dev || !can_get_ml_priv(dev))
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (dev->type != ARPHRD_CAN) {
|
||||
err = -ENODEV;
|
||||
goto out_put_dev;
|
||||
}
|
||||
if (READ_ONCE(dev->mtu) < so->ll.mtu) {
|
||||
else if (READ_ONCE(dev->mtu) < so->ll.mtu)
|
||||
err = -EINVAL;
|
||||
goto out_put_dev;
|
||||
if (!err) {
|
||||
dev_hold(dev);
|
||||
WRITE_ONCE(so->dev, dev);
|
||||
}
|
||||
if (!(dev->flags & IFF_UP))
|
||||
notify_enetdown = 1;
|
||||
rcu_read_unlock();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
ifindex = dev->ifindex;
|
||||
|
||||
if (isotp_register_rxid(so))
|
||||
can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),
|
||||
isotp_rcv, sk, "isotp", sk);
|
||||
/* switch to new settings */
|
||||
so->ifindex = dev->ifindex;
|
||||
so->rxid = rx_id;
|
||||
so->txid = tx_id;
|
||||
|
||||
/* no consecutive frame echo skb in flight */
|
||||
WRITE_ONCE(so->cfecho, 0);
|
||||
|
||||
/* register for echo skb's */
|
||||
can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
|
||||
isotp_rcv_echo, sk, "isotpe", sk);
|
||||
/* register for echo skb's first (no echo traffic at this point) */
|
||||
err = can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
|
||||
isotp_rcv_echo, sk, sock_i_ino(sk), sk);
|
||||
if (err)
|
||||
goto out_put_dev;
|
||||
|
||||
/* switch to new settings */
|
||||
so->ifindex = ifindex;
|
||||
so->rxid = rx_id;
|
||||
so->txid = tx_id;
|
||||
so->bound = 1;
|
||||
if (isotp_register_rxid(so)) {
|
||||
err = can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),
|
||||
isotp_rcv, sk, sock_i_ino(sk), sk);
|
||||
if (err) {
|
||||
/* unregister echo skb subscription */
|
||||
can_rx_unregister(net, dev, tx_id, SINGLE_MASK(tx_id),
|
||||
isotp_rcv_echo, sk);
|
||||
goto out_put_dev;
|
||||
}
|
||||
}
|
||||
|
||||
WRITE_ONCE(so->bound, 1);
|
||||
|
||||
/* bind() ok -> hold a reference for so->dev so that isotp_release()
|
||||
* can safely reach the device later, even if a concurrent
|
||||
* NETDEV_UNREGISTER has already unlisted it by ifindex.
|
||||
*/
|
||||
so->dev = dev;
|
||||
netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL);
|
||||
|
||||
/* Either isotp_notify() sees so->dev on NETDEV_DOWN or we see the
|
||||
* cleared IFF_UP, pairs with isotp_notify()
|
||||
*/
|
||||
smp_mb();
|
||||
if (!(READ_ONCE(dev->flags) & IFF_UP))
|
||||
notify_enetdown = 1;
|
||||
|
||||
out_put_dev:
|
||||
/* remove potential reference from dev_get_by_index() */
|
||||
/* revert so->ifindex and so->dev on failed bind() */
|
||||
if (err) {
|
||||
so->ifindex = 0;
|
||||
WRITE_ONCE(so->dev, NULL);
|
||||
}
|
||||
|
||||
/* remove reference from the device lookup */
|
||||
dev_put(dev);
|
||||
out:
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
|
||||
if (notify_enetdown) {
|
||||
sk->sk_err = ENETDOWN;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1681,8 +1709,8 @@ static int isotp_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
|
|||
return ISOTP_MIN_NAMELEN;
|
||||
}
|
||||
|
||||
static int isotp_setsockopt_locked(struct socket *sock, int level, int optname,
|
||||
sockptr_t optval, unsigned int optlen)
|
||||
static int isotp_setsockopt_locked(struct socket *sock, int optname,
|
||||
const void *val)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct isotp_sock *so = isotp_sk(sk);
|
||||
|
|
@ -1693,11 +1721,7 @@ static int isotp_setsockopt_locked(struct socket *sock, int level, int optname,
|
|||
|
||||
switch (optname) {
|
||||
case CAN_ISOTP_OPTS:
|
||||
if (optlen != sizeof(struct can_isotp_options))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&so->opt, optval, optlen))
|
||||
return -EFAULT;
|
||||
memcpy(&so->opt, val, sizeof(so->opt));
|
||||
|
||||
/* no separate rx_ext_address is given => use ext_address */
|
||||
if (!(so->opt.flags & CAN_ISOTP_RX_EXT_ADDR))
|
||||
|
|
@ -1722,55 +1746,37 @@ static int isotp_setsockopt_locked(struct socket *sock, int level, int optname,
|
|||
break;
|
||||
|
||||
case CAN_ISOTP_RECV_FC:
|
||||
if (optlen != sizeof(struct can_isotp_fc_options))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&so->rxfc, optval, optlen))
|
||||
return -EFAULT;
|
||||
memcpy(&so->rxfc, val, sizeof(so->rxfc));
|
||||
break;
|
||||
|
||||
case CAN_ISOTP_TX_STMIN:
|
||||
if (optlen != sizeof(u32))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&so->force_tx_stmin, optval, optlen))
|
||||
return -EFAULT;
|
||||
so->force_tx_stmin = *(const u32 *)val;
|
||||
break;
|
||||
|
||||
case CAN_ISOTP_RX_STMIN:
|
||||
if (optlen != sizeof(u32))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&so->force_rx_stmin, optval, optlen))
|
||||
return -EFAULT;
|
||||
so->force_rx_stmin = *(const u32 *)val;
|
||||
break;
|
||||
|
||||
case CAN_ISOTP_LL_OPTS:
|
||||
if (optlen == sizeof(struct can_isotp_ll_options)) {
|
||||
struct can_isotp_ll_options ll;
|
||||
case CAN_ISOTP_LL_OPTS: {
|
||||
const struct can_isotp_ll_options *ll = val;
|
||||
|
||||
if (copy_from_sockptr(&ll, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
/* check for correct ISO 11898-1 DLC data length */
|
||||
if (ll.tx_dl != padlen(ll.tx_dl))
|
||||
return -EINVAL;
|
||||
|
||||
if (ll.mtu != CAN_MTU && ll.mtu != CANFD_MTU)
|
||||
return -EINVAL;
|
||||
|
||||
if (ll.mtu == CAN_MTU &&
|
||||
(ll.tx_dl > CAN_MAX_DLEN || ll.tx_flags != 0))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&so->ll, &ll, sizeof(ll));
|
||||
|
||||
/* set ll_dl for tx path to similar place as for rx */
|
||||
so->tx.ll_dl = ll.tx_dl;
|
||||
} else {
|
||||
/* check for correct ISO 11898-1 DLC data length */
|
||||
if (ll->tx_dl != padlen(ll->tx_dl))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ll->mtu != CAN_MTU && ll->mtu != CANFD_MTU)
|
||||
return -EINVAL;
|
||||
|
||||
if (ll->mtu == CAN_MTU &&
|
||||
(ll->tx_dl > CAN_MAX_DLEN || ll->tx_flags != 0))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&so->ll, ll, sizeof(*ll));
|
||||
|
||||
/* set ll_dl for tx path to similar place as for rx */
|
||||
so->tx.ll_dl = ll->tx_dl;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = -ENOPROTOOPT;
|
||||
|
|
@ -1784,13 +1790,49 @@ static int isotp_setsockopt(struct socket *sock, int level, int optname,
|
|||
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
union {
|
||||
struct can_isotp_options opt;
|
||||
struct can_isotp_fc_options rxfc;
|
||||
struct can_isotp_ll_options ll;
|
||||
u32 stmin;
|
||||
} val;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
if (level != SOL_CAN_ISOTP)
|
||||
return -EINVAL;
|
||||
|
||||
/* copy the option values from user space before lock_sock() */
|
||||
switch (optname) {
|
||||
case CAN_ISOTP_OPTS:
|
||||
size = sizeof(val.opt);
|
||||
break;
|
||||
|
||||
case CAN_ISOTP_RECV_FC:
|
||||
size = sizeof(val.rxfc);
|
||||
break;
|
||||
|
||||
case CAN_ISOTP_TX_STMIN:
|
||||
case CAN_ISOTP_RX_STMIN:
|
||||
size = sizeof(val.stmin);
|
||||
break;
|
||||
|
||||
case CAN_ISOTP_LL_OPTS:
|
||||
size = sizeof(val.ll);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
||||
if (optlen != size)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&val, optval, size))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
ret = isotp_setsockopt_locked(sock, level, optname, optval, optlen);
|
||||
ret = isotp_setsockopt_locked(sock, optname, &val);
|
||||
release_sock(sk);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1855,12 +1897,21 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
|
|||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
return;
|
||||
|
||||
if (so->dev != dev)
|
||||
if (msg == NETDEV_DOWN)
|
||||
smp_mb(); /* pairs with isotp_bind(), see raw_notify() */
|
||||
|
||||
/* lockless fast path, see raw_notify() */
|
||||
if (READ_ONCE(so->dev) != dev)
|
||||
return;
|
||||
|
||||
switch (msg) {
|
||||
case NETDEV_UNREGISTER:
|
||||
lock_sock(sk);
|
||||
if (so->dev != dev) {
|
||||
release_sock(sk);
|
||||
break;
|
||||
}
|
||||
|
||||
/* remove current filters & unregister */
|
||||
if (so->bound) {
|
||||
if (isotp_register_rxid(so))
|
||||
|
|
@ -1875,16 +1926,19 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
|
|||
}
|
||||
|
||||
so->ifindex = 0;
|
||||
so->bound = 0;
|
||||
so->dev = NULL;
|
||||
release_sock(sk);
|
||||
WRITE_ONCE(so->bound, 0);
|
||||
WRITE_ONCE(so->dev, NULL);
|
||||
|
||||
sk->sk_err = ENODEV;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
release_sock(sk);
|
||||
break;
|
||||
|
||||
case NETDEV_DOWN:
|
||||
if (!READ_ONCE(so->bound))
|
||||
break;
|
||||
|
||||
sk->sk_err = ENETDOWN;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
|
|
@ -1897,7 +1951,7 @@ static int isotp_notifier(struct notifier_block *nb, unsigned long msg,
|
|||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
if (!can_get_ml_priv(dev))
|
||||
return NOTIFY_DONE;
|
||||
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
|
||||
return NOTIFY_DONE;
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ static int j1939_can_rx_register(struct j1939_priv *priv)
|
|||
|
||||
j1939_priv_get(priv);
|
||||
ret = can_rx_register(dev_net(ndev), ndev, J1939_CAN_ID, J1939_CAN_MASK,
|
||||
j1939_can_recv, priv, "j1939", NULL);
|
||||
j1939_can_recv, priv, 0, NULL);
|
||||
if (ret < 0) {
|
||||
j1939_priv_put(priv);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -76,16 +76,39 @@ static const char rx_list_name[][8] = {
|
|||
* af_can statistics stuff
|
||||
*/
|
||||
|
||||
static void can_reset_pkg_stats(struct can_pkg_stats *pkg_stats)
|
||||
{
|
||||
atomic_long_set(&pkg_stats->rx_frames, 0);
|
||||
atomic_long_set(&pkg_stats->tx_frames, 0);
|
||||
atomic_long_set(&pkg_stats->matches, 0);
|
||||
|
||||
pkg_stats->total_rx_rate = 0;
|
||||
pkg_stats->total_tx_rate = 0;
|
||||
pkg_stats->total_rx_match_ratio = 0;
|
||||
|
||||
pkg_stats->current_rx_rate = 0;
|
||||
pkg_stats->current_tx_rate = 0;
|
||||
pkg_stats->current_rx_match_ratio = 0;
|
||||
|
||||
pkg_stats->max_rx_rate = 0;
|
||||
pkg_stats->max_tx_rate = 0;
|
||||
pkg_stats->max_rx_match_ratio = 0;
|
||||
|
||||
atomic_long_set(&pkg_stats->rx_frames_delta, 0);
|
||||
atomic_long_set(&pkg_stats->tx_frames_delta, 0);
|
||||
atomic_long_set(&pkg_stats->matches_delta, 0);
|
||||
}
|
||||
|
||||
static void can_init_stats(struct net *net)
|
||||
{
|
||||
struct can_pkg_stats *pkg_stats = net->can.pkg_stats;
|
||||
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
|
||||
/*
|
||||
* This memset function is called from a timer context (when
|
||||
* This stats reset is called from a timer context (when
|
||||
* can_stattimer is active which is the default) OR in a process
|
||||
* context (reading the proc_fs when can_stattimer is disabled).
|
||||
*/
|
||||
memset(pkg_stats, 0, sizeof(struct can_pkg_stats));
|
||||
can_reset_pkg_stats(pkg_stats);
|
||||
pkg_stats->jiffies_init = jiffies;
|
||||
|
||||
rcv_lists_stats->stats_reset++;
|
||||
|
|
@ -121,29 +144,23 @@ void can_stat_update(struct timer_list *t)
|
|||
long rx_frames = atomic_long_read(&pkg_stats->rx_frames);
|
||||
long tx_frames = atomic_long_read(&pkg_stats->tx_frames);
|
||||
long matches = atomic_long_read(&pkg_stats->matches);
|
||||
long rx_frames_delta = atomic_long_read(&pkg_stats->rx_frames_delta);
|
||||
long tx_frames_delta = atomic_long_read(&pkg_stats->tx_frames_delta);
|
||||
long matches_delta = atomic_long_read(&pkg_stats->matches_delta);
|
||||
/* fetch and clear values for 'current rate' calculation */
|
||||
long rx_frames_delta = atomic_long_xchg(&pkg_stats->rx_frames_delta, 0);
|
||||
long tx_frames_delta = atomic_long_xchg(&pkg_stats->tx_frames_delta, 0);
|
||||
long matches_delta = atomic_long_xchg(&pkg_stats->matches_delta, 0);
|
||||
|
||||
/* restart counting in timer context on user request */
|
||||
if (user_reset)
|
||||
can_init_stats(net);
|
||||
|
||||
/* restart counting on jiffies overflow */
|
||||
if (j < pkg_stats->jiffies_init)
|
||||
can_init_stats(net);
|
||||
|
||||
/* prevent overflow in calc_rate() */
|
||||
if (rx_frames > (LONG_MAX / HZ))
|
||||
can_init_stats(net);
|
||||
|
||||
/* prevent overflow in calc_rate() */
|
||||
if (tx_frames > (LONG_MAX / HZ))
|
||||
can_init_stats(net);
|
||||
|
||||
/* matches overflow - very improbable */
|
||||
if (matches > (LONG_MAX / 100))
|
||||
/*
|
||||
* Restart counting in timer context on user request, on jiffies
|
||||
* overflow or to prevent overflows in calc_rate() and in the
|
||||
* match ratio calculation. The local snapshots are stale after
|
||||
* the reset, so skip the calculation for this interval.
|
||||
*/
|
||||
if (user_reset || j < pkg_stats->jiffies_init ||
|
||||
rx_frames > (LONG_MAX / HZ) || tx_frames > (LONG_MAX / HZ) ||
|
||||
matches > (LONG_MAX / 100)) {
|
||||
can_init_stats(net);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* calc total values */
|
||||
if (rx_frames)
|
||||
|
|
@ -172,11 +189,7 @@ void can_stat_update(struct timer_list *t)
|
|||
if (pkg_stats->max_rx_match_ratio < pkg_stats->current_rx_match_ratio)
|
||||
pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio;
|
||||
|
||||
/* clear values for 'current rate' calculation */
|
||||
atomic_long_set(&pkg_stats->tx_frames_delta, 0);
|
||||
atomic_long_set(&pkg_stats->rx_frames_delta, 0);
|
||||
atomic_long_set(&pkg_stats->matches_delta, 0);
|
||||
|
||||
out:
|
||||
/* restart timer (one second) */
|
||||
mod_timer(&net->can.stattimer, round_jiffies(jiffies + HZ));
|
||||
}
|
||||
|
|
@ -191,26 +204,32 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
|
|||
struct receiver *r;
|
||||
|
||||
hlist_for_each_entry_rcu(r, rx_list, list) {
|
||||
char symcheck[4];
|
||||
char *fmt = (r->can_id & CAN_EFF_FLAG)?
|
||||
" %-5s %08x %08x %pK %pK %8ld %s\n" :
|
||||
" %-5s %03x %08x %pK %pK %8ld %s\n";
|
||||
" %6s %08x %08x %8ld %10llu" :
|
||||
" %6s %03x %08x %8ld %10llu";
|
||||
|
||||
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
|
||||
r->func, r->data, atomic_long_read(&r->matches),
|
||||
r->ident);
|
||||
atomic_long_read(&r->matches), r->ino);
|
||||
|
||||
/* %ps falls back to the raw text address when the symbol
|
||||
* can not be resolved (KALLSYMS=n, stripped module symbols)
|
||||
*/
|
||||
snprintf(symcheck, sizeof(symcheck), "%ps", r->func);
|
||||
if (strncmp(symcheck, "0x", 2))
|
||||
seq_printf(m, " %ps\n", r->func);
|
||||
else
|
||||
seq_puts(m, " (unknown)\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void can_print_recv_banner(struct seq_file *m)
|
||||
{
|
||||
/*
|
||||
* can1. 00000000 00000000 00000000
|
||||
* ....... 0 tp20
|
||||
* device can_id can_mask matches sock_inode function
|
||||
* vcan0 000 00000000 0 43798 raw_rcv [can_raw]
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_64BIT))
|
||||
seq_puts(m, " device can_id can_mask function userdata matches ident\n");
|
||||
else
|
||||
seq_puts(m, " device can_id can_mask function userdata matches ident\n");
|
||||
seq_puts(m, " device can_id can_mask matches sock_inode function\n");
|
||||
}
|
||||
|
||||
static int can_stats_proc_show(struct seq_file *m, void *v)
|
||||
|
|
|
|||
278
net/can/raw.c
278
net/can/raw.c
|
|
@ -85,6 +85,7 @@ struct raw_sock {
|
|||
struct sock sk;
|
||||
struct net_device *dev;
|
||||
netdevice_tracker dev_tracker;
|
||||
struct net_device *bind_dev; /* hint for raw_notify(), no reference */
|
||||
struct list_head notifier;
|
||||
int ifindex;
|
||||
unsigned int bound:1;
|
||||
|
|
@ -224,9 +225,10 @@ static int raw_enable_filters(struct net *net, struct net_device *dev,
|
|||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
cond_resched();
|
||||
err = can_rx_register(net, dev, filter[i].can_id,
|
||||
filter[i].can_mask,
|
||||
raw_rcv, sk, "raw", sk);
|
||||
raw_rcv, sk, sock_i_ino(sk), sk);
|
||||
if (err) {
|
||||
/* clean up successfully registered filters */
|
||||
while (--i >= 0)
|
||||
|
|
@ -247,7 +249,7 @@ static int raw_enable_errfilter(struct net *net, struct net_device *dev,
|
|||
|
||||
if (err_mask)
|
||||
err = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG,
|
||||
raw_rcv, sk, "raw", sk);
|
||||
raw_rcv, sk, sock_i_ino(sk), sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -258,9 +260,11 @@ static void raw_disable_filters(struct net *net, struct net_device *dev,
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
for (i = 0; i < count; i++) {
|
||||
can_rx_unregister(net, dev, filter[i].can_id,
|
||||
filter[i].can_mask, raw_rcv, sk);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void raw_disable_errfilter(struct net *net,
|
||||
|
|
@ -305,16 +309,31 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
|
|||
struct net_device *dev)
|
||||
{
|
||||
struct sock *sk = &ro->sk;
|
||||
struct net_device *bind_dev;
|
||||
|
||||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
return;
|
||||
|
||||
if (ro->dev != dev)
|
||||
if (msg == NETDEV_DOWN)
|
||||
smp_mb(); /* IFF_UP is cleared before, pairs with raw_bind() */
|
||||
|
||||
/* Lockless fast path, ro->bind_dev covers a concurrent bind().
|
||||
* Read it before ro->dev, pairs with smp_store_release() in
|
||||
* raw_bind(). A stale ro->bind_dev only costs a lock_sock().
|
||||
*/
|
||||
bind_dev = smp_load_acquire(&ro->bind_dev);
|
||||
if (READ_ONCE(ro->dev) != dev &&
|
||||
(msg != NETDEV_UNREGISTER || bind_dev != dev))
|
||||
return;
|
||||
|
||||
switch (msg) {
|
||||
case NETDEV_UNREGISTER:
|
||||
lock_sock(sk);
|
||||
if (ro->dev != dev) {
|
||||
release_sock(sk);
|
||||
break;
|
||||
}
|
||||
|
||||
/* remove current filters & unregister */
|
||||
if (ro->bound) {
|
||||
raw_disable_allfilters(dev_net(dev), dev, sk);
|
||||
|
|
@ -326,13 +345,13 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
|
|||
|
||||
ro->ifindex = 0;
|
||||
ro->bound = 0;
|
||||
ro->dev = NULL;
|
||||
WRITE_ONCE(ro->dev, NULL);
|
||||
ro->count = 0;
|
||||
release_sock(sk);
|
||||
|
||||
sk->sk_err = ENODEV;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
release_sock(sk);
|
||||
break;
|
||||
|
||||
case NETDEV_DOWN:
|
||||
|
|
@ -348,7 +367,7 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg,
|
|||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
if (!can_get_ml_priv(dev))
|
||||
return NOTIFY_DONE;
|
||||
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
|
||||
return NOTIFY_DONE;
|
||||
|
|
@ -381,6 +400,7 @@ static int raw_init(struct sock *sk)
|
|||
ro->bound = 0;
|
||||
ro->ifindex = 0;
|
||||
ro->dev = NULL;
|
||||
ro->bind_dev = NULL;
|
||||
|
||||
/* set default filter to single entry dfilter */
|
||||
ro->dfilter.can_id = 0;
|
||||
|
|
@ -431,13 +451,15 @@ static int raw_release(struct socket *sock)
|
|||
list_del(&ro->notifier);
|
||||
spin_unlock(&raw_notifier_lock);
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
|
||||
/* remove current filters & unregister */
|
||||
/* remove current filters & unregister
|
||||
* the tracked reference ro->dev keeps the device's receive lists
|
||||
* accessible, even after a namespace change
|
||||
*/
|
||||
if (ro->bound) {
|
||||
if (ro->dev) {
|
||||
raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
|
||||
raw_disable_allfilters(net, ro->dev, sk);
|
||||
netdev_put(ro->dev, &ro->dev_tracker);
|
||||
} else {
|
||||
raw_disable_allfilters(net, NULL, sk);
|
||||
|
|
@ -456,7 +478,6 @@ static int raw_release(struct socket *sock)
|
|||
sock->sk = NULL;
|
||||
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
|
||||
sock_prot_inuse_add(net, sk->sk_prot, -1);
|
||||
sock_put(sk);
|
||||
|
|
@ -479,26 +500,34 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
|
|||
if (addr->can_family != AF_CAN)
|
||||
return -EINVAL;
|
||||
|
||||
rtnl_lock();
|
||||
/* lock_sock() serializes the binding with raw_notify() */
|
||||
lock_sock(sk);
|
||||
|
||||
if (ro->bound && addr->can_ifindex == ro->ifindex)
|
||||
goto out;
|
||||
|
||||
if (addr->can_ifindex) {
|
||||
dev = dev_get_by_index(sock_net(sk), addr->can_ifindex);
|
||||
/* Publish the device inside the RCU section of the lookup:
|
||||
* When it is unlisted concurrently, either raw_notify() sees
|
||||
* ro->bind_dev after synchronize_net() or the lookup fails.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
dev = dev_get_by_index_rcu(sock_net(sk), addr->can_ifindex);
|
||||
if (dev) {
|
||||
dev_hold(dev);
|
||||
/* pairs with smp_load_acquire() in raw_notify() */
|
||||
smp_store_release(&ro->bind_dev, dev);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!dev) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (dev->type != ARPHRD_CAN) {
|
||||
if (!can_get_ml_priv(dev)) {
|
||||
err = -ENODEV;
|
||||
goto out_put_dev;
|
||||
}
|
||||
|
||||
if (!(dev->flags & IFF_UP))
|
||||
notify_enetdown = 1;
|
||||
|
||||
ifindex = dev->ifindex;
|
||||
|
||||
/* filters set by default/setsockopt */
|
||||
|
|
@ -517,7 +546,7 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
|
|||
if (ro->bound) {
|
||||
/* unregister old filters */
|
||||
if (ro->dev) {
|
||||
raw_disable_allfilters(dev_net(ro->dev),
|
||||
raw_disable_allfilters(sock_net(sk),
|
||||
ro->dev, sk);
|
||||
/* drop reference to old ro->dev */
|
||||
netdev_put(ro->dev, &ro->dev_tracker);
|
||||
|
|
@ -528,24 +557,32 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
|
|||
ro->ifindex = ifindex;
|
||||
ro->bound = 1;
|
||||
/* bind() ok -> hold a reference for new ro->dev */
|
||||
ro->dev = dev;
|
||||
WRITE_ONCE(ro->dev, dev);
|
||||
if (ro->dev)
|
||||
netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
|
||||
|
||||
if (dev) {
|
||||
/* Either raw_notify() sees ro->dev on NETDEV_DOWN or
|
||||
* we see the cleared IFF_UP, pairs with raw_notify()
|
||||
*/
|
||||
smp_mb();
|
||||
if (!(READ_ONCE(dev->flags) & IFF_UP))
|
||||
notify_enetdown = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out_put_dev:
|
||||
/* remove potential reference from dev_get_by_index() */
|
||||
/* remove reference from the device lookup */
|
||||
dev_put(dev);
|
||||
out:
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
|
||||
if (notify_enetdown) {
|
||||
sk->sk_err = ENETDOWN;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -567,58 +604,36 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
|
|||
}
|
||||
|
||||
static int raw_setsockopt_locked(struct socket *sock, int optname,
|
||||
sockptr_t optval, unsigned int optlen)
|
||||
const void *val, struct can_filter *filter,
|
||||
int count)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
|
||||
struct can_filter sfilter; /* single filter */
|
||||
struct net_device *dev = NULL;
|
||||
can_err_mask_t err_mask = 0;
|
||||
int count = 0;
|
||||
can_err_mask_t err_mask;
|
||||
int flag;
|
||||
int err = 0;
|
||||
|
||||
switch (optname) {
|
||||
case CAN_RAW_FILTER:
|
||||
if (optlen % sizeof(struct can_filter) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
|
||||
return -EINVAL;
|
||||
|
||||
count = optlen / sizeof(struct can_filter);
|
||||
|
||||
if (count > 1) {
|
||||
/* filter does not fit into dfilter => alloc space */
|
||||
filter = memdup_sockptr(optval, optlen);
|
||||
if (IS_ERR(filter))
|
||||
return PTR_ERR(filter);
|
||||
} else if (count == 1) {
|
||||
if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* @filter has been copied by raw_setsockopt(). It is linked
|
||||
* to the socket on success and freed by the caller on error.
|
||||
*
|
||||
* The reg_state check is only a shortcut: On a concurrent
|
||||
* unregistration or namespace change raw_notify() waits for
|
||||
* lock_sock() and removes the filters set here.
|
||||
*/
|
||||
dev = ro->dev;
|
||||
if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) {
|
||||
if (count > 1)
|
||||
kfree(filter);
|
||||
if (ro->bound && dev &&
|
||||
READ_ONCE(dev->reg_state) != NETREG_REGISTERED)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ro->bound) {
|
||||
/* (try to) register the new filters */
|
||||
if (count == 1)
|
||||
err = raw_enable_filters(sock_net(sk), dev, sk,
|
||||
&sfilter, 1);
|
||||
else
|
||||
err = raw_enable_filters(sock_net(sk), dev, sk,
|
||||
filter, count);
|
||||
if (err) {
|
||||
if (count > 1)
|
||||
kfree(filter);
|
||||
err = raw_enable_filters(sock_net(sk), dev, sk, filter,
|
||||
count);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* remove old filter registrations */
|
||||
raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
|
||||
|
|
@ -632,7 +647,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
|
|||
/* link new filters to the socket */
|
||||
if (count == 1) {
|
||||
/* copy filter data for single filter */
|
||||
ro->dfilter = sfilter;
|
||||
ro->dfilter = *filter;
|
||||
filter = &ro->dfilter;
|
||||
}
|
||||
ro->filter = filter;
|
||||
|
|
@ -640,16 +655,12 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
|
|||
break;
|
||||
|
||||
case CAN_RAW_ERR_FILTER:
|
||||
if (optlen != sizeof(err_mask))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&err_mask, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
err_mask &= CAN_ERR_MASK;
|
||||
err_mask = *(const can_err_mask_t *)val & CAN_ERR_MASK;
|
||||
|
||||
/* lockless shortcut, see CAN_RAW_FILTER */
|
||||
dev = ro->dev;
|
||||
if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED)
|
||||
if (ro->bound && dev &&
|
||||
READ_ONCE(dev->reg_state) != NETREG_REGISTERED)
|
||||
return -ENODEV;
|
||||
|
||||
/* remove current error mask */
|
||||
|
|
@ -671,31 +682,15 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
|
|||
break;
|
||||
|
||||
case CAN_RAW_LOOPBACK:
|
||||
if (optlen != sizeof(flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&flag, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
ro->loopback = !!flag;
|
||||
ro->loopback = !!*(const int *)val;
|
||||
break;
|
||||
|
||||
case CAN_RAW_RECV_OWN_MSGS:
|
||||
if (optlen != sizeof(flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&flag, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
ro->recv_own_msgs = !!flag;
|
||||
ro->recv_own_msgs = !!*(const int *)val;
|
||||
break;
|
||||
|
||||
case CAN_RAW_FD_FRAMES:
|
||||
if (optlen != sizeof(flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&flag, optval, optlen))
|
||||
return -EFAULT;
|
||||
flag = *(const int *)val;
|
||||
|
||||
/* Enabling CAN XL includes CAN FD */
|
||||
if (ro->xl_frames && !flag)
|
||||
|
|
@ -705,13 +700,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
|
|||
break;
|
||||
|
||||
case CAN_RAW_XL_FRAMES:
|
||||
if (optlen != sizeof(flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&flag, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
ro->xl_frames = !!flag;
|
||||
ro->xl_frames = !!*(const int *)val;
|
||||
|
||||
/* Enabling CAN XL includes CAN FD */
|
||||
if (ro->xl_frames)
|
||||
|
|
@ -719,11 +708,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
|
|||
break;
|
||||
|
||||
case CAN_RAW_XL_VCID_OPTS:
|
||||
if (optlen != sizeof(ro->raw_vcid_opts))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&ro->raw_vcid_opts, optval, optlen))
|
||||
return -EFAULT;
|
||||
memcpy(&ro->raw_vcid_opts, val, sizeof(ro->raw_vcid_opts));
|
||||
|
||||
/* prepare 32 bit values for handling in hot path */
|
||||
ro->tx_vcid_shifted = ro->raw_vcid_opts.tx_vcid << CANXL_VCID_OFFSET;
|
||||
|
|
@ -732,13 +717,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
|
|||
break;
|
||||
|
||||
case CAN_RAW_JOIN_FILTERS:
|
||||
if (optlen != sizeof(flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&flag, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
ro->join_filters = !!flag;
|
||||
ro->join_filters = !!*(const int *)val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -751,18 +730,83 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
|
|||
sockptr_t optval, unsigned int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
|
||||
struct can_filter sfilter; /* single filter */
|
||||
union {
|
||||
can_err_mask_t err_mask;
|
||||
int flag;
|
||||
struct can_raw_vcid_options vcid_opts;
|
||||
} val;
|
||||
unsigned int size = 0;
|
||||
int count = 0;
|
||||
int err;
|
||||
|
||||
if (level != SOL_CAN_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
rtnl_lock();
|
||||
/* Copy the option values from user space before lock_sock(), so a
|
||||
* stalled copy can not block raw_notify() waiting for lock_sock()
|
||||
* under RTNL.
|
||||
*/
|
||||
switch (optname) {
|
||||
case CAN_RAW_FILTER:
|
||||
if (optlen % sizeof(struct can_filter) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
|
||||
return -EINVAL;
|
||||
|
||||
count = optlen / sizeof(struct can_filter);
|
||||
|
||||
if (count > 1) {
|
||||
/* filter does not fit into dfilter => alloc space */
|
||||
filter = memdup_sockptr(optval, optlen);
|
||||
if (IS_ERR(filter))
|
||||
return PTR_ERR(filter);
|
||||
} else if (count == 1) {
|
||||
if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter)))
|
||||
return -EFAULT;
|
||||
filter = &sfilter;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAN_RAW_ERR_FILTER:
|
||||
size = sizeof(val.err_mask);
|
||||
break;
|
||||
|
||||
case CAN_RAW_XL_VCID_OPTS:
|
||||
size = sizeof(val.vcid_opts);
|
||||
break;
|
||||
|
||||
case CAN_RAW_LOOPBACK:
|
||||
case CAN_RAW_RECV_OWN_MSGS:
|
||||
case CAN_RAW_FD_FRAMES:
|
||||
case CAN_RAW_XL_FRAMES:
|
||||
case CAN_RAW_JOIN_FILTERS:
|
||||
size = sizeof(val.flag);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
if (optlen != size)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_sockptr(&val, optval, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
err = raw_setsockopt_locked(sock, optname, optval, optlen);
|
||||
err = raw_setsockopt_locked(sock, optname, &val, filter, count);
|
||||
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
|
||||
/* on error free the filters allocated for CAN_RAW_FILTER above */
|
||||
if (err && count > 1)
|
||||
kfree(filter);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -784,6 +828,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
|
|||
|
||||
switch (optname) {
|
||||
case CAN_RAW_FILTER: {
|
||||
struct can_filter *filter = NULL;
|
||||
int err = 0;
|
||||
|
||||
lock_sock(sk);
|
||||
|
|
@ -798,15 +843,22 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
|
|||
} else {
|
||||
if (len > fsize)
|
||||
len = fsize;
|
||||
if (copy_to_iter(ro->filter, len,
|
||||
&opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
/* copy to user space after release_sock() */
|
||||
filter = kmemdup(ro->filter, len, GFP_KERNEL);
|
||||
if (!filter)
|
||||
err = -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
release_sock(sk);
|
||||
|
||||
if (filter) {
|
||||
if (copy_to_iter(filter, len, &opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
kfree(filter);
|
||||
}
|
||||
|
||||
if (!err)
|
||||
opt->optlen = len;
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -4320,7 +4320,9 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb)
|
|||
if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
|
||||
const struct tcphdr *th;
|
||||
|
||||
if (!pskb_may_pull(skb, hdr_len + sizeof(struct tcphdr)))
|
||||
/* A header in front of skb->data gives a negative offset. */
|
||||
if ((int)hdr_len < 0 ||
|
||||
!pskb_may_pull(skb, hdr_len + sizeof(struct tcphdr)))
|
||||
return SKB_DROP_REASON_SKB_BAD_GSO;
|
||||
|
||||
th = (const struct tcphdr *)(skb->data + hdr_len);
|
||||
|
|
@ -7073,16 +7075,22 @@ static void __skb_defer_free_flush(struct skb_defer_node *sdn, int budget)
|
|||
{
|
||||
struct llist_node *free_list;
|
||||
struct sk_buff *skb, *next;
|
||||
long n = 0;
|
||||
|
||||
if (llist_empty(&sdn->defer_list))
|
||||
return;
|
||||
atomic_long_set(&sdn->defer_count, 0);
|
||||
free_list = llist_del_all(&sdn->defer_list);
|
||||
|
||||
llist_for_each_entry_safe(skb, next, free_list, ll_node) {
|
||||
prefetch(next);
|
||||
napi_consume_skb(skb, budget);
|
||||
n++;
|
||||
}
|
||||
/* Subtract exactly what was drained. Never store an absolute value:
|
||||
* mixing one with the relative updates in skb_attempt_defer_free()
|
||||
* lets the counter go negative, and it is read as unsigned there.
|
||||
*/
|
||||
atomic_long_sub(n, &sdn->defer_count);
|
||||
}
|
||||
|
||||
void skb_defer_node_flush(struct skb_defer_node *sdn)
|
||||
|
|
@ -7389,6 +7397,13 @@ static void napi_stop_kthread(struct napi_struct *napi)
|
|||
{
|
||||
unsigned long val, new;
|
||||
|
||||
/* Instance is disabled, we can immediately stop the kthread */
|
||||
if (test_bit(NAPI_STATE_DISABLED, &napi->state)) {
|
||||
clear_bit(NAPI_STATE_THREADED, &napi->state);
|
||||
clear_bit(NAPI_STATE_THREADED_BUSY_POLL, &napi->state);
|
||||
goto stop;
|
||||
}
|
||||
|
||||
/* Wait until the napi STATE_THREADED is unset. */
|
||||
while (true) {
|
||||
val = READ_ONCE(napi->state);
|
||||
|
|
@ -7419,6 +7434,7 @@ static void napi_stop_kthread(struct napi_struct *napi)
|
|||
msleep(20);
|
||||
}
|
||||
|
||||
stop:
|
||||
kthread_stop(napi->thread);
|
||||
napi->thread = NULL;
|
||||
}
|
||||
|
|
@ -7814,6 +7830,7 @@ void netif_napi_add_weight_locked(struct net_device *dev,
|
|||
napi->list_owner = -1;
|
||||
set_bit(NAPI_STATE_SCHED, &napi->state);
|
||||
set_bit(NAPI_STATE_NPSVC, &napi->state);
|
||||
set_bit(NAPI_STATE_DISABLED, &napi->state);
|
||||
netif_napi_dev_list_add(dev, napi);
|
||||
|
||||
/* default settings from sysfs are applied to all NAPIs. any per-NAPI
|
||||
|
|
@ -7850,7 +7867,8 @@ void napi_disable_locked(struct napi_struct *n)
|
|||
val = READ_ONCE(n->state);
|
||||
}
|
||||
|
||||
new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC;
|
||||
new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC |
|
||||
NAPIF_STATE_DISABLED;
|
||||
new &= ~(NAPIF_STATE_THREADED |
|
||||
NAPIF_STATE_THREADED_BUSY_POLL |
|
||||
NAPIF_STATE_PREFER_BUSY_POLL);
|
||||
|
|
@ -7895,7 +7913,8 @@ void napi_enable_locked(struct napi_struct *n)
|
|||
do {
|
||||
BUG_ON(!test_bit(NAPI_STATE_SCHED, &val));
|
||||
|
||||
new = val & ~(NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC);
|
||||
new = val & ~(NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC |
|
||||
NAPIF_STATE_DISABLED);
|
||||
if (n->dev->threaded && n->thread)
|
||||
new |= NAPIF_STATE_THREADED;
|
||||
} while (!try_cmpxchg(&n->state, &val, new));
|
||||
|
|
|
|||
|
|
@ -8114,7 +8114,7 @@ BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
|
|||
if (sk_unhashed(sk))
|
||||
return -EOPNOTSUPP;
|
||||
if (sk_is_refcounted(sk) &&
|
||||
unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
|
||||
unlikely(!refcount_inc_not_zero_acquire(&sk->sk_refcnt)))
|
||||
return -ENOENT;
|
||||
|
||||
skb_orphan(skb);
|
||||
|
|
|
|||
|
|
@ -6858,7 +6858,7 @@ int skb_mpls_dec_ttl(struct sk_buff *skb)
|
|||
|
||||
lse = be32_to_cpu(mpls_hdr(skb)->label_stack_entry);
|
||||
ttl = (lse & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT;
|
||||
if (!--ttl)
|
||||
if (!ttl || !--ttl)
|
||||
return -EINVAL;
|
||||
|
||||
lse &= ~MPLS_LS_TTL_MASK;
|
||||
|
|
@ -7499,8 +7499,14 @@ nodefer: kfree_skb_napi_cache(skb);
|
|||
defer_max = READ_ONCE(net_hotdata.sysctl_skb_defer_max);
|
||||
defer_count = atomic_long_inc_return(&sdn->defer_count);
|
||||
|
||||
if (defer_count >= defer_max)
|
||||
if (defer_count > defer_max) {
|
||||
/* Undo the reservation. An llist_add() does not release one --
|
||||
* the skb stays charged until a flush subtracts the batch that
|
||||
* contains it -- so a rejected free must undo its own.
|
||||
*/
|
||||
atomic_long_dec(&sdn->defer_count);
|
||||
goto nodefer;
|
||||
}
|
||||
|
||||
llist_add(&skb->ll_node, &sdn->defer_list);
|
||||
|
||||
|
|
@ -7509,8 +7515,13 @@ nodefer: kfree_skb_napi_cache(skb);
|
|||
return;
|
||||
}
|
||||
|
||||
/* Send an IPI every time queue reaches half capacity. */
|
||||
kick = (defer_count - 1) == (defer_max >> 1);
|
||||
/* Half capacity, plus the skb that fills the queue. Equality alone can
|
||||
* be consumed early -- if the producer holding the kick value publishes
|
||||
* first, its purge drains only that skb and the reservations published
|
||||
* after it match nothing -- which would leave a full queue with nothing
|
||||
* pending.
|
||||
*/
|
||||
kick = (defer_count - 1) == (defer_max >> 1) || defer_count == defer_max;
|
||||
|
||||
/* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU
|
||||
* if we are unlucky enough (this seems very unlikely).
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user