summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.c47
-rw-r--r--drivers/ata/libata-core.c1
-rw-r--r--drivers/ata/pata_sl82c105.c4
-rw-r--r--drivers/dibs/dibs_main.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c54
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c48
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c21
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c5
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c5
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c27
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c6
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c3
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c7
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h6
-rw-r--r--drivers/gpu/drm/v3d/v3d_sched.c9
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c3
-rw-r--r--drivers/gpu/drm/xe/xe_uc.c7
-rw-r--r--drivers/hwmon/hwmon.c6
-rw-r--r--drivers/net/bonding/bond_alb.c10
-rw-r--r--drivers/net/ethernet/amd/pds_core/core.h1
-rw-r--r--drivers/net/ethernet/amd/pds_core/main.c14
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ring.c51
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ring.h1
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_vec.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_auxr.c9
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_netdev.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c64
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h1
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c7
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c5
-rw-r--r--drivers/net/ethernet/hisilicon/hix5hd2_gmac.c1
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c6
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c10
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c6
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_pci.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c29
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c22
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c13
-rw-r--r--drivers/net/ovpn/main.c64
-rw-r--r--drivers/net/ovpn/netlink-gen.c20
-rw-r--r--drivers/net/ovpn/netlink.c6
-rw-r--r--drivers/net/ovpn/peer.c172
-rw-r--r--drivers/net/ovpn/peer.h1
-rw-r--r--drivers/net/ovpn/socket.c9
-rw-r--r--drivers/net/phy/mediatek/mtk.h8
-rw-r--r--drivers/net/tap.c24
-rw-r--r--drivers/net/thunderbolt/main.c33
-rw-r--r--drivers/net/usb/ax88179_178a.c4
-rw-r--r--drivers/net/usb/ipheth.c11
-rw-r--r--drivers/net/usb/usbnet.c3
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq8064.c8
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq9650.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.h5
-rw-r--r--drivers/ptp/ptp_ocp.c4
-rw-r--r--drivers/s390/cio/vfio_ccw_async.c16
-rw-r--r--drivers/s390/cio/vfio_ccw_chp.c31
-rw-r--r--drivers/s390/cio/vfio_ccw_cp.c74
-rw-r--r--drivers/s390/cio/vfio_ccw_cp.h10
-rw-r--r--drivers/s390/cio/vfio_ccw_drv.c21
-rw-r--r--drivers/s390/cio/vfio_ccw_fsm.c8
-rw-r--r--drivers/s390/cio/vfio_ccw_ops.c39
-rw-r--r--drivers/s390/cio/vfio_ccw_private.h10
-rw-r--r--drivers/s390/net/ism_drv.c19
-rw-r--r--drivers/s390/net/qeth_core_main.c3
-rw-r--r--drivers/s390/net/qeth_l3_main.c5
-rw-r--r--drivers/soc/aspeed/Makefile1
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-snoop.c38
-rw-r--r--drivers/soundwire/intel_ace2x.c4
-rw-r--r--drivers/thermal/thermal_hwmon.c224
-rw-r--r--drivers/vdpa/mlx5/core/mlx5_vdpa.h2
-rw-r--r--drivers/vdpa/mlx5/core/mr.c8
-rw-r--r--drivers/vdpa/mlx5/core/resources.c11
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim.c10
-rw-r--r--drivers/vdpa/vdpa_user/iova_domain.c11
-rw-r--r--drivers/vhost/iotlb.c47
-rw-r--r--drivers/vhost/scsi.c20
-rw-r--r--drivers/vhost/vdpa.c18
-rw-r--r--drivers/vhost/vhost.c16
101 files changed, 1342 insertions, 425 deletions
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index 4628a2787265..1c63eff0a4a8 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.c
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -436,6 +436,23 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj)
amdxdna_gem_destroy_obj(abo);
}
+static void amdxdna_mark_mapp_invalid(struct amdxdna_gem_obj *abo,
+ struct vm_area_struct *vma)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ struct amdxdna_umap *mapp;
+
+ down_write(&xdna->notifier_lock);
+ abo->mem.map_invalid = true;
+ list_for_each_entry(mapp, &abo->mem.umap_list, node) {
+ if (compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) {
+ mapp->invalid = true;
+ break;
+ }
+ }
+ up_write(&xdna->notifier_lock);
+}
+
static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
struct vm_area_struct *vma)
{
@@ -450,26 +467,17 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
XDNA_ERR(xdna, "Failed shmem mmap %d", ret);
return ret;
}
-
- /* The buffer is based on memory pages. Fix the flag. */
- vm_flags_mod(vma, VM_MIXEDMAP, VM_PFNMAP);
- ret = vm_insert_pages(vma, vma->vm_start, abo->base.pages,
- &num_pages);
+ } else {
+ vma->vm_private_data = NULL;
+ vma->vm_ops = NULL;
+ ret = dma_buf_mmap(abo->dma_buf, vma, 0);
if (ret) {
- XDNA_ERR(xdna, "Failed insert pages %d", ret);
- vma->vm_ops->close(vma);
+ XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret);
return ret;
}
- return 0;
- }
-
- vma->vm_private_data = NULL;
- vma->vm_ops = NULL;
- ret = dma_buf_mmap(abo->dma_buf, vma, 0);
- if (ret) {
- XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret);
- return ret;
+ /* Drop the reference drm_gem_mmap_obj() acquired.*/
+ drm_gem_object_put(to_gobj(abo));
}
do {
@@ -478,17 +486,14 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
fault_ret = handle_mm_fault(vma, vma->vm_start + offset,
FAULT_FLAG_WRITE, NULL);
if (fault_ret & VM_FAULT_ERROR) {
- vma->vm_ops->close(vma);
XDNA_ERR(xdna, "Fault in page failed");
- return -EFAULT;
+ amdxdna_mark_mapp_invalid(abo, vma);
+ break;
}
offset += PAGE_SIZE;
} while (--num_pages);
- /* Drop the reference drm_gem_mmap_obj() acquired.*/
- drm_gem_object_put(to_gobj(abo));
-
return 0;
}
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 961d03fbc4d9..59e87b987d73 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4420,6 +4420,7 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = {
*/
{ "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM },
{ "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM },
+ { "WDC WD141KFGX-68FH9N0", NULL, ATA_QUIRK_NOLPM },
{ "WD Green 2.5 480GB", NULL, ATA_QUIRK_NOLPM },
/*
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index 2d24c6b3e9d9..25bce3fe2a10 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -264,6 +264,7 @@ static struct ata_port_operations sl82c105_port_ops = {
static int sl82c105_bridge_revision(struct pci_dev *pdev)
{
struct pci_dev *bridge;
+ u8 revision;
/*
* The bridge should be part of the same device, but function 0.
@@ -285,8 +286,9 @@ static int sl82c105_bridge_revision(struct pci_dev *pdev)
/*
* We need to find function 0's revision, not function 1
*/
+ revision = bridge->revision;
pci_dev_put(bridge);
- return bridge->revision;
+ return revision;
}
static void sl82c105_fixup(struct pci_dev *pdev)
diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c
index 14c3e2d84902..4c26fd06973f 100644
--- a/drivers/dibs/dibs_main.c
+++ b/drivers/dibs/dibs_main.c
@@ -138,6 +138,7 @@ struct dibs_dev *dibs_dev_alloc(void)
dibs = kzalloc_obj(*dibs);
if (!dibs)
return dibs;
+ spin_lock_init(&dibs->lock);
dibs->dev.release = dibs_dev_release;
dibs->dev.class = &dibs_class;
device_initialize(&dibs->dev);
@@ -186,7 +187,6 @@ int dibs_dev_add(struct dibs_dev *dibs)
int i, ret;
max_dmbs = dibs->ops->max_dmbs();
- spin_lock_init(&dibs->lock);
dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL);
if (!dibs->dmb_clientid_arr)
return -ENOMEM;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 5445f75741b5..f8bf0f6b5097 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -42,6 +42,26 @@
#include "amdgpu_ras.h"
#include "amdgpu_hmm.h"
+/*
+ * Maximum IB length (dwords) for rings whose emit_ib packet format
+ * documents a 20-bit size field.
+ */
+#define AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW 0xFFFFF
+#define AMDGPU_MM_IB_PACKET_SIZE_MAX_DW 0x7FFFF0
+
+static u32 amdgpu_cs_ib_packet_size_max_dw(enum amdgpu_ring_type type)
+{
+ switch (type) {
+ case AMDGPU_RING_TYPE_GFX:
+ case AMDGPU_RING_TYPE_COMPUTE:
+ case AMDGPU_RING_TYPE_SDMA:
+ case AMDGPU_RING_TYPE_VPE:
+ return AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW;
+ default:
+ return AMDGPU_MM_IB_PACKET_SIZE_MAX_DW;
+ }
+}
+
static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p,
struct amdgpu_device *adev,
struct drm_file *filp,
@@ -345,7 +365,6 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p,
job = p->jobs[r];
ring = amdgpu_job_ring(job);
- ib = &job->ibs[job->num_ibs++];
/* submissions to kernel queues are disabled */
if (ring->no_user_submission)
@@ -374,6 +393,12 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p,
return -EINVAL;
}
+ if (chunk_ib->ib_bytes / 4 >
+ amdgpu_cs_ib_packet_size_max_dw(ring->funcs->type))
+ return -EINVAL;
+
+ ib = &job->ibs[job->num_ibs++];
+
if (chunk_ib->flags & AMDGPU_IB_FLAG_PREAMBLE)
job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
index 6480a344006d..45dfe1c8a11b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
@@ -342,7 +342,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
struct amdgpu_ip_block *ip_block;
struct amdgpu_ring *ring;
int ver, i, j;
- u32 ring_idx, off;
+ u32 ring_idx;
bool sizing_pass;
sizing_pass = buffer == NULL;
@@ -442,7 +442,6 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
for (i = 0; i < coredump->num_rings; i++) {
ring_idx = coredump->rings[i].ring_index;
ring = coredump->adev->rings[ring_idx];
- off = coredump->rings[i].offset;
drm_printf(&p, "ring name: %s\n", ring->name);
drm_printf(&p, "Rptr: 0x%llx Wptr: 0x%llx RB mask: %x\n",
@@ -451,12 +450,18 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
ring->buf_mask);
drm_printf(&p, "Ring size in dwords: %d\n",
ring->ring_size / 4);
+
+ if (!coredump->rings[i].ring_dw) {
+ drm_printf(&p, "Ring contents unavailable\n");
+ continue;
+ }
+
drm_printf(&p, "Ring contents\n");
drm_printf(&p, "Offset \t Value\n");
for (j = 0; j < ring->ring_size; j += 4)
drm_printf(&p, "0x%x \t 0x%x\n", j,
- coredump->rings_dw[off + j / 4]);
+ coredump->rings[i].ring_dw[j / 4]);
}
}
@@ -497,10 +502,12 @@ amdgpu_devcoredump_read(char *buffer, loff_t offset, size_t count,
static void amdgpu_devcoredump_free(void *data)
{
struct amdgpu_coredump_info *coredump = data;
+ u32 i;
kvfree(coredump->formatted);
+ for (i = 0; i < coredump->num_rings; i++)
+ kvfree(coredump->rings[i].ring_dw);
kvfree(coredump->rings);
- kvfree(coredump->rings_dw);
kvfree(data);
}
@@ -542,9 +549,9 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
struct amdgpu_coredump_info *coredump;
size_t size = sizeof(*coredump);
struct drm_sched_job *s_job;
- u64 total_ring_size, ring_count;
+ u64 ring_count;
struct amdgpu_ring *ring;
- int i, off, idx;
+ int i, idx;
/* No need to generate a new coredump if there's one in progress already. */
if (work_busy(&adev->coredump_work))
@@ -553,7 +560,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
if (job && job->pasid)
size += sizeof(struct amdgpu_coredump_ib_info) * job->num_ibs;
- coredump = kzalloc(size, GFP_NOWAIT);
+ coredump = kvzalloc(size, GFP_NOWAIT);
if (!coredump)
return;
@@ -584,7 +591,6 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
/* Dump ring content if memory allocation succeeds. */
ring_count = 0;
- total_ring_size = 0;
for (i = 0; i < adev->num_rings; i++) {
ring = adev->rings[i];
@@ -593,34 +599,34 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
coredump->ring != ring)
continue;
- total_ring_size += ring->ring_size;
ring_count++;
}
- coredump->rings_dw = kzalloc(total_ring_size, GFP_NOWAIT);
- coredump->rings = kcalloc(ring_count, sizeof(struct amdgpu_coredump_ring), GFP_NOWAIT);
- if (coredump->rings && coredump->rings_dw) {
- for (i = 0, off = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) {
+ if (ring_count)
+ coredump->rings = kvcalloc(ring_count,
+ sizeof(struct amdgpu_coredump_ring),
+ GFP_NOWAIT);
+ if (coredump->rings) {
+ for (i = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) {
+ struct amdgpu_coredump_ring *cdump_ring;
+
ring = adev->rings[i];
if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq &&
coredump->ring != ring)
continue;
- coredump->rings[idx].ring_index = ring->idx;
- coredump->rings[idx].rptr = amdgpu_ring_get_rptr(ring);
- coredump->rings[idx].wptr = amdgpu_ring_get_wptr(ring);
- coredump->rings[idx].offset = off;
+ cdump_ring = &coredump->rings[idx];
+
+ cdump_ring->ring_dw = kvzalloc(ring->ring_size, GFP_NOWAIT);
+ if (cdump_ring->ring_dw)
+ memcpy(cdump_ring->ring_dw, ring->ring, ring->ring_size);
- memcpy(&coredump->rings_dw[off], ring->ring, ring->ring_size);
- off += ring->ring_size / 4;
+ cdump_ring->ring_index = ring->idx;
+ cdump_ring->rptr = amdgpu_ring_get_rptr(ring);
+ cdump_ring->wptr = amdgpu_ring_get_wptr(ring);
idx++;
}
coredump->num_rings = idx;
- } else {
- kvfree(coredump->rings_dw);
- kvfree(coredump->rings);
- coredump->rings_dw = NULL;
- coredump->rings = NULL;
}
coredump->adev = adev;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
index 63f27337c09a..cb874d8c3394 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
@@ -34,8 +34,8 @@
struct amdgpu_coredump_ring {
u64 rptr;
u64 wptr;
+ u32 *ring_dw;
u32 ring_index;
- u32 offset;
};
struct amdgpu_coredump_ib_info {
@@ -53,7 +53,6 @@ struct amdgpu_coredump_info {
struct amdgpu_ring *ring;
struct amdgpu_coredump_ring *rings;
- u32 *rings_dw;
u32 num_rings;
/* Readable form of coredevdump, generate once to speed up
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index e5f26e5892ba..bff3e06a756c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4194,6 +4194,8 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev)
iounmap(adev->rmmio);
adev->rmmio = NULL;
+ if (adev->mman.aper_base_kaddr)
+ iounmap(adev->mman.aper_base_kaddr);
adev->mman.aper_base_kaddr = NULL;
/* Memory manager related */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
index 61450af539a6..d5787d848d04 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c
@@ -135,6 +135,21 @@ int amdgpu_lockdep_init(void)
lockdep_set_class(&locks->srbm_mutex, &amdgpu_srbm_lock_key);
lockdep_set_class(&locks->grbm_idx_mutex, &amdgpu_grbm_lock_key);
lockdep_set_class(&locks->mmio_idx_lock, &amdgpu_mmio_lock_key);
+
+ /*
+ * Register fs_reclaim lock class FIRST, before taking any locks.
+ *
+ * This acquire/release pair does NOT create a static lockdep edge
+ * (no locks are held between acquire and release). It only registers
+ * the fs_reclaim lock class with lockdep.
+ *
+ * The actual fs_reclaim -> notifier_lock dependency is established at
+ * RUNTIME when memory reclaim invokes MMU notifiers:
+ * fs_reclaim (held by reclaim) -> notifier_lock (acquired in callback)
+ */
+ fs_reclaim_acquire(GFP_KERNEL);
+ fs_reclaim_release(GFP_KERNEL);
+
/*
* Take locks in the correct order to train lockdep.
* This establishes the dependency chain.
@@ -154,11 +169,6 @@ int amdgpu_lockdep_init(void)
/* Level 6: Reset control lock */
mutex_lock(&locks->reset_lock);
- /*
- * Mark potential memory reclaim boundary.
- * GPU operations might trigger memory allocation/reclaim.
- */
- fs_reclaim_acquire(GFP_KERNEL);
/* Level 7: SRBM register access */
mutex_lock(&locks->srbm_mutex);
@@ -176,7 +186,6 @@ int amdgpu_lockdep_init(void)
spin_unlock_irqrestore(&locks->mmio_idx_lock, flags);
mutex_unlock(&locks->grbm_idx_mutex);
mutex_unlock(&locks->srbm_mutex);
- fs_reclaim_release(GFP_KERNEL);
mutex_unlock(&locks->reset_lock);
up_read(&reset_domain->sem);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 5fc91f45db5b..eaa86e32912e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -2120,23 +2120,17 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
/* Change the size here instead of the init above so only lpfn is affected */
amdgpu_ttm_disable_buffer_funcs(adev);
#ifdef CONFIG_64BIT
- if (adev->gmc.xgmi.connected_to_cpu) {
- void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base,
- adev->gmc.visible_vram_size,
- MEMREMAP_WB);
- if (IS_ERR(kaddr))
- return PTR_ERR(kaddr);
- adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr;
- } else if (adev->gmc.is_app_apu) {
+#ifdef CONFIG_X86
+ if (adev->gmc.xgmi.connected_to_cpu)
+ adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base,
+ adev->gmc.visible_vram_size);
+ else if (adev->gmc.is_app_apu)
DRM_DEBUG_DRIVER(
"No need to ioremap when real vram size is 0\n");
- } else {
- adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev,
- adev->gmc.aper_base,
- adev->gmc.visible_vram_size);
- if (!adev->mman.aper_base_kaddr)
- return -ENOMEM;
- }
+ else
+#endif
+ adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base,
+ adev->gmc.visible_vram_size);
#endif
amdgpu_ttm_init_vram_resv_regions(adev);
@@ -2287,7 +2281,10 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE);
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE);
- adev->mman.aper_base_kaddr = NULL;
+ if (adev->mman.aper_base_kaddr) {
+ iounmap(adev->mman.aper_base_kaddr);
+ adev->mman.aper_base_kaddr = NULL;
+ }
if (!adev->gmc.is_app_apu)
amdgpu_vram_mgr_fini(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 3efe1ae27386..b18d78720656 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -700,7 +700,12 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
if (!adev->userq_halt_for_enforce_isolation ||
((queue->queue_type != AMDGPU_HW_IP_GFX) &&
(queue->queue_type != AMDGPU_HW_IP_COMPUTE))) {
+ /* Serialize the map against an in-progress GPU reset (MES is
+ * unresponsive during recovery), matching amdgpu_userq_cleanup().
+ */
+ down_read(&adev->reset_domain->sem);
r = amdgpu_userq_map_helper(queue);
+ up_read(&adev->reset_domain->sem);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
mutex_unlock(&uq_mgr->userq_mutex);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index da668a8d6abd..9d7a75670bee 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -1823,6 +1823,11 @@ static void gfx_v12_0_constants_init(struct amdgpu_device *adev)
gfx_v12_0_get_tcc_info(adev);
adev->gfx.config.pa_sc_tile_steering_override = 0;
+ /* Set whether texture coordinate truncation is conformant. */
+ tmp = RREG32_SOC15(GC, 0, regTA_CNTL2);
+ adev->gfx.config.ta_cntl2_truncate_coord_mode =
+ REG_GET_FIELD(tmp, TA_CNTL2, TRUNCATE_COORD_MODE);
+
/* XXX SH_MEM regs */
/* where to put LDS, scratch, GPUVM in FSA64 space */
mutex_lock(&adev->srbm_mutex);
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c
index 855cd29cbffa..5fe43f7eab29 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c
@@ -267,9 +267,24 @@ static bool gmc_v12_1_get_vmid_pasid_mapping_info(struct amdgpu_device *adev,
* by the amdgpu vm/hsa code.
*/
+/**
+ * gmc_v12_1_use_invalidate_semaphore - judge whether to use semaphore
+ *
+ * @adev: amdgpu_device pointer
+ * @vmhub: vmhub type
+ *
+ */
+static bool gmc_v12_1_use_invalidate_semaphore(struct amdgpu_device *adev,
+ uint32_t vmhub)
+{
+ return ((!AMDGPU_IS_GFXHUB(vmhub)) &&
+ (!amdgpu_sriov_vf(adev)));
+}
+
static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid,
unsigned int vmhub, uint32_t flush_type)
{
+ bool use_semaphore = gmc_v12_1_use_invalidate_semaphore(adev, vmhub);
struct amdgpu_vmhub *hub = &adev->vmhub[vmhub];
u32 inv_req = hub->vmhub_funcs->get_invalidate_req(vmid, flush_type);
u32 tmp;
@@ -283,6 +298,19 @@ static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid,
spin_lock(&adev->gmc.invalidate_lock);
+ if (use_semaphore) {
+ for (i = 0; i < adev->usec_timeout; i++) {
+ /* a read return value of 1 means semaphore acuqire */
+ tmp = RREG32_RLC_NO_KIQ(hub->vm_inv_eng0_sem + hub->eng_distance * eng, hub_ip);
+ if (tmp & 0x1)
+ break;
+ udelay(1);
+ }
+
+ if (i >= adev->usec_timeout)
+ DRM_ERROR("Timeout waiting for sem acquire in VM flush!\n");
+ }
+
WREG32_RLC_NO_KIQ(hub->vm_inv_eng0_req + hub->eng_distance * eng, inv_req, hub_ip);
/* Wait for ACK with a delay.*/
@@ -296,6 +324,9 @@ static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid,
udelay(1);
}
+ if (use_semaphore)
+ WREG32_RLC_NO_KIQ(hub->vm_inv_eng0_sem + hub->eng_distance * eng, 0, hub_ip);
+
/* Issue additional private vm invalidation to MMHUB */
if (!AMDGPU_IS_GFXHUB(vmhub) &&
(hub->vm_l2_bank_select_reserved_cid2) &&
@@ -396,7 +427,7 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev,
if (all_hub) {
/* invalidate mm_hub */
- if (test_bit(AMDGPU_MMHUB1(0), adev->vmhubs_mask)) {
+ if (test_bit(AMDGPU_MMHUB0(0), adev->vmhubs_mask)) {
input.hub_id = AMDGPU_MMHUB0(0);
adev->mes.funcs->invalidate_tlbs_pasid(&adev->mes, &input);
}
@@ -431,10 +462,17 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev,
static uint64_t gmc_v12_1_emit_flush_gpu_tlb(struct amdgpu_ring *ring,
unsigned vmid, uint64_t pd_addr)
{
+ bool use_semaphore = gmc_v12_1_use_invalidate_semaphore(ring->adev, ring->vm_hub);
struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub];
uint32_t req = hub->vmhub_funcs->get_invalidate_req(vmid, 0);
unsigned eng = ring->vm_inv_eng;
+ if (use_semaphore)
+ /* a read return value of 1 means semaphore acuqire */
+ amdgpu_ring_emit_reg_wait(ring,
+ hub->vm_inv_eng0_sem +
+ hub->eng_distance * eng, 0x1, 0x1);
+
amdgpu_ring_emit_wreg(ring, hub->ctx0_ptb_addr_lo32 +
(hub->ctx_addr_distance * vmid),
lower_32_bits(pd_addr));
@@ -449,6 +487,14 @@ static uint64_t gmc_v12_1_emit_flush_gpu_tlb(struct amdgpu_ring *ring,
hub->eng_distance * eng,
req, 1 << vmid);
+ if (use_semaphore)
+ /*
+ * add semaphore release after invalidation,
+ * write with 0 means semaphore release
+ */
+ amdgpu_ring_emit_wreg(ring, hub->vm_inv_eng0_sem +
+ hub->eng_distance * eng, 0);
+
return pd_addr;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
index a43582b9c876..3f61d5367d94 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c
@@ -772,15 +772,28 @@ static int jpeg_v4_0_5_ring_reset(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
+ struct amdgpu_device *adev = ring->adev;
+ u32 pg_flags = adev->pg_flags;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = jpeg_v4_0_5_stop(ring->adev);
- if (r)
- return r;
- r = jpeg_v4_0_5_start(ring->adev);
+
+ /*
+ * The DPG stop path only clears the JPEG_PG_MODE bit and never resets a
+ * hung JRBC, so the post-reset ring test times out and the driver falls
+ * back to a full MODE1 reset. Temporarily force the static power-gating
+ * path so the stop/start sequence actually power-cycles the JPEG block
+ * (JMI soft reset + static power off/on), matching the working jpeg_v4_0
+ * reset.
+ */
+ adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG;
+ r = jpeg_v4_0_5_stop(adev);
+ if (!r)
+ r = jpeg_v4_0_5_start(adev);
+ adev->pg_flags = pg_flags;
if (r)
return r;
+
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
index 72a4b2d0676f..c696a0dea32c 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c
@@ -648,15 +648,28 @@ static int jpeg_v5_0_0_ring_reset(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
+ struct amdgpu_device *adev = ring->adev;
+ u32 pg_flags = adev->pg_flags;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = jpeg_v5_0_0_stop(ring->adev);
- if (r)
- return r;
- r = jpeg_v5_0_0_start(ring->adev);
+
+ /*
+ * The DPG stop path only clears the JPEG_PG_MODE bit and never resets a
+ * hung JRBC, so the post-reset ring test times out and the driver falls
+ * back to a full MODE1 reset. Temporarily force the static power-gating
+ * path so the stop/start sequence actually power-cycles the JPEG block
+ * (JMI soft reset + ONO1 power off/on), matching the working jpeg_v4_0
+ * reset.
+ */
+ adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG;
+ r = jpeg_v5_0_0_stop(adev);
+ if (!r)
+ r = jpeg_v5_0_0_start(adev);
+ adev->pg_flags = pg_flags;
if (r)
return r;
+
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
index 2c3e6d9f52a6..33c2626abc80 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
@@ -655,15 +655,28 @@ static int jpeg_v5_3_0_ring_reset(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
+ struct amdgpu_device *adev = ring->adev;
+ u32 pg_flags = adev->pg_flags;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = jpeg_v5_3_0_stop(ring->adev);
- if (r)
- return r;
- r = jpeg_v5_3_0_start(ring->adev);
+
+ /*
+ * The DPG stop path only clears the JPEG_PG_MODE bit and never resets a
+ * hung JRBC, so the post-reset ring test times out and the driver falls
+ * back to a full MODE1 reset. Temporarily force the static power-gating
+ * path so the stop/start sequence actually power-cycles the JPEG block
+ * (JMI soft reset + static power off/on), matching the working jpeg_v4_0
+ * reset.
+ */
+ adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG;
+ r = jpeg_v5_3_0_stop(adev);
+ if (!r)
+ r = jpeg_v5_3_0_start(adev);
+ adev->pg_flags = pg_flags;
if (r)
return r;
+
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1820547b1dde..941c1a312824 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2189,6 +2189,11 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
init_data.flags.unify_link_enc_assignment = true;
init_data.flags.usb4_bw_alloc_support = true;
}
+
+ /* DCN201 audio desyncs using DP SS */
+ if (adev->apu_flags & AMD_APU_IS_CYAN_SKILLFISH2)
+ init_data.flags.ignore_dpref_ss = true;
+
retrieve_dmi_info(&adev->dm);
if (adev->dm.edp0_on_dp1_quirk)
init_data.flags.support_edp0_on_dp1 = true;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 95d3da3c4199..06598273d481 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -119,7 +119,11 @@ void amdgpu_dm_crtc_set_static_screen_optimze(
struct dc_link *link = stream->link;
bool set_vsync_event = !sso_enable;
- if (!allow_sr_entry)
+ /*
+ * allow_sr_entry gates only entry. A disable request must still set
+ * the vsync events to force Replay and PSR1 out and keep them blocked.
+ */
+ if (sso_enable && !allow_sr_entry)
return;
amdgpu_dm_replay_set_event(dm, stream,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index 53fffe58602f..f5b3a2eff94a 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@ -1348,7 +1348,10 @@ void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
* process the mute state, especially after link re-establishment
* with HDMI 2.0 scrambling enabled.
*/
- if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+ if (enable && pipe_ctx->stream_res.tg &&
+ pipe_ctx->stream_res.tg->funcs->is_tg_enabled &&
+ pipe_ctx->stream_res.tg->funcs->wait_for_state &&
+ pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
int i;
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index b93514023baa..39ab94a73c88 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -257,8 +257,14 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
addr_len[PAGE0_SWAUX_LENGTH - base] = (len == 0) ? SWAUX_NO_PAYLOAD :
((len - 1) & SWAUX_LENGTH_MASK);
- regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len,
- ARRAY_SIZE(addr_len));
+ ret = regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len,
+ ARRAY_SIZE(addr_len));
+ if (ret) {
+ DRM_DEV_ERROR(dev,
+ "failed to write AUX address %#x, len %zu: %d\n",
+ msg->address, len, ret);
+ return ret;
+ }
if (len && (request == DP_AUX_NATIVE_WRITE ||
request == DP_AUX_I2C_WRITE)) {
@@ -274,13 +280,22 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
}
}
- regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND);
+ ret = regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to start AUX transfer: %d\n", ret);
+ return ret;
+ }
/* Zero delay loop because i2c transactions are slow already */
- regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data,
- !(data & SWAUX_SEND), 0, 50 * 1000);
+ ret = regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data,
+ !(data & SWAUX_SEND), 0, 50 * 1000);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to complete AUX transfer: %d\n",
+ ret);
+ return ret;
+ }
- regmap_read(map, PAGE0_SWAUX_STATUS, &data);
+ ret = regmap_read(map, PAGE0_SWAUX_STATUS, &data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to read PAGE0_SWAUX_STATUS: %d\n",
ret);
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index c989459eb215..00807039f8d7 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -597,9 +597,13 @@ static vm_fault_t try_insert_pfn(struct vm_fault *vmf, unsigned int order,
#ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP
} else if (order == PMD_ORDER) {
unsigned long paddr = pfn << PAGE_SHIFT;
+ struct vm_area_struct *vma = vmf->vma;
+ unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE);
+ unsigned long end = start + PMD_SIZE;
+ bool in_range = vma->vm_start <= start && end <= vma->vm_end;
bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK);
- if (aligned &&
+ if (aligned && in_range &&
folio_test_pmd_mappable(page_folio(pfn_to_page(pfn)))) {
vm_fault_t ret;
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index e45933e333b5..fa88b2f9da76 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -603,6 +603,9 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
return -EINVAL;
}
+ if (!section_size)
+ return 0;
+
name_len = iter->size - iter->offset;
section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL);
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index a1e2eb1ca7bb..54535bae2b0c 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -802,9 +802,13 @@ static vm_fault_t insert_page(struct vm_fault *vmf, unsigned int order, struct p
} else if (order == PMD_ORDER) {
unsigned long pfn = page_to_pfn(page);
unsigned long paddr = pfn << PAGE_SHIFT;
+ struct vm_area_struct *vma = vmf->vma;
+ unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE);
+ unsigned long end = start + PMD_SIZE;
+ bool in_range = vma->vm_start <= start && end <= vma->vm_end;
bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK);
- if (aligned &&
+ if (aligned && in_range &&
folio_test_pmd_mappable(page_folio(page))) {
pfn &= PMD_MASK >> PAGE_SHIFT;
return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 02a40e4750c7..299865569252 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -360,6 +360,13 @@ static bool radeon_fence_is_signaled(struct dma_fence *f)
if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq)
return true;
+ if (down_read_trylock(&rdev->exclusive_lock)) {
+ radeon_fence_activity(rdev, ring);
+ up_read(&rdev->exclusive_lock);
+
+ if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq)
+ return true;
+ }
return false;
}
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 423bcfe331a0..8779a42c6508 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -182,6 +182,12 @@ struct v3d_dev {
*/
struct mutex reset_lock;
+ /* Ordered workqueue shared by every queue's scheduler timeout work.
+ * V3D reset is global to all queues, so the timeout handlers must not
+ * run concurrently.
+ */
+ struct workqueue_struct *reset_wq;
+
/* Lock taken when creating and pushing the GPU scheduler
* jobs, to keep the sched-fence seqnos in order.
*/
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index c35f9f70cf8d..63dd95b828c8 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -837,6 +837,7 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op
struct drm_sched_init_args args = {
.credit_limit = 1,
.timeout = msecs_to_jiffies(500),
+ .timeout_wq = v3d->reset_wq,
.dev = v3d->drm.dev,
};
@@ -851,9 +852,13 @@ v3d_sched_init(struct v3d_dev *v3d)
{
int ret;
+ v3d->reset_wq = alloc_ordered_workqueue("v3d_reset", 0);
+ if (!v3d->reset_wq)
+ return -ENOMEM;
+
ret = v3d_queue_sched_init(v3d, &v3d_bin_sched_ops, V3D_BIN, "v3d_bin");
if (ret)
- return ret;
+ goto fail;
ret = v3d_queue_sched_init(v3d, &v3d_render_sched_ops, V3D_RENDER,
"v3d_render");
@@ -896,4 +901,6 @@ v3d_sched_fini(struct v3d_dev *v3d)
if (v3d->queue[q].sched.ready)
drm_sched_fini(&v3d->queue[q].sched);
}
+
+ destroy_workqueue(v3d->reset_wq);
}
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 1b5ca3ce578a..663decb19304 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -805,6 +805,9 @@ static int exec_queue_set_hang_replay_state(struct xe_device *xe,
u64 __user *address = u64_to_user_ptr(value);
void *ptr;
+ if (q->replay_state)
+ return -EINVAL;
+
ptr = vmemdup_user(address, size);
if (XE_IOCTL_DBG(xe, IS_ERR(ptr)))
return PTR_ERR(ptr);
diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c
index 75091bde0d50..65f59f06177f 100644
--- a/drivers/gpu/drm/xe/xe_uc.c
+++ b/drivers/gpu/drm/xe/xe_uc.c
@@ -15,6 +15,7 @@
#include "xe_guc_pc.h"
#include "xe_guc_rc.h"
#include "xe_guc_engine_activity.h"
+#include "xe_guc_submit.h"
#include "xe_huc.h"
#include "xe_sriov.h"
#include "xe_wopcm.h"
@@ -159,12 +160,14 @@ static int vf_uc_load_hw(struct xe_uc *uc)
if (err)
return err;
- uc->guc.submission_state.enabled = true;
-
err = xe_guc_opt_in_features_enable(&uc->guc);
if (err)
return err;
+ err = xe_guc_submit_enable(&uc->guc);
+ if (err)
+ return err;
+
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
if (err)
return err;
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 55a9a3ddd4aa..29dc90a2c3fe 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -1083,7 +1083,6 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
- * @extra_groups: pointer to list of additional non-standard attribute groups
*
* The use of this function is restricted. It is provided for legacy reasons
* and must only be called from the thermal subsystem.
@@ -1095,13 +1094,12 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
*/
struct device *
hwmon_device_register_for_thermal(struct device *dev, const char *name,
- void *drvdata,
- const struct attribute_group **extra_groups)
+ void *drvdata)
{
if (!name || !dev)
return ERR_PTR(-EINVAL);
- return __hwmon_device_register(dev, name, drvdata, NULL, extra_groups);
+ return __hwmon_device_register(dev, name, drvdata, NULL, NULL);
}
EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, "HWMON_THERMAL");
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 2d37b07c8215..839f7482dc18 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1534,8 +1534,8 @@ void bond_alb_monitor(struct work_struct *work)
struct bonding *bond = container_of(work, struct bonding,
alb_work.work);
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+ struct slave *slave, *curr;
struct list_head *iter;
- struct slave *slave;
if (!bond_has_slaves(bond)) {
atomic_set(&bond_info->tx_rebalance_counter, 0);
@@ -1597,9 +1597,11 @@ void bond_alb_monitor(struct work_struct *work)
* because a slave was disabled then
* it can now leave promiscuous mode.
*/
- dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev,
- -1);
- bond_info->primary_is_promisc = 0;
+ curr = rtnl_dereference(bond->curr_active_slave);
+ if (bond_info->primary_is_promisc && curr) {
+ dev_set_promiscuity(curr->dev, -1);
+ bond_info->primary_is_promisc = 0;
+ }
rtnl_unlock();
rcu_read_lock();
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index b7fe9ad73349..a1d41329209f 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -171,6 +171,7 @@ struct pdsc {
struct timer_list wdtimer;
unsigned int wdtimer_period;
struct work_struct health_work;
+ bool health_stopped;
struct devlink_health_reporter *fw_reporter;
u32 fw_recoveries;
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 8d94a4d70395..9a2c64198d03 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -470,8 +470,10 @@ static void pdsc_stop_health_thread(struct pdsc *pdsc)
return;
timer_shutdown_sync(&pdsc->wdtimer);
- if (pdsc->health_work.func)
- cancel_work_sync(&pdsc->health_work);
+ if (pdsc->health_work.func && !pdsc->health_stopped) {
+ disable_work_sync(&pdsc->health_work);
+ pdsc->health_stopped = true;
+ }
}
static void pdsc_restart_health_thread(struct pdsc *pdsc)
@@ -479,6 +481,10 @@ static void pdsc_restart_health_thread(struct pdsc *pdsc)
if (pdsc->pdev->is_virtfn)
return;
+ if (pdsc->health_stopped) {
+ enable_work(&pdsc->health_work);
+ pdsc->health_stopped = false;
+ }
timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0);
mod_timer(&pdsc->wdtimer, jiffies + 1);
}
@@ -555,7 +561,11 @@ static pci_ers_result_t pdsc_pci_error_detected(struct pci_dev *pdev,
pci_channel_state_t error)
{
if (error == pci_channel_io_frozen) {
+ struct pdsc *pdsc = pci_get_drvdata(pdev);
+
pdsc_reset_prepare(pdev);
+ if (!pdev->is_virtfn)
+ cancel_work_sync(&pdsc->pci_reset_work);
return PCI_ERS_RESULT_NEED_RESET;
}
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
index 8ff07de2bd52..e1193c6719d9 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
@@ -360,6 +360,35 @@ out:
return !!budget;
}
+void aq_ring_tx_deinit(struct aq_ring_s *self)
+{
+ if (!self)
+ return;
+
+ for (; self->sw_head != self->sw_tail;
+ self->sw_head = aq_ring_next_dx(self, self->sw_head)) {
+ struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
+ struct device *ndev = aq_nic_get_dev(self->aq_nic);
+
+ if (buff->is_mapped) {
+ if (buff->is_sop) {
+ dma_unmap_single(ndev, buff->pa, buff->len,
+ DMA_TO_DEVICE);
+ } else {
+ dma_unmap_page(ndev, buff->pa, buff->len,
+ DMA_TO_DEVICE);
+ }
+ }
+
+ if (buff->is_eop) {
+ if (buff->skb)
+ dev_kfree_skb_any(buff->skb);
+ else if (buff->xdpf)
+ xdp_return_frame(buff->xdpf);
+ }
+ }
+}
+
static void aq_rx_checksum(struct aq_ring_s *self,
struct aq_ring_buff_s *buff,
struct sk_buff *skb)
@@ -921,15 +950,29 @@ err_exit:
void aq_ring_rx_deinit(struct aq_ring_s *self)
{
- if (!self)
+ unsigned int i;
+
+ if (!self || !self->buff_ring)
return;
- for (; self->sw_head != self->sw_tail;
- self->sw_head = aq_ring_next_dx(self, self->sw_head)) {
- struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
+ /* Release every page still owned by the ring.
+ *
+ * Walking [sw_head, sw_tail) is not enough: refill is batched
+ * (aq_ring_rx_fill() waits for AQ_CFG_RX_REFILL_THRES free slots),
+ * so slots that were cleaned but not yet reposted accumulate in the
+ * [sw_tail, sw_head) gap, and they keep their page for reuse. Walk
+ * the whole ring and release whatever is left.
+ */
+ for (i = 0; i < self->size; i++) {
+ struct aq_ring_buff_s *buff = &self->buff_ring[i];
+
+ if (!buff->rxdata.page)
+ continue;
aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic));
}
+
+ self->sw_head = self->sw_tail;
}
void aq_ring_free(struct aq_ring_s *self)
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h
index a70b880ada67..6431cc62962f 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h
@@ -202,6 +202,7 @@ void aq_ring_update_queue_state(struct aq_ring_s *ring);
void aq_ring_queue_wake(struct aq_ring_s *ring);
void aq_ring_queue_stop(struct aq_ring_s *ring);
bool aq_ring_tx_clean(struct aq_ring_s *self);
+void aq_ring_tx_deinit(struct aq_ring_s *self);
int aq_xdp_xmit(struct net_device *dev, int num_frames,
struct xdp_frame **frames, u32 flags);
int aq_ring_rx_clean(struct aq_ring_s *self,
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c
index 2f9033ceed8c..05814fea0f5f 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c
@@ -275,7 +275,7 @@ void aq_vec_deinit(struct aq_vec_s *self)
for (i = 0U; self->tx_rings > i; ++i) {
ring = self->ring[i];
- aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]);
+ aq_ring_tx_deinit(&ring[AQ_VEC_TX_ID]);
aq_ring_rx_deinit(&ring[AQ_VEC_RX_ID]);
}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
index 67e93e17d4d9..0955b488b6fe 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
@@ -141,12 +141,15 @@ static void bnge_aux_dev_release(struct device *dev)
{
struct bnge_auxr_priv *aux_priv =
container_of(dev, struct bnge_auxr_priv, aux_dev.dev);
- struct bnge_dev *bd = pci_get_drvdata(aux_priv->auxr_dev->pdev);
+ struct bnge_auxr_dev *auxr_dev = aux_priv->auxr_dev;
+ struct bnge_dev *bd = pci_get_drvdata(to_pci_dev(dev->parent));
ida_free(&bnge_aux_dev_ids, aux_priv->id);
- kfree(aux_priv->auxr_dev->auxr_info);
+ if (auxr_dev) {
+ kfree(auxr_dev->auxr_info);
+ kfree(auxr_dev);
+ }
bd->auxr_dev = NULL;
- kfree(aux_priv->auxr_dev);
kfree(aux_priv);
bd->aux_priv = NULL;
}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index 6f7ef506d4e1..ac4c93e5b634 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
@@ -2768,8 +2768,6 @@ static int bnge_init_nic(struct bnge_net *bn)
err_free_ring_grps:
bnge_free_ring_grps(bn);
- return rc;
-
err_free_rx_ring_pair_bufs:
bnge_free_rx_ring_pair_bufs(bn);
return rc;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
index 0e94f092813e..4711dd4945ff 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
@@ -163,7 +163,8 @@ static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx,
u16 tx_chunks = bnge_num_tx_to_cp(bd, *tx);
if (tx_chunks != *tx) {
- u16 tx_saved = tx_chunks, rc;
+ u16 tx_saved = tx_chunks;
+ int rc;
rc = bnge_fix_rings_count(rx, &tx_chunks, max_nq, sh);
if (rc)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 7513618793da..25099077fe4f 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -4611,11 +4611,14 @@ static void bnxt_init_one_rx_agg_ring_rxbd(struct bnxt *bp,
type = ((u32)rxr->rx_page_size << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_AGG_BD;
- /* On P7, setting EOP will cause the chip to disable
- * Relaxed Ordering (RO) for TPA data. Disable EOP for
- * potentially higher performance with RO.
+ /* Disable EOP if TPA is enabled to prevent overlapping zero
+ * padding with the next segment's data. On P7_PLUS, EOP will
+ * automatically disable Relaxed Ordering (RO) to prevent
+ * potential data corruption (and may degrade performance). On
+ * older chips, RO will not be automatically disabled and may
+ * cause corruption.
*/
- if (BNXT_CHIP_P5_AND_MINUS(bp) || !(bp->flags & BNXT_FLAG_TPA))
+ if (!(bp->flags & BNXT_FLAG_TPA))
type |= RX_BD_FLAGS_AGG_EOP;
bnxt_init_rxbd_pages(ring, type);
@@ -6704,22 +6707,36 @@ int bnxt_get_nr_rss_ctxs(struct bnxt *bp, int rx_rings)
static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic)
{
bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG);
- u16 i, j;
+ u16 i, j, min_j = bp->rx_nr_rings - 1;
+
+ if (!vnic->rss_table)
+ goto skip_rss_tbl;
/* Fill the RSS indirection table with ring group ids */
for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++) {
if (!no_rss)
j = bp->rss_indir_tbl[i];
+ min_j = min(j, min_j);
vnic->rss_table[i] = cpu_to_le16(vnic->fw_grp_ids[j]);
}
+
+skip_rss_tbl:
+ if (vnic->rss_table && !no_rss)
+ vnic->default_rx_ring = min_j;
+ else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
+ vnic->default_rx_ring = vnic->vnic_id - 1;
+ else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp))
+ vnic->default_rx_ring = bp->rx_nr_rings - 1;
+ else
+ vnic->default_rx_ring = 0;
}
static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp,
struct bnxt_vnic_info *vnic)
{
+ u16 tbl_size, i, min_j = bp->rx_nr_rings - 1;
__le16 *ring_tbl = vnic->rss_table;
struct bnxt_rx_ring_info *rxr;
- u16 tbl_size, i;
tbl_size = bnxt_get_rxfh_indir_size(bp->dev);
@@ -6732,6 +6749,7 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp,
j = ethtool_rxfh_context_indir(vnic->rss_ctx)[i];
else
j = bp->rss_indir_tbl[i];
+ min_j = min(j, min_j);
rxr = &bp->rx_ring[j];
ring_id = rxr->rx_ring_struct.fw_ring_id;
@@ -6739,19 +6757,15 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp,
ring_id = bnxt_cp_ring_for_rx(bp, rxr);
*ring_tbl++ = cpu_to_le16(ring_id);
}
+ vnic->default_rx_ring = min_j;
}
static void
__bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req,
struct bnxt_vnic_info *vnic)
{
- if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
- bnxt_fill_hw_rss_tbl_p5(bp, vnic);
- if (bp->flags & BNXT_FLAG_CHIP_P7)
- req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT;
- } else {
- bnxt_fill_hw_rss_tbl(bp, vnic);
- }
+ if (bp->flags & BNXT_FLAG_CHIP_P7)
+ req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT;
if (bp->rss_hash_delta) {
req->hash_type = cpu_to_le32(bp->rss_hash_delta);
@@ -6803,6 +6817,7 @@ static int bnxt_hwrm_vnic_set_rss_p5(struct bnxt *bp,
if (!set_rss)
return hwrm_req_send(bp, req);
+ bnxt_fill_hw_rss_tbl_p5(bp, vnic);
__bnxt_hwrm_vnic_set_rss(bp, req, vnic);
ring_tbl_map = vnic->rss_table_dma_addr;
nr_ctxs = bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings);
@@ -6939,8 +6954,9 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic)
return rc;
if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
- struct bnxt_rx_ring_info *rxr = &bp->rx_ring[0];
+ struct bnxt_rx_ring_info *rxr;
+ rxr = &bp->rx_ring[vnic->default_rx_ring];
req->default_rx_ring_id =
cpu_to_le16(rxr->rx_ring_struct.fw_ring_id);
req->default_cmpl_ring_id =
@@ -6973,13 +6989,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic)
req->cos_rule = cpu_to_le16(0xffff);
}
- if (vnic->flags & BNXT_VNIC_RSS_FLAG)
- ring = 0;
- else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
- ring = vnic->vnic_id - 1;
- else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp))
- ring = bp->rx_nr_rings - 1;
-
+ ring = vnic->default_rx_ring;
grp_idx = bp->rx_ring[ring].bnapi->index;
req->dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id);
req->lb_rule = cpu_to_le16(0xffff);
@@ -10866,6 +10876,7 @@ static int __bnxt_setup_vnic(struct bnxt *bp, struct bnxt_vnic_info *vnic)
}
skip_rss_ctx:
+ bnxt_fill_hw_rss_tbl(bp, vnic);
/* configure default vnic, ring grp */
rc = bnxt_hwrm_vnic_cfg(bp, vnic);
if (rc) {
@@ -11090,6 +11101,11 @@ static int bnxt_set_vnic_mru_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic,
vnic->vnic_id, rc);
return rc;
}
+ if (rxr_id == vnic->default_rx_ring) {
+ rc = bnxt_hwrm_vnic_cfg(bp, vnic);
+ if (rc)
+ return rc;
+ }
}
vnic->mru = mru;
bnxt_hwrm_vnic_update(bp, vnic,
@@ -11171,6 +11187,9 @@ static int bnxt_setup_nitroa0_vnic(struct bnxt *bp)
return rc;
}
+ /* Setup the proper default RX ring */
+ bnxt_fill_hw_rss_tbl(bp, vnic);
+
rc = bnxt_hwrm_vnic_cfg(bp, vnic);
if (rc) {
netdev_err(bp->dev, "Cannot allocate special vnic for NS2 A0: %x\n",
@@ -16217,6 +16236,7 @@ static int bnxt_queue_mem_alloc(struct net_device *dev,
clone->rx_next_cons = 0;
clone->need_head_pool = false;
clone->rx_page_size = qcfg->rx_page_size;
+ clone->rx_agg_bmap = NULL;
rc = bnxt_alloc_rx_page_pool(bp, clone, rxr->page_pool->p.nid);
if (rc)
@@ -16269,6 +16289,8 @@ err_free_tpa_info:
bnxt_free_one_tpa_info(bp, clone);
err_free_rx_agg_ring:
bnxt_free_ring(bp, &clone->rx_agg_ring_struct.ring_mem);
+ kfree(clone->rx_agg_bmap);
+ clone->rx_agg_bmap = NULL;
err_free_rx_ring:
bnxt_free_ring(bp, &clone->rx_ring_struct.ring_mem);
err_rxq_info_unreg:
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 6335dfc14c98..44ea8fc7b766 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -1334,6 +1334,7 @@ struct bnxt_vnic_info {
#define BNXT_VNIC_RSSCTX_FLAG 0x40
struct ethtool_rxfh_context *rss_ctx;
u32 vnic_id;
+ u16 default_rx_ring;
};
struct bnxt_rss_ctx {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
index 5d41dc1bc782..b89293dfd2d1 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
@@ -495,12 +495,15 @@ static int bnxt_ptp_enable(struct ptp_clock_info *ptp_info,
return rc;
case PTP_CLK_REQ_PPS:
/* Configure PHC PPS IN */
- rc = bnxt_ptp_cfg_pin(bp, 0, BNXT_PPS_PIN_PPS_IN);
+ pin_id = 0;
+ if (!on)
+ break;
+ rc = bnxt_ptp_cfg_pin(bp, pin_id, BNXT_PPS_PIN_PPS_IN);
if (rc)
return rc;
rc = bnxt_ptp_cfg_event(bp, BNXT_PPS_EVENT_INTERNAL);
if (!rc)
- ptp->pps_info.pins[0].event = BNXT_PPS_EVENT_INTERNAL;
+ ptp->pps_info.pins[pin_id].event = BNXT_PPS_EVENT_INTERNAL;
return rc;
default:
netdev_err(ptp->bp->dev, "Unrecognized PIN function\n");
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index e7125b818087..d98f7e7ccab9 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -3011,8 +3011,9 @@ static void enic_remove(struct pci_dev *pdev)
if (netdev) {
struct enic *enic = netdev_priv(netdev);
- cancel_work_sync(&enic->reset);
- cancel_work_sync(&enic->change_mtu_work);
+ disable_work_sync(&enic->reset);
+ disable_work_sync(&enic->tx_hang_reset);
+ disable_work_sync(&enic->change_mtu_work);
unregister_netdev(netdev);
enic_dev_deinit(enic);
vnic_dev_close(enic->vdev);
diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
index e3e7f2270560..02282dc86faf 100644
--- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
+++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
@@ -1282,7 +1282,6 @@ static void hix5hd2_dev_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct hix5hd2_priv *priv = netdev_priv(ndev);
- netif_napi_del(&priv->napi);
unregister_netdev(ndev);
mdiobus_unregister(priv->bus);
mdiobus_free(priv->bus);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index fc8587c80813..164c3ecf195c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -9498,12 +9498,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
if (ret)
goto err_ptp_uninit;
- if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) {
+ if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER)
hdev->hw.mac.req_autoneg = hdev->hw.mac.autoneg;
- if (hdev->hw.mac.autoneg == AUTONEG_DISABLE &&
- hdev->hw.mac.speed != SPEED_UNKNOWN)
- hdev->hw.mac.req_speed = hdev->hw.mac.speed;
- }
ret = hclge_set_autoneg_speed_dup(hdev);
if (ret) {
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index b3883a5a7d7a..39043d8ca1bf 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -3082,7 +3082,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
meta_req.tx_buffer = bi;
meta_req.meta = meta;
meta_req.used_desc = 0;
- xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops,
+ xsk_tx_metadata_request(pool, &meta, &igc_xsk_tx_metadata_ops,
&meta_req);
/* xsk_tx_metadata_request() may have updated next_to_use */
@@ -7585,11 +7585,13 @@ static int __igc_resume(struct device *dev, bool rpm)
err = __igc_open(netdev, true);
if (!rpm)
rtnl_unlock();
- if (!err)
- netif_device_attach(netdev);
+ if (err)
+ return err;
}
- return err;
+ netif_device_attach(netdev);
+
+ return 0;
}
static int igc_resume(struct device *dev)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index 0b46ec29e64e..039fd47ebf52 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
@@ -54,10 +54,12 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
if (burst) {
*burst_exp = ilog2(burst) ? ilog2(burst) - 1 : 0;
tmp = burst - rounddown_pow_of_two(burst);
- if (burst < max_mantissa)
+ if (burst <= max_mantissa) {
*burst_mantissa = tmp * 2;
- else
+ } else {
+ WARN_ON(*burst_exp < 7);
*burst_mantissa = tmp / (1ULL << (*burst_exp - 7));
+ }
} else {
*burst_exp = MAX_BURST_EXPONENT;
*burst_mantissa = max_mantissa;
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c
index 2989a77e3b42..1ad0e62a8433 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c
@@ -684,6 +684,9 @@ static int prestera_fw_hdr_parse(struct prestera_fw *fw)
struct prestera_fw_header *hdr;
u32 magic;
+ if (fw->bin->size < sizeof(*hdr))
+ return -EINVAL;
+
hdr = (struct prestera_fw_header *)fw->bin->data;
magic = be32_to_cpu(hdr->magic_number);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
index adcc73e2a5b3..1493d8106c1a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
@@ -1025,13 +1025,11 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
tracer = kvzalloc_obj(*tracer);
if (!tracer)
- return ERR_PTR(-ENOMEM);
+ return NULL;
tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer");
- if (!tracer->work_queue) {
- err = -ENOMEM;
+ if (!tracer->work_queue)
goto free_tracer;
- }
tracer->dev = dev;
@@ -1073,7 +1071,7 @@ destroy_workqueue:
destroy_workqueue(tracer->work_queue);
free_tracer:
kvfree(tracer);
- return ERR_PTR(err);
+ return NULL;
}
static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data);
@@ -1084,7 +1082,7 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
struct mlx5_core_dev *dev;
int err;
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return 0;
if (!tracer->str_db.loaded)
@@ -1134,7 +1132,7 @@ err_cancel_work:
/* Stop tracer + Cleanup HW resources */
void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
{
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return;
mutex_lock(&tracer->state_lock);
@@ -1163,7 +1161,7 @@ unlock:
/* Free software resources (Buffers, etc ..) */
void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
{
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return;
mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n");
@@ -1215,7 +1213,7 @@ int mlx5_fw_tracer_reload(struct mlx5_fw_tracer *tracer)
struct mlx5_core_dev *dev;
int err;
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return 0;
dev = tracer->dev;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 6867a5aed42c..822859c360e7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -483,7 +483,7 @@ typedef int (*mlx5e_fp_xmit_xdp_frame_check)(struct mlx5e_xdpsq *);
typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq *,
struct mlx5e_xmit_data *,
int,
- struct xsk_tx_metadata *);
+ struct xsk_tx_metadata **);
struct mlx5e_xdpsq {
/* data path */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
index 28cab4bf525c..7bfe7cdc5770 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
@@ -30,6 +30,7 @@ enum {
MLX5E_TC_FLOW_FLAG_FAILED = MLX5E_TC_FLOW_BASE + 9,
MLX5E_TC_FLOW_FLAG_SAMPLE = MLX5E_TC_FLOW_BASE + 10,
MLX5E_TC_FLOW_FLAG_USE_ACT_STATS = MLX5E_TC_FLOW_BASE + 11,
+ MLX5E_TC_FLOW_FLAG_PEER = MLX5E_TC_FLOW_BASE + 12,
};
struct mlx5e_tc_flow_parse_attr {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
index d8c7cb8837d7..77ea51bfbaae 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
@@ -452,11 +452,11 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq
INDIRECT_CALLABLE_SCOPE bool
mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
- int check_result, struct xsk_tx_metadata *meta);
+ int check_result, struct xsk_tx_metadata **meta);
INDIRECT_CALLABLE_SCOPE bool
mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
- int check_result, struct xsk_tx_metadata *meta)
+ int check_result, struct xsk_tx_metadata **meta)
{
struct mlx5e_tx_mpwqe *session = &sq->mpwqe;
struct mlx5e_xdpsq_stats *stats = sq->stats;
@@ -504,7 +504,10 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx
* and it's safe to complete it at any time.
*/
mlx5e_xdp_mpwqe_session_start(sq);
- xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, &session->wqe->eth);
+ if (meta)
+ xsk_tx_metadata_request(sq->xsk_pool, meta,
+ &mlx5e_xsk_tx_metadata_ops,
+ &session->wqe->eth);
}
mlx5e_xdp_mpwqe_add_dseg(sq, p, stats);
@@ -535,7 +538,7 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq)
INDIRECT_CALLABLE_SCOPE bool
mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
- int check_result, struct xsk_tx_metadata *meta)
+ int check_result, struct xsk_tx_metadata **meta)
{
struct mlx5e_xmit_data_frags *xdptxdf =
container_of(xdptxd, struct mlx5e_xmit_data_frags, xd);
@@ -649,7 +652,9 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
sq->pc += num_wqebbs;
- xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg);
+ if (meta)
+ xsk_tx_metadata_request(sq->xsk_pool, meta,
+ &mlx5e_xsk_tx_metadata_ops, eseg);
sq->doorbell_cseg = cseg;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
index 3c54f8962664..5b8f4094d553 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
@@ -114,11 +114,11 @@ extern const struct xsk_tx_metadata_ops mlx5e_xsk_tx_metadata_ops;
INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq,
struct mlx5e_xmit_data *xdptxd,
int check_result,
- struct xsk_tx_metadata *meta));
+ struct xsk_tx_metadata **meta));
INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq,
struct mlx5e_xmit_data *xdptxd,
int check_result,
- struct xsk_tx_metadata *meta));
+ struct xsk_tx_metadata **meta));
INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq *sq));
INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq));
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
index 8aeab4b21035..3d19dad8f868 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
@@ -105,7 +105,7 @@ bool mlx5e_xsk_tx(struct mlx5e_xdpsq *sq, unsigned int budget)
ret = INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe,
mlx5e_xmit_xdp_frame, sq, &xdptxd,
- check_result, meta);
+ check_result, &meta);
if (unlikely(!ret)) {
if (sq->mpwqe.wqe)
mlx5e_xdp_mpwqe_complete(sq);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 7d47a1da8b6b..f0407a850ea8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -1939,8 +1939,10 @@ err_free_txqsq:
void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
{
sq->txq = netdev_get_tx_queue(sq->netdev, sq->txq_ix);
+ /* Reset BQL only when the SQ has no bytes in flight. */
+ if (sq->cc == sq->pc)
+ netdev_tx_reset_queue(sq->txq);
set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
- netdev_tx_reset_queue(sq->txq);
netif_tx_start_queue(sq->txq);
netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, sq->cq.napi);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 1bc7b9019124..b290beb4369a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -2161,7 +2161,8 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
if (mlx5e_is_eswitch_flow(flow)) {
struct mlx5_devcom_comp_dev *devcom = flow->priv->mdev->priv.eswitch->devcom;
- if (!mlx5_devcom_for_each_peer_begin(devcom)) {
+ if (flow_flag_test(flow, PEER) ||
+ !mlx5_devcom_for_each_peer_begin(devcom)) {
mlx5e_tc_del_fdb_flow(priv, flow);
return;
}
@@ -4628,6 +4629,7 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
else
in_mdev = priv->mdev;
+ flow_flags |= BIT(MLX5E_TC_FLOW_FLAG_PEER);
parse_attr = flow->attr->parse_attr;
peer_flow = __mlx5e_add_fdb_flow(peer_priv, f, flow_flags,
parse_attr->filter_dev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 907ee83a722d..247e5d85ec36 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -3986,7 +3986,7 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw)
mutex_destroy(&esw->fdb_table.offloads.vports.lock);
}
-static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw)
+static void esw_changed_event_handler(struct mlx5_eswitch *esw)
{
struct mlx5_esw_pf_info host_pf_info;
u16 new_num_vfs;
@@ -3999,6 +3999,11 @@ static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw)
host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, out);
new_num_vfs = host_pf_info.num_of_vfs;
+ if (host_pf_info.pf_disabled) {
+ mlx5_sf_table_esw_changed_event_handler(esw->dev);
+ mlx5_sf_hw_table_esw_changed_event_handler(esw->dev);
+ }
+
if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_info.pf_disabled)
goto free;
@@ -4091,8 +4096,7 @@ int mlx5_esw_funcs_changed_handler(struct notifier_block *nb,
esw_funcs = mlx5_nb_cof(nb, struct mlx5_esw_functions, nb);
esw = container_of(esw_funcs, struct mlx5_eswitch, esw_funcs);
- ret = mlx5_esw_add_work(esw, esw_vfs_changed_event_handler,
- GFP_ATOMIC);
+ ret = mlx5_esw_add_work(esw, esw_changed_event_handler, GFP_ATOMIC);
if (ret)
return NOTIFY_DONE;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
index b6cecbcc392d..4564d460416b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
@@ -561,3 +561,32 @@ bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev)
return xa_empty(&table->function_ids);
}
+
+void mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev)
+{
+ struct mlx5_sf_table *table = dev->priv.sf_table;
+ unsigned long index;
+ struct mlx5_sf *sf;
+
+ trace_mlx5_sf_host_pf_disabled(dev);
+
+ if (!table)
+ return;
+
+ mutex_lock(&table->sf_state_lock);
+ xa_for_each(&table->function_ids, index, sf) {
+ if (!sf->controller)
+ continue;
+
+ if (sf->hw_state == MLX5_VHCA_STATE_IN_USE)
+ sf->hw_state = MLX5_VHCA_STATE_ACTIVE;
+ else if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST)
+ sf->hw_state = MLX5_VHCA_STATE_ALLOCATED;
+ else
+ continue;
+ trace_mlx5_sf_update_state(table->dev, sf->port_index,
+ sf->controller, sf->hw_fn_id,
+ sf->hw_state);
+ }
+ mutex_unlock(&table->sf_state_lock);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h
index 302ce00da5a9..9dc88a7e30ca 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h
@@ -11,6 +11,14 @@
#include <linux/mlx5/driver.h>
#include "sf/vhca_event.h"
+TRACE_EVENT(mlx5_sf_host_pf_disabled,
+ TP_PROTO(const struct mlx5_core_dev *dev),
+ TP_ARGS(dev),
+ TP_STRUCT__entry(__string(devname, dev_name(dev->device))),
+ TP_fast_assign(__assign_str(devname);),
+ TP_printk("(%s)\n", __get_str(devname))
+);
+
TRACE_EVENT(mlx5_sf_add,
TP_PROTO(const struct mlx5_core_dev *dev,
unsigned int port_index,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
index 0bc9146a3598..95a8b1e64ba4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
@@ -459,3 +459,25 @@ bool mlx5_sf_hw_table_supported(const struct mlx5_core_dev *dev)
{
return !!dev->priv.sf_hw_table;
}
+
+void mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev)
+{
+ struct mlx5_sf_hw_table *table;
+ struct mlx5_sf_hwc_table *hwc;
+ int i;
+
+ table = dev->priv.sf_hw_table;
+ if (!table)
+ return;
+
+ mutex_lock(&table->table_lock);
+ hwc = &table->hwc[MLX5_SF_HWC_EXT_HOST];
+ for (i = 0; i < hwc->max_fn; i++) {
+ struct mlx5_sf_hw *sf_hw;
+
+ sf_hw = &hwc->sfs[i];
+ if (sf_hw->allocated && sf_hw->pending_delete)
+ mlx5_sf_hw_table_hwc_sf_free(dev, hwc, i);
+ }
+ mutex_unlock(&table->table_lock);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
index d8a934a0e968..ed784682b9b1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
@@ -15,12 +15,14 @@ void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev);
int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev);
void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev);
void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev);
+void mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev);
int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev);
int mlx5_sf_table_init(struct mlx5_core_dev *dev);
void mlx5_sf_notifiers_cleanup(struct mlx5_core_dev *dev);
void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev);
bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev);
+void mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev);
int mlx5_devlink_sf_port_new(struct devlink *devlink,
const struct devlink_port_new_attrs *add_attr,
@@ -60,6 +62,11 @@ static inline void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
{
}
+static inline void
+mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev)
+{
+}
+
static inline int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev)
{
return 0;
@@ -83,6 +90,11 @@ static inline bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev)
return true;
}
+static inline void
+mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev)
+{
+}
+
#endif
#endif
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3801f9d45278..a71f0df26378 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2748,8 +2748,8 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
meta_req.set_ic = &set_ic;
meta_req.tbs = tx_q->tbs;
meta_req.edesc = &tx_q->dma_entx[entry];
- xsk_tx_metadata_request(meta, &stmmac_xsk_tx_metadata_ops,
- &meta_req);
+ xsk_tx_metadata_request(pool, &meta,
+ &stmmac_xsk_tx_metadata_ops, &meta_req);
if (set_ic) {
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, tx_desc);
@@ -4134,6 +4134,15 @@ static int __stmmac_open(struct net_device *dev,
dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf));
+ /* The PHY is suspended when the interface is reopened without
+ * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs
+ * to stop their receive clock while powered down, but the DMA
+ * software reset in stmmac_hw_setup() requires a running receive
+ * clock, and phylink_start() below resumes the PHY only after the
+ * hardware setup. Resume a suspended PHY here first.
+ */
+ phylink_prepare_resume(priv->phylink);
+
stmmac_reset_queues_param(priv);
ret = stmmac_hw_setup(dev);
diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c
index 9993c1dfe471..168cfe9b59a9 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -35,25 +35,11 @@ static void ovpn_priv_free(struct net_device *net)
static int ovpn_mp_alloc(struct ovpn_priv *ovpn)
{
- struct in_device *dev_v4;
int i;
if (ovpn->mode != OVPN_MODE_MP)
return 0;
- dev_v4 = __in_dev_get_rtnl(ovpn->dev);
- if (dev_v4) {
- /* disable redirects as Linux gets confused by ovpn
- * handling same-LAN routing.
- * This happens because a multipeer interface is used as
- * relay point between hosts in the same subnet, while
- * in a classic LAN this would not be needed because the
- * two hosts would be able to talk directly.
- */
- IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false);
- IPV4_DEVCONF_ALL(dev_net(ovpn->dev), SEND_REDIRECTS) = false;
- }
-
/* the peer container is fairly large, therefore we allocate it only in
* MP mode
*/
@@ -97,9 +83,38 @@ static void ovpn_net_uninit(struct net_device *dev)
gro_cells_destroy(&ovpn->gro_cells);
}
+static int ovpn_net_open(struct net_device *dev)
+{
+ struct ovpn_priv *ovpn = netdev_priv(dev);
+ struct in_device *dev_v4;
+
+ /* the IPv4 in_device (and thus its config) is recreated whenever the
+ * interface is moved to a new netns, so redirects must be disabled on
+ * every bring-up rather than once at creation time, otherwise the
+ * setting is silently lost after such a move
+ */
+ if (ovpn->mode == OVPN_MODE_MP) {
+ dev_v4 = __in_dev_get_rtnl(dev);
+ if (dev_v4) {
+ /* disable redirects as Linux gets confused by ovpn
+ * handling same-LAN routing.
+ * This happens because a multipeer interface is used as
+ * relay point between hosts in the same subnet, while
+ * in a classic LAN this would not be needed because the
+ * two hosts would be able to talk directly.
+ */
+ IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false);
+ IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false;
+ }
+ }
+
+ return 0;
+}
+
static const struct net_device_ops ovpn_netdev_ops = {
.ndo_init = ovpn_net_init,
.ndo_uninit = ovpn_net_uninit,
+ .ndo_open = ovpn_net_open,
.ndo_start_xmit = ovpn_net_xmit,
};
@@ -183,6 +198,7 @@ static int ovpn_newlink(struct net_device *dev,
struct ovpn_priv *ovpn = netdev_priv(dev);
struct nlattr **data = params->data;
enum ovpn_mode mode = OVPN_MODE_P2P;
+ int ret;
if (data && data[IFLA_OVPN_MODE]) {
mode = nla_get_u8(data[IFLA_OVPN_MODE]);
@@ -207,7 +223,17 @@ static int ovpn_newlink(struct net_device *dev,
else
netif_carrier_off(dev);
- return register_netdevice(dev);
+ ret = register_netdevice(dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static size_t ovpn_get_size(const struct net_device *dev)
+{
+ /* IFLA_OVPN_MODE */
+ return nla_total_size(sizeof(u8));
}
static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev)
@@ -228,13 +254,17 @@ static struct rtnl_link_ops ovpn_link_ops = {
.policy = ovpn_policy,
.maxtype = IFLA_OVPN_MAX,
.newlink = ovpn_newlink,
+ .get_size = ovpn_get_size,
.fill_info = ovpn_fill_info,
};
static int __init ovpn_init(void)
{
- int err = rtnl_link_register(&ovpn_link_ops);
+ int err;
+
+ ovpn_tcp_init();
+ err = rtnl_link_register(&ovpn_link_ops);
if (err) {
pr_err("ovpn: can't register rtnl link ops: %d\n", err);
return err;
@@ -246,8 +276,6 @@ static int __init ovpn_init(void)
goto unreg_rtnl;
}
- ovpn_tcp_init();
-
return 0;
unreg_rtnl:
diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c
index 2147cec7c2c5..92d2fdc17c2e 100644
--- a/drivers/net/ovpn/netlink-gen.c
+++ b/drivers/net/ovpn/netlink-gen.c
@@ -16,6 +16,14 @@ static const struct netlink_range_validation ovpn_a_peer_id_range = {
.max = 16777215ULL,
};
+static const struct netlink_range_validation ovpn_a_peer_keepalive_interval_range = {
+ .max = 86400ULL,
+};
+
+static const struct netlink_range_validation ovpn_a_peer_keepalive_timeout_range = {
+ .max = 86400ULL,
+};
+
static const struct netlink_range_validation ovpn_a_peer_tx_id_range = {
.max = 16777215ULL,
};
@@ -68,8 +76,8 @@ const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1] = {
[OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, },
[OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16),
[OVPN_A_PEER_LOCAL_PORT] = NLA_POLICY_MIN(NLA_BE16, 1),
- [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, },
- [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, },
+ [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range),
+ [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range),
[OVPN_A_PEER_DEL_REASON] = NLA_POLICY_MAX(NLA_U32, 4),
[OVPN_A_PEER_VPN_RX_BYTES] = { .type = NLA_UINT, },
[OVPN_A_PEER_VPN_TX_BYTES] = { .type = NLA_UINT, },
@@ -97,8 +105,8 @@ const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = {
[OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16),
[OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, },
[OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16),
- [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, },
- [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, },
+ [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range),
+ [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range),
[OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range),
};
@@ -112,8 +120,8 @@ const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = {
[OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16),
[OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, },
[OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16),
- [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, },
- [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, },
+ [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range),
+ [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range),
[OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range),
};
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index 4c66c1ec497e..4dad85294198 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c
@@ -534,6 +534,12 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
*/
if (ret > 0)
ovpn_peer_hash_vpn_ip(peer);
+ /* if the remote endpoint was updated, the by_transp_addr hash bucket
+ * also needs to be refreshed, otherwise incoming packets from the new
+ * remote address would fail the lockless lookup
+ */
+ if (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6])
+ ovpn_peer_hash_transp_addr(peer);
spin_unlock_bh(&ovpn->lock);
ovpn_peer_put(peer);
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index a21d02ac715e..b0519f9840d8 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -189,6 +189,9 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer,
&(*__tbl1)[ovpn_get_hash_slot(*__tbl1, _key, _key_len)];\
})
+static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
+ const struct ovpn_bind *bind);
+
/**
* ovpn_peer_endpoints_update - update remote or local endpoint for peer
* @peer: peer to update the remote endpoint for
@@ -196,7 +199,6 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer,
*/
void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
{
- struct hlist_nulls_head *nhead;
struct sockaddr_storage ss;
struct sockaddr_in6 *sa6;
bool reset_cache = false;
@@ -220,9 +222,16 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
*/
local_ip = &ip_hdr(skb)->daddr;
sa = (struct sockaddr_in *)&ss;
- sa->sin_family = AF_INET;
- sa->sin_addr.s_addr = ip_hdr(skb)->saddr;
- sa->sin_port = udp_hdr(skb)->source;
+ /* use a designated initializer so the sin_zero padding
+ * is zeroed (it ends up in the by_transp_addr hash key)
+ * without memset-ing the whole sockaddr_storage on the
+ * RX fast path
+ */
+ *sa = (struct sockaddr_in) {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = ip_hdr(skb)->saddr,
+ .sin_port = udp_hdr(skb)->source,
+ };
salen = sizeof(*sa);
reset_cache = true;
break;
@@ -248,11 +257,19 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
*/
local_ip = &ipv6_hdr(skb)->daddr;
sa6 = (struct sockaddr_in6 *)&ss;
- sa6->sin6_family = AF_INET6;
- sa6->sin6_addr = ipv6_hdr(skb)->saddr;
- sa6->sin6_port = udp_hdr(skb)->source;
- sa6->sin6_scope_id = ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr,
- skb->skb_iif);
+ /* use a designated initializer so the sin6_flowinfo
+ * padding is zeroed (it ends up in the by_transp_addr
+ * hash key) without memset-ing the whole
+ * sockaddr_storage on the RX fast path
+ */
+ *sa6 = (struct sockaddr_in6) {
+ .sin6_family = AF_INET6,
+ .sin6_addr = ipv6_hdr(skb)->saddr,
+ .sin6_port = udp_hdr(skb)->source,
+ .sin6_scope_id =
+ ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr,
+ skb->skb_iif),
+ };
salen = sizeof(*sa6);
reset_cache = true;
break;
@@ -295,42 +312,25 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
ovpn_nl_peer_float_notify(peer, &ss);
/* rehashing is required only in MP mode as P2P has one peer
- * only and thus there is no hashtable
+ * only and thus there is no hashtable.
+ *
+ * This function may be invoked concurrently, so re-read peer->bind
+ * under the proper locks and rehash against its current value.
*/
- if (peer->ovpn->mode == OVPN_MODE_MP) {
- spin_lock_bh(&peer->ovpn->lock);
- spin_lock_bh(&peer->lock);
- bind = rcu_dereference_protected(peer->bind,
- lockdep_is_held(&peer->lock));
- if (unlikely(!bind)) {
- spin_unlock_bh(&peer->lock);
- spin_unlock_bh(&peer->ovpn->lock);
- return;
- }
-
- /* This function may be invoked concurrently, therefore another
- * float may have happened in parallel: perform rehashing
- * using the peer->bind->remote directly as key
- */
-
- switch (bind->remote.in4.sin_family) {
- case AF_INET:
- salen = sizeof(*sa);
- break;
- case AF_INET6:
- salen = sizeof(*sa6);
- break;
- }
+ if (peer->ovpn->mode != OVPN_MODE_MP)
+ return;
- /* remove old hashing */
- hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr);
- /* re-add with new transport address */
- nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr,
- &bind->remote, salen);
- hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead);
- spin_unlock_bh(&peer->lock);
- spin_unlock_bh(&peer->ovpn->lock);
- }
+ /* This function may be invoked concurrently, therefore another
+ * float may have happened in parallel: re-acquire the locks and
+ * rehash using the peer->bind->remote directly as key
+ */
+ spin_lock_bh(&peer->ovpn->lock);
+ spin_lock_bh(&peer->lock);
+ bind = rcu_dereference_protected(peer->bind,
+ lockdep_is_held(&peer->lock));
+ __ovpn_peer_hash_transp_addr(peer, bind);
+ spin_unlock_bh(&peer->lock);
+ spin_unlock_bh(&peer->ovpn->lock);
return;
unlock:
spin_unlock_bh(&peer->lock);
@@ -896,6 +896,83 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
return match;
}
+/* Move @peer to the by_transp_addr bucket matching its current bind.
+ *
+ * Caller must hold both peer->ovpn->lock and peer->lock, and must have
+ * already dereferenced a valid (non-NULL) peer->bind, passed in as @bind.
+ */
+static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
+ const struct ovpn_bind *bind)
+{
+ struct sockaddr_storage sa = {};
+ struct hlist_nulls_head *nhead;
+ struct sockaddr_in6 *sa6;
+ struct sockaddr_in *sa4;
+ size_t salen;
+
+ lockdep_assert_held(&peer->ovpn->lock);
+ lockdep_assert_held(&peer->lock);
+
+ if (WARN_ON_ONCE(!bind))
+ return;
+
+ /* peer may have been concurrently removed between the caller's
+ * initial lookup and our acquisition of ovpn->lock; skip the
+ * rehash so we don't re-insert a removed peer
+ */
+ if (unlikely(hlist_unhashed(&peer->hash_entry_id)))
+ return;
+
+ /* Build the hash key from the transport identity only
+ * (family/address/port), matching ovpn_peer_add_mp() and the lookup
+ * in ovpn_peer_get_by_transp_addr(). Hashing bind->remote directly
+ * would fold in sin6_scope_id (set on the float path but never by the
+ * lookup), scattering the peer into a bucket lookups cannot reach.
+ */
+ switch (bind->remote.in4.sin_family) {
+ case AF_INET:
+ sa4 = (struct sockaddr_in *)&sa;
+ sa4->sin_family = AF_INET;
+ sa4->sin_addr.s_addr = bind->remote.in4.sin_addr.s_addr;
+ sa4->sin_port = bind->remote.in4.sin_port;
+ salen = sizeof(*sa4);
+ break;
+ case AF_INET6:
+ sa6 = (struct sockaddr_in6 *)&sa;
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_addr = bind->remote.in6.sin6_addr;
+ sa6->sin6_port = bind->remote.in6.sin6_port;
+ salen = sizeof(*sa6);
+ break;
+ default:
+ return;
+ }
+
+ /* remove old hashing (no-op if entry is not currently linked) */
+ hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr);
+ /* re-add with current transport address */
+ nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, &sa,
+ salen);
+ hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead);
+}
+
+void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer)
+{
+ struct ovpn_bind *bind;
+
+ lockdep_assert_held(&peer->ovpn->lock);
+
+ /* rehashing makes sense only in multipeer mode */
+ if (peer->ovpn->mode != OVPN_MODE_MP)
+ return;
+
+ spin_lock_bh(&peer->lock);
+ bind = rcu_dereference_protected(peer->bind,
+ lockdep_is_held(&peer->lock));
+ __ovpn_peer_hash_transp_addr(peer, bind);
+ spin_unlock_bh(&peer->lock);
+}
+
void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
{
struct hlist_nulls_head *nhead;
@@ -906,6 +983,13 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
if (peer->ovpn->mode != OVPN_MODE_MP)
return;
+ /* peer may have been concurrently removed between the caller's
+ * initial lookup and our acquisition of ovpn->lock; skip the
+ * rehash so we don't re-insert a removed peer
+ */
+ if (hlist_unhashed(&peer->hash_entry_id))
+ return;
+
if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
/* remove potential old hashing */
hlist_nulls_del_init_rcu(&peer->hash_entry_addr4);
@@ -1165,7 +1249,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
}
if (sk) {
- ovpn_sock = rcu_access_pointer(peer->sock);
+ ovpn_sock = rcu_dereference_bh(peer->sock);
if (!ovpn_sock || ovpn_sock->sk != sk) {
spin_unlock_bh(&ovpn->lock);
return;
diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h
index 86c8cffada6d..dfa5c0037e02 100644
--- a/drivers/net/ovpn/peer.h
+++ b/drivers/net/ovpn/peer.h
@@ -150,6 +150,7 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id);
struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn,
struct sk_buff *skb);
void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer);
+void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer);
bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
struct ovpn_peer *peer);
diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c
index 517caa64a4fe..6cbeb2caaeec 100644
--- a/drivers/net/ovpn/socket.c
+++ b/drivers/net/ovpn/socket.c
@@ -162,6 +162,15 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
rcu_read_lock();
ovpn_sock = rcu_dereference_sk_user_data(sk);
if (ovpn_sock) {
+ /* something else filled the sk_user_data without
+ * setting the encap_type. Reject the socket.
+ */
+ if (!type) {
+ ovpn_sock = ERR_PTR(-EBUSY);
+ rcu_read_unlock();
+ goto sock_release;
+ }
+
/* socket owned by another ovpn instance, we can't use it */
if (ovpn_sock->ovpn != peer->ovpn) {
ovpn_sock = ERR_PTR(-EBUSY);
diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h
index 320f76ffa81f..79e09727b81b 100644
--- a/drivers/net/phy/mediatek/mtk.h
+++ b/drivers/net/phy/mediatek/mtk.h
@@ -53,15 +53,15 @@
#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
MTK_PHY_LED_BLINK_100RX | \
MTK_PHY_LED_BLINK_10RX)
-#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
- MTK_PHY_LED_BLINK_100RX | \
- MTK_PHY_LED_BLINK_10RX)
+#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000TX | \
+ MTK_PHY_LED_BLINK_100TX | \
+ MTK_PHY_LED_BLINK_10TX)
#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \
MTK_GPHY_LED_ON_SET)
#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
MTK_GPHY_LED_RX_BLINK_SET)
-#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
+#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500TX | \
MTK_GPHY_LED_TX_BLINK_SET)
#define MTK_PHY_LED_STATE_FORCE_ON 0
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index fae115915c8e..5d2d34d24ce8 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -1074,10 +1074,21 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
skb_reset_mac_header(skb);
skb->protocol = eth_hdr(skb)->h_proto;
+ rcu_read_lock();
+ tap = rcu_dereference(q->tap);
+ if (!tap) {
+ kfree_skb(skb);
+ rcu_read_unlock();
+ return 0;
+ }
+ skb->dev = tap->dev;
+
if (vnet_hdr_len) {
err = tun_vnet_hdr_to_skb(q->flags, skb, gso);
- if (err)
+ if (err) {
+ rcu_read_unlock();
goto err_kfree;
+ }
}
/* Move network header to the right position for VLAN tagged packets */
@@ -1085,15 +1096,8 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
skb_set_network_header(skb, depth);
- rcu_read_lock();
- tap = rcu_dereference(q->tap);
- if (tap) {
- skb->dev = tap->dev;
- skb_probe_transport_header(skb);
- dev_queue_xmit(skb);
- } else {
- kfree_skb(skb);
- }
+ skb_probe_transport_header(skb);
+ dev_queue_xmit(skb);
rcu_read_unlock();
return 0;
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index 02a91650561a..98893732bc6e 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -386,11 +386,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
break;
}
- tb_ring_stop(net->rx_ring.ring);
- tb_ring_stop(net->tx_ring.ring);
- tbnet_free_buffers(&net->rx_ring);
- tbnet_free_buffers(&net->tx_ring);
-
+ /* Tear the paths down before stopping the rings. This mirrors
+ * tbnet_connected_work(), which enables the paths last so the
+ * Rx ring is primed before packets can arrive. Stopping a
+ * ring zeroes its descriptor base and tbnet_free_buffers()
+ * unmaps and frees the frame buffers, leaving anything still
+ * in flight with nowhere to drain to;
+ * __tb_path_deactivate_hop() then waits for the hop's
+ * 'pending' bit, which on some host routers never clears in
+ * that state.
+ */
ret = tb_xdomain_disable_paths(net->xd,
net->local_transmit_path,
net->tx_ring.ring->hop,
@@ -399,6 +404,11 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
if (ret)
netdev_warn(net->dev, "failed to disable DMA paths\n");
+ tb_ring_stop(net->rx_ring.ring);
+ tb_ring_stop(net->tx_ring.ring);
+ tbnet_free_buffers(&net->rx_ring);
+ tbnet_free_buffers(&net->tx_ring);
+
tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
net->remote_transmit_path = 0;
}
@@ -925,12 +935,8 @@ static int tbnet_open(struct net_device *dev)
netif_carrier_off(dev);
- flags = RING_FLAG_FRAME;
- /* Only enable full E2E if the other end supports it too */
- if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
- flags |= RING_FLAG_E2E;
-
- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags);
+ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE,
+ RING_FLAG_FRAME);
if (!ring) {
netdev_err(dev, "failed to allocate Tx ring\n");
return -ENOMEM;
@@ -949,6 +955,11 @@ static int tbnet_open(struct net_device *dev)
sof_mask = BIT(TBIP_PDF_FRAME_START);
eof_mask = BIT(TBIP_PDF_FRAME_END);
+ flags = RING_FLAG_FRAME;
+ /* Only enable full E2E if the other end supports it too */
+ if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
+ flags |= RING_FLAG_E2E;
+
ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags,
net->tx_ring.ring->hop, sof_mask,
eof_mask, tbnet_start_poll, net);
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 98f899ea2e94..81d8412ce8e2 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1487,8 +1487,10 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
headroom = skb_headroom(skb) - 8;
- if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb))
+ if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) {
+ dev_kfree_skb_any(skb);
return NULL;
+ }
if ((skb_header_cloned(skb) || headroom < 0) &&
pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
index bb1364f85bd1..2b490114d232 100644
--- a/drivers/net/usb/ipheth.c
+++ b/drivers/net/usb/ipheth.c
@@ -490,6 +490,7 @@ static int ipheth_open(struct net_device *net)
if (retval)
return retval;
+ enable_delayed_work(&dev->carrier_work);
schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
return retval;
}
@@ -499,7 +500,11 @@ static int ipheth_close(struct net_device *net)
struct ipheth_device *dev = netdev_priv(net);
netif_stop_queue(net);
- cancel_delayed_work_sync(&dev->carrier_work);
+ /* A TX URB can still complete with an error after this point and
+ * try to re-arm the carrier work. Disable it instead of cancelling
+ * it, so that such a schedule_delayed_work() is a no-op.
+ */
+ disable_delayed_work_sync(&dev->carrier_work);
return 0;
}
@@ -629,6 +634,10 @@ static int ipheth_probe(struct usb_interface *intf,
}
INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+ /* Armed only between ipheth_open() and ipheth_close(). Start out
+ * disabled so the enable/disable counts balance from the first open.
+ */
+ disable_delayed_work(&dev->carrier_work);
retval = ipheth_alloc_urbs(dev);
if (retval) {
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 25518635b7b7..a19ecf718f36 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1794,7 +1794,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
*/
dev->hard_mtu = net->mtu + net->hard_header_len;
net->min_mtu = 0;
- net->max_mtu = ETH_MAX_MTU;
+ net->max_mtu = net->mtu;
net->netdev_ops = &usbnet_netdev_ops;
net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
@@ -1804,6 +1804,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
// allow device-specific bind/init procedures
// NOTE net->name still not usable ...
if (info->bind) {
+ net->max_mtu = ETH_MAX_MTU;
status = info->bind(dev, udev);
if (status < 0)
goto out1;
diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c
index 78d320d56be6..0a50486337d3 100644
--- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c
+++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c
@@ -480,7 +480,7 @@ static const char * const ps_hold_groups[] = {
};
static const struct pinfunction ipq8064_functions[] = {
- IPQ_PIN_FUNCTION(gpio),
+ IPQ_GPIO_PIN_FUNCTION(gpio),
IPQ_PIN_FUNCTION(mdio),
IPQ_PIN_FUNCTION(ssbi),
IPQ_PIN_FUNCTION(spmi),
@@ -507,19 +507,19 @@ static const struct pinfunction ipq8064_functions[] = {
IPQ_PIN_FUNCTION(usb2_hsic),
IPQ_PIN_FUNCTION(rgmii2),
IPQ_PIN_FUNCTION(sata),
- IPQ_PIN_FUNCTION(pcie1_rst),
+ IPQ_GPIO_PIN_FUNCTION(pcie1_rst),
IPQ_PIN_FUNCTION(pcie1_prsnt),
IPQ_PIN_FUNCTION(pcie1_pwren_n),
IPQ_PIN_FUNCTION(pcie1_pwren),
IPQ_PIN_FUNCTION(pcie1_pwrflt),
IPQ_PIN_FUNCTION(pcie1_clk_req),
- IPQ_PIN_FUNCTION(pcie2_rst),
+ IPQ_GPIO_PIN_FUNCTION(pcie2_rst),
IPQ_PIN_FUNCTION(pcie2_prsnt),
IPQ_PIN_FUNCTION(pcie2_pwren_n),
IPQ_PIN_FUNCTION(pcie2_pwren),
IPQ_PIN_FUNCTION(pcie2_pwrflt),
IPQ_PIN_FUNCTION(pcie2_clk_req),
- IPQ_PIN_FUNCTION(pcie3_rst),
+ IPQ_GPIO_PIN_FUNCTION(pcie3_rst),
IPQ_PIN_FUNCTION(pcie3_prsnt),
IPQ_PIN_FUNCTION(pcie3_pwren_n),
IPQ_PIN_FUNCTION(pcie3_pwren),
diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9650.c b/drivers/pinctrl/qcom/pinctrl-ipq9650.c
index 64e443aa31b2..5399d7ad934c 100644
--- a/drivers/pinctrl/qcom/pinctrl-ipq9650.c
+++ b/drivers/pinctrl/qcom/pinctrl-ipq9650.c
@@ -310,11 +310,11 @@ static const char *const audio_sec_mclk_out0_groups[] = {
};
static const char *const audio_sec_mclk_in1_groups[] = {
- "gpio37",
+ "gpio39",
};
static const char *const audio_sec_mclk_out1_groups[] = {
- "gpio37",
+ "gpio39",
};
static const char *const audio_sec_groups[] = {
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index 4fbff61de6bb..b94ba1a4177e 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -24,6 +24,11 @@ struct pinctrl_pin_desc;
fname##_groups, \
ARRAY_SIZE(fname##_groups))
+#define IPQ_GPIO_PIN_FUNCTION(fname) \
+ [IPQ_MUX_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \
+ fname##_groups, \
+ ARRAY_SIZE(fname##_groups))
+
#define MSM_PIN_FUNCTION(fname) \
[msm_mux_##fname] = PINCTRL_PINFUNCTION(#fname, \
fname##_groups, \
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 35e911f1ad78..3d26ec1f7b9e 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -2177,9 +2177,11 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
if (err)
return err;
+ snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN,
+ (const char *)bp->board_id);
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
- bp->board_id);
+ buf);
if (err)
return err;
diff --git a/drivers/s390/cio/vfio_ccw_async.c b/drivers/s390/cio/vfio_ccw_async.c
index 420d89ba7f83..4aff0b58fa5d 100644
--- a/drivers/s390/cio/vfio_ccw_async.c
+++ b/drivers/s390/cio/vfio_ccw_async.c
@@ -8,6 +8,7 @@
*/
#include <linux/vfio.h>
+#include <linux/nospec.h>
#include "vfio_ccw_private.h"
@@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private,
return -EINVAL;
mutex_lock(&private->io_mutex);
+
+ if (i >= private->num_regions) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ i = array_index_nospec(i, private->num_regions);
region = private->region[i].data;
if (copy_to_user(buf, (void *)region + pos, count))
ret = -EFAULT;
else
ret = count;
+
+out_unlock:
mutex_unlock(&private->io_mutex);
return ret;
}
@@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private,
if (!mutex_trylock(&private->io_mutex))
return -EAGAIN;
+ if (i >= private->num_regions) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ i = array_index_nospec(i, private->num_regions);
region = private->region[i].data;
if (copy_from_user((void *)region + pos, buf, count)) {
ret = -EFAULT;
diff --git a/drivers/s390/cio/vfio_ccw_chp.c b/drivers/s390/cio/vfio_ccw_chp.c
index 38c176cf6295..7708eb4d6de0 100644
--- a/drivers/s390/cio/vfio_ccw_chp.c
+++ b/drivers/s390/cio/vfio_ccw_chp.c
@@ -9,6 +9,7 @@
*/
#include <linux/slab.h>
+#include <linux/nospec.h>
#include <linux/vfio.h>
#include "vfio_ccw_private.h"
@@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
return -EINVAL;
mutex_lock(&private->io_mutex);
+
+ if (i >= private->num_regions) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ i = array_index_nospec(i, private->num_regions);
region = private->region[i].data;
if (cio_update_schib(sch)) {
@@ -85,19 +93,30 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
struct ccw_crw_region *region;
struct vfio_ccw_crw *crw;
+ unsigned long flags;
int ret;
if (pos + count > sizeof(*region))
return -EINVAL;
+ mutex_lock(&private->io_mutex);
+ if (i >= private->num_regions) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ i = array_index_nospec(i, private->num_regions);
+ region = private->region[i].data;
+
+ spin_lock_irqsave(&private->crw_lock, flags);
crw = list_first_entry_or_null(&private->crw,
struct vfio_ccw_crw, next);
if (crw)
list_del(&crw->next);
- mutex_lock(&private->io_mutex);
- region = private->region[i].data;
+ /* Drop CRW lock while copying to userspace */
+ spin_unlock_irqrestore(&private->crw_lock, flags);
if (crw)
memcpy(&region->crw, &crw->crw, sizeof(region->crw));
@@ -108,14 +127,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
ret = count;
region->crw = 0;
-
- mutex_unlock(&private->io_mutex);
-
kfree(crw);
/* Notify the guest if more CRWs are on our queue */
+ spin_lock_irqsave(&private->crw_lock, flags);
if (!list_empty(&private->crw) && private->crw_trigger)
eventfd_signal(private->crw_trigger);
+ spin_unlock_irqrestore(&private->crw_lock, flags);
+
+out:
+ mutex_unlock(&private->io_mutex);
return ret;
}
diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c
index 7561aa7d3e01..58722c4baa25 100644
--- a/drivers/s390/cio/vfio_ccw_cp.c
+++ b/drivers/s390/cio/vfio_ccw_cp.c
@@ -233,6 +233,7 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len)
}
#define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k)
+#define get_idaw_size(_cp) ((_cp)->orb.cmd.c64 ? sizeof(u64) : sizeof(u32))
/*
* Helpers to operate ccwchain.
@@ -332,6 +333,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len)
goto out_err;
list_add_tail(&chain->next, &cp->ccwchain_list);
+ cp->ccwchain_count++;
return chain;
@@ -376,11 +378,9 @@ static void ccwchain_cda_free(struct ccwchain *chain, int idx)
static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
{
struct ccw1 *ccw = cp->guest_cp;
- int cnt = 0;
-
- do {
- cnt++;
+ int cnt;
+ for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) {
/*
* We want to keep counting if the current CCW has the
* command-chaining flag enabled, or if it is a TIC CCW
@@ -390,15 +390,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
* after the TIC, depending on the results of its operation.
*/
if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt))
- break;
-
- ccw++;
- } while (cnt < CCWCHAIN_LEN_MAX + 1);
-
- if (cnt == CCWCHAIN_LEN_MAX + 1)
- cnt = -EINVAL;
+ return cnt;
+ }
- return cnt;
+ return -EINVAL;
}
static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp)
@@ -441,6 +436,10 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp)
if (len < 0)
return len;
+ /* Limit number of chains in a single channel program */
+ if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX)
+ return -EINVAL;
+
/* Need alloc a new chain for this one. */
chain = ccwchain_alloc(cp, len);
if (!chain)
@@ -455,9 +454,6 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp)
/* Loop for tics on this new chain. */
ret = ccwchain_loop_tic(chain, cp);
- if (ret)
- ccwchain_free(chain);
-
return ret;
}
@@ -486,6 +482,23 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp)
return 0;
}
+static int ccwchain_build_ccws(dma32_t cda, struct channel_program *cp)
+{
+ struct ccwchain *chain, *temp;
+ int ret;
+
+ ret = ccwchain_handle_ccw(cda, cp);
+
+ if (ret) {
+ /* Cleanup if an error occurred */
+ list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) {
+ ccwchain_free(chain);
+ }
+ }
+
+ return ret;
+}
+
static int ccwchain_fetch_tic(struct ccw1 *ccw,
struct channel_program *cp)
{
@@ -511,7 +524,8 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int
&container_of(cp, struct vfio_ccw_private, cp)->vdev;
dma64_t *idaws;
dma32_t *idaws_f1;
- int idal_len = idaw_nr * sizeof(*idaws);
+ u64 first_idaw;
+ int idal_len = idaw_nr * get_idaw_size(cp);
int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE;
int idaw_mask = ~(idaw_size - 1);
int i, ret;
@@ -527,6 +541,18 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int
kfree(idaws);
return ERR_PTR(ret);
}
+
+ idaws_f1 = (dma32_t *)idaws;
+ if (cp->orb.cmd.c64)
+ first_idaw = dma64_to_u64(idaws[0]);
+ else
+ first_idaw = dma32_to_u32(idaws_f1[0]);
+
+ /* Unexpected mismatch from earlier read */
+ if (first_idaw != cp->guest_iova) {
+ kfree(idaws);
+ return ERR_PTR(-EINVAL);
+ }
} else {
/* Fabricate an IDAL based off CCW data address */
if (cp->orb.cmd.c64) {
@@ -568,7 +594,7 @@ static int ccw_count_idaws(struct ccw1 *ccw,
struct vfio_device *vdev =
&container_of(cp, struct vfio_ccw_private, cp)->vdev;
u64 iova;
- int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32);
+ int size = get_idaw_size(cp);
int ret;
int bytes = 1;
@@ -592,6 +618,9 @@ static int ccw_count_idaws(struct ccw1 *ccw,
iova = dma32_to_u32(ccw->cda);
}
+ /* Save the read address for later */
+ cp->guest_iova = iova;
+
/* Format-1 IDAWs operate on 2K each */
if (!cp->orb.cmd.c64)
return idal_2k_nr_words((void *)iova, bytes);
@@ -731,11 +760,12 @@ int cp_init(struct channel_program *cp, union orb *orb)
vdev->dev,
"Prefetching channel program even though prefetch not specified in ORB");
+ cp->ccwchain_count = 0;
INIT_LIST_HEAD(&cp->ccwchain_list);
memcpy(&cp->orb, orb, sizeof(*orb));
/* Build a ccwchain for the first CCW segment */
- ret = ccwchain_handle_ccw(orb->cmd.cpa, cp);
+ ret = ccwchain_build_ccws(orb->cmd.cpa, cp);
if (!ret)
cp->initialized = true;
@@ -947,17 +977,23 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
*/
bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length)
{
+ struct vfio_ccw_private *private =
+ container_of(cp, struct vfio_ccw_private, cp);
struct ccwchain *chain;
int i;
if (!cp->initialized)
return false;
+ mutex_lock(&private->io_mutex);
list_for_each_entry(chain, &cp->ccwchain_list, next) {
for (i = 0; i < chain->ch_len; i++)
- if (page_array_iova_pinned(&chain->ch_pa[i], iova, length))
+ if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) {
+ mutex_unlock(&private->io_mutex);
return true;
+ }
}
+ mutex_unlock(&private->io_mutex);
return false;
}
diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h
index fc31eb699807..9af98ff12d67 100644
--- a/drivers/s390/cio/vfio_ccw_cp.h
+++ b/drivers/s390/cio/vfio_ccw_cp.h
@@ -23,11 +23,19 @@
*/
#define CCWCHAIN_LEN_MAX 256
+/*
+ * Maximum number of chains
+ */
+#define CCWCHAIN_COUNT_MAX 16
+
/**
* struct channel_program - manage information for channel program
* @ccwchain_list: list head of ccwchains
* @orb: orb for the currently processed ssch request
* @initialized: whether this instance is actually initialized
+ * @guest_cp: copy of guest channel program
+ * @ccwchain_count: number of channel program segments (linked by TIC)
+ * @guest_iova: first data address of a guest channel program
*
* @ccwchain_list is the head of a ccwchain list, that contents the
* translated result of the guest channel program that pointed out by
@@ -38,6 +46,8 @@ struct channel_program {
union orb orb;
bool initialized;
struct ccw1 *guest_cp;
+ unsigned int ccwchain_count;
+ u64 guest_iova;
};
int cp_init(struct channel_program *cp, union orb *orb);
diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
index 1a095085bc72..ab6b518cc353 100644
--- a/drivers/s390/cio/vfio_ccw_drv.c
+++ b/drivers/s390/cio/vfio_ccw_drv.c
@@ -91,6 +91,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
is_final = !(scsw_actl(&irb->scsw) &
(SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
+ mutex_lock(&private->io_mutex);
if (scsw_is_solicited(&irb->scsw)) {
cp_update_scsw(&private->cp, &irb->scsw);
if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) {
@@ -98,9 +99,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
cp_is_finished = true;
}
}
- mutex_lock(&private->io_mutex);
memcpy(private->io_region->irb_area, irb, sizeof(*irb));
- mutex_unlock(&private->io_mutex);
/*
* Reset to IDLE only if processing of a channel program
@@ -110,6 +109,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
*/
if (cp_is_finished)
private->state = VFIO_CCW_STATE_IDLE;
+ mutex_unlock(&private->io_mutex);
if (private->io_trigger)
eventfd_signal(private->io_trigger);
@@ -118,11 +118,25 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
void vfio_ccw_crw_todo(struct work_struct *work)
{
struct vfio_ccw_private *private;
+ unsigned long flags;
private = container_of(work, struct vfio_ccw_private, crw_work);
+ spin_lock_irqsave(&private->crw_lock, flags);
if (!list_empty(&private->crw) && private->crw_trigger)
eventfd_signal(private->crw_trigger);
+ spin_unlock_irqrestore(&private->crw_lock, flags);
+}
+
+void vfio_ccw_notoper_todo(struct work_struct *work)
+{
+ struct vfio_ccw_private *private;
+
+ private = container_of(work, struct vfio_ccw_private, notoper_work);
+
+ mutex_lock(&private->io_mutex);
+ cp_free(&private->cp);
+ mutex_unlock(&private->io_mutex);
}
/*
@@ -275,6 +289,7 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
unsigned int rsid)
{
struct vfio_ccw_crw *crw;
+ unsigned long flags;
/*
* If unable to allocate a CRW, just drop the event and
@@ -292,7 +307,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
crw->crw.erc = erc;
crw->crw.rsid = rsid;
+ spin_lock_irqsave(&private->crw_lock, flags);
list_add_tail(&crw->next, &private->crw);
+ spin_unlock_irqrestore(&private->crw_lock, flags);
queue_work(vfio_ccw_work_q, &private->crw_work);
}
diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c
index 4d7988ea47ef..5fd94e9d5c61 100644
--- a/drivers/s390/cio/vfio_ccw_fsm.c
+++ b/drivers/s390/cio/vfio_ccw_fsm.c
@@ -170,8 +170,8 @@ static void fsm_notoper(struct vfio_ccw_private *private,
css_sched_sch_todo(sch, SCH_TODO_UNREG);
private->state = VFIO_CCW_STATE_NOT_OPER;
- /* This is usually handled during CLOSE event */
- cp_free(&private->cp);
+ /* This routine could be called from IRQ context, so defer */
+ queue_work(vfio_ccw_work_q, &private->notoper_work);
}
/*
@@ -410,7 +410,11 @@ static void fsm_close(struct vfio_ccw_private *private,
private->state = VFIO_CCW_STATE_STANDBY;
spin_unlock_irq(&sch->lock);
+
+ mutex_lock(&private->io_mutex);
cp_free(&private->cp);
+ mutex_unlock(&private->io_mutex);
+
return;
err_unlock:
diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c
index 45ec722d25ea..5ce91285c7d5 100644
--- a/drivers/s390/cio/vfio_ccw_ops.c
+++ b/drivers/s390/cio/vfio_ccw_ops.c
@@ -54,6 +54,8 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
INIT_LIST_HEAD(&private->crw);
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
+ INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo);
+ spin_lock_init(&private->crw_lock);
private->cp.guest_cp = kzalloc_objs(struct ccw1, CCWCHAIN_LEN_MAX);
if (!private->cp.guest_cp)
@@ -130,11 +132,28 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
struct vfio_ccw_private *private =
container_of(vdev, struct vfio_ccw_private, vdev);
struct vfio_ccw_crw *crw, *temp;
+ unsigned long flags;
+ /*
+ * Ensure these work items are fully drained, so none can
+ * fire after being released.
+ *
+ * notoper_work should have nothing to do here, because only
+ * open devices could have channel_program resources in use
+ * and those would be released during close. Nevertheless,
+ * call flush here as well to be certain anything that was
+ * allocated is freed.
+ */
+ cancel_work_sync(&private->io_work);
+ cancel_work_sync(&private->crw_work);
+ flush_work(&private->notoper_work);
+
+ spin_lock_irqsave(&private->crw_lock, flags);
list_for_each_entry_safe(crw, temp, &private->crw, next) {
list_del(&crw->next);
kfree(crw);
}
+ spin_unlock_irqrestore(&private->crw_lock, flags);
kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
@@ -202,6 +221,19 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
container_of(vdev, struct vfio_ccw_private, vdev);
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
+
+ /*
+ * Ensure these work items are drained, in the event the
+ * device is re-opened instead of released.
+ *
+ * notoper_work needs to be given a chance to run if it
+ * is queued, so any memory associated with the channel
+ * program can be returned.
+ */
+ cancel_work_sync(&private->io_work);
+ cancel_work_sync(&private->crw_work);
+ flush_work(&private->notoper_work);
+
vfio_ccw_unregister_dev_regions(private);
}
@@ -243,6 +275,7 @@ static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev,
return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
default:
index -= VFIO_CCW_NUM_REGIONS;
+ index = array_index_nospec(index, private->num_regions);
return private->region[index].ops->read(private, buf, count,
ppos);
}
@@ -295,6 +328,7 @@ static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev,
return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
default:
index -= VFIO_CCW_NUM_REGIONS;
+ index = array_index_nospec(index, private->num_regions);
return private->region[index].ops->write(private, buf, count,
ppos);
}
@@ -338,11 +372,8 @@ static int vfio_ccw_mdev_ioctl_get_region_info(struct vfio_device *vdev,
VFIO_CCW_NUM_REGIONS + private->num_regions)
return -EINVAL;
- info->index = array_index_nospec(info->index,
- VFIO_CCW_NUM_REGIONS +
- private->num_regions);
-
i = info->index - VFIO_CCW_NUM_REGIONS;
+ i = array_index_nospec(i, private->num_regions);
info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
info->size = private->region[i].size;
diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h
index 0501d4bbcdbd..3bd0171d38d0 100644
--- a/drivers/s390/cio/vfio_ccw_private.h
+++ b/drivers/s390/cio/vfio_ccw_private.h
@@ -88,7 +88,8 @@ struct vfio_ccw_parent {
* @state: internal state of the device
* @completion: synchronization helper of the I/O completion
* @io_region: MMIO region to input/output I/O arguments/results
- * @io_mutex: protect against concurrent update of I/O regions
+ * @io_mutex: protect against concurrent update of I/O resources
+ * and @cp lifecycle
* @region: additional regions for other subchannel operations
* @cmd_region: MMIO region for asynchronous I/O commands other than START
* @schib_region: MMIO region for SCHIB information
@@ -97,11 +98,14 @@ struct vfio_ccw_parent {
* @cp: channel program for the current I/O operation
* @irb: irb info received from interrupt
* @scsw: scsw info
+ * @crw_lock: serialization of CRW list information
+ * @crw: list of Channel Report Word elements
* @io_trigger: eventfd ctx for signaling userspace I/O results
* @crw_trigger: eventfd ctx for signaling userspace CRW information
* @req_trigger: eventfd ctx for signaling userspace to return device
* @io_work: work for deferral process of I/O handling
* @crw_work: work for deferral process of CRW handling
+ * @notoper_work: work for deferred processing in not-operational state
*/
struct vfio_ccw_private {
struct vfio_device vdev;
@@ -118,6 +122,8 @@ struct vfio_ccw_private {
struct channel_program cp;
struct irb irb;
union scsw scsw;
+
+ spinlock_t crw_lock;
struct list_head crw;
struct eventfd_ctx *io_trigger;
@@ -125,11 +131,13 @@ struct vfio_ccw_private {
struct eventfd_ctx *req_trigger;
struct work_struct io_work;
struct work_struct crw_work;
+ struct work_struct notoper_work;
} __aligned(8);
int vfio_ccw_sch_quiesce(struct subchannel *sch);
void vfio_ccw_sch_io_todo(struct work_struct *work);
void vfio_ccw_crw_todo(struct work_struct *work);
+void vfio_ccw_notoper_todo(struct work_struct *work);
extern struct mdev_driver vfio_ccw_mdev_driver;
diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c
index d99c588d3e00..242da20f27e0 100644
--- a/drivers/s390/net/ism_drv.c
+++ b/drivers/s390/net/ism_drv.c
@@ -148,13 +148,16 @@ static int unregister_sba(struct ism_dev *ism)
if (ret && ret != ISM_ERROR)
return -EIO;
+ return 0;
+}
+
+static void ism_free_sba(struct ism_dev *ism)
+{
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE,
ism->sba, ism->sba_dma_addr);
ism->sba = NULL;
ism->sba_dma_addr = 0;
-
- return 0;
}
static int unregister_ieq(struct ism_dev *ism)
@@ -168,13 +171,16 @@ static int unregister_ieq(struct ism_dev *ism)
if (ret && ret != ISM_ERROR)
return -EIO;
+ return 0;
+}
+
+static void ism_free_ieq(struct ism_dev *ism)
+{
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE,
ism->ieq, ism->ieq_dma_addr);
ism->ieq = NULL;
ism->ieq_dma_addr = 0;
-
- return 0;
}
static int ism_read_local_gid(struct dibs_dev *dibs)
@@ -573,6 +579,7 @@ static int ism_dev_init(struct ism_dev *ism)
unreg_sba:
unregister_sba(ism);
+ ism_free_sba(ism);
free_irq:
free_irq(pci_irq_vector(pdev, 0), ism);
free_vectors:
@@ -585,9 +592,13 @@ static void ism_dev_exit(struct ism_dev *ism)
{
struct pci_dev *pdev = ism->pdev;
+ /* ism will only generate new IRQs while ieq & sba are registered */
unregister_ieq(ism);
unregister_sba(ism);
+ /* drain ongoing irpt handlers */
free_irq(pci_irq_vector(pdev, 0), ism);
+ ism_free_ieq(ism);
+ ism_free_sba(ism);
pci_free_irq_vectors(pdev);
}
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index f18eed9df3c7..c3257b213360 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -4710,6 +4710,9 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata)
if (req_len > QETH_BUFSIZE)
return -EINVAL;
+ if (qinfo.udata_len < sizeof(struct qeth_snmp_ureq_hdr))
+ return -EINVAL;
+
iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, req_len);
if (!iob)
return -ENOMEM;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 1542bfc9f561..f1ac9950dcb4 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1415,6 +1415,11 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
rc = -EFAULT;
goto out;
}
+
+ if (qinfo.udata_len < QETH_QARP_ENTRIES_OFFSET) {
+ rc = -EINVAL;
+ goto out;
+ }
qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
if (!qinfo.udata) {
rc = -ENOMEM;
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b35d74592964..b5188dcde37a 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index 28e491fffc7d..b09dca34099f 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -11,6 +11,7 @@
*/
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/dev_printk.h>
#include <linux/interrupt.h>
@@ -74,7 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
- struct kfifo fifo;
+ spinlock_t lock;
+ struct kfifo fifo __guarded_by(&lock);
wait_queue_head_t wq;
struct miscdevice miscdev;
};
@@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
+ u8 *buf __free(kfree) = NULL;
unsigned int copied;
int ret = 0;
@@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
if (ret == -ERESTARTSYS)
return -EINTR;
}
- ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
- if (ret)
- return ret;
+
+ count = min_t(size_t, count, SNOOP_FIFO_SIZE);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock);
+ if (copied && copy_to_user(buffer, buf, copied))
+ return -EFAULT;
return copied;
}
@@ -151,11 +161,13 @@ static const struct file_operations snoop_fops = {
/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
- if (!kfifo_initialized(&chan->fifo))
- return;
- if (kfifo_is_full(&chan->fifo))
- kfifo_skip(&chan->fifo);
- kfifo_put(&chan->fifo, val);
+ scoped_guard(spinlock, &chan->lock) {
+ if (!kfifo_initialized(&chan->fifo))
+ return;
+ if (kfifo_is_full(&chan->fifo))
+ kfifo_skip(&chan->fifo);
+ kfifo_put(&chan->fifo, val);
+ }
wake_up_interruptible(&chan->wq);
}
@@ -239,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
if (!channel->miscdev.name)
return -ENOMEM;
- rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
- if (rc)
- return rc;
+ scoped_guard(spinlock_init, &channel->lock) {
+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+ }
rc = misc_register(&channel->miscdev);
if (rc)
diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c
index b37933efac5d..63f131d5682b 100644
--- a/drivers/soundwire/intel_ace2x.c
+++ b/drivers/soundwire/intel_ace2x.c
@@ -261,6 +261,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
__func__, str_read_write(command), ret);
ret1 = hda_sdw_bpt_close(cdns->dev->parent, /* PCI device */
+ sdw->instance,
sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl,
sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl);
if (ret1 < 0)
@@ -295,7 +296,8 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave
struct sdw_cdns *cdns = &sdw->cdns;
int ret;
- ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->bpt_ctx.bpt_tx_stream,
+ ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->instance,
+ sdw->bpt_ctx.bpt_tx_stream,
&sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream,
&sdw->bpt_ctx.dmab_rx_bdl);
if (ret < 0)
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index 386dfb9f559e..ec73d03a1e60 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -19,19 +19,28 @@
#include "thermal_hwmon.h"
#include "thermal_core.h"
-/*
- * Needs to be large enough to hold a thermal zone type string followed by an
- * underline character and a 32-bit integer in decimal representation.
- */
-#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11)
-
/* hwmon sys I/F */
/* thermal zone devices with the same type share one hwmon device */
struct thermal_hwmon_device {
- char name[THERMAL_HWMON_NAME_LENGTH];
+ char type[THERMAL_NAME_LENGTH];
struct device *device;
+ int count;
+ struct list_head tz_list;
struct list_head node;
+};
+
+struct thermal_hwmon_attr {
+ struct device_attribute attr;
+ char name[16];
+};
+
+/* one temperature input for each thermal zone */
+struct thermal_hwmon_temp {
+ struct list_head hwmon_node;
struct thermal_zone_device *tz;
+ struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
+ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
+ bool temp_crit_present;
};
static LIST_HEAD(thermal_hwmon_list);
@@ -39,14 +48,19 @@ static LIST_HEAD(thermal_hwmon_list);
static DEFINE_MUTEX(thermal_hwmon_list_lock);
static ssize_t
-temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf)
+temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
- struct thermal_zone_device *tz = hwmon->tz;
int temperature;
int ret;
+ struct thermal_hwmon_attr *hwmon_attr
+ = container_of(attr, struct thermal_hwmon_attr, attr);
+ struct thermal_hwmon_temp *temp
+ = container_of(hwmon_attr, struct thermal_hwmon_temp,
+ temp_input);
+ struct thermal_zone_device *tz = temp->tz;
ret = thermal_zone_get_temp(tz, &temperature);
+
if (ret)
return ret;
@@ -54,10 +68,14 @@ temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
+temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
- struct thermal_zone_device *tz = hwmon->tz;
+ struct thermal_hwmon_attr *hwmon_attr
+ = container_of(attr, struct thermal_hwmon_attr, attr);
+ struct thermal_hwmon_temp *temp
+ = container_of(hwmon_attr, struct thermal_hwmon_temp,
+ temp_crit);
+ struct thermal_zone_device *tz = temp->tz;
int temperature;
int ret;
@@ -70,97 +88,169 @@ temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
return sysfs_emit(buf, "%d\n", temperature);
}
-static DEVICE_ATTR_RO(temp1_input);
-static DEVICE_ATTR_RO(temp1_crit);
-
-static struct attribute *thermal_hwmon_attrs[] = {
- &dev_attr_temp1_input.attr,
- &dev_attr_temp1_crit.attr,
- NULL,
-};
-static umode_t thermal_hwmon_attr_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static struct thermal_hwmon_device *
+thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
{
- if (a == &dev_attr_temp1_input.attr)
- return a->mode;
-
- if (a == &dev_attr_temp1_crit.attr) {
- struct thermal_hwmon_device *hwmon = dev_get_drvdata(kobj_to_dev(kobj));
- struct thermal_zone_device *tz = hwmon->tz;
- int dummy;
+ struct thermal_hwmon_device *hwmon;
+ char type[THERMAL_NAME_LENGTH];
- if (tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &dummy))
- return a->mode;
+ mutex_lock(&thermal_hwmon_list_lock);
+ list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
+ strscpy(type, tz->type);
+ strreplace(type, '-', '_');
+ if (!strcmp(hwmon->type, type)) {
+ mutex_unlock(&thermal_hwmon_list_lock);
+ return hwmon;
+ }
}
+ mutex_unlock(&thermal_hwmon_list_lock);
- return 0;
+ return NULL;
}
-static const struct attribute_group thermal_hwmon_group = {
- .attrs = thermal_hwmon_attrs,
- .is_visible = thermal_hwmon_attr_is_visible,
-};
+/* Find the temperature input matching a given thermal zone */
+static struct thermal_hwmon_temp *
+thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
+ const struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_temp *temp;
-__ATTRIBUTE_GROUPS(thermal_hwmon);
+ mutex_lock(&thermal_hwmon_list_lock);
+ list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
+ if (temp->tz == tz) {
+ mutex_unlock(&thermal_hwmon_list_lock);
+ return temp;
+ }
+ mutex_unlock(&thermal_hwmon_list_lock);
+
+ return NULL;
+}
+
+static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
+{
+ int temp;
+ return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp);
+}
int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
+ struct thermal_hwmon_temp *temp;
+ int new_hwmon_device = 1;
+ int result;
+
+ hwmon = thermal_hwmon_lookup_by_type(tz);
+ if (hwmon) {
+ new_hwmon_device = 0;
+ goto register_sys_interface;
+ }
hwmon = kzalloc_obj(*hwmon);
if (!hwmon)
return -ENOMEM;
- hwmon->tz = tz;
- /*
- * Append the thermal zone ID preceded by an underline character to the
- * type to disambiguate the sensors command output.
- */
- scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id);
- strreplace(hwmon->name, '-', '_');
+ INIT_LIST_HEAD(&hwmon->tz_list);
+ strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
+ strreplace(hwmon->type, '-', '_');
hwmon->device = hwmon_device_register_for_thermal(&tz->device,
- hwmon->name, hwmon,
- thermal_hwmon_groups);
+ hwmon->type, hwmon);
if (IS_ERR(hwmon->device)) {
- int result = PTR_ERR(hwmon->device);
+ result = PTR_ERR(hwmon->device);
+ goto free_mem;
+ }
- kfree(hwmon);
- return result;
+ register_sys_interface:
+ temp = kzalloc_obj(*temp);
+ if (!temp) {
+ result = -ENOMEM;
+ goto unregister_name;
+ }
+
+ temp->tz = tz;
+ hwmon->count++;
+
+ snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
+ "temp%d_input", hwmon->count);
+ temp->temp_input.attr.attr.name = temp->temp_input.name;
+ temp->temp_input.attr.attr.mode = 0444;
+ temp->temp_input.attr.show = temp_input_show;
+ sysfs_attr_init(&temp->temp_input.attr.attr);
+ result = device_create_file(hwmon->device, &temp->temp_input.attr);
+ if (result)
+ goto free_temp_mem;
+
+ if (thermal_zone_crit_temp_valid(tz)) {
+ snprintf(temp->temp_crit.name,
+ sizeof(temp->temp_crit.name),
+ "temp%d_crit", hwmon->count);
+ temp->temp_crit.attr.attr.name = temp->temp_crit.name;
+ temp->temp_crit.attr.attr.mode = 0444;
+ temp->temp_crit.attr.show = temp_crit_show;
+ sysfs_attr_init(&temp->temp_crit.attr.attr);
+ result = device_create_file(hwmon->device,
+ &temp->temp_crit.attr);
+ if (result)
+ goto unregister_input;
+
+ temp->temp_crit_present = true;
}
- /* The list is needed for hwmon lookup during removal. */
mutex_lock(&thermal_hwmon_list_lock);
- list_add_tail(&hwmon->node, &thermal_hwmon_list);
+ if (new_hwmon_device)
+ list_add_tail(&hwmon->node, &thermal_hwmon_list);
+ list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
mutex_unlock(&thermal_hwmon_list_lock);
return 0;
+
+ unregister_input:
+ device_remove_file(hwmon->device, &temp->temp_input.attr);
+ free_temp_mem:
+ kfree(temp);
+ unregister_name:
+ if (new_hwmon_device)
+ hwmon_device_unregister(hwmon->device);
+ free_mem:
+ if (new_hwmon_device)
+ kfree(hwmon);
+
+ return result;
}
EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
-static struct thermal_hwmon_device *
-thermal_hwmon_lookup(const struct thermal_zone_device *tz)
+void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
+ struct thermal_hwmon_temp *temp;
- list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
- if (hwmon->tz == tz)
- return hwmon;
+ hwmon = thermal_hwmon_lookup_by_type(tz);
+ if (unlikely(!hwmon)) {
+ /* Should never happen... */
+ dev_dbg(&tz->device, "hwmon device lookup failed!\n");
+ return;
}
- return NULL;
-}
-void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
-{
- struct thermal_hwmon_device *hwmon;
+ temp = thermal_hwmon_lookup_temp(hwmon, tz);
+ if (unlikely(!temp)) {
+ /* Should never happen... */
+ dev_dbg(&tz->device, "temperature input lookup failed!\n");
+ return;
+ }
- scoped_guard(mutex, &thermal_hwmon_list_lock) {
- hwmon = thermal_hwmon_lookup(tz);
- if (!hwmon)
- return;
+ device_remove_file(hwmon->device, &temp->temp_input.attr);
+ if (temp->temp_crit_present)
+ device_remove_file(hwmon->device, &temp->temp_crit.attr);
- list_del(&hwmon->node);
+ mutex_lock(&thermal_hwmon_list_lock);
+ list_del(&temp->hwmon_node);
+ kfree(temp);
+ if (!list_empty(&hwmon->tz_list)) {
+ mutex_unlock(&thermal_hwmon_list_lock);
+ return;
}
+ list_del(&hwmon->node);
+ mutex_unlock(&thermal_hwmon_list_lock);
hwmon_device_unregister(hwmon->device);
kfree(hwmon);
diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
index 2cedf7e2dbc4..42f2f44b383c 100644
--- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h
+++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
@@ -11,6 +11,8 @@
#define MLX5V_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+extern int mlx5_vdpa_max_iotlb_entries;
+
struct mlx5_vdpa_direct_mr {
u64 start;
u64 end;
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index 6d02ccf9eb91..77a479aeaa85 100644
--- a/drivers/vdpa/mlx5/core/mr.c
+++ b/drivers/vdpa/mlx5/core/mr.c
@@ -233,7 +233,8 @@ static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *
cmds[i].out = cmd_mem->out;
cmds[i].outlen = sizeof(cmd_mem->out);
cmds[i].in = cmd_mem->in;
- cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount);
+ cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount) -
+ offsetof(struct mlx5_create_mkey_mem, in);
fill_create_direct_mr(mvdev, dmr, cmd_mem);
@@ -776,6 +777,9 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
{
int err;
+ if (mlx5_vdpa_max_iotlb_entries < 2)
+ return -EINVAL;
+
if (iotlb)
err = create_user_mr(mvdev, mr, iotlb);
else
@@ -784,7 +788,7 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
if (err)
return err;
- mr->iotlb = vhost_iotlb_alloc(0, 0);
+ mr->iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mr->iotlb) {
err = -ENOMEM;
goto err_mr;
diff --git a/drivers/vdpa/mlx5/core/resources.c b/drivers/vdpa/mlx5/core/resources.c
index aeae31d0cefa..28a4d7a35bf4 100644
--- a/drivers/vdpa/mlx5/core/resources.c
+++ b/drivers/vdpa/mlx5/core/resources.c
@@ -3,8 +3,14 @@
#include <linux/iova.h>
#include <linux/mlx5/driver.h>
+#include <linux/moduleparam.h>
#include "mlx5_vdpa.h"
+int mlx5_vdpa_max_iotlb_entries = 2048;
+module_param_named(max_iotlb_entries, mlx5_vdpa_max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid)
{
struct mlx5_core_dev *mdev = dev->mdev;
@@ -229,7 +235,10 @@ int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey)
static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev)
{
- mvdev->cvq.iotlb = vhost_iotlb_alloc(0, 0);
+ if (mlx5_vdpa_max_iotlb_entries < 2)
+ return -EINVAL;
+
+ mvdev->cvq.iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mvdev->cvq.iotlb)
return -ENOMEM;
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c
index 8cb1cc2ea139..4d116644851d 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c
@@ -34,7 +34,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable");
static int max_iotlb_entries = 2048;
module_param(max_iotlb_entries, int, 0444);
MODULE_PARM_DESC(max_iotlb_entries,
- "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)");
+ "Maximum number of iotlb entries for each address space. (default: 2048)");
static bool use_va = true;
module_param(use_va, bool, 0444);
@@ -201,6 +201,8 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
if (!dev_attr->alloc_size)
return ERR_PTR(-EINVAL);
+ if (max_iotlb_entries < 2)
+ return ERR_PTR(-EINVAL);
if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
if (config->device_features &
@@ -261,8 +263,10 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
for (i = 0; i < vdpasim->dev_attr.nas; i++) {
vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0);
- vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, 0,
- VHOST_MAP_RW);
+ ret = vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX,
+ 0, VHOST_MAP_RW);
+ if (ret)
+ goto err_iommu;
vdpasim->iommu_pt[i] = true;
}
diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c
index 4dc76c0d0d13..b6c958224b7c 100644
--- a/drivers/vdpa/vdpa_user/iova_domain.c
+++ b/drivers/vdpa/vdpa_user/iova_domain.c
@@ -12,11 +12,17 @@
#include <linux/file.h>
#include <linux/anon_inodes.h>
#include <linux/highmem.h>
+#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
#include <linux/vdpa.h>
#include "iova_domain.h"
+static int max_iotlb_entries = 2048;
+module_param(max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
static int vduse_iotlb_add_range(struct vduse_iova_domain *domain,
u64 start, u64 last,
u64 addr, unsigned int perm,
@@ -622,11 +628,14 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)
if (iova_limit <= bounce_size)
return NULL;
+ if (max_iotlb_entries <= 0)
+ return NULL;
+
domain = kzalloc_obj(*domain);
if (!domain)
return NULL;
- domain->iotlb = vhost_iotlb_alloc(0, 0);
+ domain->iotlb = vhost_iotlb_alloc(max_iotlb_entries, 0);
if (!domain->iotlb)
goto err_iotlb;
diff --git a/drivers/vhost/iotlb.c b/drivers/vhost/iotlb.c
index e1414c774c34..a1d4376a5b87 100644
--- a/drivers/vhost/iotlb.c
+++ b/drivers/vhost/iotlb.c
@@ -20,6 +20,14 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
rb, __u64, __subtree_last,
START, LAST, static inline, vhost_iotlb_itree);
+static void vhost_iotlb_map_unlink(struct vhost_iotlb *iotlb,
+ struct vhost_iotlb_map *map)
+{
+ vhost_iotlb_itree_remove(map, &iotlb->root);
+ list_del(&map->link);
+ iotlb->nmaps--;
+}
+
/**
* vhost_iotlb_map_free - remove a map node and free it
* @iotlb: the IOTLB
@@ -28,10 +36,8 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
struct vhost_iotlb_map *map)
{
- vhost_iotlb_itree_remove(map, &iotlb->root);
- list_del(&map->link);
+ vhost_iotlb_map_unlink(iotlb, map);
kfree(map);
- iotlb->nmaps--;
}
EXPORT_SYMBOL_GPL(vhost_iotlb_map_free);
@@ -57,14 +63,25 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
if (last < start)
return -EFAULT;
+ if (!iotlb->limit)
+ return -EINVAL;
+
/* If the range being mapped is [0, ULONG_MAX], split it into two entries
* otherwise its size would overflow u64.
*/
if (start == 0 && last == ULONG_MAX) {
u64 mid = last / 2;
- int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
- perm, opaque);
+ int err;
+
+ if (iotlb->limit < 2)
+ return -ENOSPC;
+ if (!(iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) &&
+ iotlb->nmaps > iotlb->limit - 2)
+ return -ENOSPC;
+
+ err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
+ perm, opaque);
if (err)
return err;
@@ -72,17 +89,19 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
start = mid + 1;
}
- if (iotlb->limit &&
- iotlb->nmaps == iotlb->limit &&
- iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
- map = list_first_entry(&iotlb->list, typeof(*map), link);
- vhost_iotlb_map_free(iotlb, map);
+ if (iotlb->nmaps >= iotlb->limit) {
+ if (iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
+ map = list_first_entry(&iotlb->list, typeof(*map), link);
+ vhost_iotlb_map_unlink(iotlb, map);
+ } else {
+ return -ENOSPC;
+ }
+ } else {
+ map = kmalloc_obj(*map, GFP_ATOMIC);
+ if (!map)
+ return -ENOMEM;
}
- map = kmalloc_obj(*map, GFP_ATOMIC);
- if (!map)
- return -ENOMEM;
-
map->start = start;
map->size = last - start + 1;
map->last = last;
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 9a1253b9d8c5..7a1f39a327da 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -972,6 +972,9 @@ vhost_scsi_mapal(struct vhost_scsi *vs, struct vhost_scsi_cmd *cmd,
if (prot_bytes) {
sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes,
VHOST_SCSI_PREALLOC_PROT_SGLS);
+ if (sgl_count < 0)
+ return sgl_count;
+
cmd->prot_table.sgl = cmd->prot_sgl;
ret = sg_alloc_table_chained(&cmd->prot_table, sgl_count,
cmd->prot_table.sgl,
@@ -1416,6 +1419,11 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
* actual data payload length.
*/
if (prot_bytes) {
+ if (prot_bytes >= exp_data_len) {
+ vq_err(vq, "Protection data exceeds payload length\n");
+ goto err;
+ }
+
exp_data_len -= prot_bytes;
prot_iter = data_iter;
iov_iter_truncate(&prot_iter, prot_bytes);
@@ -2219,6 +2227,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
{
struct vhost_virtqueue *vq;
bool is_log, was_log;
+ u64 old_features;
int i;
if (features & ~VHOST_SCSI_FEATURES)
@@ -2234,6 +2243,14 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
if (!vs->dev.nvqs)
goto out;
+ old_features = vs->vqs[0].vq.acked_features;
+ if (vs->vs_tpg &&
+ ((features ^ old_features) &
+ ~(1ULL << VHOST_F_LOG_ALL))) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EBUSY;
+ }
+
is_log = features & (1 << VHOST_F_LOG_ALL);
/*
* All VQs should have same feature.
@@ -2426,9 +2443,10 @@ vhost_scsi_ioctl(struct file *f,
default:
mutex_lock(&vs->dev.mutex);
r = vhost_dev_ioctl(&vs->dev, ioctl, argp);
- /* TODO: flush backend after dev ioctl. */
if (r == -ENOIOCTLCMD)
r = vhost_vring_ioctl(&vs->dev, ioctl, argp);
+ else
+ vhost_scsi_flush(vs);
mutex_unlock(&vs->dev.mutex);
return r;
}
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ac55275fa0d0..c3d913bd7cac 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -34,6 +34,11 @@ enum {
#define VHOST_VDPA_DEV_MAX (1U << MINORBITS)
+static int max_iotlb_entries = 2048;
+module_param(max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
#define VHOST_VDPA_IOTLB_BUCKETS 16
struct vhost_vdpa_as {
@@ -109,12 +114,14 @@ static struct vhost_vdpa_as *vhost_vdpa_alloc_as(struct vhost_vdpa *v, u32 asid)
if (asid >= v->vdpa->nas)
return NULL;
+ if (max_iotlb_entries <= 0)
+ return NULL;
as = kmalloc_obj(*as);
if (!as)
return NULL;
- vhost_iotlb_init(&as->iotlb, 0, 0);
+ vhost_iotlb_init(&as->iotlb, max_iotlb_entries, 0);
as->id = asid;
hlist_add_head(&as->hash_link, head);
@@ -1102,6 +1109,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
unsigned int gup_flags = FOLL_LONGTERM;
unsigned long npages, cur_base, map_pfn, last_pfn = 0;
unsigned long lock_limit, sz2pin, nchunks, i;
+ unsigned long page_offset;
u64 start = iova;
long pinned;
int ret = 0;
@@ -1114,7 +1122,13 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
if (perm & VHOST_ACCESS_WO)
gup_flags |= FOLL_WRITE;
- npages = PFN_UP(size + (iova & ~PAGE_MASK));
+ page_offset = iova & ~PAGE_MASK;
+ if (size > ULONG_MAX - page_offset) {
+ ret = -EINVAL;
+ goto free;
+ }
+
+ npages = PFN_UP(size + page_offset);
if (!npages) {
ret = -EINVAL;
goto free;
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 4c525b3e16ea..269efad90369 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -1137,6 +1137,9 @@ EXPORT_SYMBOL_GPL(vhost_dev_set_owner);
static struct vhost_iotlb *iotlb_alloc(void)
{
+ if (max_iotlb_entries <= 0)
+ return NULL;
+
return vhost_iotlb_alloc(max_iotlb_entries,
VHOST_IOTLB_FLAG_RETIRE);
}
@@ -1981,6 +1984,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
return -EOPNOTSUPP;
if (mem.nregions > max_mem_regions)
return -E2BIG;
+ if (max_iotlb_entries <= 0)
+ return -EINVAL;
newmem = kvzalloc_flex(*newmem, regions, mem.nregions);
if (!newmem)
return -ENOMEM;
@@ -2126,6 +2131,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d,
BUG();
}
+ /*
+ * The metadata cache holds the IOTLB mapping that backed the previous
+ * desc/avail/used addresses and vring size, both of which are being
+ * replaced here. iotlb_access_ok() takes a cache hit as proof that the
+ * region was validated, so the stale entries have to go.
+ */
+ __vhost_vq_meta_reset(vq);
+
mutex_unlock(&vq->mutex);
return r;
@@ -2275,6 +2288,9 @@ int vhost_init_device_iotlb(struct vhost_dev *d)
struct vhost_iotlb *niotlb, *oiotlb;
int i;
+ if (max_iotlb_entries <= 0)
+ return -EINVAL;
+
niotlb = iotlb_alloc();
if (!niotlb)
return -ENOMEM;