mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Including fixes from netfilter, Bluetooth and batman-adv.
Current release - regressions:
- bluetooth: fix using chan->conn as indication to no remote netdev
Current release - new code bugs:
- netfilter: cap to maximum number of expectation per master on updates
Previous releases - regressions:
- bluetooth:
- fix UAF of hci_conn_params in add_device_complete
- fix null ptr deref in hci_abort_conn()
- igmp: remove multicast group from hash table on device destruction
- batman-adv: prevent TVLV OOB check overflow
- eth: mlx5/mlx5e:
- fix off-by-one in single-FDB error rollback
- skip peer flow cleanup when LAG seq is unavailable
- fix crashes in dynamic per-channel stats and HV VHCA agent
- eth: mana: Sync page pool RX frags for CPU
Previous releases - always broken:
- netfilter:
- mark malformed IPv6 extension headers for hotdrop
- terminate table name before find_table_lock()
- ipvs: use parsed transport offset in TCP state lookup
- sched: act_pedit: fix TOCTOU heap OOB write in tc offload
- ethtool: rss: fix hfunc and input_xfrm parsing on big endian
- ipv4/ipv6: fix UAF and memory leak in IGMP/MLD
- tls: consume empty data records in tls_sw_read_sock()
- eth: octeontx2-af: fix VF bringup affecting PF promiscuous state
- eth: gue: validate REMCSUM private option length
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-----BEGIN PGP SIGNATURE-----
iQJGBAABCgAwFiEEg1AjqC77wbdLX2LbKSR5jcyPE6QFAmpPpEkSHHBhYmVuaUBy
ZWRoYXQuY29tAAoJECkkeY3MjxOkMrkP/3BqkhllssjF+u4Nf7Z44Tkg0p2h4l5Y
Bk97Ug1tMdMtexkO7TBz4nTLl26sfrQka4xpHcPdjWzDpYccwb4asxaXCn6oVk+9
lhfSVsw1UGDUflmYLHb8xMss/8fp6rS/DkpLVxThs9U4FPys23QDlrsJFQqbziGT
EJ4LhxcpjGZwUY0rBZ4DQxt3T1zUbCnihPOByts3SNLh43a6Km8Moqhpfq2+N3jP
fKD48tVOPv9DrCCxGXP5m0CbmcBLe4MyAAdeVOP3D7fHKH9g9B6EPQT/oG1puXnr
8ZtpkRtxLVtZHu6hEmjK8FgXLe/BQlkCZgWOt18/USoBRAvmJlhL265vzPEKgWWT
ThTnBNao67McJeL6He1WyO4Tzyd4sNYRVNo+jfe397w1VGpYxzBQdwGnw5PP+Mdw
g6s3zxdrDpCIlDhwtsxY1cMwCmHrcMdFMxbcxvAvY2sTJ7gop+0WajhKDN/XFBqS
elP6YFsQ1HF7YUzFMxuX3X0KnXGGHsz9zH8WA7uJR/dh0Ew/zoozQ5PfAI/lbGGn
5MIhGC5lRQeoYuUyY3Sk4xHCMewN5OUsIMu+u1fYkzstsL265vcFCSGdRBu0FH0/
7xoq9xHwzAT+Zc5LUOugtAIePRRw7LVvvt4GF5HjfCYJ2mHvGQzGe7M2xVMnopt0
vx3t7AqglbgC
=cxid
-----END PGP SIGNATURE-----
Merge tag 'net-7.2-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Paolo Abeni:
"Including fixes from netfilter, Bluetooth and batman-adv.
Current release - regressions:
- bluetooth: fix using chan->conn as indication to no remote netdev
Current release - new code bugs:
- netfilter: cap to maximum number of expectation per master on
updates
Previous releases - regressions:
- bluetooth:
- fix UAF of hci_conn_params in add_device_complete
- fix null ptr deref in hci_abort_conn()
- igmp: remove multicast group from hash table on device destruction
- batman-adv: prevent TVLV OOB check overflow
- eth: mlx5/mlx5e:
- fix off-by-one in single-FDB error rollback
- skip peer flow cleanup when LAG seq is unavailable
- fix crashes in dynamic per-channel stats and HV VHCA agent
- eth: mana: Sync page pool RX frags for CPU
Previous releases - always broken:
- netfilter:
- mark malformed IPv6 extension headers for hotdrop
- terminate table name before find_table_lock()
- ipvs: use parsed transport offset in TCP state lookup
- sched: act_pedit: fix TOCTOU heap OOB write in tc offload
- ethtool: rss: fix hfunc and input_xfrm parsing on big endian
- ipv4/ipv6: fix UAF and memory leak in IGMP/MLD
- tls: consume empty data records in tls_sw_read_sock()
- eth:
- octeontx2-af: fix VF bringup affecting PF promiscuous state
- gue: validate REMCSUM private option length"
* tag 'net-7.2-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (94 commits)
macsec: don't read an unset MAC header in macsec_encrypt()
dibs: loopback: validate offset and size in move_data()
octeontx2-af: fix VF bringup affecting PF promiscuous state
ethtool: rss: Fix hfunc and input_xfrm parsing on big endian
net/mlx5: Fix L3 tunnel entropy refcount leak
net: macb: drop in-flight Tx SKBs on close
net: mana: Sync page pool RX frags for CPU
net: mana: Validate the packet length reported by the NIC
selftests/net: fix EVP_MD_CTX leak in tcp_mmap
ipvs: ensure inner headers in ICMP errors are in headroom
ipvs: use parsed transport offset in SCTP state lookup
ipvs: use parsed transport offset in TCP state lookup
ipvs: pass parsed transport offset to state handlers
netfilter: handle unreadable frags
netfilter: flowtable: support IPIP tunnel with direct xmit
netfilter: flowtable: IPIP tunnel hardware offload is not yet support
netfilter: flowtable: use dst in this direction when pushing IPIP header
netfilter: ipset: allocate the proper memory for the generic hash structure
netfilter: ipset: cleanup the add/del backlog when resize failed
netfilter: ipset: exclude gc when resize is in progress
...
This commit is contained in:
commit
2c7c88a412
|
|
@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev)
|
|||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
|
||||
/* Bounded print: the device controls skb->len. */
|
||||
if (skb->len > 1) {
|
||||
int len = skb->len - 1;
|
||||
|
||||
hci_set_fw_info(hdev, "%s", skb->data + 1);
|
||||
bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1));
|
||||
hci_set_fw_info(hdev, "%.*s", len, skb->data + 1);
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -2127,6 +2127,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
|
|||
if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
|
||||
return -ENODEV;
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
|
||||
return -ENODEV;
|
||||
|
||||
/* Due to the fw limitation, the type header of the packet should be
|
||||
* 4 bytes unlike 1 byte for UART. In UART, the firmware can read
|
||||
* the first byte to get the packet type and redirect the rest of data
|
||||
|
|
@ -2485,7 +2488,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
|
||||
static void btintel_pcie_reset(struct hci_dev *hdev);
|
||||
|
||||
static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
|
||||
|
|
@ -2596,12 +2598,45 @@ static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue a Function Level Reset and hand teardown/re-init off to the PCI
|
||||
* core via device_reprobe(), mirroring the PLDR path's contract.
|
||||
*
|
||||
* Caller must hold pci_lock_rescan_remove() and must have already
|
||||
* disabled interrupts and drained both rx_work and coredump_work.
|
||||
*/
|
||||
static int btintel_pcie_perform_flr(struct btintel_pcie_data *data)
|
||||
{
|
||||
struct pci_dev *pdev = data->pdev;
|
||||
int err;
|
||||
|
||||
/* pci_try_reset_function() avoids the device_lock ABBA against
|
||||
* btintel_pcie_remove(): .remove() runs with device_lock held and
|
||||
* then waits for this work via disable_work_sync(); the blocking
|
||||
* pci_reset_function() would deadlock by trying to re-acquire
|
||||
* device_lock here.
|
||||
*/
|
||||
err = pci_try_reset_function(pdev);
|
||||
if (err) {
|
||||
BT_ERR("Failed resetting the pcie device (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* device_reprobe() always detaches the driver first (running
|
||||
* .remove(), which frees 'data'); any re-probe failure leaves the
|
||||
* device unbound but 'data' is already gone, so just log it.
|
||||
*/
|
||||
if (device_reprobe(&pdev->dev))
|
||||
BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btintel_pcie_reset_work(struct work_struct *wk)
|
||||
{
|
||||
struct btintel_pcie_data *data =
|
||||
container_of(wk, struct btintel_pcie_data, reset_work);
|
||||
struct pci_dev *pdev = data->pdev;
|
||||
int err;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
|
||||
|
|
@ -2621,60 +2656,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
|
|||
disable_work_sync(&data->coredump_work);
|
||||
|
||||
bt_dev_dbg(data->hdev, "Release bluetooth interface");
|
||||
|
||||
/* Both reset paths follow the same contract: on success they
|
||||
* destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs
|
||||
* the coredump_work with disable count 0), so enable_work() must
|
||||
* NOT be called on the success path. Only the FLR path can fail
|
||||
* with 'data' still alive, in which case we balance the
|
||||
* disable_work_sync() above so a later successful reset is not
|
||||
* permanently blocked.
|
||||
*
|
||||
* pci_lock_rescan_remove() (held above) serializes against PCI
|
||||
* device addition/removal (hotplug), so no device can be added to
|
||||
* or removed from the bus list while this code runs.
|
||||
*/
|
||||
if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {
|
||||
/* This function holds pci_lock_rescan_remove(), which acquires
|
||||
* pci_rescan_remove_lock. This mutex serializes against PCI device
|
||||
* addition/removal (hotplug), so no device can be added to or
|
||||
* removed from the bus list while this code runs.
|
||||
*
|
||||
* device_reprobe() inside btintel_pcie_perform_pldr() destroys
|
||||
* 'data' via .remove(); a fresh probe re-INIT_WORKs the
|
||||
* coredump_work with disable count 0, so we must not call
|
||||
* enable_work() on this path.
|
||||
*/
|
||||
btintel_pcie_perform_pldr(data);
|
||||
goto out;
|
||||
}
|
||||
btintel_pcie_release_hdev(data);
|
||||
|
||||
/* Use pci_try_reset_function() rather than pci_reset_function() to
|
||||
* avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core
|
||||
* calls .remove() with device_lock held, and remove() then waits for
|
||||
* this work via cancel_work_sync(); pci_reset_function() would in
|
||||
* turn try to acquire the same device_lock, deadlocking both paths.
|
||||
*/
|
||||
err = pci_try_reset_function(pdev);
|
||||
if (err) {
|
||||
BT_ERR("Failed resetting the pcie device (%d)", err);
|
||||
goto out_enable;
|
||||
}
|
||||
if (btintel_pcie_perform_flr(data))
|
||||
enable_work(&data->coredump_work);
|
||||
|
||||
btintel_pcie_enable_interrupts(data);
|
||||
btintel_pcie_config_msix(data);
|
||||
|
||||
err = btintel_pcie_enable_bt(data);
|
||||
if (err) {
|
||||
BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
|
||||
err);
|
||||
goto out_enable;
|
||||
}
|
||||
|
||||
btintel_pcie_reset_ia(data);
|
||||
btintel_pcie_start_rx(data);
|
||||
data->flags = 0;
|
||||
|
||||
err = btintel_pcie_setup_hdev(data);
|
||||
if (err) {
|
||||
BT_ERR("Failed registering hdev (%d)", err);
|
||||
goto out_enable;
|
||||
}
|
||||
|
||||
out_enable:
|
||||
/* Balance disable_work_sync() above on every exit. Leaving the
|
||||
* counter incremented on a failed reset would permanently disable
|
||||
* coredump_work even after a later successful reset.
|
||||
*/
|
||||
enable_work(&data->coredump_work);
|
||||
out:
|
||||
pci_dev_put(pdev);
|
||||
pci_unlock_rescan_remove();
|
||||
|
|
|
|||
|
|
@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
|
||||
if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size ||
|
||||
len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) {
|
||||
bt_dev_err(hdev, "FW download out of bounds, ignoring request");
|
||||
len = 0;
|
||||
goto free_skb;
|
||||
}
|
||||
serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
|
||||
nxpdev->fw_dnld_v3_offset, len);
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev)
|
|||
|
||||
BT_DBG("hdev %p tty %p", hdev, tty);
|
||||
|
||||
disable_work_sync(&hu->write_work);
|
||||
|
||||
if (hu->tx_skb) {
|
||||
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
|
||||
}
|
||||
|
|
@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev)
|
|||
|
||||
percpu_up_read(&hu->proto_lock);
|
||||
|
||||
/* Resume TX. Also reschedule in case work was queued concurrently;
|
||||
* this may schedule write_work although there's nothing to do.
|
||||
*/
|
||||
enable_work(&hu->write_work);
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev *hdev)
|
|||
/* Close device */
|
||||
static int hci_uart_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
|
||||
BT_DBG("hdev %p", hdev);
|
||||
|
||||
cancel_work_sync(&hu->write_work);
|
||||
|
||||
hci_uart_flush(hdev);
|
||||
hdev->flush = NULL;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok,
|
|||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((u64)offset + size > rmb_node->len) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy((char *)rmb_node->cpu_addr + offset, data, size);
|
||||
sba_idx = rmb_node->sba_idx;
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
|
|
|||
|
|
@ -3301,9 +3301,9 @@ static size_t amt_get_size(const struct net_device *dev)
|
|||
nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */
|
||||
nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */
|
||||
nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */
|
||||
nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */
|
||||
nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */
|
||||
nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */
|
||||
nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */
|
||||
nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */
|
||||
nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */
|
||||
}
|
||||
|
||||
static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
|
|
|
|||
|
|
@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp)
|
|||
dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma);
|
||||
|
||||
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
|
||||
kfree(queue->tx_skb);
|
||||
queue->tx_skb = NULL;
|
||||
if (queue->tx_skb) {
|
||||
unsigned int dropped = 0, tail;
|
||||
|
||||
for (tail = queue->tx_tail; tail != queue->tx_head;
|
||||
tail++) {
|
||||
if (macb_tx_skb(queue, tail)->skb)
|
||||
dropped++;
|
||||
macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
|
||||
}
|
||||
|
||||
queue->stats.tx_dropped += dropped;
|
||||
bp->dev->stats.tx_dropped += dropped;
|
||||
|
||||
kfree(queue->tx_skb);
|
||||
queue->tx_skb = NULL;
|
||||
}
|
||||
|
||||
queue->tx_head = 0;
|
||||
queue->tx_tail = 0;
|
||||
queue->tx_ring = NULL;
|
||||
queue->rx_ring = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3779,9 +3779,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
|
|||
static int octeon_enable_sriov(struct octeon_device *oct)
|
||||
{
|
||||
unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced;
|
||||
struct pci_dev *vfdev;
|
||||
int err;
|
||||
u32 u;
|
||||
|
||||
if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) {
|
||||
err = pci_enable_sriov(oct->pci_dev,
|
||||
|
|
@ -3794,23 +3792,6 @@ static int octeon_enable_sriov(struct octeon_device *oct)
|
|||
return err;
|
||||
}
|
||||
oct->sriov_info.sriov_enabled = 1;
|
||||
|
||||
/* init lookup table that maps DPI ring number to VF pci_dev
|
||||
* struct pointer
|
||||
*/
|
||||
u = 0;
|
||||
vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
|
||||
OCTEON_CN23XX_VF_VID, NULL);
|
||||
while (vfdev) {
|
||||
if (vfdev->is_virtfn &&
|
||||
(vfdev->physfn == oct->pci_dev)) {
|
||||
oct->sriov_info.dpiring_to_vfpcidev_lut[u] =
|
||||
vfdev;
|
||||
u += oct->sriov_info.rings_per_vf;
|
||||
}
|
||||
vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
|
||||
OCTEON_CN23XX_VF_VID, vfdev);
|
||||
}
|
||||
}
|
||||
|
||||
return num_vfs_alloced;
|
||||
|
|
@ -3818,8 +3799,6 @@ static int octeon_enable_sriov(struct octeon_device *oct)
|
|||
|
||||
static int lio_pci_sriov_disable(struct octeon_device *oct)
|
||||
{
|
||||
int u;
|
||||
|
||||
if (pci_vfs_assigned(oct->pci_dev)) {
|
||||
dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n");
|
||||
return -EPERM;
|
||||
|
|
@ -3827,12 +3806,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct)
|
|||
|
||||
pci_disable_sriov(oct->pci_dev);
|
||||
|
||||
u = 0;
|
||||
while (u < MAX_POSSIBLE_VFS) {
|
||||
oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL;
|
||||
u += oct->sriov_info.rings_per_vf;
|
||||
}
|
||||
|
||||
oct->sriov_info.num_vfs_alloced = 0;
|
||||
dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n",
|
||||
oct->pf_num);
|
||||
|
|
|
|||
|
|
@ -390,9 +390,6 @@ struct octeon_sriov_info {
|
|||
|
||||
struct lio_trusted_vf trusted_vf;
|
||||
|
||||
/*lookup table that maps DPI ring number to VF pci_dev struct pointer*/
|
||||
struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS];
|
||||
|
||||
u64 vf_macaddr[MAX_POSSIBLE_VFS];
|
||||
|
||||
u16 vf_vlantci[MAX_POSSIBLE_VFS];
|
||||
|
|
|
|||
|
|
@ -26,6 +26,31 @@
|
|||
#include "octeon_mailbox.h"
|
||||
#include "cn23xx_pf_device.h"
|
||||
|
||||
static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no)
|
||||
{
|
||||
struct pci_dev *vfdev = NULL;
|
||||
int vfidx;
|
||||
|
||||
if (!oct->sriov_info.rings_per_vf)
|
||||
return NULL;
|
||||
|
||||
if (q_no % oct->sriov_info.rings_per_vf)
|
||||
return NULL;
|
||||
|
||||
vfidx = q_no / oct->sriov_info.rings_per_vf;
|
||||
if (vfidx >= oct->sriov_info.num_vfs_alloced)
|
||||
return NULL;
|
||||
|
||||
while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
|
||||
OCTEON_CN23XX_VF_VID, vfdev))) {
|
||||
if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev &&
|
||||
pci_iov_vf_id(vfdev) == vfidx)
|
||||
return vfdev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_mbox_read:
|
||||
* @mbox: Pointer mailbox
|
||||
|
|
@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
|
|||
struct octeon_mbox_cmd *mbox_cmd)
|
||||
{
|
||||
struct octeon_device *oct = mbox->oct_dev;
|
||||
struct pci_dev *vfdev;
|
||||
|
||||
switch (mbox_cmd->msg.s.cmd) {
|
||||
case OCTEON_VF_ACTIVE:
|
||||
|
|
@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
|
|||
dev_info(&oct->pci_dev->dev,
|
||||
"got a request for FLR from VF that owns DPI ring %u\n",
|
||||
mbox->q_no);
|
||||
pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]);
|
||||
vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no);
|
||||
if (!vfdev)
|
||||
break;
|
||||
|
||||
pcie_flr(vfdev);
|
||||
pci_dev_put(vfdev);
|
||||
break;
|
||||
|
||||
case OCTEON_PF_CHANGED_VF_MACADDR:
|
||||
|
|
|
|||
|
|
@ -4580,7 +4580,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
|
|||
rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf,
|
||||
pfvf->rx_chan_base);
|
||||
} else {
|
||||
if (!nix_rx_multicast)
|
||||
if (!nix_rx_multicast && !is_vf(pcifunc))
|
||||
rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false);
|
||||
}
|
||||
|
||||
|
|
@ -4590,7 +4590,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
|
|||
pfvf->rx_chan_base,
|
||||
pfvf->rx_chan_cnt);
|
||||
else
|
||||
if (!nix_rx_multicast)
|
||||
if (!nix_rx_multicast && !is_vf(pcifunc))
|
||||
rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1568,15 +1568,15 @@ static void otx2_free_sq_res(struct otx2_nic *pf)
|
|||
otx2_sq_free_sqbs(pf);
|
||||
for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) {
|
||||
sq = &qset->sq[qidx];
|
||||
/* Skip freeing Qos queues if they are not initialized */
|
||||
if (!sq->sqe)
|
||||
continue;
|
||||
qmem_free(pf->dev, sq->sqe);
|
||||
qmem_free(pf->dev, sq->sqe_ring);
|
||||
qmem_free(pf->dev, sq->cpt_resp);
|
||||
qmem_free(pf->dev, sq->tso_hdrs);
|
||||
qmem_free(pf->dev, sq->timestamps);
|
||||
kfree(sq->sg);
|
||||
/* sq->sqe is not initialized for unused QoS queues */
|
||||
if (sq->sqe) {
|
||||
qmem_free(pf->dev, sq->sqe);
|
||||
qmem_free(pf->dev, sq->sqe_ring);
|
||||
qmem_free(pf->dev, sq->cpt_resp);
|
||||
qmem_free(pf->dev, sq->tso_hdrs);
|
||||
qmem_free(pf->dev, sq->timestamps);
|
||||
kfree(sq->sg);
|
||||
}
|
||||
kfree(sq->sqb_ptrs);
|
||||
}
|
||||
}
|
||||
|
|
@ -1711,13 +1711,12 @@ int otx2_init_hw_resources(struct otx2_nic *pf)
|
|||
return err;
|
||||
|
||||
err_free_nix_queues:
|
||||
otx2_free_sq_res(pf);
|
||||
otx2_free_cq_res(pf);
|
||||
otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false);
|
||||
err_free_txsch:
|
||||
otx2_txschq_stop(pf);
|
||||
err_free_sq_ptrs:
|
||||
otx2_sq_free_sqbs(pf);
|
||||
otx2_free_sq_res(pf);
|
||||
err_free_rq_ptrs:
|
||||
otx2_free_aura_ptr(pf, AURA_NIX_RQ);
|
||||
otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true);
|
||||
|
|
@ -2517,10 +2516,42 @@ EXPORT_SYMBOL(otx2_config_hwtstamp_set);
|
|||
|
||||
static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac)
|
||||
{
|
||||
struct npc_get_field_status_req *freq;
|
||||
struct npc_get_field_status_rsp *frsp;
|
||||
struct npc_install_flow_req *req;
|
||||
int err;
|
||||
|
||||
mutex_lock(&pf->mbox.lock);
|
||||
|
||||
/* Skip installing the DMAC filter if the hardware parser profile
|
||||
* does not support DMAC extraction.
|
||||
*/
|
||||
freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox);
|
||||
if (!freq) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
freq->field = NPC_DMAC;
|
||||
err = otx2_sync_mbox_msg(&pf->mbox);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp
|
||||
(&pf->mbox.mbox, 0, &freq->hdr);
|
||||
if (IS_ERR(frsp)) {
|
||||
err = PTR_ERR(frsp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!frsp->enable) {
|
||||
netdev_warn(pf->netdev,
|
||||
"VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n",
|
||||
vf);
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox);
|
||||
if (!req) {
|
||||
err = -ENOMEM;
|
||||
|
|
@ -2559,13 +2590,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
|
|||
if (!is_valid_ether_addr(mac))
|
||||
return -EINVAL;
|
||||
|
||||
config = &pf->vf_configs[vf];
|
||||
ether_addr_copy(config->mac, mac);
|
||||
|
||||
ret = otx2_do_set_vf_mac(pf, vf, mac);
|
||||
if (ret == 0)
|
||||
dev_info(&pdev->dev,
|
||||
"Load/Reload VF driver\n");
|
||||
if (ret == 0) {
|
||||
config = &pf->vf_configs[vf];
|
||||
ether_addr_copy(config->mac, mac);
|
||||
dev_info(&pdev->dev, "Load/Reload VF driver\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -987,6 +987,18 @@ struct mlx5e_priv {
|
|||
struct ethtool_fec_hist_range *fec_ranges;
|
||||
};
|
||||
|
||||
static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
|
||||
{
|
||||
/* Pairs with smp_store_release in mlx5e_stats_nch_write(). */
|
||||
return smp_load_acquire(&priv->stats_nch);
|
||||
}
|
||||
|
||||
static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n)
|
||||
{
|
||||
/* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */
|
||||
smp_store_release(&priv->stats_nch, n);
|
||||
}
|
||||
|
||||
struct mlx5e_dev {
|
||||
struct net_device *netdev;
|
||||
struct devlink_port dl_port;
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
|
|||
static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
|
||||
int buf_len)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
int ch, i = 0;
|
||||
|
||||
for (ch = 0; ch < priv->stats_nch; ch++) {
|
||||
for (ch = 0; ch < nch; ch++) {
|
||||
void *buf = data + i;
|
||||
|
||||
if (WARN_ON_ONCE(buf +
|
||||
|
|
@ -49,9 +50,16 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
|
|||
}
|
||||
|
||||
static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
|
||||
return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch;
|
||||
}
|
||||
|
||||
static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv)
|
||||
{
|
||||
return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
|
||||
priv->stats_nch);
|
||||
max(priv->max_nch, priv->stats_nch));
|
||||
}
|
||||
|
||||
static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
|
||||
|
|
@ -67,7 +75,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
|
|||
sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
|
||||
priv = container_of(sagent, struct mlx5e_priv, stats_agent);
|
||||
buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
|
||||
agent = sagent->agent;
|
||||
agent = READ_ONCE(sagent->agent);
|
||||
buf = sagent->buf;
|
||||
|
||||
memset(buf, 0, buf_len);
|
||||
|
|
@ -100,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
|
|||
sagent = &priv->stats_agent;
|
||||
|
||||
block->version = MLX5_HV_VHCA_STATS_VERSION;
|
||||
block->rings = priv->stats_nch;
|
||||
block->rings = mlx5e_stats_nch_read(priv);
|
||||
|
||||
if (!block->command) {
|
||||
cancel_delayed_work_sync(&priv->stats_agent.work);
|
||||
|
|
@ -122,18 +130,21 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
|
|||
|
||||
void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
|
||||
{
|
||||
int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
|
||||
int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv);
|
||||
struct mlx5_hv_vhca_agent *agent;
|
||||
|
||||
priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
|
||||
if (!priv->stats_agent.buf)
|
||||
return;
|
||||
|
||||
INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
|
||||
|
||||
agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
|
||||
MLX5_HV_VHCA_AGENT_STATS,
|
||||
mlx5e_hv_vhca_stats_control, NULL,
|
||||
mlx5e_hv_vhca_stats_cleanup,
|
||||
priv);
|
||||
priv,
|
||||
&priv->stats_agent.agent);
|
||||
|
||||
if (IS_ERR_OR_NULL(agent)) {
|
||||
if (IS_ERR(agent))
|
||||
|
|
@ -142,18 +153,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
|
|||
agent);
|
||||
|
||||
kvfree(priv->stats_agent.buf);
|
||||
return;
|
||||
priv->stats_agent.buf = NULL;
|
||||
}
|
||||
|
||||
priv->stats_agent.agent = agent;
|
||||
INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
|
||||
}
|
||||
|
||||
void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(priv->stats_agent.agent))
|
||||
struct mlx5_hv_vhca_agent *agent;
|
||||
|
||||
agent = READ_ONCE(priv->stats_agent.agent);
|
||||
if (IS_ERR_OR_NULL(agent))
|
||||
return;
|
||||
|
||||
mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
|
||||
mlx5_hv_vhca_agent_destroy(agent);
|
||||
WRITE_ONCE(priv->stats_agent.agent, NULL);
|
||||
kvfree(priv->stats_agent.buf);
|
||||
priv->stats_agent.buf = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu)
|
|||
GFP_KERNEL, cpu_to_node(cpu));
|
||||
if (!priv->channel_stats[ix])
|
||||
return -ENOMEM;
|
||||
priv->stats_nch++;
|
||||
mlx5e_stats_nch_write(priv, priv->stats_nch + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4040,9 +4040,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
|||
|
||||
void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
|
||||
struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq;
|
||||
struct mlx5e_rq_stats *rq_stats = &channel_stats->rq;
|
||||
|
|
@ -5488,7 +5489,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i,
|
|||
struct mlx5e_rq_stats *xskrq_stats;
|
||||
struct mlx5e_rq_stats *rq_stats;
|
||||
|
||||
if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch)
|
||||
if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv))
|
||||
return;
|
||||
|
||||
channel_stats = priv->channel_stats[i];
|
||||
|
|
@ -5512,7 +5513,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i,
|
|||
struct mlx5e_priv *priv = netdev_priv(dev);
|
||||
struct mlx5e_sq_stats *sq_stats;
|
||||
|
||||
if (!priv->stats_nch)
|
||||
if (!mlx5e_stats_nch_read(priv))
|
||||
return;
|
||||
|
||||
/* no special case needed for ptp htb etc since txq2sq_stats is kept up
|
||||
|
|
@ -5538,6 +5539,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
|
|||
struct netdev_queue_stats_tx *tx)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(dev);
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
struct mlx5e_ptp *ptp_channel;
|
||||
int i, tc;
|
||||
|
||||
|
|
@ -5549,7 +5551,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
|
|||
rx->hw_gro_wire_packets = 0;
|
||||
rx->hw_gro_wire_bytes = 0;
|
||||
|
||||
for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) {
|
||||
for (i = priv->channels.params.num_channels; i < nch; i++) {
|
||||
struct netdev_queue_stats_rx rx_i = {0};
|
||||
|
||||
mlx5e_get_queue_stats_rx(dev, i, &rx_i);
|
||||
|
|
@ -5585,7 +5587,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
|
|||
tx->stop = 0;
|
||||
tx->wake = 0;
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
|
||||
|
||||
/* handle two cases:
|
||||
|
|
|
|||
|
|
@ -515,6 +515,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c)
|
|||
static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
|
||||
{
|
||||
struct mlx5e_sw_stats *s = &priv->stats.sw;
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
int i;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
|
|
@ -522,7 +523,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
|
|||
for (i = 0; i < priv->channels.num; i++) /* for active channels only */
|
||||
mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]);
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats =
|
||||
priv->channel_stats[i];
|
||||
|
||||
|
|
@ -2614,7 +2615,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; }
|
|||
|
||||
static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
|
||||
{
|
||||
int max_nch = priv->stats_nch;
|
||||
int max_nch = mlx5e_stats_nch_read(priv);
|
||||
|
||||
return (NUM_RQ_STATS * max_nch) +
|
||||
(NUM_CH_STATS * max_nch) +
|
||||
|
|
@ -2627,8 +2628,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
|
|||
|
||||
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
|
||||
{
|
||||
int max_nch = mlx5e_stats_nch_read(priv);
|
||||
bool is_xsk = priv->xsk.ever_used;
|
||||
int max_nch = priv->stats_nch;
|
||||
int i, j, tc;
|
||||
|
||||
for (i = 0; i < max_nch; i++)
|
||||
|
|
@ -2660,8 +2661,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
|
|||
|
||||
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels)
|
||||
{
|
||||
int max_nch = mlx5e_stats_nch_read(priv);
|
||||
bool is_xsk = priv->xsk.ever_used;
|
||||
int max_nch = priv->stats_nch;
|
||||
int i, j, tc;
|
||||
|
||||
for (i = 0; i < max_nch; i++)
|
||||
|
|
|
|||
|
|
@ -5547,6 +5547,9 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
|
|||
|
||||
mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
|
||||
i = mlx5_lag_get_dev_seq(peer_esw->dev);
|
||||
if (i < 0)
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i])
|
||||
mlx5e_tc_del_fdb_peers_flow(flow);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv)
|
|||
|
||||
static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
struct rtnl_link_stats64 s = {};
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats;
|
||||
struct mlx5e_rq_stats *rq_stats;
|
||||
|
||||
|
|
|
|||
|
|
@ -194,8 +194,10 @@ static void mlx5_mpesw_work(struct work_struct *work)
|
|||
struct mlx5_lag *ldev = mpesww->lag;
|
||||
|
||||
devcom = mlx5_lag_get_devcom_comp(ldev);
|
||||
if (!devcom)
|
||||
return;
|
||||
if (!devcom) {
|
||||
mpesww->result = -ENODEV;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
mlx5_devcom_comp_lock(devcom);
|
||||
mlx5_mpesw_sd_devcoms_lock(ldev);
|
||||
|
|
@ -213,6 +215,7 @@ static void mlx5_mpesw_work(struct work_struct *work)
|
|||
mutex_unlock(&ldev->lock);
|
||||
mlx5_mpesw_sd_devcoms_unlock(ldev);
|
||||
mlx5_devcom_comp_unlock(devcom);
|
||||
complete:
|
||||
complete(&mpesww->comp);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
|
|||
}
|
||||
return 0;
|
||||
err:
|
||||
mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) {
|
||||
mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) {
|
||||
struct mlx5_eswitch *slave_esw;
|
||||
|
||||
if (j == master_idx)
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)
|
|||
return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL,
|
||||
NULL,
|
||||
mlx5_hv_vhca_control_agent_invalidate,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
|
||||
|
|
@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
void (*invalidate)(struct mlx5_hv_vhca_agent*,
|
||||
u64 block_mask),
|
||||
void (*cleaup)(struct mlx5_hv_vhca_agent *agent),
|
||||
void *priv)
|
||||
void *priv,
|
||||
struct mlx5_hv_vhca_agent **ctx_update)
|
||||
{
|
||||
struct mlx5_hv_vhca_agent *agent;
|
||||
|
||||
|
|
@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
agent->invalidate = invalidate;
|
||||
agent->cleanup = cleaup;
|
||||
|
||||
if (ctx_update)
|
||||
WRITE_ONCE(*ctx_update, agent);
|
||||
|
||||
mutex_lock(&hv_vhca->agents_lock);
|
||||
hv_vhca->agents[type] = agent;
|
||||
mutex_unlock(&hv_vhca->agents_lock);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
void (*invalidate)(struct mlx5_hv_vhca_agent*,
|
||||
u64 block_mask),
|
||||
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
|
||||
void *context);
|
||||
void *context,
|
||||
struct mlx5_hv_vhca_agent **ctx_update);
|
||||
|
||||
void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent);
|
||||
int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
|
||||
|
|
@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
void (*invalidate)(struct mlx5_hv_vhca_agent*,
|
||||
u64 block_mask),
|
||||
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
|
||||
void *context)
|
||||
void *context,
|
||||
struct mlx5_hv_vhca_agent **ctx_update)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy,
|
|||
int reformat_type)
|
||||
{
|
||||
mutex_lock(&tun_entropy->lock);
|
||||
if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN)
|
||||
if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN ||
|
||||
reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL)
|
||||
tun_entropy->num_enabling_entries--;
|
||||
else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE &&
|
||||
--tun_entropy->num_disabling_entries == 0)
|
||||
|
|
|
|||
|
|
@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin)
|
|||
kfree(admin->cache.keystream);
|
||||
kfree(admin->cache.maskstream);
|
||||
kfree(admin->cache.actionstream);
|
||||
mutex_destroy(&admin->lock);
|
||||
kfree(admin);
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl,
|
|||
if (!admin)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&admin->lock);
|
||||
admin->vctrl = ctrl;
|
||||
INIT_LIST_HEAD(&admin->list);
|
||||
INIT_LIST_HEAD(&admin->rules);
|
||||
INIT_LIST_HEAD(&admin->enabled);
|
||||
|
|
@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x)
|
|||
ctrl->ops = &lan966x_vcap_ops;
|
||||
|
||||
INIT_LIST_HEAD(&ctrl->list);
|
||||
mutex_init(&ctrl->lock);
|
||||
for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) {
|
||||
cfg = &lan966x_vcap_inst_cfg[i];
|
||||
|
||||
|
|
@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x)
|
|||
lan966x_vcap_admin_free(admin);
|
||||
}
|
||||
|
||||
mutex_destroy(&ctrl->lock);
|
||||
kfree(ctrl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1930,7 +1930,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin)
|
|||
{
|
||||
if (!admin)
|
||||
return;
|
||||
mutex_destroy(&admin->lock);
|
||||
kfree(admin->cache.keystream);
|
||||
kfree(admin->cache.maskstream);
|
||||
kfree(admin->cache.actionstream);
|
||||
|
|
@ -1950,7 +1949,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,
|
|||
INIT_LIST_HEAD(&admin->list);
|
||||
INIT_LIST_HEAD(&admin->rules);
|
||||
INIT_LIST_HEAD(&admin->enabled);
|
||||
mutex_init(&admin->lock);
|
||||
admin->vctrl = ctrl;
|
||||
admin->vtype = cfg->vtype;
|
||||
admin->vinst = cfg->vinst;
|
||||
admin->ingress = cfg->ingress;
|
||||
|
|
@ -2059,6 +2058,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5)
|
|||
ctrl->ops = &sparx5_vcap_ops;
|
||||
|
||||
INIT_LIST_HEAD(&ctrl->list);
|
||||
mutex_init(&ctrl->lock);
|
||||
for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) {
|
||||
cfg = &consts->vcaps_cfg[idx];
|
||||
admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg);
|
||||
|
|
@ -2097,5 +2097,6 @@ void sparx5_vcap_deinit(struct sparx5 *sparx5)
|
|||
list_del(&admin->list);
|
||||
sparx5_vcap_admin_free(admin);
|
||||
}
|
||||
mutex_destroy(&ctrl->lock);
|
||||
kfree(ctrl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id)
|
|||
return false;
|
||||
}
|
||||
|
||||
void vcap_lock(struct vcap_admin *admin)
|
||||
{
|
||||
mutex_lock(&admin->vctrl->lock);
|
||||
}
|
||||
|
||||
void vcap_unlock(struct vcap_admin *admin)
|
||||
{
|
||||
mutex_unlock(&admin->vctrl->lock);
|
||||
}
|
||||
|
||||
/* Find a rule with a provided rule id return a locked vcap */
|
||||
static struct vcap_rule_internal *
|
||||
vcap_get_locked_rule(struct vcap_control *vctrl, u32 id)
|
||||
|
|
@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id)
|
|||
|
||||
/* Look for the rule id in all vcaps */
|
||||
list_for_each_entry(admin, &vctrl->list, list) {
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_for_each_entry(ri, &admin->rules, list)
|
||||
if (ri->data.id == id)
|
||||
return ri;
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie)
|
|||
|
||||
/* Look for the rule id in all vcaps */
|
||||
list_for_each_entry(admin, &vctrl->list, list) {
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_for_each_entry(ri, &admin->rules, list) {
|
||||
if (ri->data.cookie == cookie) {
|
||||
id = ri->data.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
if (id)
|
||||
return id;
|
||||
}
|
||||
|
|
@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid)
|
|||
int count = 0;
|
||||
|
||||
list_for_each_entry(elem, &admin->rules, list) {
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
if (elem->data.vcap_chain_id >= min_cid &&
|
||||
elem->data.vcap_chain_id < max_cid)
|
||||
++count;
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule)
|
|||
if (ret)
|
||||
return ret;
|
||||
/* Insert the new rule in the list of vcap rules */
|
||||
mutex_lock(&ri->admin->lock);
|
||||
vcap_lock(ri->admin);
|
||||
|
||||
vcap_rule_set_state(ri);
|
||||
ret = vcap_insert_rule(ri, &move);
|
||||
|
|
@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule)
|
|||
goto out;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ri->admin->lock);
|
||||
vcap_unlock(ri->admin);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vcap_add_rule);
|
||||
|
|
@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
|
|||
if (vctrl->vcaps[admin->vtype].rows == 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
/* Check if a rule with this id already exists */
|
||||
if (vcap_rule_exists(vctrl, id)) {
|
||||
err = -EINVAL;
|
||||
|
|
@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
return (struct vcap_rule *)ri;
|
||||
|
||||
out_free:
|
||||
kfree(ri);
|
||||
out_unlock:
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
return ERR_PTR(err);
|
||||
|
||||
}
|
||||
|
|
@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id)
|
|||
return ERR_PTR(-ENOENT);
|
||||
|
||||
rule = vcap_decode_rule(elem);
|
||||
mutex_unlock(&elem->admin->lock);
|
||||
vcap_unlock(elem->admin);
|
||||
return rule;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vcap_get_rule);
|
||||
|
|
@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule)
|
|||
err = vcap_write_counter(ri, &ctr);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ri->admin->lock);
|
||||
vcap_unlock(ri->admin);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vcap_mod_rule);
|
||||
|
|
@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
|
|||
admin->last_used_addr = elem->addr;
|
||||
}
|
||||
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vcap_del_rule);
|
||||
|
|
@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_for_each_entry_safe(ri, next_ri, &admin->rules, list) {
|
||||
vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size);
|
||||
list_del(&ri->list);
|
||||
|
|
@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
|
|||
list_del(&eport->list);
|
||||
kfree(eport);
|
||||
}
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3016,7 +3026,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
|
|||
continue;
|
||||
|
||||
/* Found the admin, now find the offloadable rules */
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_for_each_entry(ri, &admin->rules, list) {
|
||||
/* Is the rule in the lookup defined by the chain */
|
||||
if (!(ri->data.vcap_chain_id >= chain &&
|
||||
|
|
@ -3034,7 +3044,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
|
|||
if (err)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
|
@ -3074,7 +3084,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl,
|
|||
continue;
|
||||
|
||||
/* Found the admin, now find the rules on the chain */
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_for_each_entry(ri, &admin->rules, list) {
|
||||
if (ri->data.vcap_chain_id != chain)
|
||||
continue;
|
||||
|
|
@ -3089,7 +3099,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl,
|
|||
if (err)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
|
@ -3133,9 +3143,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev,
|
|||
eport->cookie = cookie;
|
||||
eport->src_cid = src_cid;
|
||||
eport->dst_cid = dst_cid;
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_add_tail(&eport->list, &admin->enabled);
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
|
||||
if (vcap_path_exist(vctrl, ndev, src_cid)) {
|
||||
/* Enable chained lookups */
|
||||
|
|
@ -3185,9 +3195,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev,
|
|||
dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid);
|
||||
}
|
||||
|
||||
mutex_lock(&found->lock);
|
||||
vcap_lock(found);
|
||||
list_del(&eport->list);
|
||||
mutex_unlock(&found->lock);
|
||||
vcap_unlock(found);
|
||||
kfree(eport);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3270,9 +3280,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&ri->admin->lock);
|
||||
vcap_lock(ri->admin);
|
||||
err = vcap_write_counter(ri, ctr);
|
||||
mutex_unlock(&ri->admin->lock);
|
||||
vcap_unlock(ri->admin);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -3291,9 +3301,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&ri->admin->lock);
|
||||
vcap_lock(ri->admin);
|
||||
err = vcap_read_counter(ri, ctr);
|
||||
mutex_unlock(&ri->admin->lock);
|
||||
vcap_unlock(ri->admin);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -3395,7 +3405,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl,
|
|||
|
||||
/* Iterate all rules in each VCAP instance */
|
||||
list_for_each_entry(admin, &vctrl->list, list) {
|
||||
mutex_lock(&admin->lock);
|
||||
vcap_lock(admin);
|
||||
list_for_each_entry(ri, &admin->rules, list) {
|
||||
if (ri->data.cookie != cookie)
|
||||
continue;
|
||||
|
|
@ -3412,12 +3422,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl,
|
|||
if (err)
|
||||
goto unlock;
|
||||
}
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
}
|
||||
return err;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&admin->lock);
|
||||
vcap_unlock(admin);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie);
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ struct vcap_admin {
|
|||
struct list_head list; /* for insertion in vcap_control */
|
||||
struct list_head rules; /* list of rules */
|
||||
struct list_head enabled; /* list of enabled ports */
|
||||
struct mutex lock; /* control access to rules */
|
||||
struct vcap_control *vctrl; /* the control instance owning this vcap */
|
||||
enum vcap_type vtype; /* type of vcap */
|
||||
int vinst; /* instance number within the same type */
|
||||
int first_cid; /* first chain id in this vcap */
|
||||
|
|
@ -275,6 +275,7 @@ struct vcap_control {
|
|||
const struct vcap_info *vcaps; /* client supplied vcap models */
|
||||
const struct vcap_statistics *stats; /* client supplied vcap stats */
|
||||
struct list_head list; /* list of vcap instances */
|
||||
struct mutex lock; /* serialize access to all vcap instances */
|
||||
};
|
||||
|
||||
#endif /* __VCAP_API__ */
|
||||
|
|
|
|||
|
|
@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused)
|
|||
};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->admin->lock);
|
||||
vcap_lock(info->admin);
|
||||
ret = vcap_show_admin(info->vctrl, info->admin, &out);
|
||||
mutex_unlock(&info->admin->lock);
|
||||
vcap_unlock(info->admin);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(vcap_debugfs);
|
||||
|
|
@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused)
|
|||
};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->admin->lock);
|
||||
vcap_lock(info->admin);
|
||||
ret = vcap_show_admin_raw(info->vctrl, info->admin, &out);
|
||||
mutex_unlock(&info->admin->lock);
|
||||
vcap_unlock(info->admin);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs);
|
||||
|
|
|
|||
|
|
@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin)
|
|||
{
|
||||
/* Initialize the shared objects */
|
||||
INIT_LIST_HEAD(&test_vctrl.list);
|
||||
mutex_init(&test_vctrl.lock);
|
||||
INIT_LIST_HEAD(&admin->list);
|
||||
INIT_LIST_HEAD(&admin->rules);
|
||||
INIT_LIST_HEAD(&admin->enabled);
|
||||
mutex_init(&admin->lock);
|
||||
admin->vctrl = &test_vctrl;
|
||||
list_add_tail(&admin->list, &test_vctrl.list);
|
||||
memset(test_updateaddr, 0, sizeof(test_updateaddr));
|
||||
test_updateaddridx = 0;
|
||||
|
|
|
|||
|
|
@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin)
|
|||
{
|
||||
/* Initialize the shared objects */
|
||||
INIT_LIST_HEAD(&test_vctrl.list);
|
||||
mutex_init(&test_vctrl.lock);
|
||||
INIT_LIST_HEAD(&admin->list);
|
||||
INIT_LIST_HEAD(&admin->rules);
|
||||
INIT_LIST_HEAD(&admin->enabled);
|
||||
mutex_init(&admin->lock);
|
||||
admin->vctrl = &test_vctrl;
|
||||
list_add_tail(&admin->list, &test_vctrl.list);
|
||||
memset(test_updateaddr, 0, sizeof(test_updateaddr));
|
||||
test_updateaddridx = 0;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ struct vcap_stream_iter {
|
|||
|
||||
/* Check that the control has a valid set of callbacks */
|
||||
int vcap_api_check(struct vcap_control *ctrl);
|
||||
/* Serialize access to the vcap instances of a control */
|
||||
void vcap_lock(struct vcap_admin *admin);
|
||||
void vcap_unlock(struct vcap_admin *admin);
|
||||
/* Erase the VCAP cache area used or encoding and decoding */
|
||||
void vcap_erase_cache(struct vcap_rule_internal *ri);
|
||||
|
||||
|
|
|
|||
|
|
@ -2120,12 +2120,16 @@ static void mana_rx_skb(void *buf_va, bool from_pool,
|
|||
}
|
||||
|
||||
static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev,
|
||||
dma_addr_t *da, bool *from_pool)
|
||||
dma_addr_t *da, bool *from_pool,
|
||||
struct page **pp_page, u32 *dma_sync_offset)
|
||||
{
|
||||
struct page *page;
|
||||
u32 offset;
|
||||
void *va;
|
||||
|
||||
*from_pool = false;
|
||||
*pp_page = NULL;
|
||||
*dma_sync_offset = 0;
|
||||
|
||||
/* Don't use fragments for jumbo frames or XDP where it's 1 fragment
|
||||
* per page.
|
||||
|
|
@ -2163,31 +2167,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev,
|
|||
va = page_to_virt(page) + offset;
|
||||
*da = page_pool_get_dma_addr(page) + offset + rxq->headroom;
|
||||
*from_pool = true;
|
||||
*pp_page = page;
|
||||
*dma_sync_offset = offset + rxq->headroom;
|
||||
|
||||
return va;
|
||||
}
|
||||
|
||||
/* Allocate frag for rx buffer, and save the old buf */
|
||||
static void mana_refill_rx_oob(struct device *dev, struct mana_rxq *rxq,
|
||||
struct mana_recv_buf_oob *rxoob, void **old_buf,
|
||||
bool *old_fp)
|
||||
struct mana_recv_buf_oob *rxoob, u32 pktlen,
|
||||
void **old_buf, bool *old_fp)
|
||||
{
|
||||
struct page *pp_page;
|
||||
u32 dma_sync_offset;
|
||||
bool from_pool;
|
||||
dma_addr_t da;
|
||||
void *va;
|
||||
|
||||
va = mana_get_rxfrag(rxq, dev, &da, &from_pool);
|
||||
va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page,
|
||||
&dma_sync_offset);
|
||||
if (!va)
|
||||
return;
|
||||
if (!rxoob->from_pool || rxq->frag_count == 1)
|
||||
if (!rxoob->from_pool || rxq->frag_count == 1) {
|
||||
dma_unmap_single(dev, rxoob->sgl[0].address, rxq->datasize,
|
||||
DMA_FROM_DEVICE);
|
||||
} else {
|
||||
/* The page pool maps the whole page and only syncs for device
|
||||
* automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes
|
||||
* for the CPU before they are read: this is required if DMA
|
||||
* is incoherent or bounce buffers are used.
|
||||
*/
|
||||
page_pool_dma_sync_for_cpu(rxq->page_pool, rxoob->pp_page,
|
||||
rxoob->dma_sync_offset, pktlen);
|
||||
}
|
||||
*old_buf = rxoob->buf_va;
|
||||
*old_fp = rxoob->from_pool;
|
||||
|
||||
rxoob->buf_va = va;
|
||||
rxoob->sgl[0].address = da;
|
||||
rxoob->from_pool = from_pool;
|
||||
rxoob->pp_page = pp_page;
|
||||
rxoob->dma_sync_offset = dma_sync_offset;
|
||||
}
|
||||
|
||||
static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
|
||||
|
|
@ -2246,12 +2266,26 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
|
|||
rxbuf_oob = &rxq->rx_oobs[curr];
|
||||
WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1);
|
||||
|
||||
mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp);
|
||||
if (unlikely(pktlen > rxq->datasize)) {
|
||||
/* Increase it even if mana_rx_skb() isn't called. */
|
||||
rxq->rx_cq.work_done++;
|
||||
|
||||
/* Unsuccessful refill will have old_buf == NULL.
|
||||
* In this case, mana_rx_skb() will drop the packet.
|
||||
*/
|
||||
mana_rx_skb(old_buf, old_fp, oob, rxq, i);
|
||||
++ndev->stats.rx_dropped;
|
||||
netdev_warn_once(ndev,
|
||||
"Dropped oversized RX packet: len=%u, datasize=%u\n",
|
||||
pktlen, rxq->datasize);
|
||||
|
||||
/* Reuse the RX buffer since rxbuf_oob is unchanged. */
|
||||
} else {
|
||||
|
||||
mana_refill_rx_oob(dev, rxq, rxbuf_oob, pktlen,
|
||||
&old_buf, &old_fp);
|
||||
|
||||
/* Unsuccessful refill will have old_buf == NULL.
|
||||
* In this case, mana_rx_skb() will drop the packet.
|
||||
*/
|
||||
mana_rx_skb(old_buf, old_fp, oob, rxq, i);
|
||||
}
|
||||
|
||||
mana_move_wq_tail(rxq->gdma_rq,
|
||||
rxbuf_oob->wqe_inf.wqe_size_in_bu);
|
||||
|
|
@ -2655,6 +2689,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key,
|
|||
struct mana_rxq *rxq, struct device *dev)
|
||||
{
|
||||
struct mana_port_context *mpc = netdev_priv(rxq->ndev);
|
||||
struct page *pp_page = NULL;
|
||||
u32 dma_sync_offset = 0;
|
||||
bool from_pool = false;
|
||||
dma_addr_t da;
|
||||
void *va;
|
||||
|
|
@ -2662,13 +2698,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key,
|
|||
if (mpc->rxbufs_pre)
|
||||
va = mana_get_rxbuf_pre(rxq, &da);
|
||||
else
|
||||
va = mana_get_rxfrag(rxq, dev, &da, &from_pool);
|
||||
va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page,
|
||||
&dma_sync_offset);
|
||||
|
||||
if (!va)
|
||||
return -ENOMEM;
|
||||
|
||||
rx_oob->buf_va = va;
|
||||
rx_oob->from_pool = from_pool;
|
||||
rx_oob->pp_page = pp_page;
|
||||
rx_oob->dma_sync_offset = dma_sync_offset;
|
||||
|
||||
rx_oob->sgl[0].address = da;
|
||||
rx_oob->sgl[0].size = rxq->datasize;
|
||||
|
|
|
|||
|
|
@ -166,18 +166,23 @@ static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw)
|
|||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
**/
|
||||
static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
|
||||
static int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size)
|
||||
{
|
||||
const int size_in_words = size / sizeof(u32);
|
||||
const int size_in_words = size / sizeof(__le32);
|
||||
struct mucse_mbx_info *mbx = &hw->mbx;
|
||||
int off = MUCSE_MBX_FWPF_SHM;
|
||||
int err;
|
||||
|
||||
err = mucse_obtain_mbx_lock_pf(hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* memcpy_fromio() is unsuitable: the mailbox uses 32-bit MMIO
|
||||
* registers, not byte-addressable RAM. readl() guarantees
|
||||
* the required 32-bit access width.
|
||||
*/
|
||||
for (int i = 0; i < size_in_words; i++)
|
||||
msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i);
|
||||
msg[i] = cpu_to_le32(mbx_data_rd32(mbx, off + 4 * i));
|
||||
/* Hw needs write data_reg at last */
|
||||
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0);
|
||||
/* flush reqs as we have read this request data */
|
||||
|
|
@ -236,7 +241,7 @@ static int mucse_poll_for_msg(struct mucse_hw *hw)
|
|||
* Return: 0 if it successfully received a message notification and
|
||||
* copied it into the receive buffer, negative errno on failure
|
||||
**/
|
||||
int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
|
||||
int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
|
@ -290,9 +295,9 @@ static void mucse_mbx_inc_pf_req(struct mucse_hw *hw)
|
|||
* Return: 0 if it successfully copied message into the buffer,
|
||||
* negative errno on failure
|
||||
**/
|
||||
static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
|
||||
static int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size)
|
||||
{
|
||||
const int size_in_words = size / sizeof(u32);
|
||||
const int size_in_words = size / sizeof(__le32);
|
||||
struct mucse_mbx_info *mbx = &hw->mbx;
|
||||
int err;
|
||||
|
||||
|
|
@ -300,8 +305,12 @@ static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/* memcpy_toio() would decompose into arbitrary-width accesses;
|
||||
* the mailbox requires 32-bit MMIO writes via writel().
|
||||
*/
|
||||
for (int i = 0; i < size_in_words; i++)
|
||||
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]);
|
||||
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4,
|
||||
le32_to_cpu(msg[i]));
|
||||
|
||||
/* flush acks as we are overwriting the message buffer */
|
||||
hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx);
|
||||
|
|
@ -360,7 +369,8 @@ static int mucse_poll_for_ack(struct mucse_hw *hw)
|
|||
* Return: 0 if it successfully copied message into the buffer and
|
||||
* received an ack to that message within delay * timeout_cnt period
|
||||
**/
|
||||
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
|
||||
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, const __le32 *msg,
|
||||
u16 size)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
#define MUCSE_MBX_REQ BIT(0) /* Request a req to mailbox */
|
||||
#define MUCSE_MBX_PFU BIT(3) /* PF owns the mailbox buffer */
|
||||
|
||||
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
|
||||
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw,
|
||||
const __le32 *msg, u16 size);
|
||||
void mucse_init_mbx_params_pf(struct mucse_hw *hw);
|
||||
int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
|
||||
int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size);
|
||||
#endif /* _RNPGBE_MBX_H */
|
||||
|
|
|
|||
|
|
@ -20,32 +20,32 @@
|
|||
* Return: 0 on success, negative errno on failure
|
||||
**/
|
||||
static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,
|
||||
struct mbx_fw_cmd_req *req,
|
||||
struct mbx_fw_cmd_reply *reply)
|
||||
union mbx_fw_cmd_req_u *req,
|
||||
union mbx_fw_cmd_reply_u *reply)
|
||||
{
|
||||
int len = le16_to_cpu(req->datalen);
|
||||
int len = le16_to_cpu(req->r.datalen);
|
||||
int retry_cnt = 3;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->mbx.lock);
|
||||
err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len);
|
||||
err = mucse_write_and_wait_ack_mbx(hw, req->dwords, len);
|
||||
if (err)
|
||||
goto out;
|
||||
do {
|
||||
err = mucse_poll_and_read_mbx(hw, (u32 *)reply,
|
||||
sizeof(*reply));
|
||||
err = mucse_poll_and_read_mbx(hw, reply->dwords,
|
||||
sizeof(reply->r));
|
||||
if (err)
|
||||
goto out;
|
||||
/* mucse_write_and_wait_ack_mbx return 0 means fw has
|
||||
* received request, wait for the expect opcode
|
||||
* reply with 'retry_cnt' times.
|
||||
*/
|
||||
} while (--retry_cnt >= 0 && reply->opcode != req->opcode);
|
||||
} while (--retry_cnt >= 0 && reply->r.opcode != req->r.opcode);
|
||||
out:
|
||||
mutex_unlock(&hw->mbx.lock);
|
||||
if (!err && retry_cnt < 0)
|
||||
return -ETIMEDOUT;
|
||||
if (!err && reply->error_code)
|
||||
if (!err && reply->r.error_code)
|
||||
return -EIO;
|
||||
|
||||
return err;
|
||||
|
|
@ -61,17 +61,19 @@ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,
|
|||
**/
|
||||
static int mucse_mbx_get_info(struct mucse_hw *hw)
|
||||
{
|
||||
struct mbx_fw_cmd_req req = {
|
||||
.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(GET_HW_INFO),
|
||||
union mbx_fw_cmd_req_u req = {
|
||||
.r = {
|
||||
.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(GET_HW_INFO),
|
||||
},
|
||||
};
|
||||
struct mbx_fw_cmd_reply reply = {};
|
||||
union mbx_fw_cmd_reply_u reply = {};
|
||||
int err;
|
||||
|
||||
err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
|
||||
if (!err)
|
||||
hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0),
|
||||
le16_to_cpu(reply.hw_info.pfnum));
|
||||
le16_to_cpu(reply.r.hw_info.pfnum));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -111,21 +113,23 @@ int mucse_mbx_sync_fw(struct mucse_hw *hw)
|
|||
**/
|
||||
int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup)
|
||||
{
|
||||
struct mbx_fw_cmd_req req = {
|
||||
.datalen = cpu_to_le16(sizeof(req.powerup) +
|
||||
MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(POWER_UP),
|
||||
.powerup = {
|
||||
/* fw needs this to reply correct cmd */
|
||||
.version = cpu_to_le32(GENMASK_U32(31, 0)),
|
||||
.status = cpu_to_le32(is_powerup ? 1 : 0),
|
||||
union mbx_fw_cmd_req_u req = {
|
||||
.r = {
|
||||
.datalen = cpu_to_le16(sizeof(req.r.powerup) +
|
||||
MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(POWER_UP),
|
||||
.powerup = {
|
||||
/* fw needs this to reply correct cmd */
|
||||
.version = cpu_to_le32(GENMASK_U32(31, 0)),
|
||||
.status = cpu_to_le32(is_powerup ? 1 : 0),
|
||||
},
|
||||
},
|
||||
};
|
||||
int len, err;
|
||||
|
||||
len = le16_to_cpu(req.datalen);
|
||||
len = le16_to_cpu(req.r.datalen);
|
||||
mutex_lock(&hw->mbx.lock);
|
||||
err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len);
|
||||
err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
|
||||
mutex_unlock(&hw->mbx.lock);
|
||||
|
||||
return err;
|
||||
|
|
@ -142,11 +146,13 @@ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup)
|
|||
**/
|
||||
int mucse_mbx_reset_hw(struct mucse_hw *hw)
|
||||
{
|
||||
struct mbx_fw_cmd_req req = {
|
||||
.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(RESET_HW),
|
||||
union mbx_fw_cmd_req_u req = {
|
||||
.r = {
|
||||
.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(RESET_HW),
|
||||
},
|
||||
};
|
||||
struct mbx_fw_cmd_reply reply = {};
|
||||
union mbx_fw_cmd_reply_u reply = {};
|
||||
|
||||
return mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
|
||||
}
|
||||
|
|
@ -166,24 +172,26 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
|
|||
u8 *mac_addr,
|
||||
int port)
|
||||
{
|
||||
struct mbx_fw_cmd_req req = {
|
||||
.datalen = cpu_to_le16(sizeof(req.get_mac_addr) +
|
||||
MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(GET_MAC_ADDRESS),
|
||||
.get_mac_addr = {
|
||||
.port_mask = cpu_to_le32(BIT(port)),
|
||||
.pfvf_num = cpu_to_le32(pfvfnum),
|
||||
union mbx_fw_cmd_req_u req = {
|
||||
.r = {
|
||||
.datalen = cpu_to_le16(sizeof(req.r.get_mac_addr) +
|
||||
MUCSE_MBX_REQ_HDR_LEN),
|
||||
.opcode = cpu_to_le16(GET_MAC_ADDRESS),
|
||||
.get_mac_addr = {
|
||||
.port_mask = cpu_to_le32(BIT(port)),
|
||||
.pfvf_num = cpu_to_le32(pfvfnum),
|
||||
},
|
||||
},
|
||||
};
|
||||
struct mbx_fw_cmd_reply reply = {};
|
||||
union mbx_fw_cmd_reply_u reply = {};
|
||||
int err;
|
||||
|
||||
err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (le32_to_cpu(reply.mac_addr.ports) & BIT(port))
|
||||
memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN);
|
||||
if (le32_to_cpu(reply.r.mac_addr.ports) & BIT(port))
|
||||
memcpy(mac_addr, reply.r.mac_addr.addrs[port].mac, ETH_ALEN);
|
||||
else
|
||||
return -ENODATA;
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,20 @@ struct mbx_fw_cmd_reply {
|
|||
};
|
||||
} __packed;
|
||||
|
||||
/* Union wrappers to expose struct as __le32 dword array for mailbox
|
||||
* transport, eliminating the need for pointer casts. The __packed
|
||||
* structs have no padding, so dwords[] overlays the fields exactly.
|
||||
*/
|
||||
union mbx_fw_cmd_req_u {
|
||||
struct mbx_fw_cmd_req r;
|
||||
__le32 dwords[sizeof(struct mbx_fw_cmd_req) / sizeof(__le32)];
|
||||
};
|
||||
|
||||
union mbx_fw_cmd_reply_u {
|
||||
struct mbx_fw_cmd_reply r;
|
||||
__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];
|
||||
};
|
||||
|
||||
int mucse_mbx_sync_fw(struct mucse_hw *hw);
|
||||
int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup);
|
||||
int mucse_mbx_reset_hw(struct mucse_hw *hw);
|
||||
|
|
|
|||
|
|
@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev,
|
|||
struct sk_buff *skb;
|
||||
|
||||
skb = qede_build_skb(rxq, bd, len, pad);
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
|
||||
bd->page_offset += rxq->rx_buf_seg_size;
|
||||
|
||||
if (bd->page_offset == PAGE_SIZE) {
|
||||
|
|
@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev,
|
|||
}
|
||||
|
||||
skb = qede_build_skb(rxq, bd, len, pad);
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
|
||||
if (unlikely(qede_realloc_rx_buffer(rxq, bd))) {
|
||||
/* Incr page ref count to reuse on allocation failure so
|
||||
|
|
|
|||
|
|
@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
|
|||
|
||||
consume_skb(skb);
|
||||
} else {
|
||||
__rmnet_map_ingress_handler(skb, port);
|
||||
if (rmnet_map_validate_packet_len(skb, port))
|
||||
__rmnet_map_ingress_handler(skb, port);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
|
|||
void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
|
||||
void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size,
|
||||
u32 count, u32 time);
|
||||
u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port);
|
||||
|
||||
#endif /* _RMNET_MAP_H_ */
|
||||
|
|
|
|||
|
|
@ -333,6 +333,47 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
|
|||
return map_header;
|
||||
}
|
||||
|
||||
u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_v5_csum_header *next_hdr = NULL;
|
||||
struct rmnet_map_header *maph;
|
||||
void *data = skb->data;
|
||||
u32 packet_len;
|
||||
|
||||
if (skb->len < sizeof(*maph))
|
||||
return 0;
|
||||
|
||||
maph = (struct rmnet_map_header *)skb->data;
|
||||
|
||||
/* Some hardware can send us empty frames. Catch them */
|
||||
if (!maph->pkt_len)
|
||||
return 0;
|
||||
|
||||
packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
|
||||
packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
|
||||
} else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) &&
|
||||
!(maph->flags & MAP_CMD_FLAG)) {
|
||||
/* Mapv5 data pkt without csum hdr is invalid */
|
||||
if (!(maph->flags & MAP_NEXT_HEADER_FLAG))
|
||||
return 0;
|
||||
|
||||
packet_len += sizeof(*next_hdr);
|
||||
next_hdr = data + sizeof(*maph);
|
||||
}
|
||||
|
||||
if (skb->len < packet_len)
|
||||
return 0;
|
||||
|
||||
if (next_hdr &&
|
||||
u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) !=
|
||||
RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
|
||||
return 0;
|
||||
|
||||
return packet_len;
|
||||
}
|
||||
|
||||
/* Deaggregates a single packet
|
||||
* A whole new buffer is allocated for each portion of an aggregated frame.
|
||||
* Caller should keep calling deaggregate() on the source skb until 0 is
|
||||
|
|
@ -342,46 +383,13 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
|
|||
struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
|
||||
struct rmnet_port *port)
|
||||
{
|
||||
struct rmnet_map_v5_csum_header *next_hdr = NULL;
|
||||
struct rmnet_map_header *maph;
|
||||
void *data = skb->data;
|
||||
struct sk_buff *skbn;
|
||||
u8 nexthdr_type;
|
||||
u32 packet_len;
|
||||
|
||||
if (skb->len == 0)
|
||||
packet_len = rmnet_map_validate_packet_len(skb, port);
|
||||
if (!packet_len)
|
||||
return NULL;
|
||||
|
||||
maph = (struct rmnet_map_header *)skb->data;
|
||||
packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
|
||||
|
||||
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
|
||||
packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
|
||||
} else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
|
||||
if (!(maph->flags & MAP_CMD_FLAG)) {
|
||||
packet_len += sizeof(*next_hdr);
|
||||
if (maph->flags & MAP_NEXT_HEADER_FLAG)
|
||||
next_hdr = data + sizeof(*maph);
|
||||
else
|
||||
/* Mapv5 data pkt without csum hdr is invalid */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (((int)skb->len - (int)packet_len) < 0)
|
||||
return NULL;
|
||||
|
||||
/* Some hardware can send us empty frames. Catch them */
|
||||
if (!maph->pkt_len)
|
||||
return NULL;
|
||||
|
||||
if (next_hdr) {
|
||||
nexthdr_type = u8_get_bits(next_hdr->header_info,
|
||||
MAPV5_HDRINFO_HDR_TYPE_FMASK);
|
||||
if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
|
||||
if (!skbn)
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
|
|||
}
|
||||
|
||||
unprotected_len = skb->len;
|
||||
eth = eth_hdr(skb);
|
||||
eth = skb_eth_hdr(skb);
|
||||
sci_present = macsec_send_sci(secy);
|
||||
hh = skb_push(skb, macsec_extra_len(sci_present));
|
||||
memmove(hh, eth, 2 * ETH_ALEN);
|
||||
|
|
|
|||
|
|
@ -122,7 +122,8 @@ config MDIO_MVUSB
|
|||
|
||||
config MDIO_MSCC_MIIM
|
||||
tristate "Microsemi MIIM interface support"
|
||||
depends on HAS_IOMEM && REGMAP_MMIO
|
||||
depends on HAS_IOMEM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This driver supports the MIIM (MDIO) interface found in the network
|
||||
switches of the Microsemi SoCs; it is recommended to switch on
|
||||
|
|
|
|||
|
|
@ -1499,6 +1499,17 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param)
|
|||
return;
|
||||
}
|
||||
|
||||
static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata,
|
||||
struct net_device *netdev,
|
||||
netdev_features_t features)
|
||||
{
|
||||
if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
|
||||
!(netdev->flags & IFF_PROMISC))
|
||||
pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
|
||||
else
|
||||
pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
|
||||
}
|
||||
|
||||
static void lan78xx_set_multicast(struct net_device *netdev)
|
||||
{
|
||||
struct lan78xx_net *dev = netdev_priv(netdev);
|
||||
|
|
@ -1533,6 +1544,8 @@ static void lan78xx_set_multicast(struct net_device *netdev)
|
|||
}
|
||||
}
|
||||
|
||||
lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features);
|
||||
|
||||
if (netdev_mc_count(dev->net)) {
|
||||
struct netdev_hw_addr *ha;
|
||||
int i;
|
||||
|
|
@ -3074,10 +3087,7 @@ static int lan78xx_set_features(struct net_device *netdev,
|
|||
else
|
||||
pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_;
|
||||
|
||||
if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
|
||||
pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
|
||||
else
|
||||
pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
|
||||
lan78xx_update_vlan_filter(pdata, netdev, features);
|
||||
|
||||
spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
|
||||
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
|||
skb_trim(skb, skb->len - sizeof *trailer);
|
||||
|
||||
if ((packet_len & 0x01) == 0) {
|
||||
if (skb->data [packet_len] != PAD_BYTE) {
|
||||
if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) {
|
||||
dev->net->stats.rx_frame_errors++;
|
||||
netdev_dbg(dev->net, "bad pad\n");
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev)
|
|||
#define __in_dev_put(idev) refcount_dec(&(idev)->refcnt)
|
||||
#define in_dev_hold(idev) refcount_inc(&(idev)->refcnt)
|
||||
|
||||
static inline bool in_dev_hold_safe(struct in_device *idev)
|
||||
{
|
||||
return refcount_inc_not_zero(&idev->refcnt);
|
||||
}
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
static __inline__ __be32 inet_make_mask(int logmask)
|
||||
|
|
|
|||
|
|
@ -446,6 +446,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev)
|
|||
refcount_inc(&idev->refcnt);
|
||||
}
|
||||
|
||||
static inline bool in6_dev_hold_safe(struct inet6_dev *idev)
|
||||
{
|
||||
return refcount_inc_not_zero(&idev->refcnt);
|
||||
}
|
||||
|
||||
/* called with rcu_read_lock held */
|
||||
static inline bool ip6_ignore_linkdown(const struct net_device *dev)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3413,8 +3413,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb)
|
|||
#define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2))
|
||||
|
||||
/* ISO data length and flags pack/unpack */
|
||||
#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14)))
|
||||
#define hci_iso_data_len(h) ((h) & 0x3fff)
|
||||
#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \
|
||||
(((f) & 0x3) << 14)))
|
||||
#define hci_iso_data_len(h) ((h) & 0x0fff)
|
||||
#define hci_iso_data_flags(h) ((h) >> 14)
|
||||
|
||||
/* codec transport types */
|
||||
|
|
|
|||
|
|
@ -985,6 +985,7 @@ enum {
|
|||
HCI_CONN_AUTH_FAILURE,
|
||||
HCI_CONN_PER_ADV,
|
||||
HCI_CONN_BIG_CREATED,
|
||||
HCI_CONN_CREATE,
|
||||
HCI_CONN_CREATE_CIS,
|
||||
HCI_CONN_CREATE_BIG_SYNC,
|
||||
HCI_CONN_BIG_SYNC,
|
||||
|
|
|
|||
|
|
@ -617,7 +617,8 @@ struct l2cap_chan {
|
|||
struct l2cap_ops {
|
||||
char *name;
|
||||
|
||||
struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
|
||||
int (*new_connection)(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan);
|
||||
int (*recv) (struct l2cap_chan * chan,
|
||||
struct sk_buff *skb);
|
||||
void (*teardown) (struct l2cap_chan *chan, int err);
|
||||
|
|
@ -882,9 +883,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
|
|||
return (seq + 1) % (chan->tx_win_max + 1);
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
|
||||
static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
return NULL;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
|
|
@ -961,7 +963,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
|||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
|
||||
int l2cap_ertm_init(struct l2cap_chan *chan);
|
||||
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags)
|
|||
|
||||
static inline size_t guehdr_priv_flags_len(__be32 flags)
|
||||
{
|
||||
return 0;
|
||||
return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0;
|
||||
}
|
||||
|
||||
/* Validate standard and private flags. Returns non-zero (meaning invalid)
|
||||
|
|
|
|||
|
|
@ -752,7 +752,8 @@ struct ip_vs_protocol {
|
|||
|
||||
void (*state_transition)(struct ip_vs_conn *cp, int direction,
|
||||
const struct sk_buff *skb,
|
||||
struct ip_vs_proto_data *pd);
|
||||
struct ip_vs_proto_data *pd,
|
||||
unsigned int iph_len);
|
||||
|
||||
int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);
|
||||
|
||||
|
|
|
|||
|
|
@ -305,6 +305,14 @@ struct mana_recv_buf_oob {
|
|||
|
||||
void *buf_va;
|
||||
bool from_pool; /* allocated from a page pool */
|
||||
/* head page of the page_pool fragment; valid only when
|
||||
* from_pool && frag_count > 1.
|
||||
*/
|
||||
struct page *pp_page;
|
||||
/* Fragment offset plus rxq->headroom, passed to
|
||||
* page_pool_dma_sync_for_cpu().
|
||||
*/
|
||||
u32 dma_sync_offset;
|
||||
|
||||
/* SGL of the buffer going to be sent as part of the work request. */
|
||||
u32 num_sge;
|
||||
|
|
|
|||
|
|
@ -155,11 +155,12 @@ struct flow_offload_tuple {
|
|||
tun_num:2,
|
||||
in_vlan_ingress:2;
|
||||
u16 mtu;
|
||||
u32 dst_cookie;
|
||||
struct dst_entry *dst_cache;
|
||||
|
||||
union {
|
||||
struct {
|
||||
struct dst_entry *dst_cache;
|
||||
u32 ifidx;
|
||||
u32 dst_cookie;
|
||||
};
|
||||
struct {
|
||||
u32 ifidx;
|
||||
|
|
@ -357,6 +358,8 @@ static inline int nf_flow_register_bpf(void)
|
|||
|
||||
void nf_flow_offload_add(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow);
|
||||
void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow);
|
||||
void nf_flow_offload_del(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow);
|
||||
void nf_flow_offload_stats(struct nf_flowtable *flowtable,
|
||||
|
|
|
|||
|
|
@ -37,17 +37,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a)
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline int tcf_pedit_nkeys(const struct tc_action *a)
|
||||
/* Must be called with act->tcfa_lock held to ensure consistency of parallel
|
||||
* reads of the same action's pedit keys (e.g. flow_offload count vs fill).
|
||||
* Note, this is only used for pedit offload.
|
||||
*/
|
||||
static inline int tcf_pedit_nkeys_locked(const struct tc_action *a)
|
||||
{
|
||||
struct tcf_pedit_parms *parms;
|
||||
int nkeys;
|
||||
|
||||
rcu_read_lock();
|
||||
parms = to_pedit_parms(a);
|
||||
nkeys = parms->tcfp_nkeys;
|
||||
rcu_read_unlock();
|
||||
|
||||
return nkeys;
|
||||
lockdep_assert_held(&a->tcfa_lock);
|
||||
return rcu_dereference_protected(to_pedit(a)->parms,
|
||||
lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys;
|
||||
}
|
||||
|
||||
static inline u32 tcf_pedit_htype(const struct tc_action *a, int index)
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
|
|||
* the one with the lowest address
|
||||
*/
|
||||
if (tmp_max == max && max_orig_node &&
|
||||
batadv_compare_eth(candidate->orig, max_orig_node->orig))
|
||||
memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0)
|
||||
goto out;
|
||||
|
||||
ret = true;
|
||||
|
|
|
|||
|
|
@ -518,8 +518,10 @@ int batadv_frag_send_packet(struct sk_buff *skb,
|
|||
mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
|
||||
max_fragment_size = mtu - header_size;
|
||||
|
||||
if (skb->len == 0 || max_fragment_size == 0)
|
||||
return -EINVAL;
|
||||
if (skb->len == 0 || max_fragment_size == 0) {
|
||||
ret = -EINVAL;
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
num_fragments = (skb->len - 1) / max_fragment_size + 1;
|
||||
max_fragment_size = (skb->len - 1) / num_fragments + 1;
|
||||
|
|
@ -545,7 +547,7 @@ int batadv_frag_send_packet(struct sk_buff *skb,
|
|||
*/
|
||||
if (skb_has_frag_list(skb) && __skb_linearize(skb)) {
|
||||
ret = -ENOMEM;
|
||||
goto free_skb;
|
||||
goto put_primary_if;
|
||||
}
|
||||
|
||||
/* Create one header to be copied to all fragments */
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ int batadv_mesh_init(struct net_device *mesh_iface)
|
|||
void batadv_mesh_free(struct net_device *mesh_iface)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
|
||||
struct batadv_meshif_vlan *vlan;
|
||||
|
||||
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
|
||||
|
||||
|
|
@ -273,6 +274,13 @@ void batadv_mesh_free(struct net_device *mesh_iface)
|
|||
|
||||
batadv_mcast_free(bat_priv);
|
||||
|
||||
/* destroy the "untagged" VLAN */
|
||||
vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
|
||||
if (vlan) {
|
||||
batadv_meshif_destroy_vlan(bat_priv, vlan);
|
||||
batadv_meshif_vlan_put(vlan);
|
||||
}
|
||||
|
||||
/* Free the TT and the originator tables only after having terminated
|
||||
* all the other depending components which may use these structures for
|
||||
* their purposes.
|
||||
|
|
@ -368,7 +376,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset)
|
|||
|
||||
switch (ethhdr->h_proto) {
|
||||
case htons(ETH_P_8021Q):
|
||||
vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr),
|
||||
vhdr = skb_header_pointer(skb, offset,
|
||||
sizeof(*vhdr), &vhdr_tmp);
|
||||
if (!vhdr)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -195,6 +195,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
|
|||
if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
|
||||
goto dropped;
|
||||
|
||||
if (!pskb_may_pull(skb, ETH_HLEN))
|
||||
goto dropped;
|
||||
|
||||
/* reset control block to avoid left overs from previous users */
|
||||
memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
|
||||
|
||||
|
|
@ -592,8 +595,8 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
|
|||
* @bat_priv: the bat priv with all the mesh interface information
|
||||
* @vlan: the object to remove
|
||||
*/
|
||||
static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
|
||||
struct batadv_meshif_vlan *vlan)
|
||||
void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
|
||||
struct batadv_meshif_vlan *vlan)
|
||||
{
|
||||
/* explicitly remove the associated TT local entry because it is marked
|
||||
* with the NOPURGE flag
|
||||
|
|
@ -1088,22 +1091,13 @@ static int batadv_meshif_newlink(struct net_device *dev,
|
|||
static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
struct batadv_meshif_vlan *vlan;
|
||||
|
||||
while (!list_empty(&mesh_iface->adj_list.lower)) {
|
||||
hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next);
|
||||
batadv_hardif_disable_interface(hard_iface);
|
||||
}
|
||||
|
||||
/* destroy the "untagged" VLAN */
|
||||
vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
|
||||
if (vlan) {
|
||||
batadv_meshif_destroy_vlan(bat_priv, vlan);
|
||||
batadv_meshif_vlan_put(vlan);
|
||||
}
|
||||
|
||||
unregister_netdevice_queue(mesh_iface, head);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_device *mesh_iface,
|
|||
bool batadv_meshif_is_valid(const struct net_device *net_dev);
|
||||
extern struct rtnl_link_ops batadv_link_ops;
|
||||
int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
|
||||
void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
|
||||
struct batadv_meshif_vlan *vlan);
|
||||
void batadv_meshif_vlan_release(struct kref *ref);
|
||||
struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv,
|
||||
unsigned short vid);
|
||||
|
|
|
|||
|
|
@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
|
|||
{
|
||||
struct batadv_tvlv_mcast_tracker *mcast_tracker;
|
||||
struct batadv_neigh_node *neigh_node;
|
||||
unsigned long offset, num_dests_off;
|
||||
struct sk_buff *nexthop_skb;
|
||||
unsigned char *skb_net_hdr;
|
||||
bool local_recv = false;
|
||||
unsigned int tvlv_len;
|
||||
unsigned long offset;
|
||||
bool xmitted = false;
|
||||
u8 *dest, *next_dest;
|
||||
u16 num_dests;
|
||||
|
|
@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
|
|||
/* (at least) TVLV part needs to be linearized */
|
||||
SKB_LINEAR_ASSERT(skb);
|
||||
|
||||
/* check if num_dests is within skb length */
|
||||
num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests);
|
||||
if (num_dests_off > skb_network_header_len(skb))
|
||||
/* check if batadv_tvlv_mcast_tracker header is within skb length */
|
||||
if (sizeof(*mcast_tracker) > skb_network_header_len(skb))
|
||||
return -EINVAL;
|
||||
|
||||
skb_net_hdr = skb_network_header(skb);
|
||||
|
|
|
|||
|
|
@ -2971,7 +2971,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv,
|
|||
out:
|
||||
batadv_hardif_put(primary_if);
|
||||
|
||||
if (ret && tt_req_node) {
|
||||
if (!ret && tt_req_node) {
|
||||
spin_lock_bh(&bat_priv->tt.req_list_lock);
|
||||
if (!hlist_unhashed(&tt_req_node->list)) {
|
||||
hlist_del_init(&tt_req_node->list);
|
||||
|
|
@ -4033,7 +4033,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
|
|||
u16 tvlv_value_len)
|
||||
{
|
||||
struct batadv_tvlv_tt_data *tt_data;
|
||||
u16 tt_vlan_len, tt_num_entries;
|
||||
u16 tt_num_entries;
|
||||
size_t tt_vlan_len;
|
||||
char tt_flag;
|
||||
bool ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void)
|
|||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
|
||||
chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
|
||||
chan->mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
|
|
@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
|
|||
ifup(dev->netdev);
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
chan = chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->ops = pchan->ops;
|
||||
|
||||
BT_DBG("chan %p pchan %p", chan, pchan);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void unregister_dev(struct lowpan_btle_dev *dev)
|
||||
{
|
||||
struct hci_dev *hdev = READ_ONCE(dev->hdev);
|
||||
|
|
@ -797,20 +782,10 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
|||
struct lowpan_btle_dev *dev = NULL;
|
||||
struct lowpan_peer *peer;
|
||||
int err = -ENOENT;
|
||||
bool last = false, remove = true;
|
||||
bool last = false;
|
||||
|
||||
BT_DBG("chan %p conn %p", chan, chan->conn);
|
||||
|
||||
if (chan->conn && chan->conn->hcon) {
|
||||
if (!is_bt_6lowpan(chan->conn->hcon))
|
||||
return;
|
||||
|
||||
/* If conn is set, then the netdev is also there and we should
|
||||
* not remove it.
|
||||
*/
|
||||
remove = false;
|
||||
}
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
|
||||
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
|
||||
|
|
@ -837,10 +812,8 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
|||
|
||||
ifdown(dev->netdev);
|
||||
|
||||
if (remove) {
|
||||
INIT_WORK(&entry->delete_netdev, delete_netdev);
|
||||
schedule_work(&entry->delete_netdev);
|
||||
}
|
||||
INIT_WORK(&entry->delete_netdev, delete_netdev);
|
||||
schedule_work(&entry->delete_netdev);
|
||||
} else {
|
||||
spin_unlock(&devices_lock);
|
||||
}
|
||||
|
|
@ -901,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
|
|||
|
||||
static const struct l2cap_ops bt_6lowpan_chan_ops = {
|
||||
.name = "L2CAP 6LoWPAN channel",
|
||||
.new_connection = chan_new_conn_cb,
|
||||
.recv = chan_recv_cb,
|
||||
.close = chan_close_cb,
|
||||
.state_change = chan_state_change_cb,
|
||||
|
|
@ -1029,16 +1001,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
if (!hcon)
|
||||
if (!hcon) {
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
*conn = (struct l2cap_conn *)hcon->l2cap_data;
|
||||
*conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
|
||||
|
||||
BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1093,23 +1068,15 @@ static void disconnect_all_peers(void)
|
|||
} while (nchans);
|
||||
}
|
||||
|
||||
struct set_enable {
|
||||
struct work_struct work;
|
||||
bool flag;
|
||||
};
|
||||
|
||||
static void do_enable_set(struct work_struct *work)
|
||||
static void do_enable_set(bool flag)
|
||||
{
|
||||
struct set_enable *set_enable = container_of(work,
|
||||
struct set_enable, work);
|
||||
|
||||
if (!set_enable->flag || enable_6lowpan != set_enable->flag)
|
||||
if (!flag || enable_6lowpan != flag)
|
||||
/* Disconnect existing connections if 6lowpan is
|
||||
* disabled
|
||||
*/
|
||||
disconnect_all_peers();
|
||||
|
||||
enable_6lowpan = set_enable->flag;
|
||||
enable_6lowpan = flag;
|
||||
|
||||
mutex_lock(&set_lock);
|
||||
if (listen_chan) {
|
||||
|
|
@ -1121,22 +1088,11 @@ static void do_enable_set(struct work_struct *work)
|
|||
|
||||
listen_chan = bt_6lowpan_listen();
|
||||
mutex_unlock(&set_lock);
|
||||
|
||||
kfree(set_enable);
|
||||
}
|
||||
|
||||
static int lowpan_enable_set(void *data, u64 val)
|
||||
{
|
||||
struct set_enable *set_enable;
|
||||
|
||||
set_enable = kzalloc_obj(*set_enable);
|
||||
if (!set_enable)
|
||||
return -ENOMEM;
|
||||
|
||||
set_enable->flag = !!val;
|
||||
INIT_WORK(&set_enable->work, do_enable_set);
|
||||
|
||||
schedule_work(&set_enable->work);
|
||||
do_enable_set(!!val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1185,18 +1141,22 @@ static ssize_t lowpan_control_write(struct file *fp,
|
|||
if (conn) {
|
||||
struct lowpan_peer *peer;
|
||||
|
||||
if (!is_bt_6lowpan(conn->hcon))
|
||||
if (!is_bt_6lowpan(conn->hcon)) {
|
||||
l2cap_conn_put(conn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
peer = lookup_peer(conn);
|
||||
if (peer) {
|
||||
BT_DBG("6LoWPAN connection already exists");
|
||||
l2cap_conn_put(conn);
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
BT_DBG("conn %p dst %pMR type %d user %u", conn,
|
||||
&conn->hcon->dst, conn->hcon->dst_type,
|
||||
addr_type);
|
||||
l2cap_conn_put(conn);
|
||||
}
|
||||
|
||||
ret = bt_6lowpan_connect(&addr, addr_type);
|
||||
|
|
@ -1212,6 +1172,8 @@ static ssize_t lowpan_control_write(struct file *fp,
|
|||
return ret;
|
||||
|
||||
ret = bt_6lowpan_disconnect(conn, addr_type);
|
||||
if (conn)
|
||||
l2cap_conn_put(conn);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
|
||||
restart:
|
||||
for (sk = bt_accept_get(parent, NULL); sk; sk = next) {
|
||||
/* Prevent early freeing of sk due to unlink and sock_kill */
|
||||
/* The reference from bt_accept_get() keeps sk alive. */
|
||||
lock_sock(sk);
|
||||
|
||||
/* Check sk has not already been unlinked via
|
||||
|
|
@ -321,13 +321,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
|
||||
next = bt_accept_get(parent, sk);
|
||||
|
||||
/* sk is safely in the parent list so reduce reference count */
|
||||
sock_put(sk);
|
||||
|
||||
/* FIXME: Is this check still needed */
|
||||
if (sk->sk_state == BT_CLOSED) {
|
||||
bt_accept_unlink(sk);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -337,16 +335,6 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
if (newsock)
|
||||
sock_graft(sk, newsock);
|
||||
|
||||
/* Hand the caller a reference taken while sk is
|
||||
* still locked. bt_accept_unlink() just dropped
|
||||
* the accept-queue reference; without this hold a
|
||||
* concurrent teardown (e.g. l2cap_conn_del() ->
|
||||
* l2cap_sock_kill()) could free sk between
|
||||
* release_sock() and the caller using it. Every
|
||||
* caller drops this with sock_put() when done.
|
||||
*/
|
||||
sock_hold(sk);
|
||||
|
||||
release_sock(sk);
|
||||
if (next)
|
||||
sock_put(next);
|
||||
|
|
@ -354,6 +342,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
}
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -559,14 +559,18 @@ static int bnep_session(void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct device *bnep_get_device(struct bnep_session *session)
|
||||
static struct l2cap_conn *bnep_get_conn(struct bnep_session *session)
|
||||
{
|
||||
struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
|
||||
struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan;
|
||||
struct l2cap_conn *conn;
|
||||
|
||||
if (!conn || !conn->hcon)
|
||||
return NULL;
|
||||
l2cap_chan_lock(chan);
|
||||
conn = chan->conn;
|
||||
if (conn)
|
||||
l2cap_conn_get(conn);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
return &conn->hcon->dev;
|
||||
return conn;
|
||||
}
|
||||
|
||||
static const struct device_type bnep_type = {
|
||||
|
|
@ -578,6 +582,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
|
||||
struct net_device *dev;
|
||||
struct bnep_session *s, *ss;
|
||||
struct l2cap_conn *conn = NULL;
|
||||
u8 dst[ETH_ALEN], src[ETH_ALEN];
|
||||
int err;
|
||||
|
||||
|
|
@ -637,10 +642,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
bnep_set_default_proto_filter(s);
|
||||
#endif
|
||||
|
||||
SET_NETDEV_DEV(dev, bnep_get_device(s));
|
||||
conn = bnep_get_conn(s);
|
||||
if (!conn) {
|
||||
err = -ENOTCONN;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(dev, &conn->hcon->dev);
|
||||
SET_NETDEV_DEVTYPE(dev, &bnep_type);
|
||||
|
||||
err = register_netdev(dev);
|
||||
l2cap_conn_put(conn);
|
||||
conn = NULL;
|
||||
if (err)
|
||||
goto failed;
|
||||
|
||||
|
|
@ -662,6 +675,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
return 0;
|
||||
|
||||
failed:
|
||||
if (conn)
|
||||
l2cap_conn_put(conn);
|
||||
up_write(&bnep_session_sem);
|
||||
free_netdev(dev);
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -3178,26 +3178,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
|||
|
||||
conn->abort_reason = reason;
|
||||
|
||||
/* If the connection is pending check the command opcode since that
|
||||
* might be blocking on hci_cmd_sync_work while waiting its respective
|
||||
* event so we need to hci_cmd_sync_cancel to cancel it.
|
||||
*
|
||||
* hci_connect_le serializes the connection attempts so only one
|
||||
* connection can be in BT_CONNECT at time.
|
||||
/* Cancel the connect attempt. A return of 0 means the create command
|
||||
* was still queued and got dequeued, so there is nothing to disconnect.
|
||||
*/
|
||||
if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
|
||||
switch (hci_skb_event(hdev->sent_cmd)) {
|
||||
case HCI_EV_CONN_COMPLETE:
|
||||
case HCI_EV_LE_CONN_COMPLETE:
|
||||
case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
|
||||
case HCI_EVT_LE_CIS_ESTABLISHED:
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
break;
|
||||
}
|
||||
/* Cancel connect attempt if still queued/pending */
|
||||
} else if (!hci_cancel_connect_sync(hdev, conn)) {
|
||||
if (!hci_cancel_connect_sync(hdev, conn))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run immediately if on cmd_sync_work since this may be called
|
||||
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
|
||||
|
|
|
|||
|
|
@ -6633,6 +6633,11 @@ 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;
|
||||
|
||||
|
|
@ -6645,6 +6650,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
hdev->le_scan_type == LE_SCAN_ACTIVE &&
|
||||
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
|
||||
hci_conn_del(conn);
|
||||
hci_conn_put(conn);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
|
@ -6690,6 +6696,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
&own_addr_type);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* Mark create connection in flight so hci_cancel_connect_sync() can
|
||||
* cancel it while blocking on the connection complete event.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
|
||||
/* Send command LE Extended Create Connection if supported */
|
||||
if (use_ext_conn(hdev)) {
|
||||
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
|
||||
|
|
@ -6725,11 +6737,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
conn->conn_timeout, NULL);
|
||||
|
||||
done:
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
|
||||
if (err == -ETIMEDOUT)
|
||||
hci_le_connect_cancel_sync(hdev, conn, 0x00);
|
||||
|
||||
/* Re-enable advertising after the connection attempt is finished. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
hci_conn_put(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -7004,10 +7019,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
|||
else
|
||||
cp.role_switch = 0x00;
|
||||
|
||||
return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
conn->conn_timeout, NULL);
|
||||
/* 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.
|
||||
*/
|
||||
set_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
|
||||
err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
conn->conn_timeout, NULL);
|
||||
|
||||
clear_bit(HCI_CONN_CREATE, &conn->flags);
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
|
|
@ -7059,22 +7089,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
if (conn->state != BT_OPEN)
|
||||
return -EINVAL;
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
int err = -EBUSY;
|
||||
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
return !hci_cmd_sync_dequeue_once(hdev,
|
||||
hci_acl_create_conn_sync,
|
||||
conn, NULL);
|
||||
case LE_LINK:
|
||||
return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
|
||||
conn, create_le_conn_complete);
|
||||
/* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel
|
||||
* atomic against the worker, which takes this lock to dequeue every
|
||||
* entry: while it is held no other command can become pending, so
|
||||
* hci_cmd_sync_cancel() cannot cancel an unrelated command.
|
||||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
/* In flight: this connection owns the pending request, cancel it. */
|
||||
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
/* Still queued: a successful dequeue means it never started, so there
|
||||
* is nothing to disconnect.
|
||||
*/
|
||||
entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
if (entry) {
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
int err = -EBUSY;
|
||||
|
||||
/* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel
|
||||
* atomic against the cmd_sync worker.
|
||||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn,
|
||||
create_le_conn_complete);
|
||||
if (entry) {
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
/* LE Create CIS is shared by the whole CIG and cannot be dequeued
|
||||
* per-connection, so only an in-flight command can be cancelled.
|
||||
* cmd_sync_work_lock keeps the test and the cancel atomic against the
|
||||
* cmd_sync worker.
|
||||
*/
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
return hci_acl_cancel_create_conn_sync(hdev, conn);
|
||||
case LE_LINK:
|
||||
return hci_le_cancel_create_conn_sync(hdev, conn);
|
||||
case CIS_LINK:
|
||||
return hci_cis_cancel_create_conn_sync(hdev, conn);
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
|
|
|
|||
|
|
@ -1590,6 +1590,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
|||
{
|
||||
int err;
|
||||
struct hci_dev *hdev;
|
||||
struct iso_conn *conn;
|
||||
bdaddr_t src, dst;
|
||||
u8 src_type;
|
||||
|
||||
|
|
@ -1612,8 +1613,17 @@ static void iso_conn_big_sync(struct sock *sk)
|
|||
hci_dev_lock(hdev);
|
||||
lock_sock(sk);
|
||||
|
||||
/* The socket lock was dropped for hci_get_route(), so the connection
|
||||
* may have been torn down meanwhile: iso_chan_del() clears conn and
|
||||
* the broadcast teardown path can clear conn->hcon on its own. Check
|
||||
* both before dereferencing conn->hcon.
|
||||
*/
|
||||
conn = iso_pi(sk)->conn;
|
||||
if (!conn || !conn->hcon)
|
||||
goto unlock;
|
||||
|
||||
if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
|
||||
err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon,
|
||||
err = hci_conn_big_create_sync(hdev, conn->hcon,
|
||||
&iso_pi(sk)->qos,
|
||||
iso_pi(sk)->sync_handle,
|
||||
iso_pi(sk)->bc_num_bis,
|
||||
|
|
@ -1622,6 +1632,7 @@ static void iso_conn_big_sync(struct sock *sk)
|
|||
bt_dev_err(hdev, "hci_big_create_sync: %d", err);
|
||||
}
|
||||
|
||||
unlock:
|
||||
release_sock(sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
|
@ -2529,7 +2540,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
switch (pb) {
|
||||
case ISO_START:
|
||||
case ISO_SINGLE:
|
||||
if (conn->rx_len) {
|
||||
if (conn->rx_skb || conn->rx_len) {
|
||||
BT_ERR("Unexpected start frame (len %d)", skb->len);
|
||||
kfree_skb(conn->rx_skb);
|
||||
conn->rx_skb = NULL;
|
||||
|
|
@ -2610,12 +2621,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
break;
|
||||
|
||||
case ISO_CONT:
|
||||
BT_DBG("Cont: frag len %d (expecting %d)", skb->len,
|
||||
case ISO_END:
|
||||
BT_DBG("%s: frag len %d (expecting %d)",
|
||||
(pb == ISO_END) ? "End" : "Cont", skb->len,
|
||||
conn->rx_len);
|
||||
|
||||
if (!conn->rx_len) {
|
||||
BT_ERR("Unexpected continuation frame (len %d)",
|
||||
skb->len);
|
||||
if (!conn->rx_skb) {
|
||||
BT_ERR("Unexpected ISO %s frame (len %d)",
|
||||
(pb == ISO_END) ? "End" : "Cont", skb->len);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
|
|
@ -2631,17 +2644,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
conn->rx_len -= skb->len;
|
||||
break;
|
||||
|
||||
case ISO_END:
|
||||
if (!conn->rx_len) {
|
||||
BT_ERR("Unexpected end frame (len %d)", skb->len);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
||||
skb->len);
|
||||
conn->rx_len -= skb->len;
|
||||
if (pb == ISO_CONT)
|
||||
break;
|
||||
|
||||
if (!conn->rx_len) {
|
||||
struct sk_buff *rx_skb = conn->rx_skb;
|
||||
|
|
@ -2652,6 +2657,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
|
|||
*/
|
||||
conn->rx_skb = NULL;
|
||||
iso_recv_frame(conn, rx_skb);
|
||||
} else {
|
||||
BT_ERR("ISO fragment incomplete (len %d, expected %d)",
|
||||
skb->len, conn->rx_len);
|
||||
kfree_skb(conn->rx_skb);
|
||||
conn->rx_skb = NULL;
|
||||
conn->rx_len = 0;
|
||||
goto drop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,7 +522,10 @@ void l2cap_chan_put(struct l2cap_chan *c)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(l2cap_chan_put);
|
||||
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
||||
/* Initialise @chan with default values, inheriting from the parent channel
|
||||
* @pchan when it is given.
|
||||
*/
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
|
||||
{
|
||||
chan->fcs = L2CAP_FCS_CRC16;
|
||||
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
|
||||
|
|
@ -536,6 +539,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
|||
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
|
||||
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
|
||||
|
||||
if (pchan) {
|
||||
BT_DBG("chan %p pchan %p", chan, pchan);
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
if (chan->chan_type == L2CAP_CHAN_FIXED) {
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = pchan->scid;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chan->conf_state = 0;
|
||||
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
|
||||
|
||||
|
|
@ -1775,19 +1803,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
disable_delayed_work_sync(&conn->info_timer);
|
||||
disable_delayed_work_sync(&conn->id_addr_timer);
|
||||
|
||||
cancel_work_sync(&conn->pending_rx_work);
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
|
||||
kfree_skb(conn->rx_skb);
|
||||
|
||||
skb_queue_purge(&conn->pending_rx);
|
||||
|
||||
/* We can not call flush_work(&conn->pending_rx_work) here since we
|
||||
* might block if we are running on a worker from the same workqueue
|
||||
* pending_rx_work is waiting on.
|
||||
*/
|
||||
if (work_pending(&conn->pending_rx_work))
|
||||
cancel_work_sync(&conn->pending_rx_work);
|
||||
|
||||
ida_destroy(&conn->tx_ida);
|
||||
|
||||
l2cap_unregister_all_users(conn);
|
||||
|
|
@ -3051,13 +3073,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
|
||||
unsigned long *val)
|
||||
static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type,
|
||||
int *olen, unsigned long *val)
|
||||
{
|
||||
struct l2cap_conf_opt *opt = *ptr;
|
||||
int len;
|
||||
|
||||
/* opt->len is attacker-controlled. Validate that the full option
|
||||
* (header + value) actually fits in the buffer before touching
|
||||
* opt->val, otherwise the switch below reads past the end of the
|
||||
* caller's buffer.
|
||||
*/
|
||||
if (end - *ptr < L2CAP_CONF_OPT_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
len = L2CAP_CONF_OPT_SIZE + opt->len;
|
||||
if (end - *ptr < len)
|
||||
return -EINVAL;
|
||||
|
||||
*ptr += len;
|
||||
|
||||
*type = opt->type;
|
||||
|
|
@ -3429,6 +3462,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
|
|||
void *ptr = rsp->data;
|
||||
void *endptr = data + data_size;
|
||||
void *req = chan->conf_req;
|
||||
void *req_end = req + chan->conf_len;
|
||||
int len = chan->conf_len;
|
||||
int type, hint, olen;
|
||||
unsigned long val;
|
||||
|
|
@ -3442,9 +3476,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
|
|||
BT_DBG("chan %p", chan);
|
||||
|
||||
while (len >= L2CAP_CONF_OPT_SIZE) {
|
||||
len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
|
||||
if (len < 0)
|
||||
int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
len -= ret;
|
||||
|
||||
hint = type & L2CAP_CONF_HINT;
|
||||
type &= L2CAP_CONF_MASK;
|
||||
|
|
@ -3672,6 +3708,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
|
|||
struct l2cap_conf_req *req = data;
|
||||
void *ptr = req->data;
|
||||
void *endptr = data + size;
|
||||
void *rsp_end = rsp + len;
|
||||
int type, olen;
|
||||
unsigned long val;
|
||||
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
|
||||
|
|
@ -3680,9 +3717,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
|
|||
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
|
||||
|
||||
while (len >= L2CAP_CONF_OPT_SIZE) {
|
||||
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
|
||||
if (len < 0)
|
||||
int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
len -= ret;
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_MTU:
|
||||
|
|
@ -3933,6 +3972,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
|
|||
{
|
||||
int type, olen;
|
||||
unsigned long val;
|
||||
void *rsp_end = rsp + len;
|
||||
/* Use sane default values in case a misbehaving remote device
|
||||
* did not send an RFC or extended window size option.
|
||||
*/
|
||||
|
|
@ -3951,9 +3991,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
|
|||
return;
|
||||
|
||||
while (len >= L2CAP_CONF_OPT_SIZE) {
|
||||
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
|
||||
if (len < 0)
|
||||
int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
len -= ret;
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_RFC:
|
||||
|
|
@ -4010,6 +4052,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate and initialise a channel for an incoming connection.
|
||||
*
|
||||
* The channel inherits its configuration from @pchan and is linked into @conn
|
||||
* before ->new_connection() runs, so the conn list reference keeps it alive if
|
||||
* the callback exposes it (e.g. via the socket accept queue) before this
|
||||
* returns. The l2cap_chan_create() reference is taken over by the subsystem on
|
||||
* success and dropped here on failure.
|
||||
*/
|
||||
static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
|
||||
struct l2cap_chan *pchan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
l2cap_chan_set_defaults(chan, pchan);
|
||||
chan->ops = pchan->ops;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
if (pchan->ops->new_connection &&
|
||||
pchan->ops->new_connection(pchan, chan) < 0) {
|
||||
l2cap_chan_del(chan, 0);
|
||||
l2cap_chan_put(chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
||||
u8 *data, u8 rsp_code)
|
||||
{
|
||||
|
|
@ -4056,7 +4130,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
|||
goto response;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan)
|
||||
goto response;
|
||||
|
||||
|
|
@ -4074,8 +4148,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
|||
chan->psm = psm;
|
||||
chan->dcid = scid;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
dcid = chan->scid;
|
||||
|
||||
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
||||
|
|
@ -4807,6 +4879,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id)
|
|||
case L2CAP_ECHO_RSP:
|
||||
case L2CAP_INFO_RSP:
|
||||
case L2CAP_CONN_PARAM_UPDATE_RSP:
|
||||
case L2CAP_LE_CONN_RSP:
|
||||
case L2CAP_ECRED_CONN_RSP:
|
||||
case L2CAP_ECRED_RECONF_RSP:
|
||||
/* First do a lookup since the remote may send bogus ids that
|
||||
|
|
@ -4958,7 +5031,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
goto response_unlock;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan) {
|
||||
result = L2CAP_CR_LE_NO_MEM;
|
||||
goto response_unlock;
|
||||
|
|
@ -4973,8 +5046,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
|||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
|
||||
|
||||
dcid = chan->scid;
|
||||
|
|
@ -5182,7 +5253,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
|||
continue;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan) {
|
||||
result = L2CAP_CR_LE_NO_MEM;
|
||||
continue;
|
||||
|
|
@ -5197,8 +5268,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
|||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
|
||||
|
||||
/* Init response */
|
||||
|
|
@ -6704,6 +6773,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
|
|||
struct l2cap_conn *conn = chan->conn;
|
||||
struct l2cap_le_credits pkt;
|
||||
u16 return_credits = l2cap_le_rx_credits(chan);
|
||||
int ident;
|
||||
|
||||
if (chan->mode != L2CAP_MODE_LE_FLOWCTL &&
|
||||
chan->mode != L2CAP_MODE_EXT_FLOWCTL)
|
||||
|
|
@ -6721,9 +6791,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
|
|||
pkt.cid = cpu_to_le16(chan->scid);
|
||||
pkt.credits = cpu_to_le16(return_credits);
|
||||
|
||||
chan->ident = l2cap_get_ident(conn);
|
||||
ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
|
||||
l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
|
||||
|
||||
/* L2CAP_LE_CREDITS has no response so the ident is never released by
|
||||
* l2cap_put_ident() - release it right away, otherwise the tx_ida
|
||||
* range is exhausted after 254 packets and from then on credits are
|
||||
* sent with the invalid ident 0, which some remote stacks ignore,
|
||||
* stalling the channel.
|
||||
*/
|
||||
if (ident > 0)
|
||||
ida_free(&conn->tx_ida, ident);
|
||||
}
|
||||
|
||||
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail)
|
||||
|
|
@ -7478,14 +7557,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
|||
goto next;
|
||||
|
||||
l2cap_chan_lock(pchan);
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (chan) {
|
||||
bacpy(&chan->src, &hcon->src);
|
||||
bacpy(&chan->dst, &hcon->dst);
|
||||
chan->src_type = bdaddr_src_type(hcon);
|
||||
chan->dst_type = dst_type;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(pchan);
|
||||
|
|
@ -7702,6 +7779,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
|
|||
|
||||
return c;
|
||||
}
|
||||
EXPORT_SYMBOL(l2cap_conn_hold_unless_zero);
|
||||
|
||||
int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle,
|
||||
struct sk_buff *skb, u16 flags)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ static struct bt_sock_list l2cap_sk_list = {
|
|||
static const struct proto_ops l2cap_sock_ops;
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern);
|
||||
int proto, gfp_t prio, int kern,
|
||||
struct l2cap_chan *chan);
|
||||
static void l2cap_sock_cleanup_listen(struct sock *parent);
|
||||
|
||||
bool l2cap_is_socket(struct socket *sock)
|
||||
|
|
@ -1284,6 +1285,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Release the sock's ref on chan and clear the pointer so that the ref is
|
||||
* dropped exactly once even if both l2cap_sock_kill() and
|
||||
* l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
|
||||
* task from dereferencing the now-dead sock pointer.
|
||||
*/
|
||||
static void l2cap_sock_put_chan(struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
|
||||
if (!chan)
|
||||
return;
|
||||
|
||||
chan->data = NULL;
|
||||
l2cap_pi(sk)->chan = NULL;
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
/* Kill socket (only if zapped and orphan)
|
||||
* Must be called on unlocked socket, with l2cap channel lock.
|
||||
*/
|
||||
|
|
@ -1294,13 +1312,9 @@ static void l2cap_sock_kill(struct sock *sk)
|
|||
|
||||
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
|
||||
|
||||
/* Sock is dead, so set chan data to NULL, avoid other task use invalid
|
||||
* sock pointer.
|
||||
*/
|
||||
l2cap_pi(sk)->chan->data = NULL;
|
||||
/* Kill poor orphan */
|
||||
l2cap_sock_put_chan(sk);
|
||||
|
||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||
/* Kill poor orphan */
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
|
@ -1492,8 +1506,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
|
||||
/* Close not yet accepted channels.
|
||||
*
|
||||
* bt_accept_dequeue() now returns sk with an extra reference held
|
||||
* (taken while sk was still locked) so a concurrent l2cap_conn_del()
|
||||
* bt_accept_dequeue() returns sk with its temporary queue-walk
|
||||
* reference held, so a concurrent l2cap_conn_del()
|
||||
* -> l2cap_sock_kill() cannot free sk under us.
|
||||
*
|
||||
* cleanup_listen() runs under the parent sk lock, so unlike
|
||||
|
|
@ -1543,12 +1557,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
}
|
||||
}
|
||||
|
||||
static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
||||
static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
struct sock *sk, *parent = chan->data;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(parent);
|
||||
|
||||
|
|
@ -1556,25 +1571,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
|||
if (sk_acceptq_is_full(parent)) {
|
||||
BT_DBG("backlog full %d", parent->sk_ack_backlog);
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||
GFP_ATOMIC, 0);
|
||||
GFP_ATOMIC, 0, new_chan);
|
||||
if (!sk) {
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
|
||||
|
||||
l2cap_sock_init(sk, parent);
|
||||
|
||||
/* The conn list reference taken by l2cap_new_connection() keeps new_chan
|
||||
* alive once release_sock() lets another task free this socket.
|
||||
*/
|
||||
bt_accept_enqueue(parent, sk, false);
|
||||
|
||||
release_sock(parent);
|
||||
|
||||
return l2cap_pi(sk)->chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
|
|
@ -1871,10 +1889,7 @@ static void l2cap_sock_destruct(struct sock *sk)
|
|||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (l2cap_pi(sk)->chan) {
|
||||
l2cap_pi(sk)->chan->data = NULL;
|
||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||
}
|
||||
l2cap_sock_put_chan(sk);
|
||||
|
||||
list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
|
||||
kfree_skb(rx_busy->skb);
|
||||
|
|
@ -1907,30 +1922,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (parent) {
|
||||
struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
|
||||
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->flags = bt_sk(parent)->flags;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
chan->mode = pchan->mode;
|
||||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
if (chan->chan_type == L2CAP_CHAN_FIXED) {
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = pchan->scid;
|
||||
}
|
||||
|
||||
/* Channel configuration is inherited from the parent by
|
||||
* l2cap_new_connection().
|
||||
*/
|
||||
security_sk_clone(parent, sk);
|
||||
} else {
|
||||
switch (sk->sk_type) {
|
||||
|
|
@ -1956,7 +1953,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
chan->mode = L2CAP_MODE_BASIC;
|
||||
}
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
|
|
@ -1975,10 +1972,10 @@ static struct proto l2cap_proto = {
|
|||
};
|
||||
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern)
|
||||
int proto, gfp_t prio, int kern,
|
||||
struct l2cap_chan *chan)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
|
|
@ -1989,16 +1986,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
|||
|
||||
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan) {
|
||||
sk_free(sk);
|
||||
if (sock)
|
||||
sock->sk = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
|
||||
/* The sock takes ownership of the caller's reference on chan. */
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
return sk;
|
||||
|
|
@ -2008,6 +1996,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||
int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("sock %p", sock);
|
||||
|
||||
|
|
@ -2022,10 +2011,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||
|
||||
sock->ops = &l2cap_sock_ops;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
|
||||
if (!sk) {
|
||||
l2cap_chan_put(chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l2cap_sock_init(sk, NULL);
|
||||
bt_sock_link(&l2cap_sk_list, sk);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -5375,6 +5375,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
|
|||
if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED)
|
||||
monitor->state = ADV_MONITOR_STATE_REGISTERED;
|
||||
hci_update_passive_scan(hdev);
|
||||
} else {
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
}
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
|
||||
|
|
@ -7658,6 +7660,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
|
|||
if (!err) {
|
||||
struct hci_conn_params *params;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
|
||||
le_addr_type(cp->addr.type));
|
||||
|
||||
|
|
@ -7666,6 +7670,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
|
|||
device_flags_changed(NULL, hdev, &cp->addr.bdaddr,
|
||||
cp->addr.type, hdev->conn_flags,
|
||||
params ? params->flags : 0);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE,
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
|
|||
monitor->state = ADV_MONITOR_STATE_OFFLOADED;
|
||||
|
||||
unlock:
|
||||
if (status)
|
||||
if (status && msft->resuming)
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
|
|||
|
|
@ -570,10 +570,23 @@ static void __sco_sock_close(struct sock *sk)
|
|||
/* Must be called on unlocked socket. */
|
||||
static void sco_sock_close(struct sock *sk)
|
||||
{
|
||||
struct sco_conn *conn;
|
||||
|
||||
lock_sock(sk);
|
||||
conn = sco_pi(sk)->conn;
|
||||
if (conn)
|
||||
sco_conn_hold(conn);
|
||||
release_sock(sk);
|
||||
|
||||
if (conn)
|
||||
disable_delayed_work_sync(&conn->timeout_work);
|
||||
|
||||
lock_sock(sk);
|
||||
sco_sock_clear_timer(sk);
|
||||
__sco_sock_close(sk);
|
||||
release_sock(sk);
|
||||
|
||||
if (conn)
|
||||
sco_conn_put(conn);
|
||||
}
|
||||
|
||||
static void sco_sock_init(struct sock *sk, struct sock *parent)
|
||||
|
|
|
|||
|
|
@ -3201,34 +3201,19 @@ static const struct l2cap_ops smp_chan_ops = {
|
|||
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
|
||||
};
|
||||
|
||||
static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
|
||||
static inline int smp_new_conn_cb(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("pchan %p", pchan);
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->ops = &smp_chan_ops;
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = chan->scid;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
new_chan->ops = &smp_chan_ops;
|
||||
|
||||
/* Other L2CAP channels may request SMP routines in order to
|
||||
* change the security level. This means that the SMP channel
|
||||
* lock must be considered in its own category to avoid lockdep
|
||||
* warnings.
|
||||
*/
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
|
||||
atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
|
||||
|
||||
BT_DBG("created chan %p", chan);
|
||||
|
||||
return chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct l2cap_ops smp_root_chan_ops = {
|
||||
|
|
@ -3288,7 +3273,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
|||
|
||||
l2cap_add_scid(chan, cid);
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
|
||||
if (cid == L2CAP_CID_SMP) {
|
||||
u8 bdaddr_type;
|
||||
|
|
|
|||
|
|
@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par,
|
|||
left - sizeof(struct ebt_entry_match) < m->match_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
|
||||
return -EINVAL;
|
||||
|
||||
match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision);
|
||||
if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) {
|
||||
if (!IS_ERR(match))
|
||||
|
|
@ -921,8 +924,7 @@ static int translate_table(struct net *net, const char *name,
|
|||
* if an error occurs
|
||||
*/
|
||||
newinfo->chainstack =
|
||||
vmalloc_array(nr_cpu_ids,
|
||||
sizeof(*(newinfo->chainstack)));
|
||||
vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack)));
|
||||
if (!newinfo->chainstack)
|
||||
return -ENOMEM;
|
||||
for_each_possible_cpu(i) {
|
||||
|
|
@ -1434,6 +1436,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len)
|
|||
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
|
||||
return -EFAULT;
|
||||
|
||||
hlp.name[sizeof(hlp.name) - 1] = '\0';
|
||||
|
||||
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -2273,6 +2277,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
|
|||
|
||||
memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
|
||||
|
||||
repl->name[sizeof(repl->name) - 1] = '\0';
|
||||
|
||||
/* starting with hook_entry, 32 vs. 64 bit structures are different */
|
||||
for (i = 0; i < NF_BR_NUMHOOKS; i++)
|
||||
repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]);
|
||||
|
|
@ -2395,6 +2401,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg,
|
|||
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
|
||||
return -EFAULT;
|
||||
|
||||
hlp.name[sizeof(hlp.name) - 1] = '\0';
|
||||
|
||||
/* try real handler in case userland supplied needed padding */
|
||||
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
|
||||
return update_counters(net, arg, len);
|
||||
|
|
|
|||
|
|
@ -114,6 +114,34 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr,
|
|||
*mod = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ethnl_update_u8_u32() - update u8 value from an NLA_U32 attribute
|
||||
* @dst: value to update
|
||||
* @attr: netlink attribute with new value or null
|
||||
* @mod: pointer to bool for modification tracking
|
||||
*
|
||||
* Some attributes are NLA_U32 on the wire but are stored in a u8. Read the
|
||||
* full 32-bit value from NLA_U32 netlink attribute @attr and narrow it into
|
||||
* the u8 pointed to by @dst; do nothing if @attr is null.
|
||||
* Bool pointed to by @mod is set to true if this function changed the value
|
||||
* of *dst, otherwise it is left as is.
|
||||
*/
|
||||
static inline void ethnl_update_u8_u32(u8 *dst, const struct nlattr *attr,
|
||||
bool *mod)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!attr)
|
||||
return;
|
||||
val = nla_get_u32(attr);
|
||||
DEBUG_NET_WARN_ON_ONCE(val > U8_MAX);
|
||||
if (*dst == val)
|
||||
return;
|
||||
|
||||
*dst = val;
|
||||
*mod = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute
|
||||
* @dst: value to update
|
||||
|
|
|
|||
|
|
@ -570,7 +570,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = {
|
|||
const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = {
|
||||
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
|
||||
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
|
||||
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
|
||||
[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
|
||||
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
|
||||
[ETHTOOL_A_RSS_INPUT_XFRM] =
|
||||
|
|
@ -851,7 +851,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
|
|||
indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
|
||||
|
||||
rxfh.hfunc = data.hfunc;
|
||||
ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
|
||||
ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
|
||||
if (rxfh.hfunc == data.hfunc)
|
||||
rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
|
||||
|
||||
|
|
@ -860,7 +860,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
|
|||
goto exit_free_indir;
|
||||
|
||||
rxfh.input_xfrm = data.input_xfrm;
|
||||
ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
|
||||
ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
|
||||
&mod);
|
||||
xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
|
||||
if (rxfh.input_xfrm == data.input_xfrm)
|
||||
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
|
||||
|
|
@ -934,7 +935,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = {
|
|||
const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = {
|
||||
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
|
||||
[ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
|
||||
[ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1),
|
||||
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
|
||||
[ETHTOOL_A_RSS_INPUT_XFRM] =
|
||||
|
|
@ -1048,14 +1049,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
goto exit_clean_data;
|
||||
indir_user_size = ret;
|
||||
|
||||
ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
|
||||
ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
|
||||
|
||||
ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
|
||||
if (ret)
|
||||
goto exit_free_indir;
|
||||
|
||||
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
|
||||
ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
|
||||
ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
|
||||
&mod);
|
||||
|
||||
ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size);
|
||||
if (!ctx) {
|
||||
|
|
|
|||
|
|
@ -217,13 +217,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf)
|
|||
|
||||
static void igmp_stop_timer(struct ip_mc_list *im)
|
||||
{
|
||||
bool put = false;
|
||||
|
||||
spin_lock_bh(&im->lock);
|
||||
if (timer_delete(&im->timer))
|
||||
refcount_dec(&im->refcnt);
|
||||
put = true;
|
||||
WRITE_ONCE(im->tm_running, 0);
|
||||
WRITE_ONCE(im->reporter, 0);
|
||||
im->unsolicit_count = 0;
|
||||
spin_unlock_bh(&im->lock);
|
||||
|
||||
if (put)
|
||||
ip_ma_put(im);
|
||||
}
|
||||
|
||||
/* It must be called with locked im->lock */
|
||||
|
|
@ -248,20 +253,26 @@ static void igmp_gq_start_timer(struct in_device *in_dev)
|
|||
return;
|
||||
|
||||
in_dev->mr_gq_running = 1;
|
||||
if (!mod_timer(&in_dev->mr_gq_timer, exp))
|
||||
in_dev_hold(in_dev);
|
||||
if (in_dev_hold_safe(in_dev)) {
|
||||
if (mod_timer(&in_dev->mr_gq_timer, exp))
|
||||
in_dev_put(in_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
|
||||
{
|
||||
int tv = get_random_u32_below(delay);
|
||||
if (in_dev_hold_safe(in_dev)) {
|
||||
int tv = get_random_u32_below(delay);
|
||||
|
||||
if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))
|
||||
in_dev_hold(in_dev);
|
||||
if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2))
|
||||
in_dev_put(in_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
|
||||
{
|
||||
bool put = false;
|
||||
|
||||
spin_lock_bh(&im->lock);
|
||||
im->unsolicit_count = 0;
|
||||
if (timer_delete(&im->timer)) {
|
||||
|
|
@ -271,10 +282,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
|
|||
spin_unlock_bh(&im->lock);
|
||||
return;
|
||||
}
|
||||
refcount_dec(&im->refcnt);
|
||||
put = true;
|
||||
}
|
||||
igmp_start_timer(im, max_delay);
|
||||
spin_unlock_bh(&im->lock);
|
||||
|
||||
if (put)
|
||||
ip_ma_put(im);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1922,6 +1936,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev)
|
|||
#endif
|
||||
|
||||
while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) {
|
||||
ip_mc_hash_remove(in_dev, i);
|
||||
in_dev->mc_list = i->next_rcu;
|
||||
WRITE_ONCE(in_dev->mc_count, in_dev->mc_count - 1);
|
||||
ip_mc_clear_src(i);
|
||||
|
|
|
|||
|
|
@ -1083,8 +1083,10 @@ static void mld_gq_start_work(struct inet6_dev *idev)
|
|||
mc_assert_locked(idev);
|
||||
|
||||
idev->mc_gq_running = 1;
|
||||
if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
|
||||
in6_dev_hold(idev);
|
||||
if (in6_dev_hold_safe(idev)) {
|
||||
if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
|
||||
in6_dev_put(idev);
|
||||
}
|
||||
}
|
||||
|
||||
static void mld_gq_stop_work(struct inet6_dev *idev)
|
||||
|
|
@ -1102,8 +1104,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay)
|
|||
|
||||
mc_assert_locked(idev);
|
||||
|
||||
if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
|
||||
in6_dev_hold(idev);
|
||||
if (in6_dev_hold_safe(idev)) {
|
||||
if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
|
||||
in6_dev_put(idev);
|
||||
}
|
||||
}
|
||||
|
||||
static void mld_ifc_stop_work(struct inet6_dev *idev)
|
||||
|
|
@ -1121,8 +1125,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay)
|
|||
|
||||
mc_assert_locked(idev);
|
||||
|
||||
if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
|
||||
in6_dev_hold(idev);
|
||||
if (in6_dev_hold_safe(idev)) {
|
||||
if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
|
||||
in6_dev_put(idev);
|
||||
}
|
||||
}
|
||||
|
||||
static void mld_dad_stop_work(struct inet6_dev *idev)
|
||||
|
|
@ -1395,18 +1401,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
|
|||
void igmp6_event_query(struct sk_buff *skb)
|
||||
{
|
||||
struct inet6_dev *idev = __in6_dev_get(skb->dev);
|
||||
bool put = false;
|
||||
|
||||
if (!idev || idev->dead)
|
||||
goto out;
|
||||
|
||||
spin_lock_bh(&idev->mc_query_lock);
|
||||
if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) {
|
||||
if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS &&
|
||||
in6_dev_hold_safe(idev)) {
|
||||
__skb_queue_tail(&idev->mc_query_queue, skb);
|
||||
if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
|
||||
in6_dev_hold(idev);
|
||||
if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
|
||||
put = true;
|
||||
skb = NULL;
|
||||
}
|
||||
spin_unlock_bh(&idev->mc_query_lock);
|
||||
|
||||
if (put)
|
||||
in6_dev_put(idev);
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
@ -1570,18 +1581,23 @@ static void mld_query_work(struct work_struct *work)
|
|||
void igmp6_event_report(struct sk_buff *skb)
|
||||
{
|
||||
struct inet6_dev *idev = __in6_dev_get(skb->dev);
|
||||
bool put = false;
|
||||
|
||||
if (!idev || idev->dead)
|
||||
goto out;
|
||||
|
||||
spin_lock_bh(&idev->mc_report_lock);
|
||||
if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) {
|
||||
if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS &&
|
||||
in6_dev_hold_safe(idev)) {
|
||||
__skb_queue_tail(&idev->mc_report_queue, skb);
|
||||
if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
|
||||
in6_dev_hold(idev);
|
||||
if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
|
||||
put = true;
|
||||
skb = NULL;
|
||||
}
|
||||
spin_unlock_bh(&idev->mc_report_lock);
|
||||
|
||||
if (put)
|
||||
in6_dev_put(idev);
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
}
|
||||
|
||||
hdrlen = ipv6_authlen(ah);
|
||||
if (skb->len - ptr < hdrlen) {
|
||||
/* Packet smaller than its length field */
|
||||
par->hotdrop = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
|
||||
pr_debug("RES %04X ", ah->reserved);
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
hdrlen = ipv6_optlen(oh);
|
||||
if (skb->len - ptr < hdrlen) {
|
||||
/* Packet smaller than it's length field */
|
||||
par->hotdrop = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
|
||||
hdrlen = ipv6_optlen(rh);
|
||||
if (skb->len - ptr < hdrlen) {
|
||||
/* Pcket smaller than its length field */
|
||||
/* Packet smaller than its length field */
|
||||
par->hotdrop = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
|
|||
skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0];
|
||||
memmove(skb->head + sizeof(struct frag_hdr), skb->head,
|
||||
(skb->data - skb->head) - sizeof(struct frag_hdr));
|
||||
skb->mac_header += sizeof(struct frag_hdr);
|
||||
if (skb_mac_header_was_set(skb))
|
||||
skb->mac_header += sizeof(struct frag_hdr);
|
||||
skb->network_header += sizeof(struct frag_hdr);
|
||||
|
||||
skb_reset_transport_header(skb);
|
||||
|
|
@ -418,7 +419,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
|
|||
return -1;
|
||||
}
|
||||
if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
|
||||
BUG();
|
||||
return -1;
|
||||
if (nexthdr == NEXTHDR_AUTH)
|
||||
hdrlen = ipv6_authlen(&hdr);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -312,6 +312,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
|
|||
/* assign new connection to its SAP */
|
||||
llc_sap_add_socket(sap, sk);
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
llc_sap_put(sap);
|
||||
rc = 0;
|
||||
out:
|
||||
dev_put(dev);
|
||||
|
|
|
|||
|
|
@ -703,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local)
|
|||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
|
||||
list_del(&sdata->list);
|
||||
list_del_rcu(&sdata->list);
|
||||
|
||||
unregister_netdevice(sdata->dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,15 +75,17 @@ struct hbucket {
|
|||
struct htable_gc {
|
||||
struct delayed_work dwork;
|
||||
struct ip_set *set; /* Set the gc belongs to */
|
||||
spinlock_t lock; /* Lock to exclude gc and resize */
|
||||
u32 region; /* Last gc run position */
|
||||
};
|
||||
|
||||
/* The hash table: the table size stored here in order to make resizing easy */
|
||||
struct htable {
|
||||
atomic_t ref; /* References for resizing */
|
||||
bool resizing; /* Mark ongoing resize */
|
||||
atomic_t uref; /* References for dumping and gc */
|
||||
u8 htable_bits; /* size of hash table == 2^htable_bits */
|
||||
u32 maxelem; /* Maxelem per region */
|
||||
struct list_head ad; /* Resize add|del backlist */
|
||||
struct ip_set_region *hregion; /* Region locks and ext sizes */
|
||||
struct hbucket __rcu *bucket[]; /* hashtable buckets */
|
||||
};
|
||||
|
|
@ -301,11 +303,13 @@ struct htype {
|
|||
u8 netmask; /* netmask value for subnets to store */
|
||||
union nf_inet_addr bitmask; /* stores bitmask */
|
||||
#endif
|
||||
struct list_head ad; /* Resize add|del backlist */
|
||||
struct mtype_elem next; /* temporary storage for uadd */
|
||||
#ifdef IP_SET_HASH_WITH_NETS
|
||||
struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */
|
||||
#endif
|
||||
/* Because 'next' is IPv4/IPv6 dependent, no elements of this
|
||||
* structure and referred in create() may come after 'next'.
|
||||
*/
|
||||
struct mtype_elem next; /* temporary storage for uadd */
|
||||
};
|
||||
|
||||
/* ADD|DEL entries saved during resize */
|
||||
|
|
@ -451,13 +455,14 @@ static void
|
|||
mtype_destroy(struct ip_set *set)
|
||||
{
|
||||
struct htype *h = set->data;
|
||||
struct htable *t = (__force struct htable *)h->table;
|
||||
struct list_head *l, *lt;
|
||||
|
||||
mtype_ahash_destroy(set, (__force struct htable *)h->table, true);
|
||||
list_for_each_safe(l, lt, &h->ad) {
|
||||
list_for_each_safe(l, lt, &t->ad) {
|
||||
list_del(l);
|
||||
kfree(l);
|
||||
}
|
||||
mtype_ahash_destroy(set, t, true);
|
||||
kfree(h);
|
||||
|
||||
set->data = NULL;
|
||||
|
|
@ -569,9 +574,10 @@ mtype_gc(struct work_struct *work)
|
|||
set = gc->set;
|
||||
h = set->data;
|
||||
|
||||
spin_lock_bh(&set->lock);
|
||||
t = ipset_dereference_set(h->table, set);
|
||||
rcu_read_lock_bh();
|
||||
t = rcu_dereference_bh(h->table);
|
||||
atomic_inc(&t->uref);
|
||||
rcu_read_unlock_bh();
|
||||
numof_locks = ahash_numof_locks(t->htable_bits);
|
||||
r = gc->region++;
|
||||
if (r >= numof_locks) {
|
||||
|
|
@ -580,11 +586,13 @@ mtype_gc(struct work_struct *work)
|
|||
next_run = (IPSET_GC_PERIOD(set->timeout) * HZ) / numof_locks;
|
||||
if (next_run < HZ/10)
|
||||
next_run = HZ/10;
|
||||
spin_unlock_bh(&set->lock);
|
||||
|
||||
mtype_gc_do(set, h, t, r);
|
||||
spin_lock_bh(&gc->lock);
|
||||
if (!t->resizing)
|
||||
mtype_gc_do(set, h, t, r);
|
||||
spin_unlock_bh(&gc->lock);
|
||||
|
||||
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
|
||||
if (atomic_dec_and_test(&t->uref) && t->resizing) {
|
||||
pr_debug("Table destroy after resize by expire: %p\n", t);
|
||||
mtype_ahash_destroy(set, t, false);
|
||||
}
|
||||
|
|
@ -668,15 +676,18 @@ mtype_resize(struct ip_set *set, bool retried)
|
|||
}
|
||||
t->htable_bits = htable_bits;
|
||||
t->maxelem = h->maxelem / ahash_numof_locks(htable_bits);
|
||||
INIT_LIST_HEAD(&t->ad);
|
||||
for (i = 0; i < ahash_numof_locks(htable_bits); i++)
|
||||
spin_lock_init(&t->hregion[i].lock);
|
||||
|
||||
/* There can't be another parallel resizing,
|
||||
* but dumping, gc, kernel side add/del are possible
|
||||
* but dumping and kernel side add/del are possible
|
||||
*/
|
||||
orig = ipset_dereference_bh_nfnl(h->table);
|
||||
atomic_set(&orig->ref, 1);
|
||||
atomic_inc(&orig->uref);
|
||||
spin_lock_bh(&h->gc.lock);
|
||||
orig->resizing = true;
|
||||
spin_unlock_bh(&h->gc.lock);
|
||||
pr_debug("attempt to resize set %s from %u to %u, t %p\n",
|
||||
set->name, orig->htable_bits, htable_bits, orig);
|
||||
for (r = 0; r < ahash_numof_locks(orig->htable_bits); r++) {
|
||||
|
|
@ -768,7 +779,7 @@ mtype_resize(struct ip_set *set, bool retried)
|
|||
* Kernel-side add cannot trigger a resize and userspace actions
|
||||
* are serialized by the mutex.
|
||||
*/
|
||||
list_for_each_safe(l, lt, &h->ad) {
|
||||
list_for_each_safe(l, lt, &orig->ad) {
|
||||
x = list_entry(l, struct mtype_resize_ad, list);
|
||||
if (x->ad == IPSET_ADD) {
|
||||
mtype_add(set, &x->d, &x->ext, &x->mext, x->flags);
|
||||
|
|
@ -792,11 +803,24 @@ mtype_resize(struct ip_set *set, bool retried)
|
|||
|
||||
cleanup:
|
||||
rcu_read_unlock_bh();
|
||||
atomic_set(&orig->ref, 0);
|
||||
spin_lock_bh(&h->gc.lock);
|
||||
orig->resizing = false;
|
||||
spin_unlock_bh(&h->gc.lock);
|
||||
/* Make sure parallel readers see that orig->resizing is false
|
||||
* before we decrement uref */
|
||||
synchronize_rcu();
|
||||
atomic_dec(&orig->uref);
|
||||
mtype_ahash_destroy(set, t, false);
|
||||
if (ret == -EAGAIN)
|
||||
goto retry;
|
||||
|
||||
/* Cleanup the backlog of ADD/DEL elements */
|
||||
spin_lock_bh(&set->lock);
|
||||
list_for_each_safe(l, lt, &orig->ad) {
|
||||
list_del(l);
|
||||
kfree(l);
|
||||
}
|
||||
spin_unlock_bh(&set->lock);
|
||||
goto out;
|
||||
|
||||
hbwarn:
|
||||
|
|
@ -860,15 +884,13 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
key = HKEY(value, h->initval, t->htable_bits);
|
||||
r = ahash_region(key);
|
||||
atomic_inc(&t->uref);
|
||||
rcu_read_unlock_bh();
|
||||
elements = t->hregion[r].elements;
|
||||
maxelem = t->maxelem;
|
||||
if (elements >= maxelem) {
|
||||
u32 e;
|
||||
if (SET_WITH_TIMEOUT(set)) {
|
||||
rcu_read_unlock_bh();
|
||||
if (SET_WITH_TIMEOUT(set))
|
||||
mtype_gc_do(set, h, t, r);
|
||||
rcu_read_lock_bh();
|
||||
}
|
||||
maxelem = h->maxelem;
|
||||
elements = 0;
|
||||
for (e = 0; e < ahash_numof_locks(t->htable_bits); e++)
|
||||
|
|
@ -876,7 +898,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
if (elements >= maxelem && SET_WITH_FORCEADD(set))
|
||||
forceadd = true;
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
spin_lock_bh(&t->hregion[r].lock);
|
||||
n = rcu_dereference_bh(hbucket(t, key));
|
||||
|
|
@ -1003,7 +1024,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
ret = 0;
|
||||
resize:
|
||||
spin_unlock_bh(&t->hregion[r].lock);
|
||||
if (atomic_read(&t->ref) && ext->target) {
|
||||
if (t->resizing && ext && ext->target) {
|
||||
/* Resize is in process and kernel side add, save values */
|
||||
struct mtype_resize_ad *x;
|
||||
|
||||
|
|
@ -1017,7 +1038,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
memcpy(&x->mext, mext, sizeof(struct ip_set_ext));
|
||||
x->flags = flags;
|
||||
spin_lock_bh(&set->lock);
|
||||
list_add_tail(&x->list, &h->ad);
|
||||
list_add_tail(&x->list, &t->ad);
|
||||
spin_unlock_bh(&set->lock);
|
||||
}
|
||||
goto out;
|
||||
|
|
@ -1030,7 +1051,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
unlock:
|
||||
spin_unlock_bh(&t->hregion[r].lock);
|
||||
out:
|
||||
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
|
||||
if (atomic_dec_and_test(&t->uref) && t->resizing) {
|
||||
pr_debug("Table destroy after resize by add: %p\n", t);
|
||||
mtype_ahash_destroy(set, t, false);
|
||||
}
|
||||
|
|
@ -1093,7 +1114,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
#endif
|
||||
ip_set_ext_destroy(set, data);
|
||||
|
||||
if (atomic_read(&t->ref) && ext->target) {
|
||||
if (t->resizing && ext && ext->target) {
|
||||
/* Resize is in process and kernel side del,
|
||||
* save values
|
||||
*/
|
||||
|
|
@ -1141,10 +1162,10 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
|||
spin_unlock_bh(&t->hregion[r].lock);
|
||||
if (x) {
|
||||
spin_lock_bh(&set->lock);
|
||||
list_add(&x->list, &h->ad);
|
||||
list_add(&x->list, &t->ad);
|
||||
spin_unlock_bh(&set->lock);
|
||||
}
|
||||
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
|
||||
if (atomic_dec_and_test(&t->uref) && t->resizing) {
|
||||
pr_debug("Table destroy after resize by del: %p\n", t);
|
||||
mtype_ahash_destroy(set, t, false);
|
||||
}
|
||||
|
|
@ -1353,7 +1374,7 @@ mtype_uref(struct ip_set *set, struct netlink_callback *cb, bool start)
|
|||
rcu_read_unlock_bh();
|
||||
} else if (cb->args[IPSET_CB_PRIVATE]) {
|
||||
t = (struct htable *)cb->args[IPSET_CB_PRIVATE];
|
||||
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
|
||||
if (atomic_dec_and_test(&t->uref) && t->resizing) {
|
||||
pr_debug("Table destroy after resize "
|
||||
" by dump: %p\n", t);
|
||||
mtype_ahash_destroy(set, t, false);
|
||||
|
|
@ -1566,7 +1587,13 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
|
|||
if (tb[IPSET_ATTR_MAXELEM])
|
||||
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
|
||||
|
||||
hsize = sizeof(*h);
|
||||
#ifdef IP_SET_PROTO_UNDEF
|
||||
hsize = sizeof(struct htype);
|
||||
#else
|
||||
hsize = set->family == NFPROTO_IPV6 ?
|
||||
sizeof(struct IPSET_TOKEN(HTYPE, 6)) :
|
||||
sizeof(struct IPSET_TOKEN(HTYPE, 4));
|
||||
#endif
|
||||
h = kzalloc(hsize, GFP_KERNEL);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
|
@ -1593,6 +1620,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
|
|||
return -ENOMEM;
|
||||
}
|
||||
h->gc.set = set;
|
||||
spin_lock_init(&h->gc.lock);
|
||||
for (i = 0; i < ahash_numof_locks(hbits); i++)
|
||||
spin_lock_init(&t->hregion[i].lock);
|
||||
h->maxelem = maxelem;
|
||||
|
|
@ -1619,9 +1647,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
|
|||
}
|
||||
t->htable_bits = hbits;
|
||||
t->maxelem = h->maxelem / ahash_numof_locks(hbits);
|
||||
INIT_LIST_HEAD(&t->ad);
|
||||
RCU_INIT_POINTER(h->table, t);
|
||||
|
||||
INIT_LIST_HEAD(&h->ad);
|
||||
set->data = h;
|
||||
#ifndef IP_SET_PROTO_UNDEF
|
||||
if (set->family == NFPROTO_IPV4) {
|
||||
|
|
|
|||
|
|
@ -1420,8 +1420,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af,
|
|||
cp->app = NULL;
|
||||
cp->app_data = NULL;
|
||||
/* reset struct ip_vs_seq */
|
||||
cp->in_seq.delta = 0;
|
||||
cp->out_seq.delta = 0;
|
||||
memset(&cp->in_seq, 0, sizeof(cp->in_seq));
|
||||
memset(&cp->out_seq, 0, sizeof(cp->out_seq));
|
||||
|
||||
if (unlikely(flags & IP_VS_CONN_F_NO_CPORT)) {
|
||||
int af_id = ip_vs_af_index(cp->af);
|
||||
|
|
|
|||
|
|
@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
|
|||
static inline void
|
||||
ip_vs_set_state(struct ip_vs_conn *cp, int direction,
|
||||
const struct sk_buff *skb,
|
||||
struct ip_vs_proto_data *pd)
|
||||
struct ip_vs_proto_data *pd, unsigned int iph_len)
|
||||
{
|
||||
if (likely(pd->pp->state_transition))
|
||||
pd->pp->state_transition(cp, direction, skb, pd);
|
||||
pd->pp->state_transition(cp, direction, skb, pd, iph_len);
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
|
@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
|
|||
ip_vs_in_stats(cp, skb);
|
||||
|
||||
/* set state */
|
||||
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
|
||||
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len);
|
||||
|
||||
/* transmit the first SYN packet */
|
||||
ret = cp->packet_xmit(skb, cp, pd->pp, iph);
|
||||
|
|
@ -1484,7 +1484,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
|
|||
|
||||
after_nat:
|
||||
ip_vs_out_stats(cp, skb);
|
||||
ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
|
||||
ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len);
|
||||
skb->ipvs_property = 1;
|
||||
if (!(cp->flags & IP_VS_CONN_F_NFCT))
|
||||
ip_vs_notrack(skb);
|
||||
|
|
@ -1767,6 +1767,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
bool tunnel, new_cp = false;
|
||||
union nf_inet_addr *raddr;
|
||||
char *outer_proto = "IPIP";
|
||||
unsigned int hlen_ipip;
|
||||
int ulen = 0;
|
||||
|
||||
*related = 1;
|
||||
|
||||
|
|
@ -1803,9 +1805,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
/* Now find the contained IP header */
|
||||
offset += sizeof(_icmph);
|
||||
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
|
||||
if (cih == NULL)
|
||||
if (!(cih && cih->version == 4 && cih->ihl >= 5))
|
||||
return NF_ACCEPT; /* The packet looks wrong, ignore */
|
||||
raddr = (union nf_inet_addr *)&cih->daddr;
|
||||
hlen_ipip = cih->ihl * 4;
|
||||
|
||||
/* Special case for errors for IPIP/UDP/GRE tunnel packets */
|
||||
tunnel = false;
|
||||
|
|
@ -1821,9 +1824,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
/* Only for known tunnel */
|
||||
if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
|
||||
return NF_ACCEPT;
|
||||
offset += cih->ihl * 4;
|
||||
offset += hlen_ipip;
|
||||
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
|
||||
if (cih == NULL)
|
||||
if (!(cih && cih->version == 4 && cih->ihl >= 5))
|
||||
return NF_ACCEPT; /* The packet looks wrong, ignore */
|
||||
tunnel = true;
|
||||
} else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */
|
||||
|
|
@ -1831,12 +1834,11 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
/* Error for our tunnel must arrive at LOCAL_IN */
|
||||
(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) {
|
||||
__u8 iproto;
|
||||
int ulen;
|
||||
|
||||
/* Non-first fragment has no UDP/GRE header */
|
||||
if (unlikely(cih->frag_off & htons(IP_OFFSET)))
|
||||
return NF_ACCEPT;
|
||||
offset2 = offset + cih->ihl * 4;
|
||||
offset2 = offset + hlen_ipip;
|
||||
if (cih->protocol == IPPROTO_UDP) {
|
||||
ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET,
|
||||
raddr, &iproto);
|
||||
|
|
@ -1905,6 +1907,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
}
|
||||
|
||||
if (tunnel) {
|
||||
unsigned int hlen_orig = cih->ihl * 4;
|
||||
__be32 info = ic->un.gateway;
|
||||
__u8 type = ic->type;
|
||||
__u8 code = ic->code;
|
||||
|
|
@ -1921,6 +1924,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
goto ignore_tunnel;
|
||||
offset2 -= ihl + sizeof(_icmph);
|
||||
skb_reset_network_header(skb);
|
||||
/* Ensure the IP header is present in headroom */
|
||||
if (!pskb_may_pull(skb, hlen_ipip))
|
||||
goto ignore_tunnel;
|
||||
IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n",
|
||||
outer_proto, &ip_hdr(skb)->saddr,
|
||||
&ip_hdr(skb)->daddr, mtu);
|
||||
|
|
@ -1936,8 +1942,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
if (dest_dst)
|
||||
mtu = dst_mtu(dest_dst->dst_cache);
|
||||
}
|
||||
if (mtu > 68 + sizeof(struct iphdr))
|
||||
mtu -= sizeof(struct iphdr);
|
||||
if (mtu > 68 + hlen_ipip + ulen)
|
||||
mtu -= hlen_ipip + ulen;
|
||||
info = htonl(mtu);
|
||||
}
|
||||
/* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
|
||||
|
|
@ -1946,6 +1952,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
|
|||
if (pskb_pull(skb, offset2) == NULL)
|
||||
goto ignore_tunnel;
|
||||
skb_reset_network_header(skb);
|
||||
/* Ensure the IP header is present in headroom */
|
||||
if (!pskb_may_pull(skb, hlen_orig))
|
||||
goto ignore_tunnel;
|
||||
IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
|
||||
&ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
|
||||
type, code, ntohl(info));
|
||||
|
|
@ -2233,7 +2242,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
|
|||
IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet");
|
||||
|
||||
ip_vs_in_stats(cp, skb);
|
||||
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
|
||||
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len);
|
||||
if (cp->packet_xmit)
|
||||
ret = cp->packet_xmit(skb, cp, pp, &iph);
|
||||
/* do not touch skb anymore */
|
||||
|
|
|
|||
|
|
@ -372,20 +372,15 @@ static const char *sctp_state_name(int state)
|
|||
|
||||
static inline void
|
||||
set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
|
||||
int direction, const struct sk_buff *skb)
|
||||
int direction, const struct sk_buff *skb,
|
||||
unsigned int iph_len)
|
||||
{
|
||||
struct sctp_chunkhdr _sctpch, *sch;
|
||||
unsigned char chunk_type;
|
||||
int event, next_state;
|
||||
int ihl, cofs;
|
||||
int cofs;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
|
||||
#else
|
||||
ihl = ip_hdrlen(skb);
|
||||
#endif
|
||||
|
||||
cofs = ihl + sizeof(struct sctphdr);
|
||||
cofs = iph_len + sizeof(struct sctphdr);
|
||||
sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch);
|
||||
if (sch == NULL)
|
||||
return;
|
||||
|
|
@ -468,10 +463,11 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
|
|||
|
||||
static void
|
||||
sctp_state_transition(struct ip_vs_conn *cp, int direction,
|
||||
const struct sk_buff *skb, struct ip_vs_proto_data *pd)
|
||||
const struct sk_buff *skb, struct ip_vs_proto_data *pd,
|
||||
unsigned int iph_len)
|
||||
{
|
||||
spin_lock_bh(&cp->lock);
|
||||
set_sctp_state(pd, cp, direction, skb);
|
||||
set_sctp_state(pd, cp, direction, skb, iph_len);
|
||||
spin_unlock_bh(&cp->lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -579,17 +579,12 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
|
|||
static void
|
||||
tcp_state_transition(struct ip_vs_conn *cp, int direction,
|
||||
const struct sk_buff *skb,
|
||||
struct ip_vs_proto_data *pd)
|
||||
struct ip_vs_proto_data *pd,
|
||||
unsigned int iph_len)
|
||||
{
|
||||
struct tcphdr _tcph, *th;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
|
||||
#else
|
||||
int ihl = ip_hdrlen(skb);
|
||||
#endif
|
||||
|
||||
th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph);
|
||||
th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -444,7 +444,8 @@ static const char * udp_state_name(int state)
|
|||
static void
|
||||
udp_state_transition(struct ip_vs_conn *cp, int direction,
|
||||
const struct sk_buff *skb,
|
||||
struct ip_vs_proto_data *pd)
|
||||
struct ip_vs_proto_data *pd,
|
||||
unsigned int iph_len)
|
||||
{
|
||||
if (unlikely(!pd)) {
|
||||
pr_err("UDP no ns data\n");
|
||||
|
|
|
|||
|
|
@ -127,12 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow,
|
|||
|
||||
switch (route->tuple[dir].xmit_type) {
|
||||
case FLOW_OFFLOAD_XMIT_DIRECT:
|
||||
if (flow_tuple->tun_num) {
|
||||
flow_tuple->dst_cache = dst;
|
||||
flow_tuple->dst_cookie =
|
||||
flow_offload_dst_cookie(flow_tuple);
|
||||
}
|
||||
memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest,
|
||||
ETH_ALEN);
|
||||
memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
|
||||
ETH_ALEN);
|
||||
flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
|
||||
dst_release(dst);
|
||||
if (!flow_tuple->tun_num)
|
||||
dst_release(dst);
|
||||
break;
|
||||
case FLOW_OFFLOAD_XMIT_XFRM:
|
||||
case FLOW_OFFLOAD_XMIT_NEIGH:
|
||||
|
|
@ -152,9 +158,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
|
|||
static void nft_flow_dst_release(struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir)
|
||||
{
|
||||
if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
|
||||
flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
|
||||
dst_release(flow->tuplehash[dir].tuple.dst_cache);
|
||||
dst_release(flow->tuplehash[dir].tuple.dst_cache);
|
||||
}
|
||||
|
||||
void flow_offload_route_init(struct flow_offload *flow,
|
||||
|
|
@ -345,10 +349,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
|
|||
|
||||
nf_ct_refresh(flow->ct, NF_CT_DAY);
|
||||
|
||||
if (nf_flowtable_hw_offload(flow_table)) {
|
||||
__set_bit(NF_FLOW_HW, &flow->flags);
|
||||
if (nf_flowtable_hw_offload(flow_table))
|
||||
nf_flow_offload_add(flow_table, flow);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -369,7 +371,8 @@ void flow_offload_refresh(struct nf_flowtable *flow_table,
|
|||
test_bit(NF_FLOW_CLOSING, &flow->flags))
|
||||
return;
|
||||
|
||||
nf_flow_offload_add(flow_table, flow);
|
||||
if (test_bit(NF_FLOW_HW, &flow->flags))
|
||||
nf_flow_offload_refresh(flow_table, flow);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(flow_offload_refresh);
|
||||
|
||||
|
|
|
|||
|
|
@ -299,8 +299,7 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
|
|||
|
||||
static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
|
||||
{
|
||||
if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH &&
|
||||
tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM)
|
||||
if (!tuple->dst_cache)
|
||||
return true;
|
||||
|
||||
return dst_check(tuple->dst_cache, tuple->dst_cookie);
|
||||
|
|
@ -590,10 +589,10 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,
|
|||
|
||||
static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
|
||||
struct flow_offload_tuple *tuple,
|
||||
__be32 *ip_daddr)
|
||||
struct dst_entry *dst, __be32 *ip_daddr)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
|
||||
struct rtable *rt = dst_rtable(tuple->dst_cache);
|
||||
struct rtable *rt = dst_rtable(dst);
|
||||
u8 tos = iph->tos, ttl = iph->ttl;
|
||||
__be16 frag_off = iph->frag_off;
|
||||
u32 headroom = sizeof(*iph);
|
||||
|
|
@ -636,21 +635,22 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
|
|||
|
||||
static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
|
||||
struct flow_offload_tuple *tuple,
|
||||
__be32 *ip_daddr)
|
||||
struct dst_entry *dst, __be32 *ip_daddr)
|
||||
{
|
||||
if (tuple->tun_num)
|
||||
return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
|
||||
return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
|
||||
struct flow_offload_tuple *tuple,
|
||||
struct dst_entry *dst,
|
||||
struct in6_addr **ip6_daddr)
|
||||
{
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
|
||||
struct rtable *rt = dst_rtable(tuple->dst_cache);
|
||||
__u8 dsfield = ipv6_get_dsfield(ip6h);
|
||||
struct rtable *rt = dst_rtable(dst);
|
||||
struct flowi6 fl6 = {
|
||||
.daddr = tuple->tun.src_v6,
|
||||
.saddr = tuple->tun.dst_v6,
|
||||
|
|
@ -696,10 +696,11 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
|
|||
|
||||
static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
|
||||
struct flow_offload_tuple *tuple,
|
||||
struct dst_entry *dst,
|
||||
struct in6_addr **ip6_daddr)
|
||||
{
|
||||
if (tuple->tun_num)
|
||||
return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
|
||||
return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -842,7 +843,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
|
|||
other_tuple = &flow->tuplehash[!dir].tuple;
|
||||
ip_daddr = other_tuple->src_v4.s_addr;
|
||||
|
||||
if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0)
|
||||
if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple,
|
||||
tuplehash->tuple.dst_cache, &ip_daddr) < 0)
|
||||
return NF_DROP;
|
||||
|
||||
switch (tuplehash->tuple.xmit_type) {
|
||||
|
|
@ -1158,6 +1160,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
|
|||
ip6_daddr = &other_tuple->src_v6;
|
||||
|
||||
if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
|
||||
tuplehash->tuple.dst_cache,
|
||||
&ip6_daddr) < 0)
|
||||
return NF_DROP;
|
||||
|
||||
|
|
|
|||
|
|
@ -1101,9 +1101,17 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable,
|
|||
return offload;
|
||||
}
|
||||
|
||||
static bool nf_flow_offload_unsupported(struct flow_offload *flow)
|
||||
{
|
||||
if (flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.tun_num ||
|
||||
flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.tun_num)
|
||||
return true;
|
||||
|
||||
void nf_flow_offload_add(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow)
|
||||
return false;
|
||||
}
|
||||
|
||||
void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow)
|
||||
{
|
||||
struct flow_offload_work *offload;
|
||||
|
||||
|
|
@ -1114,6 +1122,16 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable,
|
|||
flow_offload_queue_work(offload);
|
||||
}
|
||||
|
||||
void nf_flow_offload_add(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow)
|
||||
{
|
||||
if (nf_flow_offload_unsupported(flow))
|
||||
return;
|
||||
|
||||
set_bit(NF_FLOW_HW, &flow->flags);
|
||||
nf_flow_offload_refresh(flowtable, flow);
|
||||
}
|
||||
|
||||
void nf_flow_offload_del(struct nf_flowtable *flowtable,
|
||||
struct flow_offload *flow)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -289,13 +289,24 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
|
|||
|
||||
/* Mangle destination port for Cisco phones, then fix up checksums */
|
||||
if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) {
|
||||
int doff = *dptr - (const char *)skb->data;
|
||||
struct udphdr *uh;
|
||||
|
||||
if (doff <= 0) {
|
||||
DEBUG_NET_WARN_ON_ONCE(1);
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
/* ct_sip_info->forced_dport only expected with UDP */
|
||||
if (nf_ct_protonum(ct) != IPPROTO_UDP)
|
||||
return NF_DROP;
|
||||
|
||||
if (skb_ensure_writable(skb, skb->len)) {
|
||||
nf_ct_helper_log(skb, ct, "cannot mangle packet");
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
*dptr = skb->data + doff;
|
||||
uh = (void *)skb->data + protoff;
|
||||
uh->dest = ct_sip_info->forced_dport;
|
||||
|
||||
|
|
|
|||
|
|
@ -6563,6 +6563,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!elem.priv)
|
||||
return 0;
|
||||
|
||||
err = -ENOMEM;
|
||||
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
|
|
|
|||
|
|
@ -316,6 +316,8 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy,
|
|||
|
||||
new_policy->max_expected =
|
||||
ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
|
||||
if (!new_policy->max_expected)
|
||||
new_policy->max_expected = NF_CT_EXPECT_MAX_CNT;
|
||||
if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -676,7 +676,7 @@ __build_packet_message(struct nfnl_log_net *log,
|
|||
goto nla_put_failure;
|
||||
|
||||
if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
|
||||
BUG();
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
nlh->nlmsg_len = inst->skb->tail - old_tail;
|
||||
|
|
@ -698,6 +698,21 @@ static const struct nf_loginfo default_loginfo = {
|
|||
},
|
||||
};
|
||||
|
||||
static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int copy_len)
|
||||
{
|
||||
unsigned int len = skb->len;
|
||||
|
||||
if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) &&
|
||||
li->u.ulog.copy_len < copy_len)
|
||||
copy_len = li->u.ulog.copy_len;
|
||||
if (!skb_frags_readable(skb))
|
||||
len = skb_headlen(skb);
|
||||
|
||||
return min(len, copy_len);
|
||||
}
|
||||
|
||||
/* log handler for internal netfilter logging api */
|
||||
static void
|
||||
nfulnl_log_packet(struct net *net,
|
||||
|
|
@ -790,14 +805,7 @@ nfulnl_log_packet(struct net *net,
|
|||
break;
|
||||
|
||||
case NFULNL_COPY_PACKET:
|
||||
data_len = inst->copy_range;
|
||||
if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) &&
|
||||
(li->u.ulog.copy_len < data_len))
|
||||
data_len = li->u.ulog.copy_len;
|
||||
|
||||
if (data_len > skb->len)
|
||||
data_len = skb->len;
|
||||
|
||||
data_len = nfulnl_get_copy_len(li, skb, inst->copy_range);
|
||||
size += nla_total_size(data_len);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -690,6 +690,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr,
|
|||
}
|
||||
#endif
|
||||
|
||||
static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb,
|
||||
unsigned int copy_range)
|
||||
{
|
||||
unsigned int data_len = entskb->len;
|
||||
|
||||
if (!skb_frags_readable(entskb))
|
||||
data_len = skb_headlen(entskb);
|
||||
|
||||
return min(data_len, copy_range);
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
||||
struct nf_queue_entry *entry,
|
||||
|
|
@ -755,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
|||
nf_queue_checksum_help(entskb))
|
||||
return NULL;
|
||||
|
||||
data_len = READ_ONCE(queue->copy_range);
|
||||
if (data_len > entskb->len)
|
||||
data_len = entskb->len;
|
||||
|
||||
data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range));
|
||||
hlen = skb_zerocopy_headlen(entskb);
|
||||
hlen = min_t(unsigned int, hlen, data_len);
|
||||
size += sizeof(struct nlattr) + hlen;
|
||||
|
|
|
|||
|
|
@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr,
|
|||
bool found;
|
||||
|
||||
ext = nft_set_do_lookup(net, set, ®s->data[priv->sreg]);
|
||||
if (!ext)
|
||||
ext = nft_set_catchall_lookup(net, set);
|
||||
|
||||
found = !!ext ^ priv->invert;
|
||||
if (!found) {
|
||||
ext = nft_set_catchall_lookup(net, set);
|
||||
if (!ext) {
|
||||
regs->verdict.code = NFT_BREAK;
|
||||
return;
|
||||
}
|
||||
regs->verdict.code = NFT_BREAK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ext) {
|
||||
|
|
|
|||
|
|
@ -184,10 +184,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,
|
|||
if (!interval || nft_set_elem_expired(interval->from))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if (flags & NFT_SET_ELEM_INTERVAL_END)
|
||||
if (flags & NFT_SET_ELEM_INTERVAL_END) {
|
||||
if (!interval->to)
|
||||
return NULL;
|
||||
|
||||
rbe = container_of(interval->to, struct nft_rbtree_elem, ext);
|
||||
else
|
||||
} else {
|
||||
rbe = container_of(interval->from, struct nft_rbtree_elem, ext);
|
||||
}
|
||||
|
||||
return &rbe->priv;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user