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:
Linus Torvalds 2026-07-09 08:26:51 -07:00
commit 2c7c88a412
112 changed files with 1407 additions and 745 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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];

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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:

View File

@ -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++)

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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__ */

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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_ */

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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)
{

View File

@ -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 */

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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)

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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 */

View File

@ -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);
}

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr,
bool found;
ext = nft_set_do_lookup(net, set, &regs->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) {

View File

@ -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