mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 17:35:18 -04:00
Merge tag 'net-7.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Paolo Abeni:
"This is again larger than usual: the backlog accumulated in the past weeks
is not done yet. I'm not aware of any known pending regression.
Including fixes from netfilter, Bluetooth, WiFi and CAN.
Current release - regressions:
- bluetooth: remove unnecessary hci_conn_get in create_conn_sync
- can: isotp: fix timer drain order, wakeup handling and tx_gen
ordering
- eth:
- tun/vhost: revert avoid ptr_ring tail-drop when a qdisc is
present
Previous releases - regressions:
- core: do not send ICMP/NDISC Redirects when peer allocation fails
- ipv6: take nexthop lock for f6i_list walks in replace check and
notify
- wifi: fix an ath12k MLO regression impacting WCN7850/QCC2072.
- netfilter: nf_tables: make nft_object rhltable per table
- af_unix: fix listen() succeeding on sockets in the wrong state
- openvswitch: fix potential UAF on meter attach failure
- bluetooth:
- fix advertising data UAFs
- avoid deadlocks in iso_sock_timeout
- smc: fix socket use-after-free during link group termination
- dpll: use pin owner's dpll ref for pin-level attribute reporting
- eth:
- veth: convert frag_list skbs before running XDP
- ice: wait for reset completion in ice_resume()
- igc: remove napi_synchronize() in igc_down()
- vxlan: use pskb_network_may_pull() for transmit path header pulls
Previous releases - always broken:
- xsk: fix AF_XDP multi-buffer Tx descriptor reclaim
- psp: fix NULL genl_sock deref race with concurrent netns teardown
- netfilter: widen NAT rewrite delta to s32 in sip_help_tcp()
- can: peak_usb: fix double free of transfer buffer on URB submit error
- dibs: fix use-after-free of dmb_node in loopback attach/detach/unregister
- sctp: prevent peer transport count overflow
- dsa: mt7530: error out on failed reads in MT7531 PHY polling
- eth:
- idpf: bound interrupt-vector register fill to the allocated array"
* tag 'net-7.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (156 commits)
qede: sync udp_tunnel ports outside qede_lock in the recovery path
net: openvswitch: fix potential UAF on meter attach failure
octeontx2-pf: Set correct sequence for carrier off and tx queue stop
net: libwx: fix FDIR ATR queue mismatch for software VLAN packets
net: dsa: realtek: use devm_mutex_init for l2_lock
net: dsa: realtek: use devm_mutex_init for vlan_lock
net: dsa: realtek: use devm_mutex_init for regmap lock
net: dsa: realtek: rtl8365mb: use devm_mutex_init for mib_lock
ptp: netc: fix potential interrupt storm caused by incorrect unbind order
net: mana: Return error code from mana_create_rxq()
net: openvswitch: fix skb leak on flow key update failure during ct
net: openvswitch: fix skb leak on flow key update failure during recirculation
net: stmmac: Fix E2E delay mechanism
net: dsa: mt7530: error out on failed reads in MT7531 PHY polling
net: dsa: mt7530: error out on failed reads in ATC/VTCR command polling
net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend
Revert "tun/tap: add ptr_ring consume helper with netdev queue wakeup"
Revert "vhost-net: wake queue of tun/tap after ptr_ring consume"
Revert "ptr_ring: move free-space check into separate helper"
Revert "tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present"
...
This commit is contained in:
1
.mailmap
1
.mailmap
@@ -72,6 +72,7 @@ Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@nvidia.com>
|
||||
Alice Mikityanska <alice.kernel@fastmail.im> <maxim@isovalent.com>
|
||||
Alice Mikityanska <alice.kernel@fastmail.im> <alice@isovalent.com>
|
||||
Aloka Dixit <quic_alokad@quicinc.com> <alokad@codeaurora.org>
|
||||
Alvin Šipraga <alvin.sipraga@analog.com> <alsi@bang-olufsen.dk>
|
||||
Al Viro <viro@ftp.linux.org.uk>
|
||||
Al Viro <viro@zenIV.linux.org.uk>
|
||||
Amit Blay <quic_ablay@quicinc.com> <ablay@codeaurora.org>
|
||||
|
||||
@@ -43,12 +43,13 @@ UMEM also has two rings: the FILL ring and the COMPLETION ring. The
|
||||
FILL ring is used by the application to send down addr for the kernel
|
||||
to fill in with RX packet data. References to these frames will then
|
||||
appear in the RX ring once each packet has been received. The
|
||||
COMPLETION ring, on the other hand, contains frame addr that the
|
||||
kernel has transmitted completely and can now be used again by user
|
||||
space, for either TX or RX. Thus, the frame addrs appearing in the
|
||||
COMPLETION ring are addrs that were previously transmitted using the
|
||||
TX ring. In summary, the RX and FILL rings are used for the RX path
|
||||
and the TX and COMPLETION rings are used for the TX path.
|
||||
COMPLETION ring, on the other hand, contains frame addresses from Tx
|
||||
descriptors that the kernel has finished processing and that can now be
|
||||
used again by user space, for either Tx or Rx. This includes frames whose
|
||||
transmission has completed as well as frames referenced by invalid Tx
|
||||
descriptors rejected by the kernel. A completion therefore returns
|
||||
ownership of a frame to user space, but does not by itself guarantee that
|
||||
the packet was successfully transmitted.
|
||||
|
||||
The socket is then finally bound with a bind() call to a device and a
|
||||
specific queue id on that device, and it is not until bind is
|
||||
@@ -169,14 +170,15 @@ chunks mode, then the incoming addr will be left untouched.
|
||||
UMEM Completion Ring
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The COMPLETION Ring is used transfer ownership of UMEM frames from
|
||||
The COMPLETION Ring is used to transfer ownership of UMEM frames from
|
||||
kernel-space to user-space. Just like the FILL ring, UMEM indices are
|
||||
used.
|
||||
|
||||
Frames passed from the kernel to user-space are frames that has been
|
||||
sent (TX ring) and can be used by user-space again.
|
||||
|
||||
The user application consumes UMEM addrs from this ring.
|
||||
used. Frames passed from the kernel to user-space are frames referenced
|
||||
by Tx descriptors that the kernel has finished processing and can be
|
||||
used by user-space again. This includes both frames whose transmission
|
||||
has completed and frames referenced by invalid Tx descriptors that were
|
||||
rejected and reclaimed by the kernel. A completion entry does not
|
||||
guarantee successful packet transmission. The user application consumes
|
||||
UMEM addrs from this ring.
|
||||
|
||||
|
||||
RX Ring
|
||||
@@ -504,21 +506,25 @@ will be treated as an invalid descriptor.
|
||||
These are the semantics for producing packets onto AF_XDP Tx ring
|
||||
consisting of multiple frames:
|
||||
|
||||
* When an invalid descriptor is found, all the other
|
||||
descriptors/frames of this packet are marked as invalid and not
|
||||
completed. The next descriptor is treated as the start of a new
|
||||
packet, even if this was not the intent (because we cannot guess
|
||||
the intent). As before, if your program is producing invalid
|
||||
descriptors you have a bug that must be fixed.
|
||||
* When an invalid descriptor is found, the complete packet is treated as
|
||||
invalid. The kernel consumes descriptors through the descriptor marking
|
||||
the end of the packet and returns all their frame addresses through the
|
||||
COMPLETION ring. A standalone invalid descriptor is treated as a
|
||||
one-descriptor invalid packet. The descriptor following the end of the
|
||||
invalid packet is treated as the start of a new packet. As before, if
|
||||
your program is producing invalid descriptors you have a bug that must
|
||||
be fixed. Rejected descriptors are reported in the ``tx_invalid_descs``
|
||||
statistic.
|
||||
|
||||
* Zero length descriptors are treated as invalid descriptors.
|
||||
|
||||
* For copy mode, the maximum supported number of frames in a packet is
|
||||
equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all
|
||||
descriptors accumulated so far are dropped and treated as
|
||||
invalid. To produce an application that will work on any system
|
||||
regardless of this config setting, limit the number of frags to 18,
|
||||
as the minimum value of the config is 17.
|
||||
equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all descriptors
|
||||
through the end of the oversized packet are consumed, treated as invalid,
|
||||
and their frame addresses are returned through the COMPLETION ring. To
|
||||
produce an application that will work on any system regardless of this
|
||||
config setting, limit the number of frags to 18, as the minimum value of
|
||||
the config is 17.
|
||||
|
||||
* For zero-copy mode, the limit is up to what the NIC HW
|
||||
supports. Usually at least five on the NICs we have checked. We
|
||||
|
||||
@@ -22736,7 +22736,8 @@ F: drivers/watchdog/realtek_otto_wdt.c
|
||||
|
||||
REALTEK RTL83xx SMI DSA ROUTER CHIPS
|
||||
M: Linus Walleij <linusw@kernel.org>
|
||||
M: Alvin Šipraga <alsi@bang-olufsen.dk>
|
||||
M: Luiz Angelo Daros de Luca <luizluca@gmail.com>
|
||||
R: Alvin Šipraga <alvin.sipraga@analog.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/dsa/realtek.yaml
|
||||
F: drivers/net/dsa/realtek/*
|
||||
@@ -24633,8 +24634,7 @@ SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
|
||||
M: D. Wythe <alibuda@linux.alibaba.com>
|
||||
M: Dust Li <dust.li@linux.alibaba.com>
|
||||
M: Sidraya Jayagond <sidraya@linux.ibm.com>
|
||||
M: Wenjia Zhang <wenjia@linux.ibm.com>
|
||||
R: Mahanta Jambigi <mjambigi@linux.ibm.com>
|
||||
M: Mahanta Jambigi <mjambigi@linux.ibm.com>
|
||||
R: Tony Lu <tonylu@linux.alibaba.com>
|
||||
R: Wen Gu <guwen@linux.alibaba.com>
|
||||
L: linux-rdma@vger.kernel.org
|
||||
|
||||
@@ -3771,6 +3771,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct intel_tlv *tlv = (void *)&skb->data[5];
|
||||
|
||||
if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0]))
|
||||
goto recv_frame;
|
||||
|
||||
/* The first event is always an event type TLV */
|
||||
if (tlv->type != INTEL_TLV_TYPE_ID)
|
||||
goto recv_frame;
|
||||
|
||||
@@ -804,56 +804,44 @@ static int btmtk_usb_uhw_reg_write(struct hci_dev *hdev, u32 reg, u32 val)
|
||||
static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
|
||||
{
|
||||
struct btmtk_data *data = hci_get_priv(hdev);
|
||||
int pipe, err;
|
||||
void *buf;
|
||||
u8 buf[sizeof(u32)];
|
||||
int err;
|
||||
|
||||
buf = kzalloc(4, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvctrlpipe(data->udev, 0);
|
||||
err = usb_control_msg(data->udev, pipe, 0x01,
|
||||
0xDE,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, 4, USB_CTRL_GET_TIMEOUT);
|
||||
if (err < 0) {
|
||||
*val = 0;
|
||||
err = usb_control_msg_recv(data->udev, 0, 0x01,
|
||||
0xDE,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, sizeof(buf), USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
|
||||
goto err_free_buf;
|
||||
return err;
|
||||
}
|
||||
|
||||
*val = get_unaligned_le32(buf);
|
||||
bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
|
||||
|
||||
err_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
|
||||
{
|
||||
struct btmtk_data *data = hci_get_priv(hdev);
|
||||
int pipe, err, size = sizeof(u32);
|
||||
void *buf;
|
||||
u8 buf[sizeof(u32)];
|
||||
int err;
|
||||
|
||||
buf = kzalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvctrlpipe(data->udev, 0);
|
||||
err = usb_control_msg(data->udev, pipe, 0x63,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, size, USB_CTRL_GET_TIMEOUT);
|
||||
*val = 0;
|
||||
err = usb_control_msg_recv(data->udev, 0, 0x63,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, sizeof(buf), USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (err < 0)
|
||||
goto err_free_buf;
|
||||
return err;
|
||||
|
||||
*val = get_unaligned_le32(buf);
|
||||
|
||||
err_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtk_usb_id_get(struct hci_dev *hdev, u32 reg, u32 *id)
|
||||
@@ -877,7 +865,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
|
||||
if (dev_id == 0x7922) {
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= 0x00002020;
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
|
||||
@@ -887,7 +875,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= BIT(0);
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
|
||||
@@ -896,14 +884,14 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
msleep(100);
|
||||
} else if (dev_id == 0x7925 || dev_id == 0x6639) {
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= (1 << 5);
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val &= 0xFFFF00FF;
|
||||
val |= (1 << 13);
|
||||
@@ -914,7 +902,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
val |= (1 << 0);
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
|
||||
@@ -924,13 +912,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
msleep(100);
|
||||
} else {
|
||||
@@ -940,7 +928,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
/* Reset the bluetooth chip via USB interface. */
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1);
|
||||
@@ -950,13 +938,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
/* MT7921 need to delay 20ms between toggle reset bit */
|
||||
msleep(20);
|
||||
@@ -964,7 +952,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -980,7 +968,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
|
||||
}
|
||||
|
||||
err = btmtk_usb_id_get(hdev, 0x70010200, &val);
|
||||
if (err < 0 || (!val && dev_id != 0x6639))
|
||||
if (err || (!val && dev_id != 0x6639))
|
||||
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
|
||||
|
||||
return err;
|
||||
@@ -1324,24 +1312,24 @@ int btmtk_usb_setup(struct hci_dev *hdev)
|
||||
calltime = ktime_get();
|
||||
|
||||
err = btmtk_usb_id_get(hdev, 0x80000008, &dev_id);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!dev_id || dev_id != 0x7663) {
|
||||
err = btmtk_usb_id_get(hdev, 0x70010200, &dev_id);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
err = btmtk_usb_id_get(hdev, 0x80021004, &fw_version);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get fw version (%d)", err);
|
||||
return err;
|
||||
}
|
||||
err = btmtk_usb_id_get(hdev, 0x70010020, &fw_flavor);
|
||||
if (err < 0) {
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Failed to get fw flavor (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -3424,28 +3424,16 @@ static const char *qca_get_fw_subdirectory(const struct qca_version *ver)
|
||||
static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request,
|
||||
void *data, u16 size)
|
||||
{
|
||||
int pipe, err;
|
||||
u8 *buf;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
int err;
|
||||
|
||||
/* Found some of USB hosts have IOT issues with ours so that we should
|
||||
* not wait until HCI layer is ready.
|
||||
*/
|
||||
pipe = usb_rcvctrlpipe(udev, 0);
|
||||
err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
0, 0, buf, size, USB_CTRL_GET_TIMEOUT);
|
||||
if (err < 0) {
|
||||
err = usb_control_msg_recv(udev, 0, request, USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
0, 0, data, size, USB_CTRL_GET_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (err)
|
||||
dev_err(&udev->dev, "Failed to access otp area (%d)", err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(data, buf, size);
|
||||
|
||||
done:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -3652,7 +3640,7 @@ static bool btusb_qca_need_patch(struct usb_device *udev)
|
||||
struct qca_version ver;
|
||||
|
||||
if (btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver)) < 0)
|
||||
sizeof(ver)))
|
||||
return false;
|
||||
/* only low ROM versions need patches */
|
||||
return !(le32_to_cpu(ver.rom_version) & ~0xffffU);
|
||||
@@ -3670,7 +3658,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver));
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ver_rom = le32_to_cpu(ver.rom_version);
|
||||
@@ -3693,7 +3681,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_CHECK_STATUS, &status,
|
||||
sizeof(status));
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(status & QCA_PATCH_UPDATED)) {
|
||||
@@ -3704,7 +3692,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
||||
|
||||
err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver,
|
||||
sizeof(ver));
|
||||
if (err < 0)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version);
|
||||
|
||||
@@ -118,14 +118,9 @@ static int dibs_lo_register_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __dibs_lo_unregister_dmb(struct dibs_lo_dev *ldev,
|
||||
struct dibs_lo_dmb_node *dmb_node)
|
||||
static void dibs_lo_free_dmb(struct dibs_lo_dev *ldev,
|
||||
struct dibs_lo_dmb_node *dmb_node)
|
||||
{
|
||||
/* remove dmb from hash table */
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_del(&dmb_node->list);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask);
|
||||
folio_put(virt_to_folio(dmb_node->cpu_addr));
|
||||
kfree(dmb_node);
|
||||
@@ -139,27 +134,33 @@ static int dibs_lo_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
|
||||
struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
unsigned long flags;
|
||||
bool last;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
|
||||
/* find dmb from hash table */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
|
||||
if (tmp_node->token == dmb->dmb_tok) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
if (!dmb_node)
|
||||
if (!dmb_node) {
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
last = refcount_dec_and_test(&dmb_node->refcnt);
|
||||
if (last)
|
||||
hash_del(&dmb_node->list);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (refcount_dec_and_test(&dmb_node->refcnt)) {
|
||||
if (last) {
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
|
||||
__dibs_lo_unregister_dmb(ldev, dmb_node);
|
||||
dibs_lo_free_dmb(ldev, dmb_node);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -188,14 +189,9 @@ static int dibs_lo_attach_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
refcount_inc(&dmb_node->refcnt);
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (!refcount_inc_not_zero(&dmb_node->refcnt))
|
||||
/* the dmb is being unregistered, but has
|
||||
* not been removed from the hash table.
|
||||
*/
|
||||
return -EINVAL;
|
||||
|
||||
/* provide dmb information */
|
||||
dmb->idx = dmb_node->sba_idx;
|
||||
dmb->dmb_tok = dmb_node->token;
|
||||
@@ -209,11 +205,12 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token)
|
||||
{
|
||||
struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
bool last;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
|
||||
/* find dmb_node according to dmb->dmb_tok */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) {
|
||||
if (tmp_node->token == token) {
|
||||
dmb_node = tmp_node;
|
||||
@@ -221,13 +218,17 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token)
|
||||
}
|
||||
}
|
||||
if (!dmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
last = refcount_dec_and_test(&dmb_node->refcnt);
|
||||
if (last)
|
||||
hash_del(&dmb_node->list);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (last)
|
||||
dibs_lo_free_dmb(ldev, dmb_node);
|
||||
|
||||
if (refcount_dec_and_test(&dmb_node->refcnt))
|
||||
__dibs_lo_unregister_dmb(ldev, dmb_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1142,6 +1142,33 @@ void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
|
||||
return reg->priv;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpll_pin_own_dpll_ref_first - find the first owner dpll ref of a pin
|
||||
* @pin: pointer to a dpll pin
|
||||
*
|
||||
* Search pin's dpll_refs for a ref whose dpll matches the pin's
|
||||
* (module, clock_id) tuple, i.e. the dpll registered by the driver
|
||||
* that created the pin. This ensures pin-level attributes are
|
||||
* reported and modified using the owner's ops even when the pin is
|
||||
* also registered with dplls from other drivers.
|
||||
*
|
||||
* Return: pointer to the owner's dpll_pin_ref, or NULL if no
|
||||
* owner ref is found.
|
||||
*/
|
||||
struct dpll_pin_ref *dpll_pin_own_dpll_ref_first(struct dpll_pin *pin)
|
||||
{
|
||||
struct dpll_pin_ref *ref;
|
||||
unsigned long i;
|
||||
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
if (ref->dpll->module == pin->module &&
|
||||
ref->dpll->clock_id == pin->clock_id)
|
||||
return ref;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
|
||||
{
|
||||
struct dpll_pin_registration *reg;
|
||||
|
||||
@@ -93,6 +93,7 @@ void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
|
||||
|
||||
const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
|
||||
struct dpll_device *dpll_device_get_by_id(int id);
|
||||
struct dpll_pin_ref *dpll_pin_own_dpll_ref_first(struct dpll_pin *pin);
|
||||
const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
|
||||
struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
|
||||
extern struct xarray dpll_device_xa;
|
||||
|
||||
@@ -699,7 +699,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref;
|
||||
int ret;
|
||||
|
||||
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
|
||||
ref = dpll_pin_own_dpll_ref_first(pin);
|
||||
if (!ref)
|
||||
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
|
||||
ASSERT_NOT_NULL(ref);
|
||||
|
||||
ret = dpll_msg_add_pin_handle(msg, pin);
|
||||
@@ -1090,12 +1092,19 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->frequency_set || !ops->frequency_get) {
|
||||
NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
|
||||
if ((!ops->frequency_set || !ops->frequency_get) &&
|
||||
ref->dpll->module == pin->module &&
|
||||
ref->dpll->clock_id == pin->clock_id) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"frequency set not supported by the device");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
|
||||
ref = dpll_pin_own_dpll_ref_first(pin);
|
||||
if (!ref) {
|
||||
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
ops = dpll_pin_ops(ref);
|
||||
dpll = ref->dpll;
|
||||
ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
|
||||
@@ -1109,6 +1118,8 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->frequency_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
dpll, dpll_priv(dpll), freq, extack);
|
||||
@@ -1128,6 +1139,8 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
if (ref == failed)
|
||||
break;
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->frequency_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
dpll, dpll_priv(dpll), old_freq, extack))
|
||||
@@ -1151,13 +1164,19 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->esync_set || !ops->esync_get) {
|
||||
if ((!ops->esync_set || !ops->esync_get) &&
|
||||
ref->dpll->module == pin->module &&
|
||||
ref->dpll->clock_id == pin->clock_id) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"embedded sync feature is not supported by this device");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
|
||||
ref = dpll_pin_own_dpll_ref_first(pin);
|
||||
if (!ref) {
|
||||
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
ops = dpll_pin_ops(ref);
|
||||
dpll = ref->dpll;
|
||||
ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
|
||||
@@ -1181,6 +1200,8 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
void *pin_dpll_priv;
|
||||
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->esync_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
|
||||
ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
|
||||
@@ -1204,6 +1225,8 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
if (ref == failed)
|
||||
break;
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->esync_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
|
||||
if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
|
||||
@@ -1238,8 +1261,11 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
|
||||
NL_SET_ERR_MSG(extack, "reference sync pin not available");
|
||||
return -EINVAL;
|
||||
}
|
||||
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
|
||||
ASSERT_NOT_NULL(ref);
|
||||
ref = dpll_pin_own_dpll_ref_first(pin);
|
||||
if (!ref) {
|
||||
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->ref_sync_set || !ops->ref_sync_get) {
|
||||
NL_SET_ERR_MSG(extack, "reference sync not supported by this pin");
|
||||
@@ -1258,6 +1284,8 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
|
||||
return 0;
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->ref_sync_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
ref_sync_pin,
|
||||
@@ -1280,6 +1308,8 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
|
||||
if (ref == failed)
|
||||
break;
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->ref_sync_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
if (ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
ref_sync_pin,
|
||||
@@ -1471,12 +1501,18 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
|
||||
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
|
||||
if ((!ops->phase_adjust_set || !ops->phase_adjust_get) &&
|
||||
ref->dpll->module == pin->module &&
|
||||
ref->dpll->clock_id == pin->clock_id) {
|
||||
NL_SET_ERR_MSG(extack, "phase adjust not supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
|
||||
ref = dpll_pin_own_dpll_ref_first(pin);
|
||||
if (!ref) {
|
||||
NL_SET_ERR_MSG(extack, "pin owner dpll not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
ops = dpll_pin_ops(ref);
|
||||
dpll = ref->dpll;
|
||||
ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
@@ -1491,6 +1527,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
|
||||
|
||||
xa_for_each(&pin->dpll_refs, i, ref) {
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->phase_adjust_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
ret = ops->phase_adjust_set(pin,
|
||||
dpll_pin_on_dpll_priv(dpll, pin),
|
||||
@@ -1513,6 +1551,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
|
||||
if (ref == failed)
|
||||
break;
|
||||
ops = dpll_pin_ops(ref);
|
||||
if (!ops->phase_adjust_set)
|
||||
continue;
|
||||
dpll = ref->dpll;
|
||||
if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
dpll, dpll_priv(dpll), old_phase_adj,
|
||||
|
||||
@@ -597,20 +597,20 @@ static int c_can_chip_config(struct net_device *dev)
|
||||
return err;
|
||||
|
||||
/* enable automatic retransmission */
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR);
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR | CONTROL_INIT);
|
||||
|
||||
if ((priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) &&
|
||||
(priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)) {
|
||||
/* loopback + silent mode : useful for hot self-test */
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT);
|
||||
priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT);
|
||||
} else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
|
||||
/* loopback mode : useful for self-test function */
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT);
|
||||
priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK);
|
||||
} else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
|
||||
/* silent mode : bus-monitoring mode */
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
|
||||
priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT);
|
||||
priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT);
|
||||
}
|
||||
|
||||
|
||||
@@ -340,8 +340,8 @@ static void ctucan_set_mode(struct ctucan_priv *priv, const struct can_ctrlmode
|
||||
(mode_reg & ~REG_MODE_FDE);
|
||||
|
||||
mode_reg = (mode->flags & CAN_CTRLMODE_PRESUME_ACK) ?
|
||||
(mode_reg | REG_MODE_ACF) :
|
||||
(mode_reg & ~REG_MODE_ACF);
|
||||
(mode_reg | REG_MODE_STM) :
|
||||
(mode_reg & ~REG_MODE_STM);
|
||||
|
||||
mode_reg = (mode->flags & CAN_CTRLMODE_FD_NON_ISO) ?
|
||||
(mode_reg | REG_MODE_NISOFD) :
|
||||
@@ -869,7 +869,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, u32 isr)
|
||||
break;
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CNT;
|
||||
cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
|
||||
cf->data[1] = CAN_ERR_CRTL_ACTIVE;
|
||||
cf->data[6] = bec.txerr;
|
||||
cf->data[7] = bec.rxerr;
|
||||
@@ -1136,8 +1136,12 @@ static irqreturn_t ctucan_interrupt(int irq, void *dev_id)
|
||||
/* Error interrupts */
|
||||
if (FIELD_GET(REG_INT_STAT_EWLI, isr) ||
|
||||
FIELD_GET(REG_INT_STAT_FCSI, isr) ||
|
||||
FIELD_GET(REG_INT_STAT_ALI, isr)) {
|
||||
icr = isr & (REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI);
|
||||
FIELD_GET(REG_INT_STAT_ALI, isr) ||
|
||||
FIELD_GET(REG_INT_STAT_BEI, isr)) {
|
||||
icr = isr & (REG_INT_STAT_EWLI |
|
||||
REG_INT_STAT_FCSI |
|
||||
REG_INT_STAT_ALI |
|
||||
REG_INT_STAT_BEI);
|
||||
|
||||
ctucan_netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x\n", icr);
|
||||
ctucan_write32(priv, CTUCANFD_INT_STAT, icr);
|
||||
|
||||
@@ -194,7 +194,7 @@ static int ctucan_pci_probe(struct pci_dev *pdev,
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
kfree(bdata);
|
||||
err_pci_iounmap_bar0:
|
||||
pci_iounmap(pdev, cra_addr);
|
||||
pci_iounmap(pdev, bar0_base);
|
||||
err_pci_iounmap_bar1:
|
||||
pci_iounmap(pdev, addr);
|
||||
err_release_regions:
|
||||
@@ -266,6 +266,7 @@ static const struct pci_device_id ctucan_pci_tbl[] = {
|
||||
CTUCAN_WITH_CTUCAN_ID)},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ctucan_pci_tbl);
|
||||
|
||||
static struct pci_driver ctucan_pci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
|
||||
@@ -2003,20 +2003,12 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv)
|
||||
u32 ch, sts;
|
||||
int err;
|
||||
|
||||
err = reset_control_reset(gpriv->rstc1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_control_reset(gpriv->rstc2);
|
||||
if (err)
|
||||
goto fail_reset1;
|
||||
|
||||
/* Enable peripheral clock for register access */
|
||||
err = clk_prepare_enable(gpriv->clkp);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable peripheral clock: %pe\n",
|
||||
ERR_PTR(err));
|
||||
goto fail_reset2;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable RAM clock */
|
||||
@@ -2027,10 +2019,18 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv)
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
err = reset_control_reset(gpriv->rstc1);
|
||||
if (err)
|
||||
goto fail_ram_clk;
|
||||
|
||||
err = reset_control_reset(gpriv->rstc2);
|
||||
if (err)
|
||||
goto fail_reset1;
|
||||
|
||||
err = rcar_canfd_reset_controller(gpriv);
|
||||
if (err) {
|
||||
dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
|
||||
goto fail_ram_clk;
|
||||
goto fail_reset2;
|
||||
}
|
||||
|
||||
/* Controller in Global reset & Channel reset mode */
|
||||
@@ -2068,14 +2068,14 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv)
|
||||
|
||||
fail_mode:
|
||||
rcar_canfd_disable_global_interrupts(gpriv);
|
||||
fail_ram_clk:
|
||||
clk_disable_unprepare(gpriv->clk_ram);
|
||||
fail_clk:
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
fail_reset2:
|
||||
reset_control_assert(gpriv->rstc2);
|
||||
fail_reset1:
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
fail_ram_clk:
|
||||
clk_disable_unprepare(gpriv->clk_ram);
|
||||
fail_clk:
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2090,10 +2090,10 @@ static void rcar_canfd_global_deinit(struct rcar_canfd_global *gpriv, bool full)
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(gpriv->clk_ram);
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
reset_control_assert(gpriv->rstc2);
|
||||
reset_control_assert(gpriv->rstc1);
|
||||
clk_disable_unprepare(gpriv->clk_ram);
|
||||
clk_disable_unprepare(gpriv->clkp);
|
||||
}
|
||||
|
||||
static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -91,12 +91,12 @@ int softing_bootloader_command(struct softing *card, int16_t cmd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr,
|
||||
uint16_t *plen, const uint8_t **pdat)
|
||||
static int fw_parse(const u8 **pmem, const u8 *limit, u16 *ptype,
|
||||
u32 *paddr, u16 *plen, const u8 **pdat)
|
||||
{
|
||||
uint16_t checksum[2];
|
||||
const uint8_t *mem;
|
||||
const uint8_t *end;
|
||||
const u8 *mem;
|
||||
const u8 *record_end;
|
||||
|
||||
/*
|
||||
* firmware records are a binary, unaligned stream composed of:
|
||||
@@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr,
|
||||
* endianness & alignment.
|
||||
*/
|
||||
mem = *pmem;
|
||||
/* A record needs an 8-byte prefix and a 2-byte checksum. */
|
||||
if (mem > limit || limit - mem < 10)
|
||||
return -EINVAL;
|
||||
|
||||
*ptype = le16_to_cpup((void *)&mem[0]);
|
||||
*paddr = le32_to_cpup((void *)&mem[2]);
|
||||
*plen = le16_to_cpup((void *)&mem[6]);
|
||||
if (*plen > limit - mem - 10)
|
||||
return -EINVAL;
|
||||
|
||||
*pdat = &mem[8];
|
||||
/* verify checksum */
|
||||
end = &mem[8 + *plen];
|
||||
checksum[0] = le16_to_cpup((void *)end);
|
||||
for (checksum[1] = 0; mem < end; ++mem)
|
||||
record_end = &mem[8 + *plen];
|
||||
checksum[0] = le16_to_cpup((void *)record_end);
|
||||
for (checksum[1] = 0; mem < record_end; ++mem)
|
||||
checksum[1] += *mem;
|
||||
if (checksum[0] != checksum[1])
|
||||
return -EINVAL;
|
||||
@@ -139,6 +146,7 @@ int softing_load_fw(const char *file, struct softing *card,
|
||||
uint16_t type, len;
|
||||
uint32_t addr;
|
||||
uint8_t *buf = NULL, *new_buf;
|
||||
s64 dpram_offset;
|
||||
int buflen = 0;
|
||||
int8_t type_end = 0;
|
||||
|
||||
@@ -153,7 +161,7 @@ int softing_load_fw(const char *file, struct softing *card,
|
||||
mem = fw->data;
|
||||
end = &mem[fw->size];
|
||||
/* look for header record */
|
||||
ret = fw_parse(&mem, &type, &addr, &len, &dat);
|
||||
ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
|
||||
if (ret < 0)
|
||||
goto failed;
|
||||
if (type != 0xffff)
|
||||
@@ -164,7 +172,7 @@ int softing_load_fw(const char *file, struct softing *card,
|
||||
}
|
||||
/* ok, we had a header */
|
||||
while (mem < end) {
|
||||
ret = fw_parse(&mem, &type, &addr, &len, &dat);
|
||||
ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
|
||||
if (ret < 0)
|
||||
goto failed;
|
||||
if (type == 3) {
|
||||
@@ -179,9 +187,13 @@ int softing_load_fw(const char *file, struct softing *card,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if ((addr + len + offset) > size)
|
||||
dpram_offset = (s64)addr + offset;
|
||||
if (dpram_offset < 0 || dpram_offset > size ||
|
||||
len > size - dpram_offset) {
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
memcpy_toio(&dpram[addr + offset], dat, len);
|
||||
}
|
||||
memcpy_toio(&dpram[dpram_offset], dat, len);
|
||||
/* be sure to flush caches from IO space */
|
||||
mb();
|
||||
if (len > buflen) {
|
||||
@@ -195,7 +207,7 @@ int softing_load_fw(const char *file, struct softing *card,
|
||||
buf = new_buf;
|
||||
}
|
||||
/* verify record data */
|
||||
memcpy_fromio(buf, &dpram[addr + offset], len);
|
||||
memcpy_fromio(buf, &dpram[dpram_offset], len);
|
||||
if (memcmp(buf, dat, len)) {
|
||||
/* is not ok */
|
||||
dev_alert(&card->pdev->dev, "DPRAM readback failed\n");
|
||||
@@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file, struct softing *card)
|
||||
mem = fw->data;
|
||||
end = &mem[fw->size];
|
||||
/* look for header record */
|
||||
ret = fw_parse(&mem, &type, &addr, &len, &dat);
|
||||
ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
|
||||
if (ret)
|
||||
goto failed;
|
||||
ret = -EINVAL;
|
||||
@@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file, struct softing *card)
|
||||
}
|
||||
/* ok, we had a header */
|
||||
while (mem < end) {
|
||||
ret = fw_parse(&mem, &type, &addr, &len, &dat);
|
||||
ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
@@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file, struct softing *card)
|
||||
/* work in 16bit (target) */
|
||||
sum &= 0xffff;
|
||||
|
||||
if (card->pdat->app.offs > card->dpram_size ||
|
||||
len > card->dpram_size - card->pdat->app.offs) {
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len);
|
||||
iowrite32(card->pdat->app.offs + card->pdat->app.addr,
|
||||
&card->dpram[DPRAM_COMMAND + 2]);
|
||||
|
||||
@@ -409,6 +409,40 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg)
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static bool ems_usb_rx_msg_len_valid(struct ems_cpc_msg *msg)
|
||||
{
|
||||
size_t len = msg->length;
|
||||
size_t can_len;
|
||||
|
||||
switch (msg->type) {
|
||||
case CPC_MSG_TYPE_CAN_STATE:
|
||||
return len >= sizeof(msg->msg.can_state);
|
||||
|
||||
case CPC_MSG_TYPE_CAN_FRAME:
|
||||
case CPC_MSG_TYPE_EXT_CAN_FRAME:
|
||||
case CPC_MSG_TYPE_RTR_FRAME:
|
||||
case CPC_MSG_TYPE_EXT_RTR_FRAME:
|
||||
if (len < CPC_CAN_MSG_MIN_SIZE)
|
||||
return false;
|
||||
|
||||
if (msg->type == CPC_MSG_TYPE_RTR_FRAME ||
|
||||
msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME)
|
||||
return true;
|
||||
|
||||
can_len = can_cc_dlc2len(msg->msg.can_msg.length & 0xf);
|
||||
return len >= CPC_CAN_MSG_MIN_SIZE + can_len;
|
||||
|
||||
case CPC_MSG_TYPE_CAN_FRAME_ERROR:
|
||||
return len >= sizeof(msg->msg.error);
|
||||
|
||||
case CPC_MSG_TYPE_OVERRUN:
|
||||
return len >= sizeof(msg->msg.overrun);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* callback for bulk IN urb
|
||||
*/
|
||||
@@ -451,6 +485,15 @@ static void ems_usb_read_bulk_callback(struct urb *urb)
|
||||
}
|
||||
|
||||
msg = (struct ems_cpc_msg *)&ibuf[start];
|
||||
if (msg->length >
|
||||
urb->actual_length - start - CPC_MSG_HEADER_LEN) {
|
||||
netdev_err(netdev, "format error\n");
|
||||
break;
|
||||
}
|
||||
if (!ems_usb_rx_msg_len_valid(msg)) {
|
||||
netdev_err(netdev, "format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (msg->type) {
|
||||
case CPC_MSG_TYPE_CAN_STATE:
|
||||
|
||||
@@ -1476,7 +1476,6 @@ static void es58x_read_bulk_callback(struct urb *urb)
|
||||
dev_err_ratelimited(dev,
|
||||
"Failed resubmitting read bulk urb: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return;
|
||||
|
||||
free_urb:
|
||||
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
||||
|
||||
@@ -674,7 +674,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
|
||||
if (hf->flags & GS_CAN_FLAG_FD) {
|
||||
skb = alloc_canfd_skb(netdev, &cfd);
|
||||
if (!skb)
|
||||
return;
|
||||
goto resubmit_urb;
|
||||
|
||||
cfd->can_id = le32_to_cpu(hf->can_id);
|
||||
cfd->len = data_length;
|
||||
@@ -687,7 +687,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
|
||||
} else {
|
||||
skb = alloc_can_skb(netdev, &cf);
|
||||
if (!skb)
|
||||
return;
|
||||
goto resubmit_urb;
|
||||
|
||||
cf->can_id = le32_to_cpu(hf->can_id);
|
||||
can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode);
|
||||
|
||||
@@ -1626,6 +1626,7 @@ static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv,
|
||||
reinit_completion(&priv->get_busparams_comp);
|
||||
|
||||
err = kvaser_usb_send_cmd(dev, cmd, cmd_len);
|
||||
kfree(cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -691,13 +691,22 @@ static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos + tmp->len > actual_len) {
|
||||
if (tmp->len < CMD_HEADER_LEN ||
|
||||
tmp->len > actual_len - pos) {
|
||||
dev_err_ratelimited(&dev->intf->dev,
|
||||
"Format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp->id == id) {
|
||||
if (tmp->len > sizeof(*cmd)) {
|
||||
dev_err_ratelimited(&dev->intf->dev,
|
||||
"Received command %u too large (%u)\n",
|
||||
tmp->id, tmp->len);
|
||||
err = -EIO;
|
||||
goto end;
|
||||
}
|
||||
|
||||
memcpy(cmd, tmp, tmp->len);
|
||||
goto end;
|
||||
}
|
||||
@@ -1737,7 +1746,7 @@ static void kvaser_usb_leaf_read_bulk_callback(struct kvaser_usb *dev,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos + cmd->len > len) {
|
||||
if (cmd->len < CMD_HEADER_LEN || cmd->len > len - pos) {
|
||||
dev_err_ratelimited(&dev->intf->dev, "Format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -470,7 +470,6 @@ static int peak_usb_start(struct peak_usb_device *dev)
|
||||
netif_device_detach(dev->netdev);
|
||||
|
||||
usb_unanchor_urb(urb);
|
||||
kfree(buf);
|
||||
usb_free_urb(urb);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -566,6 +566,13 @@ static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if,
|
||||
dev->can.ctrlmode);
|
||||
}
|
||||
|
||||
if (!(rx_msg_flags & PUCAN_MSG_RTR) &&
|
||||
le16_to_cpu(rx_msg->size) - offsetof(struct pucan_rx_msg, d) <
|
||||
cfd->len) {
|
||||
kfree_skb(skb);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
cfd->can_id = le32_to_cpu(rm->can_id);
|
||||
|
||||
if (rx_msg_flags & PUCAN_MSG_EXT_ID)
|
||||
@@ -714,6 +721,24 @@ static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if,
|
||||
peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low));
|
||||
}
|
||||
|
||||
static size_t pcan_usb_fd_rx_msg_min_size(u16 rx_msg_type)
|
||||
{
|
||||
switch (rx_msg_type) {
|
||||
case PUCAN_MSG_CAN_RX:
|
||||
return offsetof(struct pucan_rx_msg, d);
|
||||
case PCAN_UFD_MSG_CALIBRATION:
|
||||
return sizeof(struct pcan_ufd_ts_msg);
|
||||
case PUCAN_MSG_ERROR:
|
||||
return sizeof(struct pucan_error_msg);
|
||||
case PUCAN_MSG_STATUS:
|
||||
return sizeof(struct pucan_status_msg);
|
||||
case PCAN_UFD_MSG_OVERRUN:
|
||||
return sizeof(struct pcan_ufd_ovr_msg);
|
||||
default:
|
||||
return sizeof(struct pucan_msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* callback for bulk IN urb */
|
||||
static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
|
||||
{
|
||||
@@ -728,6 +753,12 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
|
||||
msg_end = urb->transfer_buffer + urb->actual_length;
|
||||
for (; msg_ptr < msg_end;) {
|
||||
u16 rx_msg_type, rx_msg_size;
|
||||
size_t rx_msg_min_size;
|
||||
|
||||
if (msg_end - msg_ptr < sizeof(*rx_msg)) {
|
||||
err = -EBADMSG;
|
||||
break;
|
||||
}
|
||||
|
||||
rx_msg = (struct pucan_msg *)msg_ptr;
|
||||
if (!rx_msg->size) {
|
||||
@@ -739,13 +770,20 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
|
||||
rx_msg_type = le16_to_cpu(rx_msg->type);
|
||||
|
||||
/* check if the record goes out of current packet */
|
||||
if (msg_ptr + rx_msg_size > msg_end) {
|
||||
if (rx_msg_size > msg_end - msg_ptr) {
|
||||
netdev_err(netdev,
|
||||
"got frag rec: should inc usb rx buf sze\n");
|
||||
err = -EBADMSG;
|
||||
break;
|
||||
}
|
||||
|
||||
rx_msg_min_size = pcan_usb_fd_rx_msg_min_size(rx_msg_type);
|
||||
if (rx_msg_size < rx_msg_min_size) {
|
||||
netdev_err(netdev, "got short rec\n");
|
||||
err = -EBADMSG;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rx_msg_type) {
|
||||
case PUCAN_MSG_CAN_RX:
|
||||
err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg);
|
||||
|
||||
@@ -534,12 +534,18 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if,
|
||||
struct pcan_usb_pro_rxmsg *rx)
|
||||
{
|
||||
const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f;
|
||||
struct peak_usb_device *dev = usb_if->dev[ctrl_idx];
|
||||
struct net_device *netdev = dev->netdev;
|
||||
struct peak_usb_device *dev;
|
||||
struct net_device *netdev;
|
||||
struct can_frame *can_frame;
|
||||
struct sk_buff *skb;
|
||||
struct skb_shared_hwtstamps *hwts;
|
||||
|
||||
if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
|
||||
return -EINVAL;
|
||||
|
||||
dev = usb_if->dev[ctrl_idx];
|
||||
netdev = dev->netdev;
|
||||
|
||||
skb = alloc_can_skb(netdev, &can_frame);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
@@ -573,14 +579,20 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if,
|
||||
{
|
||||
const u16 raw_status = le16_to_cpu(er->status);
|
||||
const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f;
|
||||
struct peak_usb_device *dev = usb_if->dev[ctrl_idx];
|
||||
struct net_device *netdev = dev->netdev;
|
||||
struct peak_usb_device *dev;
|
||||
struct net_device *netdev;
|
||||
struct can_frame *can_frame;
|
||||
enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
|
||||
u8 err_mask = 0;
|
||||
struct sk_buff *skb;
|
||||
struct skb_shared_hwtstamps *hwts;
|
||||
|
||||
if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
|
||||
return -EINVAL;
|
||||
|
||||
dev = usb_if->dev[ctrl_idx];
|
||||
netdev = dev->netdev;
|
||||
|
||||
/* nothing should be sent while in BUS_OFF state */
|
||||
if (dev->can.state == CAN_STATE_BUS_OFF)
|
||||
return 0;
|
||||
|
||||
@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
lo = bus->read(bus, priv->mdiodev->addr, r);
|
||||
hi = bus->read(bus, priv->mdiodev->addr, 0x10);
|
||||
ret = bus->read(bus, priv->mdiodev->addr, r);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
lo = ret;
|
||||
|
||||
ret = bus->read(bus, priv->mdiodev->addr, 0x10);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
hi = ret;
|
||||
|
||||
*val = (hi << 16) | (lo & 0xffff);
|
||||
|
||||
|
||||
@@ -191,12 +191,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
|
||||
mt7530_mutex_unlock(priv);
|
||||
}
|
||||
|
||||
static u32
|
||||
_mt7530_unlocked_read(struct mt7530_dummy_poll *p)
|
||||
{
|
||||
return mt7530_mii_read(p->priv, p->reg);
|
||||
}
|
||||
|
||||
static u32
|
||||
_mt7530_read(struct mt7530_dummy_poll *p)
|
||||
{
|
||||
@@ -248,15 +242,20 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
struct mt7530_dummy_poll p;
|
||||
|
||||
/* Set the command operating upon the MAC address entries */
|
||||
val = ATC_BUSY | ATC_MAT(0) | cmd;
|
||||
mt7530_write(priv, MT7530_ATC, val);
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC);
|
||||
ret = readx_poll_timeout(_mt7530_read, &p, val,
|
||||
!(val & ATC_BUSY), 20, 20000);
|
||||
mt7530_mutex_lock(priv);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7530_ATC, val,
|
||||
!(val & ATC_BUSY), 20, 20000);
|
||||
if (!ret)
|
||||
ret = regmap_read(priv->regmap, MT7530_ATC, &val);
|
||||
|
||||
mt7530_mutex_unlock(priv);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "reset timeout\n");
|
||||
return ret;
|
||||
@@ -265,7 +264,6 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp)
|
||||
/* Additional sanity for read command if the specified
|
||||
* entry is invalid
|
||||
*/
|
||||
val = mt7530_read(priv, MT7530_ATC);
|
||||
if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -549,16 +547,13 @@ static int
|
||||
mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
|
||||
int regnum)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
u32 reg, val;
|
||||
int ret;
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
|
||||
|
||||
mt7530_mutex_lock(priv);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -568,8 +563,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
|
||||
MT7531_MDIO_DEV_ADDR(devad) | regnum;
|
||||
mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -579,8 +574,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
|
||||
MT7531_MDIO_DEV_ADDR(devad);
|
||||
mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -597,16 +592,13 @@ static int
|
||||
mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
|
||||
int regnum, u16 data)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
u32 val, reg;
|
||||
int ret;
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
|
||||
|
||||
mt7530_mutex_lock(priv);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -616,8 +608,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
|
||||
MT7531_MDIO_DEV_ADDR(devad) | regnum;
|
||||
mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -627,8 +619,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
|
||||
MT7531_MDIO_DEV_ADDR(devad) | data;
|
||||
mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -643,16 +635,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
|
||||
static int
|
||||
mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
|
||||
|
||||
mt7530_mutex_lock(priv);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -663,8 +652,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
|
||||
|
||||
mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
|
||||
!(val & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -681,16 +670,13 @@ static int
|
||||
mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
|
||||
u16 data)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
|
||||
|
||||
mt7530_mutex_lock(priv);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
|
||||
!(reg & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
|
||||
!(reg & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -701,8 +687,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
|
||||
|
||||
mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
|
||||
|
||||
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
|
||||
!(reg & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
|
||||
!(reg & MT7531_PHY_ACS_ST), 20, 100000);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
goto out;
|
||||
@@ -1626,22 +1612,26 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
static int
|
||||
mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
|
||||
{
|
||||
struct mt7530_dummy_poll p;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
|
||||
mt7530_write(priv, MT7530_VTCR, val);
|
||||
|
||||
INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
|
||||
ret = readx_poll_timeout(_mt7530_read, &p, val,
|
||||
!(val & VTCR_BUSY), 20, 20000);
|
||||
mt7530_mutex_lock(priv);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MT7530_VTCR, val,
|
||||
!(val & VTCR_BUSY), 20, 20000);
|
||||
if (!ret)
|
||||
ret = regmap_read(priv->regmap, MT7530_VTCR, &val);
|
||||
|
||||
mt7530_mutex_unlock(priv);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "poll timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = mt7530_read(priv, MT7530_VTCR);
|
||||
if (val & VTCR_INVALID) {
|
||||
dev_err(priv->dev, "read VTCR invalid\n");
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1988,16 +1988,19 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port,
|
||||
spin_unlock(&p->stats_lock);
|
||||
}
|
||||
|
||||
static void rtl8365mb_stats_setup(struct realtek_priv *priv)
|
||||
static int rtl8365mb_stats_setup(struct realtek_priv *priv)
|
||||
{
|
||||
struct rtl8365mb *mb = priv->chip_data;
|
||||
struct dsa_switch *ds = &priv->ds;
|
||||
struct dsa_port *dp;
|
||||
int ret;
|
||||
|
||||
/* Per-chip global mutex to protect MIB counter access, since doing
|
||||
* so requires accessing a series of registers in a particular order.
|
||||
*/
|
||||
mutex_init(&mb->mib_lock);
|
||||
ret = devm_mutex_init(priv->dev, &mb->mib_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dsa_switch_for_each_available_port(dp, ds) {
|
||||
struct rtl8365mb_port *p = &mb->ports[dp->index];
|
||||
@@ -2010,6 +2013,8 @@ static void rtl8365mb_stats_setup(struct realtek_priv *priv)
|
||||
*/
|
||||
INIT_DELAYED_WORK(&p->mib_work, rtl8365mb_stats_poll);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtl8365mb_stats_teardown(struct realtek_priv *priv)
|
||||
@@ -2567,7 +2572,12 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
|
||||
}
|
||||
|
||||
/* Start statistics counter polling */
|
||||
rtl8365mb_stats_setup(priv);
|
||||
ret = rtl8365mb_stats_setup(priv);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to setup stats: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
goto out_teardown_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -156,9 +156,17 @@ rtl83xx_probe(struct device *dev,
|
||||
if (!priv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&priv->map_lock);
|
||||
mutex_init(&priv->vlan_lock);
|
||||
mutex_init(&priv->l2_lock);
|
||||
ret = devm_mutex_init(dev, &priv->map_lock);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = devm_mutex_init(dev, &priv->vlan_lock);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = devm_mutex_init(dev, &priv->l2_lock);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
rc.lock_arg = priv;
|
||||
priv->map = devm_regmap_init(dev, NULL, priv, &rc);
|
||||
|
||||
@@ -1222,11 +1222,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
|
||||
if (hw->flash_address)
|
||||
iounmap(hw->flash_address);
|
||||
err_mdio_ioremap:
|
||||
kfree(adapter->tx_ring);
|
||||
kfree(adapter->rx_ring);
|
||||
err_dma:
|
||||
err_sw_init:
|
||||
err_mdio_ioremap:
|
||||
iounmap(hw->ce4100_gbe_mdio_base_virt);
|
||||
iounmap(hw->hw_addr);
|
||||
err_ioremap:
|
||||
|
||||
@@ -793,7 +793,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
|
||||
ret,
|
||||
libie_aq_str(pf->hw.adminq.sq_last_status),
|
||||
pin_type_name[pin_type], pin->idx);
|
||||
else
|
||||
else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
|
||||
dev_err_ratelimited(ice_pf_to_dev(pf),
|
||||
"err:%d %s failed to update %s pin:%u\n",
|
||||
ret,
|
||||
@@ -3024,7 +3024,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf,
|
||||
*phase_offset_pins_updated = 0;
|
||||
ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas,
|
||||
ARRAY_SIZE(meas));
|
||||
if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) {
|
||||
if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN ||
|
||||
pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) {
|
||||
return 0;
|
||||
} else if (ret) {
|
||||
dev_err(ice_pf_to_dev(pf),
|
||||
@@ -3086,10 +3087,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
|
||||
d->dpll_idx, d->prev_input_idx, d->input_idx,
|
||||
d->dpll_state, d->prev_dpll_state, d->mode);
|
||||
if (ret) {
|
||||
dev_err(ice_pf_to_dev(pf),
|
||||
"update dpll=%d state failed, ret=%d %s\n",
|
||||
d->dpll_idx, ret,
|
||||
libie_aq_str(pf->hw.adminq.sq_last_status));
|
||||
/* EBUSY is expected during reset recovery, don't log error */
|
||||
if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
|
||||
dev_err(ice_pf_to_dev(pf),
|
||||
"update dpll=%d state failed, ret=%d %s\n",
|
||||
d->dpll_idx, ret,
|
||||
libie_aq_str(pf->hw.adminq.sq_last_status));
|
||||
return ret;
|
||||
}
|
||||
if (init) {
|
||||
@@ -3158,7 +3161,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work)
|
||||
d->periodic_counter % dp->phase_offset_monitor_period == 0)
|
||||
ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf);
|
||||
if (ret) {
|
||||
d->cgu_state_acq_err_num++;
|
||||
/* EBUSY is expected during reset recovery */
|
||||
if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
|
||||
d->cgu_state_acq_err_num++;
|
||||
/* stop rescheduling this worker */
|
||||
if (d->cgu_state_acq_err_num >
|
||||
ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
|
||||
|
||||
@@ -1069,18 +1069,18 @@ static int ice_lbtest_prepare_rings(struct ice_vsi *vsi)
|
||||
|
||||
status = ice_vsi_cfg_lan(vsi);
|
||||
if (status)
|
||||
goto err_setup_rx_ring;
|
||||
goto err_cfg_lan;
|
||||
|
||||
status = ice_vsi_start_all_rx_rings(vsi);
|
||||
if (status)
|
||||
goto err_start_rx_ring;
|
||||
goto err_cfg_lan;
|
||||
|
||||
return 0;
|
||||
|
||||
err_start_rx_ring:
|
||||
ice_vsi_free_rx_rings(vsi);
|
||||
err_setup_rx_ring:
|
||||
err_cfg_lan:
|
||||
ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0);
|
||||
err_setup_rx_ring:
|
||||
ice_vsi_free_rx_rings(vsi);
|
||||
err_setup_tx_ring:
|
||||
ice_vsi_free_tx_rings(vsi);
|
||||
|
||||
|
||||
@@ -5637,6 +5637,16 @@ static int ice_resume(struct device *dev)
|
||||
/* Restart the service task */
|
||||
mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
|
||||
|
||||
/* Best-effort wait for the scheduled reset to finish so that the
|
||||
* device is operational before returning. Without this, userspace
|
||||
* (e.g. NetworkManager) may try to open the net device while the
|
||||
* asynchronous reset is still in progress, hitting -EBUSY.
|
||||
*/
|
||||
ret = ice_wait_for_reset(pf, secs_to_jiffies(10));
|
||||
if (ret)
|
||||
dev_err(dev, "Wait for reset timed out (10s) during resume: %d\n",
|
||||
ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -848,6 +848,30 @@ static void ice_notify_vf_reset(struct ice_vf *vf)
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_reset_interrupts - clear all queue interrupt configuration for a VSI
|
||||
* @vsi: the VSI whose interrupt registers should be cleared
|
||||
*
|
||||
* Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues
|
||||
* in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX,
|
||||
* CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only
|
||||
* clears the CAUSE_ENA bit.
|
||||
*/
|
||||
void ice_reset_interrupts(struct ice_vsi *vsi)
|
||||
{
|
||||
struct ice_pf *pf = vsi->back;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
int i;
|
||||
|
||||
ice_for_each_alloc_rxq(vsi, i)
|
||||
wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0);
|
||||
|
||||
ice_for_each_alloc_txq(vsi, i)
|
||||
wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0);
|
||||
|
||||
ice_flush(hw);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_reset_vf - Reset a particular VF
|
||||
* @vf: pointer to the VF structure
|
||||
@@ -919,6 +943,9 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags)
|
||||
|
||||
ice_dis_vf_qs(vf);
|
||||
|
||||
/* cleanup interrupt registers */
|
||||
ice_reset_interrupts(vsi);
|
||||
|
||||
/* Call Disable LAN Tx queue AQ whether or not queues are
|
||||
* enabled. This is needed for successful completion of VFR.
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
void ice_initialize_vf_entry(struct ice_vf *vf);
|
||||
void ice_deinitialize_vf_entry(struct ice_vf *vf);
|
||||
void ice_dis_vf_qs(struct ice_vf *vf);
|
||||
void ice_reset_interrupts(struct ice_vsi *vsi);
|
||||
int ice_check_vf_init(struct ice_vf *vf);
|
||||
enum virtchnl_status_code ice_err_to_virt_err(int err);
|
||||
struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf);
|
||||
|
||||
@@ -224,6 +224,24 @@ void ice_vf_ena_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
|
||||
wr32(hw, QINT_RQCTL(pfq), reg | QINT_RQCTL_CAUSE_ENA_M);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL
|
||||
* @vsi: VSI of the VF to configure
|
||||
* @q_idx: VF queue index used to determine the queue in the PF's space
|
||||
*/
|
||||
static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
|
||||
{
|
||||
struct ice_hw *hw = &vsi->back->hw;
|
||||
u32 pfq = vsi->rxq_map[q_idx];
|
||||
u32 reg;
|
||||
|
||||
reg = rd32(hw, QINT_RQCTL(pfq));
|
||||
reg &= ~QINT_RQCTL_CAUSE_ENA_M;
|
||||
wr32(hw, QINT_RQCTL(pfq), reg);
|
||||
|
||||
ice_flush(hw);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_vc_ena_qs_msg
|
||||
* @vf: pointer to the VF info
|
||||
@@ -416,6 +434,8 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg)
|
||||
goto error_param;
|
||||
}
|
||||
|
||||
for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF)
|
||||
ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
|
||||
bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF);
|
||||
} else if (q_map) {
|
||||
for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
|
||||
@@ -436,6 +456,7 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg)
|
||||
goto error_param;
|
||||
}
|
||||
|
||||
ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
|
||||
/* Clear enabled queues flag */
|
||||
clear_bit(vf_q_id, vf->rxq_ena);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
|
||||
if (!reg_vals)
|
||||
return -ENOMEM;
|
||||
|
||||
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals);
|
||||
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
|
||||
if (num_regs < num_vecs) {
|
||||
err = -EINVAL;
|
||||
goto free_reg_vals;
|
||||
|
||||
@@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter)
|
||||
if (err) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"IRQ request for mailbox failed, error: %d\n", err);
|
||||
|
||||
kfree(name);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -3097,10 +3097,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
|
||||
|
||||
tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
|
||||
tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
|
||||
/* Set the RE bit to periodically "clean" the descriptor ring.
|
||||
* MIN_GAP is set to MIN_RING size to ensure it will be set at
|
||||
* least once each time around the ring.
|
||||
*/
|
||||
/* Set the RE bit periodically to "clean" the descriptor ring */
|
||||
if (idpf_tx_splitq_need_re(tx_q)) {
|
||||
tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE;
|
||||
tx_q->txq_grp->num_completions_pending++;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
/* Mailbox Queue */
|
||||
#define IDPF_MAX_MBXQ 1
|
||||
|
||||
#define IDPF_MIN_TXQ_DESC 64
|
||||
#define IDPF_MIN_TXQ_DESC 128
|
||||
#define IDPF_MIN_RXQ_DESC 64
|
||||
#define IDPF_MIN_TXQ_COMPLQ_DESC 256
|
||||
#define IDPF_MAX_QIDS 256
|
||||
|
||||
@@ -86,7 +86,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
|
||||
if (!reg_vals)
|
||||
return -ENOMEM;
|
||||
|
||||
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals);
|
||||
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
|
||||
if (num_regs < num_vecs) {
|
||||
err = -EINVAL;
|
||||
goto free_reg_vals;
|
||||
|
||||
@@ -1318,11 +1318,12 @@ idpf_vport_init_queue_reg_chunks(struct idpf_vport_config *vport_config,
|
||||
* idpf_get_reg_intr_vecs - Get vector queue register offset
|
||||
* @adapter: adapter structure to get the vector chunks
|
||||
* @reg_vals: Register offsets to store in
|
||||
* @num_vecs: number of entries the @reg_vals array can hold
|
||||
*
|
||||
* Return: number of registers that got populated
|
||||
*/
|
||||
int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
|
||||
struct idpf_vec_regs *reg_vals)
|
||||
struct idpf_vec_regs *reg_vals, int num_vecs)
|
||||
{
|
||||
struct virtchnl2_vector_chunks *chunks;
|
||||
struct idpf_vec_regs reg_val;
|
||||
@@ -1346,7 +1347,7 @@ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
|
||||
dynctl_reg_spacing = le32_to_cpu(chunk->dynctl_reg_spacing);
|
||||
itrn_reg_spacing = le32_to_cpu(chunk->itrn_reg_spacing);
|
||||
|
||||
for (i = 0; i < num_vec; i++) {
|
||||
for (i = 0; i < num_vec && num_regs < num_vecs; i++) {
|
||||
reg_vals[num_regs].dyn_ctl_reg = reg_val.dyn_ctl_reg;
|
||||
reg_vals[num_regs].itrn_reg = reg_val.itrn_reg;
|
||||
reg_vals[num_regs].itrn_index_spacing =
|
||||
|
||||
@@ -104,7 +104,7 @@ int idpf_vc_core_init(struct idpf_adapter *adapter);
|
||||
void idpf_vc_core_deinit(struct idpf_adapter *adapter);
|
||||
|
||||
int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
|
||||
struct idpf_vec_regs *reg_vals);
|
||||
struct idpf_vec_regs *reg_vals, int num_vecs);
|
||||
int idpf_queue_reg_init(struct idpf_vport *vport,
|
||||
struct idpf_q_vec_rsrc *rsrc,
|
||||
struct idpf_queue_id_reg_info *chunks);
|
||||
|
||||
@@ -2190,8 +2190,6 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
|
||||
buffer_info->time_stamp = 0;
|
||||
buffer_info->length = 0;
|
||||
buffer_info->mapped_as_page = false;
|
||||
if (count)
|
||||
count--;
|
||||
|
||||
/* clear timestamp and dma mappings for remaining portion of packet */
|
||||
while (count--) {
|
||||
|
||||
@@ -5352,9 +5352,8 @@ void igc_down(struct igc_adapter *adapter)
|
||||
|
||||
for (i = 0; i < adapter->num_q_vectors; i++) {
|
||||
if (adapter->q_vector[i]) {
|
||||
napi_synchronize(&adapter->q_vector[i]->napi);
|
||||
igc_set_queue_napi(adapter, i, NULL);
|
||||
napi_disable(&adapter->q_vector[i]->napi);
|
||||
igc_set_queue_napi(adapter, i, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -518,6 +518,18 @@ int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind)
|
||||
{
|
||||
struct cgx *cgx = cgxd;
|
||||
|
||||
if (!is_lmac_valid(cgx, lmac_id))
|
||||
return -ENODEV;
|
||||
|
||||
*pkind = cgx_read(cgx, lmac_id, cgx->mac_ops->rxid_map_offset);
|
||||
*pkind = *pkind & 0x3F;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 cgx_get_lmac_type(void *cgxd, int lmac_id)
|
||||
{
|
||||
struct cgx *cgx = cgxd;
|
||||
|
||||
@@ -141,6 +141,7 @@ int cgx_get_cgxid(void *cgxd);
|
||||
int cgx_get_lmac_cnt(void *cgxd);
|
||||
void *cgx_get_pdata(int cgx_id);
|
||||
int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind);
|
||||
int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind);
|
||||
int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id);
|
||||
int cgx_lmac_evh_unregister(void *cgxd, int lmac_id);
|
||||
int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat);
|
||||
|
||||
@@ -1115,6 +1115,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
|
||||
u8 *intf, u8 *ena);
|
||||
int npc_config_cntr_default_entries(struct rvu *rvu, bool enable);
|
||||
bool is_cgx_config_permitted(struct rvu *rvu, u16 pcifunc);
|
||||
bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind);
|
||||
bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc);
|
||||
bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature);
|
||||
u32 rvu_cgx_get_fifolen(struct rvu *rvu);
|
||||
void *rvu_first_cgx_pdata(struct rvu *rvu);
|
||||
|
||||
@@ -1355,3 +1355,82 @@ void rvu_mac_reset(struct rvu *rvu, u16 pcifunc)
|
||||
if (mac_ops->mac_reset(cgxd, lmac, !is_vf(pcifunc)))
|
||||
dev_err(rvu->dev, "Failed to reset MAC\n");
|
||||
}
|
||||
|
||||
/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds
|
||||
* (HiGig, EDSA, etc.) are in use on the shared LMAC. VFs must not program
|
||||
* NPC_TX_DEF_PKIND on NIX_AF_LFX_TX_PARSE_CFG in that case: the PF owns
|
||||
* parse mode and no separate NPC_TX_HIGIG_PKIND is installed on the VF LF.
|
||||
* TX-parse callers skip the write when denied; rvu_lf_reset() clears each LF
|
||||
* before alloc so the next permitted owner programs NPC_TX_DEF_PKIND.
|
||||
*/
|
||||
bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc)
|
||||
{
|
||||
int pf, err, rxpkind;
|
||||
u8 cgx_id, lmac_id;
|
||||
void *cgxd;
|
||||
|
||||
pf = rvu_get_pf(rvu->pdev, pcifunc);
|
||||
|
||||
if (!(pcifunc & RVU_PFVF_FUNC_MASK))
|
||||
return true;
|
||||
|
||||
if (!is_pf_cgxmapped(rvu, pf))
|
||||
return true;
|
||||
|
||||
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
|
||||
cgxd = rvu_cgx_pdata(cgx_id, rvu);
|
||||
err = cgx_get_pkind(cgxd, lmac_id, &rxpkind);
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
switch (rxpkind) {
|
||||
case NPC_RX_HIGIG_PKIND:
|
||||
case NPC_RX_EDSA_PKIND:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds
|
||||
* (HiGig, EDSA, etc.) are in use on the shared LMAC.
|
||||
*/
|
||||
bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind)
|
||||
{
|
||||
int pf, err, rxpkind;
|
||||
u8 cgx_id, lmac_id;
|
||||
struct cgx *cgxd;
|
||||
|
||||
pf = rvu_get_pf(rvu->pdev, pcifunc);
|
||||
|
||||
if (!is_pf_cgxmapped(rvu, pf))
|
||||
return false;
|
||||
|
||||
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
|
||||
cgxd = rvu_cgx_pdata(cgx_id, rvu);
|
||||
|
||||
mutex_lock(&cgxd->lock);
|
||||
if (!is_vf(pcifunc))
|
||||
goto set;
|
||||
|
||||
err = cgx_get_pkind(cgxd, lmac_id, &rxpkind);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
switch (rxpkind) {
|
||||
case NPC_RX_HIGIG_PKIND:
|
||||
case NPC_RX_EDSA_PKIND:
|
||||
goto err;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
set:
|
||||
cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind);
|
||||
mutex_unlock(&cgxd->lock);
|
||||
return true;
|
||||
|
||||
err:
|
||||
mutex_unlock(&cgxd->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -363,8 +363,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
|
||||
pfvf->tx_chan_cnt = 1;
|
||||
rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id;
|
||||
|
||||
cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind);
|
||||
rvu_npc_set_pkind(rvu, pkind, pfvf);
|
||||
if (rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, pkind))
|
||||
rvu_npc_set_pkind(rvu, pkind, pfvf);
|
||||
break;
|
||||
case NIX_INTF_TYPE_LBK:
|
||||
vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
|
||||
@@ -1505,13 +1505,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
|
||||
struct nix_lf_alloc_req *req,
|
||||
struct nix_lf_alloc_rsp *rsp)
|
||||
{
|
||||
int nixlf, qints, hwctx_size, intf, rc = 0;
|
||||
int nixlf, qints, hwctx_size, intf, rc = 0, pf;
|
||||
u16 bcast, mcast, promisc, ucast;
|
||||
struct rvu_hwinfo *hw = rvu->hw;
|
||||
u16 pcifunc = req->hdr.pcifunc;
|
||||
u8 cgx_id = 0, lmac_id = 0;
|
||||
bool rules_created = false;
|
||||
struct rvu_block *block;
|
||||
struct rvu_pfvf *pfvf;
|
||||
struct cgx *cgxd;
|
||||
u64 cfg, ctx_cfg;
|
||||
int blkaddr;
|
||||
|
||||
@@ -1685,8 +1687,20 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg);
|
||||
|
||||
/* Configure pkind for TX parse config */
|
||||
|
||||
pf = rvu_get_pf(rvu->pdev, pcifunc);
|
||||
cfg = NPC_TX_DEF_PKIND;
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
|
||||
|
||||
if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) {
|
||||
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
|
||||
cgxd = rvu_cgx_pdata(cgx_id, rvu);
|
||||
mutex_lock(&cgxd->lock);
|
||||
if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc))
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
|
||||
mutex_unlock(&cgxd->lock);
|
||||
} else {
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg);
|
||||
}
|
||||
|
||||
if (is_rep_dev(rvu, pcifunc)) {
|
||||
pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "cn20k/npc.h"
|
||||
#include "rvu_npc.h"
|
||||
#include "cn20k/reg.h"
|
||||
#include "lmac_common.h"
|
||||
|
||||
#define RSVD_MCAM_ENTRIES_PER_PF 3 /* Broadcast, Promisc and AllMulticast */
|
||||
#define RSVD_MCAM_ENTRIES_PER_NIXLF 1 /* Ucast for LFs */
|
||||
@@ -4200,10 +4201,11 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
|
||||
|
||||
{
|
||||
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
|
||||
int blkaddr, nixlf, rc, intf_mode;
|
||||
int pf = rvu_get_pf(rvu->pdev, pcifunc);
|
||||
int blkaddr, nixlf, rc, intf_mode;
|
||||
u8 cgx_id = 0, lmac_id = 0;
|
||||
u64 rxpkind, txpkind;
|
||||
u8 cgx_id, lmac_id;
|
||||
struct cgx *cgxd;
|
||||
|
||||
/* use default pkind to disable edsa/higig */
|
||||
rxpkind = rvu_npc_get_pkind(rvu, pf);
|
||||
@@ -4227,12 +4229,8 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
|
||||
/* rx pkind set req valid only for cgx mapped PFs */
|
||||
if (!is_cgx_config_permitted(rvu, pcifunc))
|
||||
return 0;
|
||||
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
|
||||
|
||||
rc = cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id,
|
||||
rxpkind);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, rxpkind))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dir & PKIND_TX) {
|
||||
@@ -4241,8 +4239,19 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf),
|
||||
txpkind);
|
||||
if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) {
|
||||
rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id,
|
||||
&lmac_id);
|
||||
cgxd = rvu_cgx_pdata(cgx_id, rvu);
|
||||
mutex_lock(&cgxd->lock);
|
||||
if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc))
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf),
|
||||
txpkind);
|
||||
mutex_unlock(&cgxd->lock);
|
||||
} else {
|
||||
rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf),
|
||||
txpkind);
|
||||
}
|
||||
}
|
||||
|
||||
pfvf->intf_mode = intf_mode;
|
||||
|
||||
@@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
|
||||
netif_carrier_on(netdev);
|
||||
netif_tx_start_all_queues(netdev);
|
||||
} else {
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
netif_carrier_off(netdev);
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3467,7 +3467,7 @@ static void mtk_poll_controller(struct net_device *dev)
|
||||
|
||||
mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
|
||||
mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
|
||||
mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev);
|
||||
mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth);
|
||||
mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
|
||||
mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
|
||||
}
|
||||
|
||||
@@ -984,7 +984,6 @@ struct mlx5e_priv {
|
||||
struct mlx5e_mqprio_rl *mqprio_rl;
|
||||
struct dentry *dfs_root;
|
||||
struct mlx5_devcom_comp_dev *devcom;
|
||||
struct ethtool_fec_hist_range *fec_ranges;
|
||||
};
|
||||
|
||||
static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
|
||||
|
||||
@@ -6415,14 +6415,8 @@ int mlx5e_priv_init(struct mlx5e_priv *priv,
|
||||
if (!priv->channel_stats)
|
||||
goto err_free_tx_rates;
|
||||
|
||||
priv->fec_ranges = kzalloc_objs(*priv->fec_ranges, ETHTOOL_FEC_HIST_MAX);
|
||||
if (!priv->fec_ranges)
|
||||
goto err_free_channel_stats;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_channel_stats:
|
||||
kfree(priv->channel_stats);
|
||||
err_free_tx_rates:
|
||||
kfree(priv->tx_rates);
|
||||
err_free_txq2sq_stats:
|
||||
@@ -6447,7 +6441,6 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv)
|
||||
if (!priv->mdev)
|
||||
return;
|
||||
|
||||
kfree(priv->fec_ranges);
|
||||
for (i = 0; i < priv->stats_nch; i++)
|
||||
kvfree(priv->channel_stats[i]);
|
||||
kfree(priv->channel_stats);
|
||||
|
||||
@@ -1550,7 +1550,7 @@ static bool fec_rs_validate_hist_type(int mode, int hist_type)
|
||||
|
||||
static u8
|
||||
fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
|
||||
const struct ethtool_fec_hist_range **ranges)
|
||||
struct ethtool_fec_hist_range *ranges)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = priv->mdev;
|
||||
u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0};
|
||||
@@ -1558,8 +1558,6 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
|
||||
int sz = MLX5_ST_SZ_BYTES(pphcr_reg);
|
||||
u8 hist_type, num_of_bins;
|
||||
|
||||
memset(priv->fec_ranges, 0,
|
||||
ETHTOOL_FEC_HIST_MAX * sizeof(*priv->fec_ranges));
|
||||
MLX5_SET(pphcr_reg, in, local_port, 1);
|
||||
if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPHCR, 0, 0))
|
||||
return 0;
|
||||
@@ -1575,12 +1573,11 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
|
||||
for (int i = 0; i < num_of_bins; i++) {
|
||||
void *bin_range = MLX5_ADDR_OF(pphcr_reg, out, bin_range[i]);
|
||||
|
||||
priv->fec_ranges[i].high = MLX5_GET(bin_range_layout, bin_range,
|
||||
high_val);
|
||||
priv->fec_ranges[i].low = MLX5_GET(bin_range_layout, bin_range,
|
||||
low_val);
|
||||
ranges[i].high = MLX5_GET(bin_range_layout, bin_range,
|
||||
high_val);
|
||||
ranges[i].low = MLX5_GET(bin_range_layout, bin_range,
|
||||
low_val);
|
||||
}
|
||||
*ranges = priv->fec_ranges;
|
||||
|
||||
return num_of_bins;
|
||||
}
|
||||
@@ -1622,10 +1619,12 @@ static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode,
|
||||
case MLX5E_FEC_LLRS_272_257_1:
|
||||
case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD:
|
||||
num_of_bins =
|
||||
fec_rs_histogram_fill_ranges(priv, mode, &hist->ranges);
|
||||
if (num_of_bins)
|
||||
fec_rs_histogram_fill_ranges(priv, mode, hist->ranges_buf);
|
||||
if (num_of_bins) {
|
||||
hist->ranges = hist->ranges_buf;
|
||||
return fec_rs_histogram_fill_stats(priv, num_of_bins,
|
||||
hist);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
@@ -2829,7 +2829,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
|
||||
|
||||
rxq = kvzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size);
|
||||
if (!rxq)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rxq->ndev = ndev;
|
||||
rxq->num_rx_buf = apc->rx_queue_size;
|
||||
@@ -2930,7 +2930,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
|
||||
|
||||
mana_destroy_rxq(apc, rxq, false);
|
||||
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
|
||||
@@ -2964,8 +2964,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
|
||||
|
||||
for (i = 0; i < apc->num_queues; i++) {
|
||||
rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
|
||||
if (!rxq) {
|
||||
err = -ENOMEM;
|
||||
if (IS_ERR(rxq)) {
|
||||
err = PTR_ERR(rxq);
|
||||
netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev)
|
||||
struct net_device *dev = pci_get_drvdata(pci_dev);
|
||||
struct fe_priv *np = netdev_priv(dev);
|
||||
|
||||
free_percpu(np->txrx_stats);
|
||||
|
||||
unregister_netdev(dev);
|
||||
|
||||
free_percpu(np->txrx_stats);
|
||||
|
||||
nv_restore_mac_addr(pci_dev);
|
||||
|
||||
/* restore any phy related changes */
|
||||
|
||||
@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev);
|
||||
static void qede_shutdown(struct pci_dev *pdev);
|
||||
static void qede_link_update(void *dev, struct qed_link_output *link);
|
||||
static void qede_schedule_recovery_handler(void *dev);
|
||||
static void qede_recovery_handler(struct qede_dev *edev);
|
||||
static bool qede_recovery_handler(struct qede_dev *edev);
|
||||
static void qede_schedule_hw_err_handler(void *dev,
|
||||
enum qed_hw_err_type err_type);
|
||||
static void qede_get_eth_tlv_data(void *edev, void *data);
|
||||
@@ -1043,21 +1043,6 @@ void __qede_unlock(struct qede_dev *edev)
|
||||
mutex_unlock(&edev->qede_lock);
|
||||
}
|
||||
|
||||
/* This version of the lock should be used when acquiring the RTNL lock is also
|
||||
* needed in addition to the internal qede lock.
|
||||
*/
|
||||
static void qede_lock(struct qede_dev *edev)
|
||||
{
|
||||
rtnl_lock();
|
||||
__qede_lock(edev);
|
||||
}
|
||||
|
||||
static void qede_unlock(struct qede_dev *edev)
|
||||
{
|
||||
__qede_unlock(edev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static void qede_periodic_task(struct work_struct *work)
|
||||
{
|
||||
struct qede_dev *edev = container_of(work, struct qede_dev,
|
||||
@@ -1094,6 +1079,8 @@ static void qede_sp_task(struct work_struct *work)
|
||||
*/
|
||||
|
||||
if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) {
|
||||
bool reloaded;
|
||||
|
||||
cancel_delayed_work_sync(&edev->periodic_task);
|
||||
#ifdef CONFIG_QED_SRIOV
|
||||
/* SRIOV must be disabled outside the lock to avoid a deadlock.
|
||||
@@ -1102,9 +1089,17 @@ static void qede_sp_task(struct work_struct *work)
|
||||
if (pci_num_vf(edev->pdev))
|
||||
qede_sriov_configure(edev->pdev, 0);
|
||||
#endif
|
||||
qede_lock(edev);
|
||||
qede_recovery_handler(edev);
|
||||
qede_unlock(edev);
|
||||
rtnl_lock();
|
||||
__qede_lock(edev);
|
||||
reloaded = qede_recovery_handler(edev);
|
||||
__qede_unlock(edev);
|
||||
|
||||
/* The udp_tunnel core synchronously calls back into
|
||||
* qede_udp_tunnel_sync(), which takes the qede lock.
|
||||
*/
|
||||
if (reloaded)
|
||||
udp_tunnel_nic_reset_ntf(edev->ndev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
__qede_lock(edev);
|
||||
@@ -2645,9 +2640,13 @@ static void qede_recovery_failed(struct qede_dev *edev)
|
||||
edev->ops->common->set_power_state(edev->cdev, PCI_D3hot);
|
||||
}
|
||||
|
||||
static void qede_recovery_handler(struct qede_dev *edev)
|
||||
/* Returns true if an open device was successfully reloaded and its
|
||||
* udp_tunnel ports need to be re-synced by the caller.
|
||||
*/
|
||||
static bool qede_recovery_handler(struct qede_dev *edev)
|
||||
{
|
||||
u32 curr_state = edev->state;
|
||||
bool reloaded = false;
|
||||
int rc;
|
||||
|
||||
DP_NOTICE(edev, "Starting a recovery process\n");
|
||||
@@ -2677,17 +2676,18 @@ static void qede_recovery_handler(struct qede_dev *edev)
|
||||
goto err;
|
||||
|
||||
qede_config_rx_mode(edev->ndev);
|
||||
udp_tunnel_nic_reset_ntf(edev->ndev);
|
||||
reloaded = true;
|
||||
}
|
||||
|
||||
edev->state = curr_state;
|
||||
|
||||
DP_NOTICE(edev, "Recovery handling is done\n");
|
||||
|
||||
return;
|
||||
return reloaded;
|
||||
|
||||
err:
|
||||
qede_recovery_failed(edev);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void qede_atomic_hw_err_handler(struct qede_dev *edev)
|
||||
|
||||
@@ -1623,6 +1623,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
|
||||
err_dma_1:
|
||||
ring->skbuff[entry] = NULL;
|
||||
rtase_tx_clear_range(ring, ring->cur_idx + 1, frags);
|
||||
if (frags)
|
||||
/* the frags were cleared above, along with the skb */
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
err_dma_0:
|
||||
tp->stats.tx_dropped++;
|
||||
|
||||
@@ -597,14 +597,13 @@ static int init_dma_desc_rings(struct net_device *netd)
|
||||
|
||||
return 0;
|
||||
|
||||
txalloc_err:
|
||||
while (queue_num--)
|
||||
free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
|
||||
return ret;
|
||||
|
||||
rxalloc_err:
|
||||
while (queue_num--)
|
||||
free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize);
|
||||
queue_num = SXGBE_TX_QUEUES;
|
||||
txalloc_err:
|
||||
while (queue_num--)
|
||||
free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1079,7 +1078,9 @@ static int sxgbe_open(struct net_device *dev)
|
||||
priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE);
|
||||
priv->tx_tc = TC_DEFAULT;
|
||||
priv->rx_tc = TC_DEFAULT;
|
||||
init_dma_desc_rings(dev);
|
||||
ret = init_dma_desc_rings(dev);
|
||||
if (ret)
|
||||
goto init_phy_error;
|
||||
|
||||
/* DMA initialization and SW reset */
|
||||
ret = sxgbe_init_dma_engine(priv);
|
||||
@@ -1188,6 +1189,7 @@ static int sxgbe_open(struct net_device *dev)
|
||||
|
||||
init_error:
|
||||
free_dma_desc_resources(priv);
|
||||
init_phy_error:
|
||||
if (dev->phydev)
|
||||
phy_disconnect(dev->phydev);
|
||||
phy_error:
|
||||
|
||||
@@ -755,7 +755,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev,
|
||||
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||||
ptp_v2 = PTP_TCR_TSVER2ENA;
|
||||
snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
|
||||
if (priv->synopsys_id < DWMAC_CORE_4_10)
|
||||
if (priv->synopsys_id < DWMAC_CORE_3_70 &&
|
||||
priv->plat->core_type != DWMAC_CORE_XGMAC)
|
||||
ts_event_en = PTP_TCR_TSEVNTENA;
|
||||
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
|
||||
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
|
||||
|
||||
@@ -1606,6 +1606,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
|
||||
if (skb_vlan_tag_present(skb)) {
|
||||
tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT;
|
||||
tx_flags |= WX_TX_FLAGS_HW_VLAN;
|
||||
} else if (eth_type_vlan(skb->protocol)) {
|
||||
tx_flags |= WX_TX_FLAGS_SW_VLAN;
|
||||
}
|
||||
|
||||
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
|
||||
|
||||
@@ -1875,8 +1875,8 @@ struct phylink *phylink_create(struct phylink_config *config,
|
||||
} else if (config->type == PHYLINK_DEV) {
|
||||
pl->dev = config->dev;
|
||||
} else {
|
||||
kfree(pl);
|
||||
return ERR_PTR(-EINVAL);
|
||||
ret = -EINVAL;
|
||||
goto free_pl;
|
||||
}
|
||||
|
||||
pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops);
|
||||
@@ -1909,28 +1909,29 @@ struct phylink *phylink_create(struct phylink_config *config,
|
||||
phylink_validate(pl, pl->supported, &pl->link_config);
|
||||
|
||||
ret = phylink_parse_mode(pl, fwnode);
|
||||
if (ret < 0) {
|
||||
kfree(pl);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto free_pl;
|
||||
|
||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
|
||||
ret = phylink_parse_fixedlink(pl, fwnode);
|
||||
if (ret < 0) {
|
||||
kfree(pl);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto release_link_gpio;
|
||||
}
|
||||
|
||||
pl->req_link_an_mode = pl->cfg_link_an_mode;
|
||||
|
||||
ret = phylink_register_sfp(pl, fwnode);
|
||||
if (ret < 0) {
|
||||
kfree(pl);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto release_link_gpio;
|
||||
|
||||
return pl;
|
||||
|
||||
release_link_gpio:
|
||||
if (pl->link_gpio)
|
||||
gpiod_put(pl->link_gpio);
|
||||
free_pl:
|
||||
kfree(pl);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phylink_create);
|
||||
|
||||
|
||||
@@ -145,8 +145,6 @@ struct tun_file {
|
||||
struct list_head next;
|
||||
struct tun_struct *detached;
|
||||
struct ptr_ring tx_ring;
|
||||
/* Protected by tx_ring.consumer_lock */
|
||||
int cons_cnt;
|
||||
struct xdp_rxq_info xdp_rxq;
|
||||
};
|
||||
|
||||
@@ -590,13 +588,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
|
||||
rcu_assign_pointer(tun->tfiles[index],
|
||||
tun->tfiles[tun->numqueues - 1]);
|
||||
ntfile = rtnl_dereference(tun->tfiles[index]);
|
||||
spin_lock(&ntfile->tx_ring.consumer_lock);
|
||||
ntfile->queue_index = index;
|
||||
ntfile->xdp_rxq.queue_index = index;
|
||||
ntfile->cons_cnt = 0;
|
||||
if (__ptr_ring_empty(&ntfile->tx_ring))
|
||||
netif_wake_subqueue(tun->dev, index);
|
||||
spin_unlock(&ntfile->tx_ring.consumer_lock);
|
||||
rcu_assign_pointer(tun->tfiles[tun->numqueues - 1],
|
||||
NULL);
|
||||
|
||||
@@ -737,9 +730,6 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&tfile->tx_ring.consumer_lock);
|
||||
tfile->cons_cnt = 0;
|
||||
spin_unlock(&tfile->tx_ring.consumer_lock);
|
||||
tfile->queue_index = tun->numqueues;
|
||||
tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
|
||||
|
||||
@@ -1018,7 +1008,6 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
struct netdev_queue *queue;
|
||||
struct tun_file *tfile;
|
||||
int len = skb->len;
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
tfile = rcu_dereference(tun->tfiles[txq]);
|
||||
@@ -1073,33 +1062,13 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
nf_reset_ct(skb);
|
||||
|
||||
queue = netdev_get_tx_queue(dev, txq);
|
||||
|
||||
spin_lock(&tfile->tx_ring.producer_lock);
|
||||
ret = __ptr_ring_produce(&tfile->tx_ring, skb);
|
||||
if (!qdisc_txq_has_no_queue(queue) &&
|
||||
__ptr_ring_check_produce(&tfile->tx_ring) == -ENOSPC) {
|
||||
netif_tx_stop_queue(queue);
|
||||
/* Paired with smp_mb() in __tun_wake_queue() */
|
||||
smp_mb__after_atomic();
|
||||
if (!__ptr_ring_check_produce(&tfile->tx_ring))
|
||||
netif_tx_wake_queue(queue);
|
||||
}
|
||||
spin_unlock(&tfile->tx_ring.producer_lock);
|
||||
|
||||
if (ret) {
|
||||
/* This should be a rare case if a qdisc is present, but
|
||||
* can happen due to lltx.
|
||||
* Since skb_tx_timestamp(), skb_orphan(),
|
||||
* run_ebpf_filter() and pskb_trim() could have tinkered
|
||||
* with the SKB, returning NETDEV_TX_BUSY is unsafe and
|
||||
* we must drop instead.
|
||||
*/
|
||||
if (ptr_ring_produce(&tfile->tx_ring, skb)) {
|
||||
drop_reason = SKB_DROP_REASON_FULL_RING;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* dev->lltx requires to do our own update of trans_start */
|
||||
queue = netdev_get_tx_queue(dev, txq);
|
||||
txq_trans_cond_update(queue);
|
||||
|
||||
/* Notify and wake up reader process */
|
||||
@@ -2147,46 +2116,13 @@ static ssize_t tun_put_user(struct tun_struct *tun,
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Callers must hold ring.consumer_lock */
|
||||
static void __tun_wake_queue(struct tun_struct *tun,
|
||||
struct tun_file *tfile, int consumed)
|
||||
{
|
||||
struct netdev_queue *txq = netdev_get_tx_queue(tun->dev,
|
||||
tfile->queue_index);
|
||||
|
||||
/* Paired with smp_mb__after_atomic() in tun_net_xmit() */
|
||||
smp_mb();
|
||||
if (netif_tx_queue_stopped(txq)) {
|
||||
tfile->cons_cnt += consumed;
|
||||
if (tfile->cons_cnt >= tfile->tx_ring.size / 2 ||
|
||||
__ptr_ring_empty(&tfile->tx_ring)) {
|
||||
netif_tx_wake_queue(txq);
|
||||
tfile->cons_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *tun_ring_consume(struct tun_struct *tun, struct tun_file *tfile)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
spin_lock(&tfile->tx_ring.consumer_lock);
|
||||
ptr = __ptr_ring_consume(&tfile->tx_ring);
|
||||
if (ptr)
|
||||
__tun_wake_queue(tun, tfile, 1);
|
||||
|
||||
spin_unlock(&tfile->tx_ring.consumer_lock);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *tun_ring_recv(struct tun_struct *tun, struct tun_file *tfile,
|
||||
int noblock, int *err)
|
||||
static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
void *ptr = NULL;
|
||||
int error = 0;
|
||||
|
||||
ptr = tun_ring_consume(tun, tfile);
|
||||
ptr = ptr_ring_consume(&tfile->tx_ring);
|
||||
if (ptr)
|
||||
goto out;
|
||||
if (noblock) {
|
||||
@@ -2198,7 +2134,7 @@ static void *tun_ring_recv(struct tun_struct *tun, struct tun_file *tfile,
|
||||
|
||||
while (1) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
ptr = tun_ring_consume(tun, tfile);
|
||||
ptr = ptr_ring_consume(&tfile->tx_ring);
|
||||
if (ptr)
|
||||
break;
|
||||
if (signal_pending(current)) {
|
||||
@@ -2235,7 +2171,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
|
||||
|
||||
if (!ptr) {
|
||||
/* Read frames from ring */
|
||||
ptr = tun_ring_recv(tun, tfile, noblock, &err);
|
||||
ptr = tun_ring_recv(tfile, noblock, &err);
|
||||
if (!ptr)
|
||||
return err;
|
||||
}
|
||||
@@ -3690,16 +3626,6 @@ static int tun_queue_resize(struct tun_struct *tun)
|
||||
dev->tx_queue_len, GFP_KERNEL,
|
||||
tun_ptr_free);
|
||||
|
||||
if (!ret) {
|
||||
for (i = 0; i < tun->numqueues; i++) {
|
||||
tfile = rtnl_dereference(tun->tfiles[i]);
|
||||
spin_lock(&tfile->tx_ring.consumer_lock);
|
||||
netif_wake_subqueue(tun->dev, tfile->queue_index);
|
||||
tfile->cons_cnt = 0;
|
||||
spin_unlock(&tfile->tx_ring.consumer_lock);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(rings);
|
||||
return ret;
|
||||
}
|
||||
@@ -3808,29 +3734,6 @@ struct ptr_ring *tun_get_tx_ring(struct file *file)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tun_get_tx_ring);
|
||||
|
||||
/* Callers must hold ring.consumer_lock */
|
||||
void tun_wake_queue(struct file *file, int consumed)
|
||||
{
|
||||
struct tun_file *tfile;
|
||||
struct tun_struct *tun;
|
||||
|
||||
if (file->f_op != &tun_fops)
|
||||
return;
|
||||
|
||||
tfile = file->private_data;
|
||||
if (!tfile)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
tun = rcu_dereference(tfile->tun);
|
||||
if (tun)
|
||||
__tun_wake_queue(tun, tfile, consumed);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tun_wake_queue);
|
||||
|
||||
module_init(tun_init);
|
||||
module_exit(tun_cleanup);
|
||||
MODULE_DESCRIPTION(DRV_DESCRIPTION);
|
||||
|
||||
@@ -756,7 +756,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq,
|
||||
u32 frame_sz;
|
||||
|
||||
if (skb_shared(skb) || skb_head_is_locked(skb) ||
|
||||
skb_shinfo(skb)->nr_frags ||
|
||||
skb_is_nonlinear(skb) ||
|
||||
skb_headroom(skb) < XDP_PACKET_HEADROOM) {
|
||||
if (skb_pp_cow_data(rq->page_pool, pskb, XDP_PACKET_HEADROOM))
|
||||
goto drop;
|
||||
@@ -771,7 +771,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq,
|
||||
xdp_prepare_buff(xdp, skb->head, skb_headroom(skb),
|
||||
skb_headlen(skb), true);
|
||||
|
||||
if (skb_is_nonlinear(skb)) {
|
||||
if (skb_shinfo(skb)->nr_frags) {
|
||||
skb_shinfo(skb)->xdp_frags_size = skb->data_len;
|
||||
xdp_buff_set_frags_flag(xdp);
|
||||
} else {
|
||||
|
||||
@@ -1850,7 +1850,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
|
||||
if (dev->flags & IFF_NOARP)
|
||||
goto out;
|
||||
|
||||
if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
|
||||
if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {
|
||||
dev_dstats_tx_dropped(dev);
|
||||
vxlan_vnifilter_count(vxlan, vni, NULL,
|
||||
VXLAN_VNI_STATS_TX_DROPS, 0);
|
||||
@@ -2111,7 +2111,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *pip;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
|
||||
return false;
|
||||
pip = ip_hdr(skb);
|
||||
n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
|
||||
@@ -2137,7 +2137,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
|
||||
*/
|
||||
if (!ipv6_mod_enabled())
|
||||
return false;
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
return false;
|
||||
pip6 = ipv6_hdr(skb);
|
||||
n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
|
||||
@@ -2159,13 +2159,19 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
if (n) {
|
||||
u8 haddr[ETH_ALEN];
|
||||
bool diff;
|
||||
|
||||
diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha);
|
||||
neigh_ha_snapshot(haddr, n, dev);
|
||||
diff = !ether_addr_equal_unaligned(eth_hdr(skb)->h_dest, haddr);
|
||||
if (diff) {
|
||||
if (skb_cow_head(skb, 0)) {
|
||||
neigh_release(n);
|
||||
return false;
|
||||
}
|
||||
memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
|
||||
dev->addr_len);
|
||||
memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len);
|
||||
memcpy(eth_hdr(skb)->h_dest, haddr, dev->addr_len);
|
||||
}
|
||||
neigh_release(n);
|
||||
return diff;
|
||||
@@ -2757,8 +2763,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
return arp_reduce(dev, skb, vni);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
|
||||
pskb_may_pull(skb, sizeof(struct ipv6hdr) +
|
||||
sizeof(struct nd_msg)) &&
|
||||
pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
|
||||
sizeof(struct nd_msg)) &&
|
||||
ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
|
||||
struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);
|
||||
|
||||
@@ -2796,6 +2802,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
(ntohs(eth->h_proto) == ETH_P_IP ||
|
||||
ntohs(eth->h_proto) == ETH_P_IPV6)) {
|
||||
did_rsc = route_shortcircuit(dev, skb);
|
||||
eth = eth_hdr(skb);
|
||||
if (did_rsc)
|
||||
f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
|
||||
}
|
||||
|
||||
@@ -1631,7 +1631,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
|
||||
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
|
||||
return NULL;
|
||||
group.dst.sa.sa_family = AF_INET;
|
||||
group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
|
||||
@@ -1640,7 +1640,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case htons(ETH_P_IPV6):
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
return NULL;
|
||||
group.dst.sa.sa_family = AF_INET6;
|
||||
group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr;
|
||||
|
||||
@@ -1544,6 +1544,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
|
||||
}
|
||||
|
||||
wiphy_unlock(ah->hw->wiphy);
|
||||
|
||||
complete(&ah->peer_ml_id_done);
|
||||
}
|
||||
|
||||
wake_up(&ab->wmi_ab.tx_credits_wq);
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
|
||||
#define ATH12K_MAX_MLO_PEERS 256
|
||||
#define ATH12K_MLO_PEER_ID_INVALID 0xFFFF
|
||||
#define ATH12K_MLO_PEER_ID_PENDING 0xFFFE
|
||||
|
||||
#define ATH12K_INVALID_RSSI_FULL -1
|
||||
#define ATH12K_INVALID_RSSI_EMPTY -128
|
||||
@@ -793,6 +794,8 @@ struct ath12k_hw {
|
||||
enum ath12k_hw_state state;
|
||||
bool regd_updated;
|
||||
bool use_6ghz_regd;
|
||||
bool host_alloc_ml_id;
|
||||
struct completion peer_ml_id_done;
|
||||
|
||||
u8 num_radio;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "peer.h"
|
||||
#include "dp_peer.h"
|
||||
#include "htc.h"
|
||||
#include "dp_htt.h"
|
||||
#include "debugfs_htt_stats.h"
|
||||
@@ -575,6 +576,51 @@ static void ath12k_htt_mlo_offset_event_handler(struct ath12k_base *ab,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data;
|
||||
struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev;
|
||||
u16 raw_peer_id, peer_id, addr_h16;
|
||||
u8 peer_addr[ETH_ALEN];
|
||||
int ret;
|
||||
|
||||
if (skb->len < sizeof(*ev)) {
|
||||
ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n",
|
||||
skb->len);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_peer_id = le32_get_bits(ev->info0,
|
||||
HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID);
|
||||
peer_id = raw_peer_id | ATH12K_PEER_ML_ID_VALID;
|
||||
|
||||
addr_h16 = le32_get_bits(ev->info1,
|
||||
HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16);
|
||||
ath12k_dp_get_mac_addr(le32_to_cpu(ev->mac_addr_l32), addr_h16,
|
||||
peer_addr);
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n",
|
||||
peer_addr, peer_id);
|
||||
|
||||
/*
|
||||
* Fix up the dp_peer entry created with ATH12K_MLO_PEER_ID_PENDING
|
||||
* earlier; on chips with host_alloc_ml_id == false this is the only
|
||||
* point at which the host learns the firmware-assigned ID. Chips
|
||||
* that allocate the ID on the host also receive this event but the
|
||||
* firmware-reported ID matches the host-allocated one, so there is
|
||||
* nothing to fix up.
|
||||
*/
|
||||
if (!ab->hw_params->host_alloc_ml_id) {
|
||||
ret = ath12k_dp_peer_fixup_peer_id(ab, peer_addr,
|
||||
peer_id);
|
||||
if (ret)
|
||||
ath12k_warn(ab,
|
||||
"failed to fix up peer id %u for dp peer %pM: %d\n",
|
||||
peer_id, peer_addr, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -659,6 +705,9 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
|
||||
case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND:
|
||||
ath12k_htt_mlo_offset_event_handler(ab, skb);
|
||||
break;
|
||||
case HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP:
|
||||
ath12k_dp_htt_mlo_peer_map_handler(ab, skb);
|
||||
break;
|
||||
default:
|
||||
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt event %d not handled\n",
|
||||
type);
|
||||
|
||||
@@ -930,6 +930,7 @@ enum htt_t2h_msg_type {
|
||||
HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c,
|
||||
HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24,
|
||||
HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND = 0x28,
|
||||
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP = 0x29,
|
||||
HTT_T2H_MSG_TYPE_PEER_MAP3 = 0x2b,
|
||||
HTT_T2H_MSG_TYPE_VDEV_TXRX_STATS_PERIODIC_IND = 0x2c,
|
||||
};
|
||||
@@ -974,11 +975,22 @@ struct htt_t2h_peer_unmap_event {
|
||||
__le32 info1;
|
||||
} __packed;
|
||||
|
||||
#define HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID GENMASK(23, 8)
|
||||
#define HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0)
|
||||
|
||||
struct htt_t2h_mlo_peer_map_event {
|
||||
__le32 info0;
|
||||
__le32 mac_addr_l32;
|
||||
__le32 info1;
|
||||
__le32 reserved[5];
|
||||
} __packed;
|
||||
|
||||
struct htt_resp_msg {
|
||||
union {
|
||||
struct htt_t2h_version_conf_msg version_msg;
|
||||
struct htt_t2h_peer_map_event peer_map_ev;
|
||||
struct htt_t2h_peer_unmap_event peer_unmap_ev;
|
||||
struct htt_t2h_mlo_peer_map_event mlo_peer_map_ev;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
||||
@@ -475,7 +475,9 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
|
||||
dp_peer->is_mlo = params->is_mlo;
|
||||
|
||||
/*
|
||||
* For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer
|
||||
* For MLO client, the ML peer ID, either known or PENDING, needs to be
|
||||
* initialized here since the following logic depends on it.
|
||||
*
|
||||
* For non-MLO client, host gets link peer ID from firmware and will be
|
||||
* assigned at the time of link peer creation
|
||||
*/
|
||||
@@ -491,13 +493,17 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
|
||||
list_add(&dp_peer->list, &dp_hw->dp_peers_list);
|
||||
|
||||
/*
|
||||
* For MLO client, the peer_id for ath12k_dp_peer is allocated by host
|
||||
* and that peer_id is known at this point, and hence this ath12k_dp_peer
|
||||
* can be added to the RCU table using the peer_id.
|
||||
* For non-MLO client, this addition to RCU table shall be done at the
|
||||
* time of assignment of ath12k_dp_link_peer to ath12k_dp_peer.
|
||||
* For an MLO client whose ML peer ID is allocated by the host, the
|
||||
* peer_id is known here and the dp_peer can be added to the RCU
|
||||
* table using it. For an MLO client on chips where the firmware
|
||||
* allocates the ID, peer_id is ATH12K_MLO_PEER_ID_PENDING and the
|
||||
* RCU table publish is deferred to the
|
||||
* HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For a non-MLO client
|
||||
* the publish happens later, at the time of assignment of
|
||||
* ath12k_dp_link_peer to ath12k_dp_peer.
|
||||
*/
|
||||
if (dp_peer->is_mlo)
|
||||
if (dp_peer->is_mlo &&
|
||||
dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
|
||||
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer);
|
||||
|
||||
spin_unlock_bh(&dp_hw->peer_lock);
|
||||
@@ -518,7 +524,8 @@ void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr,
|
||||
return;
|
||||
}
|
||||
|
||||
if (dp_peer->is_mlo)
|
||||
if (dp_peer->is_mlo &&
|
||||
dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
|
||||
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
|
||||
|
||||
list_del(&dp_peer->list);
|
||||
@@ -695,3 +702,55 @@ void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr)
|
||||
if (rx_stats)
|
||||
memset(rx_stats, 0, sizeof(*rx_stats));
|
||||
}
|
||||
|
||||
int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab,
|
||||
const u8 *peer_addr, u16 peer_id)
|
||||
{
|
||||
struct ath12k_dp_link_peer *link_peer;
|
||||
struct ath12k_dp_peer *dp_peer = NULL;
|
||||
struct ath12k_hw_group *ag = ab->ag;
|
||||
struct ath12k_dp_hw *dp_hw = NULL;
|
||||
struct ath12k_hw *ah;
|
||||
int i;
|
||||
|
||||
if (peer_id >= (ATH12K_PEER_ML_ID_VALID | ATH12K_MAX_MLO_PEERS))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ag->num_hw; i++) {
|
||||
ah = ag->ah[i];
|
||||
if (!ah)
|
||||
continue;
|
||||
|
||||
spin_lock_bh(&ah->dp_hw.peer_lock);
|
||||
dp_peer = ath12k_dp_peer_find_by_addr(&ah->dp_hw,
|
||||
(u8 *)peer_addr);
|
||||
if (dp_peer) {
|
||||
dp_hw = &ah->dp_hw;
|
||||
break;
|
||||
}
|
||||
spin_unlock_bh(&ah->dp_hw.peer_lock);
|
||||
}
|
||||
|
||||
if (!dp_peer)
|
||||
return -ENOENT;
|
||||
|
||||
/* dp_hw->peer_lock is held */
|
||||
|
||||
dp_peer->peer_id = peer_id;
|
||||
rcu_assign_pointer(dp_hw->dp_peers[peer_id], dp_peer);
|
||||
|
||||
for (i = 0; i < ATH12K_NUM_MAX_LINKS; i++) {
|
||||
link_peer = rcu_dereference_protected(dp_peer->link_peers[i],
|
||||
lockdep_is_held(&dp_hw->peer_lock));
|
||||
if (link_peer)
|
||||
link_peer->ml_id = peer_id;
|
||||
}
|
||||
|
||||
ath12k_sta_to_ahsta(dp_peer->sta)->ml_peer_id = peer_id;
|
||||
|
||||
spin_unlock_bh(&dp_hw->peer_lock);
|
||||
|
||||
complete(&ah->peer_ml_id_done);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -181,4 +181,6 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p
|
||||
struct ath12k_dp_link_peer *
|
||||
ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id);
|
||||
void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer);
|
||||
int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, const u8 *peer_addr,
|
||||
u16 peer_id);
|
||||
#endif
|
||||
|
||||
@@ -236,6 +236,8 @@ struct ath12k_hw_params {
|
||||
u32 max_client_dbs;
|
||||
u32 max_client_dbs_sbs;
|
||||
} client;
|
||||
|
||||
bool host_alloc_ml_id;
|
||||
};
|
||||
|
||||
struct ath12k_hw_ops {
|
||||
|
||||
@@ -1282,13 +1282,17 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah)
|
||||
struct ath12k_dp_peer *dp_peer, *tmp;
|
||||
struct ath12k_dp_hw *dp_hw = &ah->dp_hw;
|
||||
|
||||
lockdep_assert_wiphy(ah->hw->wiphy);
|
||||
|
||||
INIT_LIST_HEAD(&peers);
|
||||
|
||||
spin_lock_bh(&dp_hw->peer_lock);
|
||||
list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) {
|
||||
if (dp_peer->is_mlo) {
|
||||
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
|
||||
clear_bit(dp_peer->peer_id, ah->free_ml_peer_id_map);
|
||||
if (dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
|
||||
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id],
|
||||
NULL);
|
||||
ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta));
|
||||
}
|
||||
|
||||
list_move(&dp_peer->list, &peers);
|
||||
@@ -3531,11 +3535,16 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
|
||||
struct ath12k_sta *ahsta = arsta->ahsta;
|
||||
struct ath12k_link_sta *arsta_p;
|
||||
struct ath12k_link_vif *arvif;
|
||||
struct ath12k_hw *ah = arsta->arvif->ar->ah;
|
||||
unsigned long links;
|
||||
u8 link_id;
|
||||
int i;
|
||||
|
||||
if (!sta->mlo || ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID)
|
||||
if (!sta->mlo)
|
||||
return;
|
||||
|
||||
if (ah->host_alloc_ml_id &&
|
||||
ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID)
|
||||
return;
|
||||
|
||||
ml->enabled = true;
|
||||
@@ -3543,12 +3552,25 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
|
||||
|
||||
/* For now considering the primary umac based on assoc link */
|
||||
ml->primary_umac = arsta->is_assoc_link;
|
||||
ml->peer_id_valid = true;
|
||||
/*
|
||||
* Only chips that allocate the MLD peer ID on the host send a valid
|
||||
* ml_peer_id in WMI_PEER_ASSOC_CMDID. For chips where the firmware
|
||||
* picks the ID, leave peer_id_valid false to avoid unexpected issues.
|
||||
*/
|
||||
ml->peer_id_valid = ah->host_alloc_ml_id;
|
||||
ml->logical_link_idx_valid = true;
|
||||
|
||||
ether_addr_copy(ml->mld_addr, sta->addr);
|
||||
ml->logical_link_idx = arsta->link_idx;
|
||||
ml->ml_peer_id = ahsta->ml_peer_id;
|
||||
/*
|
||||
* WMI_MLO_PEER_ASSOC_PARAMS expects the raw ML peer ID without
|
||||
* the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit. For chips
|
||||
* where the firmware allocates the ID, the field is unused (the
|
||||
* firmware always allocates regardless of the value here); send 0
|
||||
* to make that intent explicit.
|
||||
*/
|
||||
ml->ml_peer_id = ah->host_alloc_ml_id ?
|
||||
(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID) : 0;
|
||||
ml->ieee_link_id = arsta->link_id;
|
||||
ml->num_partner_links = 0;
|
||||
ml->eml_cap = sta->eml_cap;
|
||||
@@ -3595,8 +3617,6 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar,
|
||||
|
||||
memset(arg, 0, sizeof(*arg));
|
||||
|
||||
reinit_completion(&ar->peer_assoc_done);
|
||||
|
||||
arg->peer_new_assoc = !reassoc;
|
||||
ath12k_peer_assoc_h_basic(ar, arvif, arsta, arg);
|
||||
ath12k_peer_assoc_h_crypto(ar, arvif, arsta, arg);
|
||||
@@ -3836,6 +3856,52 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
|
||||
return bw;
|
||||
}
|
||||
|
||||
static int ath12k_mac_peer_assoc(struct ath12k *ar,
|
||||
struct ath12k_wmi_peer_assoc_arg *peer_arg)
|
||||
{
|
||||
struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
|
||||
int ret;
|
||||
|
||||
reinit_completion(&ar->peer_assoc_done);
|
||||
reinit_completion(&ah->peer_ml_id_done);
|
||||
|
||||
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
|
||||
if (ret) {
|
||||
ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
|
||||
peer_arg->peer_mac, peer_arg->vdev_id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
|
||||
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
|
||||
peer_arg->peer_mac, peer_arg->vdev_id);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* For devices where the firmware allocates the MLD peer ID, the host
|
||||
* learns the real ID only from the MLO_RX_PEER_MAP HTT event, which is
|
||||
* handled in a softirq (BH workqueue) context that cannot take the
|
||||
* wiphy lock. Block here, while still holding the wiphy lock, until
|
||||
* that event has fixed up the ID. This serialises the fixup against
|
||||
* all other wiphy-locked ml_peer_id accesses.
|
||||
*
|
||||
* The firmware sends the event only once, in response to the assoc-link
|
||||
* peer assoc, so block only for that link.
|
||||
*/
|
||||
if (!ah->host_alloc_ml_id &&
|
||||
peer_arg->is_assoc &&
|
||||
peer_arg->ml.enabled &&
|
||||
peer_arg->ml.assoc_link &&
|
||||
!wait_for_completion_timeout(&ah->peer_ml_id_done, 1 * HZ)) {
|
||||
ath12k_warn(ar->ab, "failed to get MLO peer map event for %pM vdev %i\n",
|
||||
peer_arg->peer_mac, peer_arg->vdev_id);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath12k_bss_assoc(struct ath12k *ar,
|
||||
struct ath12k_link_vif *arvif,
|
||||
struct ieee80211_bss_conf *bss_conf)
|
||||
@@ -3916,18 +3982,10 @@ static void ath12k_bss_assoc(struct ath12k *ar,
|
||||
}
|
||||
|
||||
peer_arg->is_assoc = true;
|
||||
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
|
||||
if (ret) {
|
||||
ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
|
||||
bss_conf->bssid, arvif->vdev_id, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
|
||||
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
|
||||
bss_conf->bssid, arvif->vdev_id);
|
||||
ret = ath12k_mac_peer_assoc(ar, peer_arg);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ath12k_setup_peer_smps(ar, arvif, bss_conf->bssid,
|
||||
&link_sta->ht_cap, &link_sta->he_6ghz_capa);
|
||||
@@ -6481,18 +6539,10 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
|
||||
}
|
||||
|
||||
peer_arg->is_assoc = true;
|
||||
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
|
||||
if (ret) {
|
||||
ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
|
||||
arsta->addr, arvif->vdev_id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
|
||||
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
|
||||
arsta->addr, arvif->vdev_id);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
ret = ath12k_mac_peer_assoc(ar, peer_arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_vht_rates = ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask);
|
||||
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask);
|
||||
@@ -6841,14 +6891,8 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
|
||||
peer_arg, true);
|
||||
|
||||
peer_arg->is_assoc = false;
|
||||
err = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
|
||||
if (err)
|
||||
ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
|
||||
arsta->addr, arvif->vdev_id, err);
|
||||
|
||||
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ))
|
||||
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
|
||||
arsta->addr, arvif->vdev_id);
|
||||
ath12k_mac_peer_assoc(ar, peer_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7266,10 +7310,8 @@ static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif,
|
||||
ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id);
|
||||
}
|
||||
|
||||
if (sta->mlo) {
|
||||
clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
|
||||
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
|
||||
}
|
||||
if (sta->mlo)
|
||||
ath12k_peer_ml_free(ah, ahsta);
|
||||
}
|
||||
|
||||
static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
|
||||
@@ -7733,15 +7775,23 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
|
||||
/* ML sta */
|
||||
if (sta->mlo && !ahsta->links_map &&
|
||||
(hweight16(sta->valid_links) == 1)) {
|
||||
ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah);
|
||||
if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
|
||||
ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM",
|
||||
sta->addr);
|
||||
goto exit;
|
||||
if (ah->host_alloc_ml_id) {
|
||||
ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah);
|
||||
if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
|
||||
ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM",
|
||||
sta->addr);
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* firmware allocates the ML peer ID and notifies
|
||||
* the host via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP
|
||||
*/
|
||||
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_PENDING;
|
||||
}
|
||||
|
||||
dp_params.is_mlo = true;
|
||||
dp_params.peer_id = ahsta->ml_peer_id | ATH12K_PEER_ML_ID_VALID;
|
||||
dp_params.peer_id = ahsta->ml_peer_id;
|
||||
}
|
||||
|
||||
dp_params.sta = sta;
|
||||
@@ -7878,10 +7928,8 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
|
||||
peer_delete:
|
||||
ath12k_dp_peer_delete(&ah->dp_hw, sta->addr, sta);
|
||||
ml_peer_id_clear:
|
||||
if (sta->mlo) {
|
||||
clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
|
||||
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
|
||||
}
|
||||
if (sta->mlo)
|
||||
ath12k_peer_ml_free(ah, ahsta);
|
||||
exit:
|
||||
/* update the state if everything went well */
|
||||
if (!ret)
|
||||
@@ -15310,6 +15358,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag,
|
||||
ah->num_radio = num_pdev_map;
|
||||
|
||||
mutex_init(&ah->hw_mutex);
|
||||
init_completion(&ah->peer_ml_id_done);
|
||||
|
||||
spin_lock_init(&ah->dp_hw.peer_lock);
|
||||
INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list);
|
||||
@@ -15384,8 +15433,9 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
|
||||
int mac_id, device_id, total_radio, num_hw;
|
||||
struct ath12k_base *ab;
|
||||
struct ath12k_hw *ah;
|
||||
int ret, i, j;
|
||||
bool conf = false;
|
||||
u8 radio_per_hw;
|
||||
int ret, i, j;
|
||||
|
||||
total_radio = 0;
|
||||
for (i = 0; i < ag->num_devices; i++) {
|
||||
@@ -15425,6 +15475,20 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
|
||||
}
|
||||
|
||||
ab = ag->ab[device_id];
|
||||
|
||||
/*
|
||||
* the assumption is all devices within an ah
|
||||
* share the same host_alloc_ml_id configuration
|
||||
*/
|
||||
if (j == 0) {
|
||||
conf = ab->hw_params->host_alloc_ml_id;
|
||||
} else if (conf != ab->hw_params->host_alloc_ml_id) {
|
||||
ath12k_warn(ab, "inconsistent ML ID config within ah, device 0 uses %s allocated ID, while device %u doesn't\n",
|
||||
conf ? "host" : "firmware", device_id);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pdev_map[j].ab = ab;
|
||||
pdev_map[j].pdev_idx = mac_id;
|
||||
mac_id++;
|
||||
@@ -15449,6 +15513,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
|
||||
}
|
||||
|
||||
ah->dev = ab->dev;
|
||||
ah->host_alloc_ml_id = conf;
|
||||
|
||||
ag->ah[i] = ah;
|
||||
ag->num_hw++;
|
||||
|
||||
@@ -230,7 +230,16 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
|
||||
/* Fill ML info into created peer */
|
||||
if (sta->mlo) {
|
||||
ml_peer_id = ahsta->ml_peer_id;
|
||||
peer->ml_id = ml_peer_id | ATH12K_PEER_ML_ID_VALID;
|
||||
/*
|
||||
* For chips where firmware allocates the ML peer ID,
|
||||
* ml_peer_id is ATH12K_MLO_PEER_ID_PENDING here. The
|
||||
* MLO_RX_PEER_MAP HTT event handler fixes up
|
||||
* peer->ml_id once the ID is known.
|
||||
*/
|
||||
if (ml_peer_id == ATH12K_MLO_PEER_ID_PENDING)
|
||||
peer->ml_id = ATH12K_MLO_PEER_ID_INVALID;
|
||||
else
|
||||
peer->ml_id = ml_peer_id;
|
||||
ether_addr_copy(peer->ml_addr, sta->addr);
|
||||
|
||||
/* the assoc link is considered primary for now */
|
||||
@@ -276,9 +285,25 @@ u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah)
|
||||
}
|
||||
|
||||
if (ml_peer_id == ATH12K_MAX_MLO_PEERS)
|
||||
ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
|
||||
return ATH12K_MLO_PEER_ID_INVALID;
|
||||
|
||||
return ml_peer_id;
|
||||
return ml_peer_id | ATH12K_PEER_ML_ID_VALID;
|
||||
}
|
||||
|
||||
void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta)
|
||||
{
|
||||
lockdep_assert_wiphy(ah->hw->wiphy);
|
||||
|
||||
/*
|
||||
* Only devices that allocate the ID on the host own a slot in
|
||||
* free_ml_peer_id_map.
|
||||
*/
|
||||
if (ah->host_alloc_ml_id &&
|
||||
(ahsta->ml_peer_id <
|
||||
(ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID)))
|
||||
clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID,
|
||||
ah->free_ml_peer_id_map);
|
||||
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
|
||||
}
|
||||
|
||||
int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta)
|
||||
|
||||
@@ -26,4 +26,5 @@ int ath12k_link_sta_rhash_add(struct ath12k_base *ab, struct ath12k_link_sta *ar
|
||||
struct ath12k_link_sta *ath12k_link_sta_find_by_addr(struct ath12k_base *ab,
|
||||
const u8 *addr);
|
||||
u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah);
|
||||
void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta);
|
||||
#endif /* _PEER_H_ */
|
||||
|
||||
@@ -442,6 +442,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
|
||||
.max_client_dbs = 128,
|
||||
.max_client_dbs_sbs = 128,
|
||||
},
|
||||
|
||||
.host_alloc_ml_id = true,
|
||||
},
|
||||
{
|
||||
.name = "wcn7850 hw2.0",
|
||||
@@ -533,6 +535,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
|
||||
.max_client_dbs = 128,
|
||||
.max_client_dbs_sbs = 128,
|
||||
},
|
||||
|
||||
.host_alloc_ml_id = false,
|
||||
},
|
||||
{
|
||||
.name = "qcn9274 hw2.0",
|
||||
@@ -620,6 +624,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
|
||||
.max_client_dbs = 128,
|
||||
.max_client_dbs_sbs = 128,
|
||||
},
|
||||
|
||||
.host_alloc_ml_id = true,
|
||||
},
|
||||
{
|
||||
.name = "ipq5332 hw1.0",
|
||||
@@ -700,6 +706,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
|
||||
.max_client_dbs = 128,
|
||||
.max_client_dbs_sbs = 128,
|
||||
},
|
||||
|
||||
.host_alloc_ml_id = true,
|
||||
},
|
||||
{
|
||||
.name = "qcc2072 hw1.0",
|
||||
@@ -792,6 +800,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
|
||||
.max_client_dbs = 128,
|
||||
.max_client_dbs_sbs = 128,
|
||||
},
|
||||
|
||||
.host_alloc_ml_id = false,
|
||||
},
|
||||
{
|
||||
.name = "ipq5424 hw1.0",
|
||||
@@ -876,6 +886,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
|
||||
.max_client_dbs = 128,
|
||||
.max_client_dbs_sbs = 128,
|
||||
},
|
||||
|
||||
.host_alloc_ml_id = true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2179,8 +2179,8 @@ il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr)
|
||||
il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
|
||||
|
||||
il->num_stations--;
|
||||
|
||||
BUG_ON(il->num_stations < 0);
|
||||
if (WARN_ON(il->num_stations < 0))
|
||||
il->num_stations = 0;
|
||||
|
||||
spin_unlock_irqrestore(&il->sta_lock, flags);
|
||||
|
||||
@@ -2328,7 +2328,8 @@ il_dealloc_bcast_stations(struct il_priv *il)
|
||||
|
||||
il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
|
||||
il->num_stations--;
|
||||
BUG_ON(il->num_stations < 0);
|
||||
if (WARN_ON(il->num_stations < 0))
|
||||
il->num_stations = 0;
|
||||
kfree(il->stations[i].lq);
|
||||
il->stations[i].lq = NULL;
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ il_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count,
|
||||
int i, j, pos = 0;
|
||||
ssize_t ret;
|
||||
/* Add 30 for initial string */
|
||||
const size_t bufsz = 30 + sizeof(char) * 500 * (il->num_stations);
|
||||
const size_t bufsz = 30 + sizeof(char) * 500 * max_sta;
|
||||
|
||||
buf = kmalloc(bufsz, GFP_KERNEL);
|
||||
if (!buf)
|
||||
|
||||
@@ -44,7 +44,7 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
|
||||
ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
|
||||
mwifiex_process_tdls_action_frame(priv,
|
||||
(u8 *)rx_hdr,
|
||||
skb->len);
|
||||
rx_skb->len);
|
||||
}
|
||||
|
||||
if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
|
||||
|
||||
@@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv)
|
||||
TMR_CTRL_TE | TMR_CTRL_FS;
|
||||
netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
|
||||
netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
|
||||
netc_timer_wr(priv, NETC_TMR_TEMASK, 0);
|
||||
|
||||
/* Disable FIPER by default */
|
||||
fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
|
||||
@@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
|
||||
/* Clear interrupts status */
|
||||
netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
|
||||
|
||||
if (!tmr_event) {
|
||||
spin_unlock(&priv->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (tmr_event & TMR_TEVENT_ALMEN(0))
|
||||
netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
|
||||
|
||||
@@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv)
|
||||
}
|
||||
|
||||
priv->irq = pci_irq_vector(pdev, 0);
|
||||
err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv);
|
||||
err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN,
|
||||
priv->irq_name, priv);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "request_irq() failed\n");
|
||||
pci_free_irq_vectors(pdev);
|
||||
@@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv)
|
||||
{
|
||||
struct pci_dev *pdev = priv->pdev;
|
||||
|
||||
disable_irq(priv->irq);
|
||||
free_irq(priv->irq, priv);
|
||||
pci_free_irq_vectors(pdev);
|
||||
}
|
||||
@@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev,
|
||||
goto free_msix_irq;
|
||||
}
|
||||
|
||||
enable_irq(priv->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
free_msix_irq:
|
||||
@@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct netc_timer *priv = pci_get_drvdata(pdev);
|
||||
|
||||
disable_irq(priv->irq);
|
||||
ptp_clock_unregister(priv->clock);
|
||||
netc_timer_wr(priv, NETC_TMR_TEMASK, 0);
|
||||
netc_timer_wr(priv, NETC_TMR_CTRL, 0);
|
||||
ptp_clock_unregister(priv->clock);
|
||||
netc_timer_free_msix_irq(priv);
|
||||
netc_timer_pci_remove(pdev);
|
||||
}
|
||||
|
||||
@@ -6525,6 +6525,9 @@ int qeth_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *d
|
||||
struct qeth_card *card = dev->ml_priv;
|
||||
int rc = 0;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOC_QETH_ADP_SET_SNMP_CONTROL:
|
||||
rc = qeth_snmp_command(card, data);
|
||||
|
||||
@@ -176,21 +176,13 @@ static void *vhost_net_buf_consume(struct vhost_net_buf *rxq)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhost_net_buf_produce(struct sock *sk,
|
||||
struct vhost_net_virtqueue *nvq)
|
||||
static int vhost_net_buf_produce(struct vhost_net_virtqueue *nvq)
|
||||
{
|
||||
struct file *file = sk->sk_socket->file;
|
||||
struct vhost_net_buf *rxq = &nvq->rxq;
|
||||
|
||||
rxq->head = 0;
|
||||
spin_lock(&nvq->rx_ring->consumer_lock);
|
||||
rxq->tail = __ptr_ring_consume_batched(nvq->rx_ring, rxq->queue,
|
||||
VHOST_NET_BATCH);
|
||||
|
||||
if (rxq->tail)
|
||||
tun_wake_queue(file, rxq->tail);
|
||||
|
||||
spin_unlock(&nvq->rx_ring->consumer_lock);
|
||||
rxq->tail = ptr_ring_consume_batched(nvq->rx_ring, rxq->queue,
|
||||
VHOST_NET_BATCH);
|
||||
return rxq->tail;
|
||||
}
|
||||
|
||||
@@ -217,15 +209,14 @@ static int vhost_net_buf_peek_len(void *ptr)
|
||||
return __skb_array_len_with_tag(ptr);
|
||||
}
|
||||
|
||||
static int vhost_net_buf_peek(struct sock *sk,
|
||||
struct vhost_net_virtqueue *nvq)
|
||||
static int vhost_net_buf_peek(struct vhost_net_virtqueue *nvq)
|
||||
{
|
||||
struct vhost_net_buf *rxq = &nvq->rxq;
|
||||
|
||||
if (!vhost_net_buf_is_empty(rxq))
|
||||
goto out;
|
||||
|
||||
if (!vhost_net_buf_produce(sk, nvq))
|
||||
if (!vhost_net_buf_produce(nvq))
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -1013,7 +1004,7 @@ static int peek_head_len(struct vhost_net_virtqueue *rvq, struct sock *sk)
|
||||
unsigned long flags;
|
||||
|
||||
if (rvq->rx_ring)
|
||||
return vhost_net_buf_peek(sk, rvq);
|
||||
return vhost_net_buf_peek(rvq);
|
||||
|
||||
spin_lock_irqsave(&sk->sk_receive_queue.lock, flags);
|
||||
head = skb_peek(&sk->sk_receive_queue);
|
||||
|
||||
@@ -562,6 +562,7 @@ struct ethtool_fec_hist {
|
||||
u64 per_lane[ETHTOOL_MAX_LANES];
|
||||
} values[ETHTOOL_FEC_HIST_MAX];
|
||||
const struct ethtool_fec_hist_range *ranges;
|
||||
struct ethtool_fec_hist_range ranges_buf[ETHTOOL_FEC_HIST_MAX];
|
||||
};
|
||||
/**
|
||||
* struct ethtool_fec_stats - statistics for IEEE 802.3 FEC
|
||||
|
||||
@@ -22,7 +22,6 @@ struct tun_msg_ctl {
|
||||
#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
|
||||
struct socket *tun_get_socket(struct file *);
|
||||
struct ptr_ring *tun_get_tx_ring(struct file *file);
|
||||
void tun_wake_queue(struct file *file, int consumed);
|
||||
|
||||
static inline bool tun_is_xdp_frame(void *ptr)
|
||||
{
|
||||
@@ -56,8 +55,6 @@ static inline struct ptr_ring *tun_get_tx_ring(struct file *f)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static inline void tun_wake_queue(struct file *f, int consumed) {}
|
||||
|
||||
static inline bool tun_is_xdp_frame(void *ptr)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
|
||||
unsigned int *datalen);
|
||||
|
||||
void (*seq_adjust)(struct sk_buff *skb,
|
||||
unsigned int protoff, s16 off);
|
||||
unsigned int protoff, s32 off);
|
||||
|
||||
unsigned int (*expect)(struct sk_buff *skb,
|
||||
unsigned int protoff,
|
||||
|
||||
@@ -96,20 +96,6 @@ static inline bool ptr_ring_full_bh(struct ptr_ring *r)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Note: callers invoking this in a loop must use a compiler barrier,
|
||||
* for example cpu_relax(). Callers must hold producer_lock.
|
||||
*/
|
||||
static inline int __ptr_ring_check_produce(struct ptr_ring *r)
|
||||
{
|
||||
if (unlikely(!r->size))
|
||||
return -EINVAL;
|
||||
|
||||
if (data_race(r->queue[r->producer]))
|
||||
return -ENOSPC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note: callers invoking this in a loop must use a compiler barrier,
|
||||
* for example cpu_relax(). Callers must hold producer_lock.
|
||||
* Callers are responsible for making sure pointer that is being queued
|
||||
@@ -117,10 +103,8 @@ static inline int __ptr_ring_check_produce(struct ptr_ring *r)
|
||||
*/
|
||||
static inline int __ptr_ring_produce(struct ptr_ring *r, void *ptr)
|
||||
{
|
||||
int p = __ptr_ring_check_produce(r);
|
||||
|
||||
if (p)
|
||||
return p;
|
||||
if (unlikely(!r->size) || data_race(r->queue[r->producer]))
|
||||
return -ENOSPC;
|
||||
|
||||
/* Make sure the pointer we are storing points to a valid data. */
|
||||
/* Pairs with the dependency ordering in __ptr_ring_consume. */
|
||||
|
||||
@@ -767,9 +767,11 @@ struct hci_conn {
|
||||
struct dentry *debugfs;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t proto_lock; /* lock guarding protocol data */
|
||||
void *l2cap_data;
|
||||
void *sco_data;
|
||||
void *iso_data;
|
||||
void *iso_data __guarded_by(&proto_lock);
|
||||
|
||||
struct list_head link_list;
|
||||
struct hci_conn *parent;
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
#include <linux/netfilter.h> /* for union nf_inet_addr */
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h> /* for struct ipv6hdr */
|
||||
#include <net/route.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_fib.h>
|
||||
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#endif
|
||||
@@ -1972,8 +1974,8 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
|
||||
int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp, int offset,
|
||||
unsigned int hooknum, struct ip_vs_iphdr *iph);
|
||||
struct ip_vs_protocol *pp, unsigned int toff,
|
||||
unsigned int hooknum, struct ip_vs_iphdr *ciph);
|
||||
void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
@@ -1986,8 +1988,8 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
|
||||
int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp, int offset,
|
||||
unsigned int hooknum, struct ip_vs_iphdr *iph);
|
||||
struct ip_vs_protocol *pp, unsigned int toff,
|
||||
unsigned int hooknum, struct ip_vs_iphdr *ciph);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
@@ -2059,15 +2061,15 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
|
||||
}
|
||||
|
||||
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
|
||||
struct ip_vs_conn *cp, int dir);
|
||||
struct ip_vs_conn *cp, int dir, unsigned int toff,
|
||||
bool has_ports);
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
|
||||
struct ip_vs_conn *cp, int dir);
|
||||
struct ip_vs_conn *cp, int dir, unsigned int toff,
|
||||
bool has_ports, struct ip_vs_iphdr *ciph);
|
||||
#endif
|
||||
|
||||
__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
|
||||
|
||||
static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
|
||||
{
|
||||
__be32 diff[2] = { ~old, new };
|
||||
@@ -2093,6 +2095,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
|
||||
return csum_partial(diff, sizeof(diff), oldsum);
|
||||
}
|
||||
|
||||
static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
|
||||
{
|
||||
/* Checksum unnecessary or already validated? */
|
||||
if (skb_csum_unnecessary(skb))
|
||||
return false;
|
||||
/* LOCAL_OUT ? */
|
||||
if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
|
||||
return false;
|
||||
/* !LOCAL_IN (FORWARD) ? */
|
||||
if (af == AF_INET6) {
|
||||
if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
|
||||
return false;
|
||||
} else {
|
||||
if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
|
||||
int offset, int proto, int af)
|
||||
{
|
||||
if (!ip_vs_checksum_needed(skb, af))
|
||||
return true;
|
||||
return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
|
||||
}
|
||||
|
||||
/* Forget current conntrack (unconfirmed) and attach notrack entry */
|
||||
static inline void ip_vs_notrack(struct sk_buff *skb)
|
||||
{
|
||||
|
||||
@@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect,
|
||||
return nf_ct_expect_related_report(expect, 0, 0, flags);
|
||||
}
|
||||
|
||||
int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
|
||||
unsigned int flag);
|
||||
|
||||
struct nf_conn_help;
|
||||
void nf_ct_expectation_gc(struct nf_conn_help *master_help);
|
||||
|
||||
|
||||
@@ -1294,6 +1294,7 @@ static inline void nft_use_inc_restore(u32 *use)
|
||||
* @sets: sets in the table
|
||||
* @objects: stateful objects in the table
|
||||
* @flowtables: flow tables in the table
|
||||
* @objname_ht: hashtable for objects lookup by name
|
||||
* @hgenerator: handle generator state
|
||||
* @handle: table handle
|
||||
* @use: number of chain references to this table
|
||||
@@ -1313,6 +1314,7 @@ struct nft_table {
|
||||
struct list_head sets;
|
||||
struct list_head objects;
|
||||
struct list_head flowtables;
|
||||
struct rhltable objname_ht;
|
||||
u64 hgenerator;
|
||||
u64 handle;
|
||||
u32 use;
|
||||
@@ -1400,7 +1402,7 @@ static inline void *nft_obj_data(const struct nft_object *obj)
|
||||
#define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr))
|
||||
|
||||
struct nft_object *nft_obj_lookup(const struct net *net,
|
||||
const struct nft_table *table,
|
||||
struct nft_table *table,
|
||||
const struct nlattr *nla, u32 objtype,
|
||||
u8 genmask);
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ struct xdp_sock {
|
||||
* call of __xsk_generic_xmit().
|
||||
*/
|
||||
struct sk_buff *skb;
|
||||
bool drain_cont;
|
||||
|
||||
struct list_head map_list;
|
||||
/* Protects map_list */
|
||||
|
||||
@@ -78,6 +78,9 @@ struct xsk_buff_pool {
|
||||
u32 chunk_size;
|
||||
u32 chunk_shift;
|
||||
u32 frame_len;
|
||||
u32 tx_descs_nentries;
|
||||
u32 reclaim_descs;
|
||||
u32 tx_zc_pending_descs;
|
||||
u32 xdp_zc_max_segs;
|
||||
u8 tx_metadata_len; /* inherited from umem */
|
||||
u8 cached_need_wakeup;
|
||||
@@ -102,12 +105,14 @@ struct xsk_buff_pool {
|
||||
|
||||
/* AF_XDP core. */
|
||||
struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
|
||||
struct xdp_umem *umem);
|
||||
struct xdp_umem *umem,
|
||||
u32 max_segs);
|
||||
int xp_assign_dev(struct xsk_buff_pool *pool, struct net_device *dev,
|
||||
u16 queue_id, u16 flags);
|
||||
int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs,
|
||||
struct net_device *dev, u16 queue_id);
|
||||
int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs);
|
||||
int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
|
||||
u32 max_segs);
|
||||
void xp_destroy(struct xsk_buff_pool *pool);
|
||||
void xp_get_pool(struct xsk_buff_pool *pool);
|
||||
bool xp_put_pool(struct xsk_buff_pool *pool);
|
||||
|
||||
@@ -1123,6 +1123,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
|
||||
INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
|
||||
INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
|
||||
|
||||
spin_lock_init(&conn->proto_lock);
|
||||
|
||||
atomic_set(&conn->refcnt, 0);
|
||||
|
||||
hci_dev_hold(hdev);
|
||||
@@ -3163,6 +3165,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
|
||||
return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
|
||||
}
|
||||
|
||||
static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
@@ -3188,7 +3197,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
||||
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
|
||||
* already queue its callback on cmd_sync_work.
|
||||
*/
|
||||
err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
|
||||
err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
|
||||
abort_conn_destroy);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
|
||||
@@ -1233,10 +1233,11 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance,
|
||||
}
|
||||
|
||||
static int
|
||||
hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
|
||||
hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance,
|
||||
const struct hci_cp_le_set_ext_adv_params *cp,
|
||||
struct hci_rp_le_set_ext_adv_params *rp)
|
||||
{
|
||||
struct adv_info *adv;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
|
||||
@@ -1264,11 +1265,15 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
|
||||
|
||||
if (!rp->status) {
|
||||
hdev->adv_addr_type = cp->own_addr_type;
|
||||
if (!cp->handle) {
|
||||
if (!instance) {
|
||||
/* Store in hdev for instance 0 */
|
||||
hdev->adv_tx_power = rp->tx_power;
|
||||
} else if (adv) {
|
||||
adv->tx_power = rp->tx_power;
|
||||
} else {
|
||||
hci_dev_lock(hdev);
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (adv)
|
||||
adv->tx_power = rp->tx_power;
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1284,9 +1289,13 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
int err;
|
||||
|
||||
if (instance) {
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !adv->adv_data_changed)
|
||||
if (!adv || !adv->adv_data_changed) {
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
len = eir_create_adv_data(hdev, instance, pdu->data,
|
||||
@@ -1297,16 +1306,27 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
|
||||
pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
|
||||
|
||||
if (adv) {
|
||||
adv->adv_data_changed = false;
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
|
||||
struct_size(pdu, data, len), pdu,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
return err;
|
||||
if (err) {
|
||||
if (instance) {
|
||||
hci_dev_lock(hdev);
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (adv)
|
||||
adv->adv_data_changed = true;
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
/* Update data if the command succeed */
|
||||
if (adv) {
|
||||
adv->adv_data_changed = false;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
memcpy(hdev->adv_data, pdu->data, len);
|
||||
hdev->adv_data_len = len;
|
||||
}
|
||||
@@ -1360,22 +1380,22 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
struct adv_info *adv;
|
||||
bool secondary_adv;
|
||||
|
||||
if (instance > 0) {
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
adv = NULL;
|
||||
}
|
||||
|
||||
/* Updating parameters of an active instance will return a
|
||||
* Command Disallowed error, so we must first disable the
|
||||
* instance if it is active.
|
||||
* Command Disallowed error, so disable it before taking a snapshot.
|
||||
*/
|
||||
if (adv) {
|
||||
if (instance > 0) {
|
||||
err = hci_disable_ext_adv_instance_sync(hdev, instance);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv) {
|
||||
hci_dev_unlock(hdev);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
adv = NULL;
|
||||
}
|
||||
|
||||
flags = hci_adv_instance_flags(hdev, instance);
|
||||
@@ -1386,8 +1406,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
|
||||
mgmt_get_connectable(hdev);
|
||||
|
||||
if (!is_advertising_allowed(hdev, connectable))
|
||||
if (!is_advertising_allowed(hdev, connectable)) {
|
||||
if (instance)
|
||||
hci_dev_unlock(hdev);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* Set require_privacy to true only when non-connectable
|
||||
* advertising is used and it is not periodic.
|
||||
@@ -1398,8 +1421,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
err = hci_get_random_address(hdev, require_privacy,
|
||||
adv_use_rpa(hdev, flags), adv,
|
||||
&own_addr_type, &random_addr);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
if (instance)
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
@@ -1450,6 +1476,9 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
cp.channel_map = hdev->le_adv_channel_map;
|
||||
cp.handle = adv ? adv->handle : instance;
|
||||
|
||||
if (instance)
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (flags & MGMT_ADV_FLAG_SEC_2M) {
|
||||
cp.primary_phy = HCI_ADV_PHY_1M;
|
||||
cp.secondary_phy = HCI_ADV_PHY_2M;
|
||||
@@ -1462,12 +1491,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
cp.secondary_phy = HCI_ADV_PHY_1M;
|
||||
}
|
||||
|
||||
err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
|
||||
err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Update adv data as tx power is known now */
|
||||
err = hci_set_ext_adv_data_sync(hdev, cp.handle);
|
||||
err = hci_set_ext_adv_data_sync(hdev, instance);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1475,9 +1504,14 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
|
||||
own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) &&
|
||||
bacmp(&random_addr, BDADDR_ANY)) {
|
||||
/* Check if random address need to be updated */
|
||||
if (adv) {
|
||||
if (!bacmp(&random_addr, &adv->random_addr))
|
||||
if (instance) {
|
||||
hci_dev_lock(hdev);
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !bacmp(&random_addr, &adv->random_addr)) {
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
hci_dev_unlock(hdev);
|
||||
} else {
|
||||
if (!bacmp(&random_addr, &hdev->random_addr))
|
||||
return 0;
|
||||
@@ -1499,9 +1533,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
int err;
|
||||
|
||||
if (instance) {
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !adv->scan_rsp_changed)
|
||||
if (!adv || !adv->scan_rsp_changed) {
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
len = eir_create_scan_rsp(hdev, instance, pdu->data);
|
||||
@@ -1511,15 +1549,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
|
||||
pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
|
||||
|
||||
if (adv) {
|
||||
adv->scan_rsp_changed = false;
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
|
||||
struct_size(pdu, data, len), pdu,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
return err;
|
||||
if (err) {
|
||||
if (instance) {
|
||||
hci_dev_lock(hdev);
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (adv)
|
||||
adv->scan_rsp_changed = true;
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
if (adv) {
|
||||
adv->scan_rsp_changed = false;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
memcpy(hdev->scan_rsp_data, pdu->data, len);
|
||||
hdev->scan_rsp_data_len = len;
|
||||
}
|
||||
@@ -1534,8 +1584,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (instance)
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
len = eir_create_scan_rsp(hdev, instance, cp.data);
|
||||
|
||||
if (instance)
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (hdev->scan_rsp_data_len == len &&
|
||||
!memcmp(cp.data, hdev->scan_rsp_data, len))
|
||||
return 0;
|
||||
@@ -1670,9 +1726,13 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
struct adv_info *adv = NULL;
|
||||
|
||||
if (instance) {
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !adv->periodic)
|
||||
if (!adv || !adv->periodic) {
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
len = eir_create_per_adv_data(hdev, instance, pdu->data);
|
||||
@@ -1681,6 +1741,9 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
pdu->handle = adv ? adv->handle : instance;
|
||||
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
|
||||
|
||||
if (adv)
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
|
||||
struct_size(pdu, data, len), pdu,
|
||||
HCI_CMD_TIMEOUT);
|
||||
@@ -6523,7 +6586,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp);
|
||||
err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -6678,11 +6741,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
/* Hold a reference so conn stays valid for the HCI_CONN_CREATE
|
||||
* clear_bit() at done.
|
||||
*/
|
||||
hci_conn_get(conn);
|
||||
|
||||
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||
conn->state = BT_CONNECT;
|
||||
|
||||
@@ -6694,8 +6752,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
|
||||
hdev->le_scan_type == LE_SCAN_ACTIVE &&
|
||||
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
|
||||
hci_conn_del(conn);
|
||||
hci_conn_put(conn);
|
||||
conn->state = BT_OPEN;
|
||||
hci_abort_conn_sync(hdev, conn,
|
||||
HCI_ERROR_REJ_LIMITED_RESOURCES);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@@ -6793,7 +6852,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
/* Re-enable advertising after the connection attempt is finished. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
hci_conn_put(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -7068,11 +7126,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
else
|
||||
cp.role_switch = 0x00;
|
||||
|
||||
/* Hold a reference so conn stays valid for the HCI_CONN_CREATE
|
||||
* clear_bit() below.
|
||||
*/
|
||||
hci_conn_get(conn);
|
||||
|
||||
/* Mark create connection in flight so hci_cancel_connect_sync() can
|
||||
* cancel it while blocking on the connection complete event.
|
||||
*/
|
||||
@@ -7084,17 +7137,27 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
conn->conn_timeout, NULL);
|
||||
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
|
||||
int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
|
||||
hci_conn_get(conn),
|
||||
hci_acl_create_conn_sync_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -7105,36 +7168,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
goto done;
|
||||
goto unlock;
|
||||
|
||||
if (!err) {
|
||||
hci_connect_le_scan_cleanup(conn, 0x00);
|
||||
goto done;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Check if connection is still pending */
|
||||
if (conn != hci_lookup_le_connect(hdev))
|
||||
goto done;
|
||||
goto unlock;
|
||||
|
||||
/* Flush to make sure we send create conn cancel command if needed */
|
||||
flush_delayed_work(&conn->le_conn_timeout);
|
||||
hci_conn_failed(conn, bt_status(err));
|
||||
|
||||
done:
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
|
||||
hci_conn_get(conn),
|
||||
create_le_conn_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -7257,7 +7325,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@@ -7281,6 +7349,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
@@ -7431,8 +7501,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync,
|
||||
hci_conn_get(conn),
|
||||
create_pa_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -7443,10 +7516,12 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
if (hci_conn_valid(hdev, conn))
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
|
||||
|
||||
done:
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
|
||||
@@ -7498,8 +7573,14 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync,
|
||||
hci_conn_get(conn),
|
||||
create_big_complete);
|
||||
if (err)
|
||||
hci_conn_put(conn);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -7514,6 +7595,8 @@ static void past_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
hci_conn_put(past->conn);
|
||||
hci_conn_put(past->le);
|
||||
kfree(past);
|
||||
}
|
||||
|
||||
@@ -7578,8 +7661,8 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->conn = conn;
|
||||
data->le = le;
|
||||
data->conn = hci_conn_get(conn);
|
||||
data->le = hci_conn_get(le);
|
||||
|
||||
if (conn->role == HCI_ROLE_MASTER)
|
||||
err = hci_cmd_sync_queue_once(conn->hdev,
|
||||
@@ -7589,8 +7672,11 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
||||
err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
|
||||
data, past_complete);
|
||||
|
||||
if (err)
|
||||
if (err) {
|
||||
hci_conn_put(data->conn);
|
||||
hci_conn_put(data->le);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -543,9 +543,10 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
|
||||
param == session->waiting_report_type) {
|
||||
param == session->waiting_report_type) {
|
||||
if (session->waiting_report_number < 0 ||
|
||||
session->waiting_report_number == skb->data[0]) {
|
||||
(skb->len &&
|
||||
session->waiting_report_number == skb->data[0])) {
|
||||
/* hidp_get_raw_report() is waiting on this report. */
|
||||
session->report_return = skb;
|
||||
done_with_skb = 0;
|
||||
@@ -560,16 +561,18 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
||||
static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned char hdr, type, param;
|
||||
unsigned char type, param;
|
||||
u8 *hdr;
|
||||
int free_skb = 1;
|
||||
|
||||
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
|
||||
|
||||
hdr = skb->data[0];
|
||||
skb_pull(skb, 1);
|
||||
hdr = skb_pull_data(skb, 1);
|
||||
if (!hdr)
|
||||
goto free;
|
||||
|
||||
type = hdr & HIDP_HEADER_TRANS_MASK;
|
||||
param = hdr & HIDP_HEADER_PARAM_MASK;
|
||||
type = *hdr & HIDP_HEADER_TRANS_MASK;
|
||||
param = *hdr & HIDP_HEADER_PARAM_MASK;
|
||||
|
||||
switch (type) {
|
||||
case HIDP_TRANS_HANDSHAKE:
|
||||
@@ -590,6 +593,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
||||
break;
|
||||
}
|
||||
|
||||
free:
|
||||
if (free_skb)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
@@ -597,14 +601,15 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
||||
static void hidp_recv_intr_frame(struct hidp_session *session,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned char hdr;
|
||||
u8 *hdr;
|
||||
|
||||
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
|
||||
|
||||
hdr = skb->data[0];
|
||||
skb_pull(skb, 1);
|
||||
hdr = skb_pull_data(skb, 1);
|
||||
if (!hdr)
|
||||
goto free;
|
||||
|
||||
if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
|
||||
if (*hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
|
||||
hidp_set_timer(session);
|
||||
|
||||
if (session->input)
|
||||
@@ -616,9 +621,10 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
|
||||
BT_DBG("report len %d", skb->len);
|
||||
}
|
||||
} else {
|
||||
BT_DBG("Unsupported protocol header 0x%02x", hdr);
|
||||
BT_DBG("Unsupported protocol header 0x%02x", *hdr);
|
||||
}
|
||||
|
||||
free:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,15 +24,19 @@ static struct bt_sock_list iso_sk_list = {
|
||||
};
|
||||
|
||||
/* ---- ISO connections ---- */
|
||||
enum {
|
||||
ISO_CONN_DROPPED,
|
||||
__ISO_CONN_NUM_FLAGS
|
||||
};
|
||||
|
||||
struct iso_conn {
|
||||
struct hci_conn *hcon;
|
||||
DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);
|
||||
|
||||
/* @lock: spinlock protecting changes to iso_conn fields */
|
||||
spinlock_t lock;
|
||||
struct sock *sk;
|
||||
|
||||
struct delayed_work timeout_work;
|
||||
|
||||
struct sk_buff *rx_skb;
|
||||
__u32 rx_len;
|
||||
__u16 tx_sn;
|
||||
@@ -56,6 +60,7 @@ static void iso_sock_kill(struct sock *sk);
|
||||
enum {
|
||||
BT_SK_BIG_SYNC,
|
||||
BT_SK_PA_SYNC,
|
||||
BT_SK_KILLED,
|
||||
};
|
||||
|
||||
struct iso_pinfo {
|
||||
@@ -74,6 +79,7 @@ struct iso_pinfo {
|
||||
__u8 base_len;
|
||||
__u8 base[BASE_MAX_LENGTH];
|
||||
struct iso_conn *conn;
|
||||
struct delayed_work timeout_work;
|
||||
};
|
||||
|
||||
static struct bt_iso_qos default_qos;
|
||||
@@ -102,16 +108,18 @@ static void iso_conn_free(struct kref *ref)
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (conn->sk)
|
||||
iso_pi(conn->sk)->conn = NULL;
|
||||
|
||||
if (conn->hcon) {
|
||||
conn->hcon->iso_data = NULL;
|
||||
hci_conn_drop(conn->hcon);
|
||||
}
|
||||
spin_lock(&conn->hcon->proto_lock);
|
||||
|
||||
/* Ensure no more work items will run since hci_conn has been dropped */
|
||||
disable_delayed_work_sync(&conn->timeout_work);
|
||||
/* Check we are not racing with iso_conn_add */
|
||||
if (conn->hcon->iso_data == conn) {
|
||||
conn->hcon->iso_data = NULL;
|
||||
if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
|
||||
hci_conn_drop(conn->hcon);
|
||||
}
|
||||
|
||||
spin_unlock(&conn->hcon->proto_lock);
|
||||
}
|
||||
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
@@ -125,7 +133,21 @@ static void iso_conn_put(struct iso_conn *conn)
|
||||
|
||||
BT_DBG("conn %p refcnt %d", conn, kref_read(&conn->ref));
|
||||
|
||||
/* The following race vs. iso_conn_del() is possible:
|
||||
*
|
||||
* 1. conn->hcon != NULL here
|
||||
* 2. kref_put puts the last reference
|
||||
* 3. concurrent iso_conn_del() gets iso_conn_hold_unless_zero() -> NULL
|
||||
* and returns immediately, so conn->hcon is not cleared
|
||||
* 4. iso_conn_free() dereferences conn->hcon
|
||||
*
|
||||
* To avoid UAF in step 4, take RCU before decrementing the refcount.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
|
||||
kref_put(&conn->ref, iso_conn_free);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
|
||||
@@ -141,6 +163,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
|
||||
return conn;
|
||||
}
|
||||
|
||||
static struct iso_conn *iso_conn_hold(struct iso_conn *conn)
|
||||
{
|
||||
BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref));
|
||||
|
||||
kref_get(&conn->ref);
|
||||
return conn;
|
||||
}
|
||||
|
||||
static struct sock *iso_sock_hold(struct iso_conn *conn)
|
||||
{
|
||||
if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk))
|
||||
@@ -153,78 +183,81 @@ static struct sock *iso_sock_hold(struct iso_conn *conn)
|
||||
|
||||
static void iso_sock_timeout(struct work_struct *work)
|
||||
{
|
||||
struct iso_conn *conn = container_of(work, struct iso_conn,
|
||||
timeout_work.work);
|
||||
struct sock *sk;
|
||||
|
||||
conn = iso_conn_hold_unless_zero(conn);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
iso_conn_lock(conn);
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
iso_conn_put(conn);
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
struct iso_pinfo *pi = container_of(work, struct iso_pinfo,
|
||||
timeout_work.work);
|
||||
struct sock *sk = &pi->bt.sk;
|
||||
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
|
||||
lock_sock(sk);
|
||||
sk->sk_err = ETIMEDOUT;
|
||||
sk->sk_state_change(sk);
|
||||
if (!sock_flag(sk, SOCK_ZAPPED)) {
|
||||
sk->sk_err = ETIMEDOUT;
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static void iso_sock_set_timer(struct sock *sk, long timeout)
|
||||
{
|
||||
lockdep_assert(lockdep_sock_is_held(sk));
|
||||
|
||||
cancel_delayed_work(&iso_pi(sk)->timeout_work);
|
||||
|
||||
if (!iso_pi(sk)->conn)
|
||||
return;
|
||||
|
||||
BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
|
||||
cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
|
||||
schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout);
|
||||
schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout);
|
||||
}
|
||||
|
||||
static void iso_sock_clear_timer(struct sock *sk)
|
||||
{
|
||||
if (!iso_pi(sk)->conn)
|
||||
return;
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
cancel_delayed_work(&iso_pi(sk)->timeout_work);
|
||||
}
|
||||
|
||||
static void iso_sock_disable_timer(struct sock *sk)
|
||||
{
|
||||
lockdep_assert(!lockdep_sock_is_held(sk));
|
||||
|
||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||
cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
|
||||
disable_delayed_work_sync(&iso_pi(sk)->timeout_work);
|
||||
}
|
||||
|
||||
/* ---- ISO connections ---- */
|
||||
static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct iso_conn *conn = hcon->iso_data;
|
||||
struct iso_conn *conn;
|
||||
|
||||
conn = iso_conn_hold_unless_zero(conn);
|
||||
spin_lock(&hcon->proto_lock);
|
||||
|
||||
conn = iso_conn_hold_unless_zero(hcon->iso_data);
|
||||
if (conn) {
|
||||
if (!conn->hcon) {
|
||||
iso_conn_lock(conn);
|
||||
conn->hcon = hcon;
|
||||
iso_conn_unlock(conn);
|
||||
}
|
||||
iso_conn_put(conn);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
return conn;
|
||||
}
|
||||
|
||||
conn = kzalloc_obj(*conn);
|
||||
if (!conn)
|
||||
conn = kzalloc_obj(*conn, GFP_ATOMIC);
|
||||
if (!conn) {
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kref_init(&conn->ref);
|
||||
spin_lock_init(&conn->lock);
|
||||
INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout);
|
||||
|
||||
hcon->iso_data = conn;
|
||||
conn->hcon = hcon;
|
||||
conn->tx_sn = 0;
|
||||
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
BT_DBG("hcon %p conn %p", hcon, conn);
|
||||
|
||||
return conn;
|
||||
@@ -263,11 +296,14 @@ static void iso_chan_del(struct sock *sk, int err)
|
||||
}
|
||||
|
||||
static void iso_conn_del(struct hci_conn *hcon, int err)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
struct iso_conn *conn = hcon->iso_data;
|
||||
struct iso_conn *conn;
|
||||
struct sock *sk;
|
||||
|
||||
conn = iso_conn_hold_unless_zero(conn);
|
||||
spin_lock(&hcon->proto_lock);
|
||||
conn = iso_conn_hold_unless_zero(hcon->iso_data);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
@@ -277,18 +313,28 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
|
||||
iso_conn_lock(conn);
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
iso_conn_put(conn);
|
||||
|
||||
if (!sk) {
|
||||
iso_conn_put(conn);
|
||||
return;
|
||||
}
|
||||
if (!sk)
|
||||
goto done;
|
||||
|
||||
iso_sock_disable_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
iso_chan_del(sk, err);
|
||||
release_sock(sk);
|
||||
iso_sock_kill(sk);
|
||||
sock_put(sk);
|
||||
|
||||
done:
|
||||
/* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
|
||||
spin_lock(&hcon->proto_lock);
|
||||
iso_conn_lock(conn);
|
||||
conn->hcon = NULL;
|
||||
hcon->iso_data = NULL;
|
||||
iso_conn_unlock(conn);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
iso_conn_put(conn);
|
||||
}
|
||||
|
||||
static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
|
||||
@@ -304,8 +350,14 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
iso_pi(sk)->conn = conn;
|
||||
if (!conn->hcon) {
|
||||
BT_ERR("conn->hcon missing");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
iso_pi(sk)->conn = iso_conn_hold(conn);
|
||||
conn->sk = sk;
|
||||
clear_bit(ISO_CONN_DROPPED, conn->flags);
|
||||
|
||||
if (parent)
|
||||
bt_accept_enqueue(parent, sk, true);
|
||||
@@ -402,6 +454,8 @@ static int iso_connect_bis(struct sock *sk)
|
||||
iso_pi(sk)->bc_sid = hcon->sid;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
@@ -410,6 +464,7 @@ static int iso_connect_bis(struct sock *sk)
|
||||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
iso_conn_put(conn);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
@@ -504,6 +559,8 @@ static int iso_connect_cis(struct sock *sk)
|
||||
}
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
@@ -512,6 +569,7 @@ static int iso_connect_cis(struct sock *sk)
|
||||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
iso_conn_put(conn);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
@@ -790,9 +848,15 @@ static void iso_sock_cleanup_listen(struct sock *parent)
|
||||
*/
|
||||
static void iso_sock_kill(struct sock *sk)
|
||||
{
|
||||
iso_sock_disable_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
|
||||
sock_flag(sk, SOCK_DEAD))
|
||||
test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) {
|
||||
release_sock(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
|
||||
@@ -806,6 +870,9 @@ static void iso_sock_kill(struct sock *sk)
|
||||
/* Kill poor orphan */
|
||||
bt_sock_unlink(&iso_sk_list, sk);
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
set_bit(BT_SK_KILLED, &iso_pi(sk)->flags);
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
@@ -825,8 +892,8 @@ static void iso_sock_disconn(struct sock *sk)
|
||||
*/
|
||||
if (bis_sk) {
|
||||
hcon->state = BT_OPEN;
|
||||
hcon->iso_data = NULL;
|
||||
iso_pi(sk)->conn->hcon = NULL;
|
||||
set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags);
|
||||
|
||||
iso_sock_clear_timer(sk);
|
||||
iso_chan_del(sk, bt_to_errno(hcon->abort_reason));
|
||||
sock_put(bis_sk);
|
||||
@@ -835,10 +902,8 @@ static void iso_sock_disconn(struct sock *sk)
|
||||
}
|
||||
|
||||
sk->sk_state = BT_DISCONN;
|
||||
iso_conn_lock(iso_pi(sk)->conn);
|
||||
hci_conn_drop(iso_pi(sk)->conn->hcon);
|
||||
iso_pi(sk)->conn->hcon = NULL;
|
||||
iso_conn_unlock(iso_pi(sk)->conn);
|
||||
if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags))
|
||||
hci_conn_drop(iso_pi(sk)->conn->hcon);
|
||||
}
|
||||
|
||||
static void __iso_sock_close(struct sock *sk)
|
||||
@@ -880,11 +945,11 @@ static void __iso_sock_close(struct sock *sk)
|
||||
/* Must be called on unlocked socket. */
|
||||
static void iso_sock_close(struct sock *sk)
|
||||
{
|
||||
iso_sock_disable_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
__iso_sock_close(sk);
|
||||
release_sock(sk);
|
||||
iso_sock_kill(sk);
|
||||
}
|
||||
|
||||
static void iso_sock_init(struct sock *sk, struct sock *parent)
|
||||
@@ -951,6 +1016,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
|
||||
iso_pi(sk)->qos = default_qos;
|
||||
iso_pi(sk)->sync_handle = -1;
|
||||
|
||||
INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout);
|
||||
|
||||
bt_sock_link(&iso_sk_list, sk);
|
||||
return sk;
|
||||
}
|
||||
@@ -1033,15 +1100,15 @@ static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
|
||||
goto done;
|
||||
}
|
||||
|
||||
iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
|
||||
|
||||
for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++)
|
||||
for (int i = 0; i < sa->iso_bc->bc_num_bis; i++)
|
||||
if (sa->iso_bc->bc_bis[i] < 0x01 ||
|
||||
sa->iso_bc->bc_bis[i] > 0x1f) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
|
||||
|
||||
memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
|
||||
iso_pi(sk)->bc_num_bis);
|
||||
|
||||
@@ -1276,6 +1343,8 @@ static int iso_listen_bis(struct sock *sk)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&hcon->hdev->lock);
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
@@ -1284,10 +1353,9 @@ static int iso_listen_bis(struct sock *sk)
|
||||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
if (err) {
|
||||
hci_conn_drop(hcon);
|
||||
iso_conn_put(conn);
|
||||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
@@ -1466,6 +1534,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
|
||||
BT_DBG("sock %p, sk %p", sock, sk);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
addr->sa_family = AF_BLUETOOTH;
|
||||
|
||||
if (peer) {
|
||||
@@ -1487,6 +1557,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
sa->iso_bdaddr_type = iso_pi(sk)->src_type;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -1786,7 +1858,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
|
||||
return false;
|
||||
|
||||
if (!qos->bcast.timeout)
|
||||
qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
|
||||
qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
|
||||
|
||||
if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
|
||||
return false;
|
||||
@@ -2028,8 +2100,16 @@ static int iso_sock_release(struct socket *sock)
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
/* Make sure sk is valid even if iso_conn_del() is concurrent */
|
||||
sock_hold(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
sock_orphan(sk);
|
||||
release_sock(sk);
|
||||
|
||||
iso_sock_kill(sk);
|
||||
|
||||
sock_put(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2037,14 +2117,17 @@ static void iso_sock_ready(struct sock *sk)
|
||||
{
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
lockdep_assert(lockdep_sock_is_held(sk));
|
||||
|
||||
switch (sk->sk_state) {
|
||||
case BT_DISCONN:
|
||||
case BT_CLOSED:
|
||||
return;
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
iso_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
sk->sk_state_change(sk);
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
static bool iso_match_big(struct sock *sk, void *data)
|
||||
@@ -2074,7 +2157,7 @@ static bool iso_match_dst(struct sock *sk, void *data)
|
||||
static void iso_conn_ready(struct iso_conn *conn)
|
||||
{
|
||||
struct sock *parent = NULL;
|
||||
struct sock *sk = conn->sk;
|
||||
struct sock *sk;
|
||||
struct hci_ev_le_big_sync_established *ev = NULL;
|
||||
struct hci_ev_le_pa_sync_established *ev2 = NULL;
|
||||
struct hci_ev_le_per_adv_report *ev3 = NULL;
|
||||
@@ -2083,7 +2166,22 @@ static void iso_conn_ready(struct iso_conn *conn)
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
iso_conn_lock(conn);
|
||||
sk = iso_sock_hold(conn);
|
||||
iso_conn_unlock(conn);
|
||||
|
||||
if (sk) {
|
||||
lock_sock(sk);
|
||||
|
||||
/* conn->sk may have become NULL if racing with sk close, but
|
||||
* due to held hdev->lock, it can't become different sk.
|
||||
*/
|
||||
if (!conn->sk) {
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt to update source address in case of BIS Sender if
|
||||
* the advertisement is using a random address.
|
||||
*/
|
||||
@@ -2096,14 +2194,15 @@ static void iso_conn_ready(struct iso_conn *conn)
|
||||
adv = hci_find_adv_instance(bis->hdev,
|
||||
bis->iso_qos.bcast.bis);
|
||||
if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
|
||||
lock_sock(sk);
|
||||
iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
|
||||
bacpy(&iso_pi(sk)->src, &adv->random_addr);
|
||||
release_sock(sk);
|
||||
}
|
||||
}
|
||||
|
||||
iso_sock_ready(conn->sk);
|
||||
iso_sock_ready(sk);
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
} else {
|
||||
hcon = conn->hcon;
|
||||
if (!hcon)
|
||||
@@ -2350,7 +2449,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
hcon = iso_pi(sk)->conn->hcon;
|
||||
hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
|
||||
iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
|
||||
|
||||
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
|
||||
@@ -2390,9 +2489,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
if (!sk)
|
||||
goto done;
|
||||
|
||||
hcon = iso_pi(sk)->conn->hcon;
|
||||
lock_sock(sk);
|
||||
|
||||
hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
|
||||
if (!hcon)
|
||||
goto done;
|
||||
goto release3;
|
||||
|
||||
if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
|
||||
/* The controller was unable to retrieve PA data. */
|
||||
@@ -2400,12 +2501,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
HCI_MAX_PER_AD_TOT_LEN);
|
||||
hcon->le_per_adv_data_len = 0;
|
||||
hcon->le_per_adv_data_offset = 0;
|
||||
goto done;
|
||||
goto release3;
|
||||
}
|
||||
|
||||
if (hcon->le_per_adv_data_offset + ev3->length >
|
||||
HCI_MAX_PER_AD_TOT_LEN)
|
||||
goto done;
|
||||
goto release3;
|
||||
|
||||
memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
|
||||
ev3->data, ev3->length);
|
||||
@@ -2424,18 +2525,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
&base_len);
|
||||
|
||||
if (!base || base_len > BASE_MAX_LENGTH)
|
||||
goto done;
|
||||
goto release3;
|
||||
|
||||
lock_sock(sk);
|
||||
memcpy(iso_pi(sk)->base, base, base_len);
|
||||
iso_pi(sk)->base_len = base_len;
|
||||
release_sock(sk);
|
||||
} else {
|
||||
/* This is a PA data fragment. Keep pa_data_len set to 0
|
||||
* until all data has been reassembled.
|
||||
*/
|
||||
hcon->le_per_adv_data_len = 0;
|
||||
}
|
||||
|
||||
release3:
|
||||
release_sock(sk);
|
||||
} else {
|
||||
sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY,
|
||||
BT_LISTEN, iso_match_dst, BDADDR_ANY);
|
||||
@@ -2454,6 +2556,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
}
|
||||
|
||||
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
|
||||
hcon->type != PA_LINK) {
|
||||
@@ -2465,8 +2568,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
struct hci_link *link, *t;
|
||||
|
||||
list_for_each_entry_safe(link, t, &hcon->link_list,
|
||||
list)
|
||||
list) {
|
||||
lockdep_assert_held(&link->conn->hdev->lock);
|
||||
iso_conn_del(link->conn, bt_to_errno(status));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -2488,14 +2593,17 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
struct iso_conn *conn;
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (conn)
|
||||
if (conn) {
|
||||
iso_conn_ready(conn);
|
||||
iso_conn_put(conn);
|
||||
}
|
||||
} else {
|
||||
iso_conn_del(hcon, bt_to_errno(status));
|
||||
}
|
||||
}
|
||||
|
||||
static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
__must_hold(&hcon->hdev->lock)
|
||||
{
|
||||
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
|
||||
hcon->type != PA_LINK)
|
||||
@@ -2522,7 +2630,10 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
spin_lock(&hcon->proto_lock);
|
||||
conn = iso_conn_hold_unless_zero(hcon->iso_data);
|
||||
spin_unlock(&hcon->proto_lock);
|
||||
|
||||
hcon = NULL;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
@@ -4820,6 +4820,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
chan = l2cap_chan_hold_unless_zero(chan);
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
err = 0;
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
@@ -4865,6 +4869,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2696,18 +2696,28 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
|
||||
static bool pending_eir_or_class(struct hci_dev *hdev)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
bool pending = false;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
|
||||
switch (cmd->opcode) {
|
||||
case MGMT_OP_ADD_UUID:
|
||||
case MGMT_OP_REMOVE_UUID:
|
||||
case MGMT_OP_SET_DEV_CLASS:
|
||||
case MGMT_OP_SET_LOCAL_NAME:
|
||||
case MGMT_OP_SET_POWERED:
|
||||
return true;
|
||||
pending = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pending)
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static const u8 bluetooth_base_uuid[] = {
|
||||
@@ -3514,11 +3524,13 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
|
||||
static struct mgmt_pending_cmd *remove_pairing(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
|
||||
if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
|
||||
continue;
|
||||
@@ -3526,9 +3538,39 @@ static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
|
||||
if (cmd->user_data != conn)
|
||||
continue;
|
||||
|
||||
list_del(&cmd->list);
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mgmt_pending_cmd *remove_pairing_by_addr(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_conn *conn;
|
||||
|
||||
mutex_lock(&hdev->mgmt_pending_lock);
|
||||
|
||||
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
|
||||
if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
|
||||
continue;
|
||||
|
||||
conn = cmd->user_data;
|
||||
if (bacmp(bdaddr, &conn->dst) != 0)
|
||||
continue;
|
||||
|
||||
list_del(&cmd->list);
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
mutex_unlock(&hdev->mgmt_pending_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -3566,10 +3608,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete)
|
||||
u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
if (cmd) {
|
||||
cmd->cmd_complete(cmd, status);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3579,14 +3621,14 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
|
||||
|
||||
BT_DBG("status %u", status);
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
if (!cmd) {
|
||||
BT_DBG("Unable to find a pending command");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->cmd_complete(cmd, mgmt_status(status));
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
|
||||
@@ -3598,14 +3640,14 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
|
||||
if (!status)
|
||||
return;
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
if (!cmd) {
|
||||
BT_DBG("Unable to find a pending command");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->cmd_complete(cmd, mgmt_status(status));
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
@@ -3762,23 +3804,17 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev);
|
||||
cmd = remove_pairing_by_addr(hdev, &addr->bdaddr);
|
||||
if (!cmd) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
conn = cmd->user_data;
|
||||
|
||||
if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
conn = hci_conn_get(cmd->user_data);
|
||||
|
||||
cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
|
||||
addr, sizeof(*addr));
|
||||
@@ -3796,6 +3832,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
if (conn->conn_reason == CONN_REASON_PAIR_DEVICE)
|
||||
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
|
||||
hci_conn_put(conn);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
@@ -4043,6 +4081,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
|
||||
|
||||
if (!hdev_is_powered(hdev)) {
|
||||
@@ -10137,14 +10181,14 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status)
|
||||
ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
|
||||
ev.status = status;
|
||||
|
||||
cmd = find_pairing(conn);
|
||||
cmd = remove_pairing(conn);
|
||||
|
||||
mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev),
|
||||
cmd ? cmd->sk : NULL);
|
||||
|
||||
if (cmd) {
|
||||
cmd->cmd_complete(cmd, status);
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1795,6 +1795,11 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
|
||||
return s;
|
||||
}
|
||||
|
||||
if (skb->len < sizeof(*hdr) + 1) {
|
||||
kfree_skb(skb);
|
||||
return s;
|
||||
}
|
||||
|
||||
dlci = __get_dlci(hdr->addr);
|
||||
type = __get_type(hdr->ctrl);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user