5.15.217-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 111/272 mtd: maps: vmu-flash: fix fault in unaligned fixup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fuchs <[email protected]>
[ Upstream commit 79d1661502c6e4b6f626185cef72cf2fa78116e1 ]
Use kzalloc_obj() / kzalloc_objs() to allocate the memcard structs, instead of kmalloc_obj() / kmalloc_objs() to prevent access to uninitialized data.
Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at mtd_get_fact_prot_info.
--- drivers/mtd/maps/vmu-flash.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/mtd/maps/vmu-flash.c +3 -5
@@ -610,7 +610,7 @@ static int vmu_connect(struct maple_devi basic_flash_data =be32_to_cpu (mdev->devinfo.function_data[c -1 ]); - card =kmalloc (sizeof (struct memcard ),GFP_KERNEL ); + card = kzalloc_obj(struct memcard);if (!card) { error = -ENOMEM ; goto fail_nomem;
@@ -628,15 +628,13 @@ static int vmu_connect(struct maple_devi* Not sure there are actually any multi-partition devices in the * real world, but the hardware supports them, so, so will we */ - card->parts =kmalloc_array (card->partitions,sizeof (struct vmupart ), -GFP_KERNEL ); + card->parts = kzalloc_objs(struct vmupart, card->partitions);if (!card->parts) { error = -ENOMEM ; goto fail_partitions; } - card->mtd =kmalloc_array (card->partitions,sizeof (struct mtd_info ), -GFP_KERNEL ); + card->mtd = kzalloc_objs(struct mtd_info, card->partitions);if (!card->mtd) { error = -ENOMEM ; goto fail_mtd_info;
Re: [PATCH 5.15 111/272] mtd: maps: vmu-flash: fix fault in unaligned fixup
Hi Greg/Sasha,
Quoted 41 lines · Greg Kroah-Hartman
5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fuchs <[email protected]>
[ Upstream commit
79d1661502c6e4b6f626185cef72cf2fa78116e1]Use
kzalloc_obj()/kzalloc_objs()to allocate the memcard structs, instead ofkmalloc_obj()/kmalloc_objs()to prevent access to uninitialized data.Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at
mtd_get_fact_prot_info.Fixes 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.")Signed-off-by Florian Fuchs <[email protected]>Signed-off-by Miquel Raynal <[email protected]>Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/mtd/maps/vmu-flash.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)drivers/mtd/maps/vmu-flash.c +1
-2@@ -610,7 +610,7 @@ static int vmu_connect(struct maple_devi basic_flash_data =be32_to_cpu (mdev->devinfo.function_data[c -1 ]); - card =kmalloc (sizeof (struct memcard ),GFP_KERNEL ); + card = kzalloc_obj(struct memcard);if (!card) { error = -ENOMEM ; goto fail_nomem;@@ -628,15 +628,13 @@ static int vmu_connect(struct maple_devi* Not sure there are actually any multi-partition devices in the * real world, but the hardware supports them, so, so will we */ - card->parts = kmalloc_array(card->partitions, sizeof(struct vmupart),
drivers/mtd/maps/vmu-flash.c static int vmu_connect(struct maple_devi · line 637
Quoted 10 lines
drivers/mtd/maps/vmu-flash.c +2
-3- GFP_KERNEL); + card->parts = kzalloc_objs(struct vmupart, card->partitions);if (!card->parts) { error = -ENOMEM ; goto fail_partitions; } - card->mtd =kmalloc_array (card->partitions,sizeof (struct mtd_info ), -GFP_KERNEL ); + card->mtd = kzalloc_objs(struct mtd_info, card->partitions);
Hi Greg/Sasha,
I ran an AI-assisted backport review of 631edc934e47 and checked the finding against the 5.15.217 endpoint. It spotted a 5.15 allocator API mismatch.
Upstream has:
card = kzalloc_obj(struct memcard);
card->parts = kzalloc_objs(struct vmupart, card->partitions);
card->mtd = kzalloc_objs(struct mtd_info, card->partitions);5.15 has only the legacy zeroing interfaces:
kzalloc(size, flags)
kcalloc(count, size, flags)
kzalloc_obj() and kzalloc_objs() are not defined there. They came from 2932ba8d9c99875b98c951d9d3fd6d651d35df3a ("slab: Introduce kmalloc_obj()
and family"), and the argument-less form additionally relies on e19e1b480ac73c3e62ffebbca1174f0f511f43e7 ("add default_gfp() helper macro and use it in the new *alloc_obj() helpers"). Every CONFIG_MTD_VMU build would likely fail, so the zero-initialization fix cannot be used.
Maybe we could work by adapting this for 5.15 with kzalloc(sizeof(*card), GFP_KERNEL) and kcalloc(..., sizeof(*ptr), GFP_KERNEL) at the two array allocations.
Or maybe we should revert this path, interesting that we probably don't build test this config :), so do we care ?
thanks,
Harshit
Quoted 6 lines
drivers/mtd/maps/vmu-flash.c +0
-0if (!card->mtd) { error = -ENOMEM ; goto fail_mtd_info;
Greg Kroah-Hartman
maintainerPATCH 112/272 net: thunderbolt: Fix frags[] overflow by bounding frame_count
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
[ Upstream commit 55d9895f89970501fe126d1026b586b04a224c27 ]
tbnet_poll() assembles a multi-frame ThunderboltIP packet into one skb. The first frame goes into the skb linear area and every further frame is added as a page fragment.
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, hdr_size, frame_size, TBNET_RX_PAGE_SIZE - hdr_size);
A packet of frame_count frames therefore ends up with frame_count - 1 fragments. tbnet_check_frame() only bounds the peer supplied frame_count to TBNET_RING_SIZE / 4 (64), which is far above MAX_SKB_FRAGS (17 by default). A peer that sends a packet of 19 or more small frames pushes nr_frags past MAX_SKB_FRAGS, so skb_add_rx_frag() writes past skb_shinfo()->frags[] and corrupts memory after the shared info.
Tighten the start of packet bound to MAX_SKB_FRAGS + 1 so a packet can never produce more fragments than frags[] can hold. This matches the recent skb frags overflow fixes in other receive paths, for example f0813bcd2d9d ("net:
wwan: t7xx: fix potential skb->frags overflow in RX path") and 600dc40554dc ("net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()").
--- drivers/net/thunderbolt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/thunderbolt.c +6 -2
@@ -756,8 +756,12 @@ static bool tbnet_check_frame(struct tbnreturn true; } -/* Start of packet, validate the frame header */ -if (frame_count ==0 || frame_count >TBNET_RING_SIZE /4 ) { +/* Start of packet, validate the frame header. tbnet_poll() puts the +* first frame in the skb linear area and every further frame in a page +* fragment, so a packet may not span more than MAX_SKB_FRAGS + 1 frames +* without overflowing skb_shinfo()->frags[]. +*/ +if (frame_count ==0 || frame_count >MAX_SKB_FRAGS +1 ) { net->stats.rx_length_errors++;return false; }
Greg Kroah-Hartman
maintainerPATCH 113/272 mtd: spi-nor: Fix spi_nor_try_unlock_all()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Walle <[email protected]>
[ Upstream commit 2e3a7476ec3989e77270b9481e76e137824b17c0 ]
Commit ff67592cbdfc ("mtd: spi-nor: Introduce spi_nor_set_mtd_info()") moved all initialization of the mtd fields at the end of spi_nor_scan(). Normally, the mtd info is only needed for the mtd ops on the device, with one exception: spi_nor_try_unlock_all(), which will also make use of the mtd->size parameter. With that commit, the size will always be zero because it is not initialized. Fix that by not using the size of the mtd_info struct, but use the size from struct spi_nor_flash_parameter.
Stable-dep-of: e1d456b26bf2 ("mtd: spi-nor: swp: Improve locking user experience")
--- drivers/mtd/spi-nor/swp.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-)
drivers/mtd/spi-nor/swp.c +8 -11
@@ -52,7 +52,6 @@ static u64 spi_nor_get_min_prot_length_sstatic void spi_nor_get_locked_range_sr (struct spi_nor *nor,u8 sr,loff_t *ofs,uint64_t *len) { -struct mtd_info *mtd = &nor->mtd;u64 min_prot_len;u8 mask =spi_nor_get_sr_bp_mask (nor);u8 tb_mask =spi_nor_get_sr_tb_mask (nor);
@@ -73,13 +72,13 @@ static void spi_nor_get_locked_range_sr( min_prot_len =spi_nor_get_min_prot_length_sr (nor); *len = min_prot_len << (bp -1 ); -if (*len > mtd->size) - *len = mtd->size; +if (*len > nor->params->size) + *len = nor->params->size;if (nor->flags &SNOR_F_HAS_SR_TB && sr & tb_mask) *ofs =0 ;else - *ofs = mtd->size - *len; + *ofs = nor->params->size - *len; }/*
@@ -155,7 +154,6 @@ static bool spi_nor_is_unlocked_sr(struc*/ static int spi_nor_sr_lock (struct spi_nor *nor,loff_t ofs,uint64_t len) { -struct mtd_info *mtd = &nor->mtd;u64 min_prot_len;int ret, status_old, status_new;u8 mask =spi_nor_get_sr_bp_mask (nor);
@@ -180,7 +178,7 @@ static int spi_nor_sr_lock(struct spi_no can_be_bottom = false;/* If anything above us is unlocked, we can't use 'top' protection */ -if (!spi_nor_is_locked_sr (nor, ofs + len, mtd->size - (ofs + len), +if (!spi_nor_is_locked_sr (nor, ofs + len, nor->params->size - (ofs + len), status_old)) can_be_top = false;
@@ -192,11 +190,11 @@ static int spi_nor_sr_lock(struct spi_no/* lock_len: length of region that should end up locked */ if (use_top) - lock_len = mtd->size - ofs; + lock_len = nor->params->size - ofs;else lock_len = ofs + len; -if (lock_len == mtd->size) { +if (lock_len == nor->params->size) { val = mask; }else { min_prot_len =spi_nor_get_min_prot_length_sr (nor);
@@ -240,7 +238,6 @@ static int spi_nor_sr_lock(struct spi_no*/ static int spi_nor_sr_unlock (struct spi_nor *nor,loff_t ofs,uint64_t len) { -struct mtd_info *mtd = &nor->mtd;u64 min_prot_len;int ret, status_old, status_new;u8 mask =spi_nor_get_sr_bp_mask (nor);
@@ -265,7 +262,7 @@ static int spi_nor_sr_unlock(struct spi_ can_be_top = false;/* If anything above us is locked, we can't use 'bottom' protection */ -if (!spi_nor_is_unlocked_sr (nor, ofs + len, mtd->size - (ofs + len), +if (!spi_nor_is_unlocked_sr (nor, ofs + len, nor->params->size - (ofs + len), status_old)) can_be_bottom = false;
@@ -277,7 +274,7 @@ static int spi_nor_sr_unlock(struct spi_/* lock_len: length of region that should remain locked */ if (use_top) - lock_len = mtd->size - (ofs + len); + lock_len = nor->params->size - (ofs + len);else lock_len = ofs;
Greg Kroah-Hartman
maintainerPATCH 114/272 mtd: spi-nor: swp: Improve locking user experience
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miquel Raynal <[email protected]>
[ Upstream commit e1d456b26bf23e30db305a6184e8abd9ab68bbf2 ]
In the case of the first block being locked (or the few first blocks), if the user want to fully unlock the device it has two possibilities:
- either it asks to unlock the entire device, and this works;
- or it asks to unlock just the block(s) that are currently locked, which fails.
It fails because the conditions "can_be_top" and "can_be_bottom" are true. Indeed, in this case, we unlock everything, so the TB bit does not matter. However in the current implementation, use_top would be true (as this is the favourite option) and lock_len, which in practice should be reduced down to 0, is set to "nor->params->size - (ofs + len)" which is a positive number. This is wrong.
An easy way is to simply add an extra condition. In the unlock() path, if we can achieve the same result from both sides, it means we unlock everything and lock_len must simply be 0. A comment is added to clarify that logic.
--- drivers/mtd/spi-nor/swp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/mtd/spi-nor/swp.c +9 -2
@@ -272,8 +272,15 @@ static int spi_nor_sr_unlock(struct spi_/* Prefer top, if both are valid */ use_top = can_be_top; -/* lock_len: length of region that should remain locked */ -if (use_top) +/* +* lock_len: length of region that should remain locked. +* +* When can_be_top and can_be_bottom booleans are true, both adjacent +* regions are unlocked, thus the entire flash can be unlocked. +*/ +if (can_be_top && can_be_bottom) + lock_len =0 ; +else if (use_top) lock_len = nor->params->size - (ofs + len);else lock_len = ofs;
Greg Kroah-Hartman
maintainerPATCH 115/272 dmaengine: dw-edma: Remove unused irq field in struct dw_edma_chip
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Li <[email protected]>
[ Upstream commit 5a0e4529d9aee8ce348f628ad476c9ddb6cf457d ]
The "irq" field of struct dw_edma_chip was never used. Remove it.
Stable-dep-of: 8ffba0171c6b ("dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK")
--- drivers/dma/dw-edma/dw-edma-pcie.c | 1 - include/linux/dma/edma.h | 2 -- 2 files changed, 3 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +0 -1
@@ -231,7 +231,6 @@ static int dw_edma_pcie_probe(struct pci chip->dw = dw; chip->dev = dev; chip->id = pdev->devfn; - chip->irq = pdev->irq; dw->mf = vsec_data.mf; dw->nr_irqs = nr_irqs;
include/linux/dma/edma.h +0 -2
@@ -18,13 +18,11 @@ struct dw_edma;* struct dw_edma_chip - representation of DesignWare eDMA controller hardware * @dev: struct device of the eDMA controller * @id: instance ID -* @irq: irq line * @dw: struct dw_edma that is filed by dw_edma_probe() */ struct dw_edma_chip {struct device *dev;int id; -int irq;struct dw_edma *dw; };
Greg Kroah-Hartman
maintainerPATCH 116/272 dmaengine: dw-edma: Detach the private data and chip info structures
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Li <[email protected]>
[ Upstream commit 203184571388a988283543f0fd7da1a0da7c3f91 ]
"struct dw_edma_chip" contains an internal structure "struct dw_edma" that is used by the eDMA core internally and should not be touched by the eDMA controller drivers themselves. But currently, the eDMA controller drivers like "dw-edma-pci" allocate and populate this internal structure before passing it on to the eDMA core. The eDMA core further populates the structure and uses it. This is wrong!
Hence, move all the "struct dw_edma" specifics from controller drivers to the eDMA core.
Stable-dep-of: 8ffba0171c6b ("dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK")
--- drivers/dma/dw-edma/dw-edma-core.c | 90 ++++++++++++++++--------------- drivers/dma/dw-edma/dw-edma-core.h | 31 +--------- drivers/dma/dw-edma/dw-edma-pcie.c | 82 +++++++++++----------------- drivers/dma/dw-edma/dw-edma-v0-core.c | 32 +++++------ drivers/dma/dw-edma/dw-edma-v0-core.h | 4 - drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 18 +++--- drivers/dma/dw-edma/dw-edma-v0-debugfs.h | 8 +- include/linux/dma/edma.h | 48 ++++++++++++++++ 8 files changed, 164 insertions(+), 149 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +49 -41
@@ -64,8 +64,8 @@ static struct dw_edma_burst *dw_edma_allstatic struct dw_edma_chunk *dw_edma_alloc_chunk (struct dw_edma_desc *desc) { +struct dw_edma_chip *chip = desc->chan->dw->chip;struct dw_edma_chan *chan = desc->chan; -struct dw_edma *dw = chan->chip->dw;struct dw_edma_chunk *chunk; chunk =kzalloc (sizeof (*chunk),GFP_NOWAIT );
@@ -82,11 +82,11 @@ static struct dw_edma_chunk *dw_edma_all*/ chunk->cb = !(desc->chunks_alloc %2 );if (chan->dir ==EDMA_DIR_WRITE ) { - chunk->ll_region.paddr = dw->ll_region_wr[chan->id].paddr; - chunk->ll_region.vaddr = dw->ll_region_wr[chan->id].vaddr; + chunk->ll_region.paddr = chip->ll_region_wr[chan->id].paddr; + chunk->ll_region.vaddr = chip->ll_region_wr[chan->id].vaddr; }else { - chunk->ll_region.paddr = dw->ll_region_rd[chan->id].paddr; - chunk->ll_region.vaddr = dw->ll_region_rd[chan->id].vaddr; + chunk->ll_region.paddr = chip->ll_region_rd[chan->id].paddr; + chunk->ll_region.vaddr = chip->ll_region_rd[chan->id].vaddr; }if (desc->chunk) {
@@ -695,7 +695,7 @@ static int dw_edma_alloc_chan_resources(if (chan->status !=EDMA_ST_IDLE )return -EBUSY ; -pm_runtime_get (chan->chip->dev); +pm_runtime_get (chan->dw->chip->dev);return 0 ; }
@@ -717,15 +717,15 @@ static void dw_edma_free_chan_resources(cpu_relax (); } -pm_runtime_put (chan->chip->dev); +pm_runtime_put (chan->dw->chip->dev); } -static int dw_edma_channel_setup (struct dw_edma_chip *chip,bool write, +static int dw_edma_channel_setup (struct dw_edma *dw,bool write,u32 wr_alloc,u32 rd_alloc) { +struct dw_edma_chip *chip = dw->chip;struct dw_edma_region *dt_region;struct device *dev = chip->dev; -struct dw_edma *dw = chip->dw;struct dw_edma_chan *chan;struct dw_edma_irq *irq;struct dma_device *dma;
@@ -758,7 +758,7 @@ static int dw_edma_channel_setup(struct chan->vc.chan.private = dt_region; - chan->chip = chip; + chan->dw = dw; chan->id = j; chan->dir = write ?EDMA_DIR_WRITE :EDMA_DIR_READ ; chan->configured = false;
@@ -766,9 +766,9 @@ static int dw_edma_channel_setup(struct chan->status =EDMA_ST_IDLE ;if (write) - chan->ll_max = (dw->ll_region_wr[j].sz /EDMA_LL_SZ ); + chan->ll_max = (chip->ll_region_wr[j].sz /EDMA_LL_SZ );else - chan->ll_max = (dw->ll_region_rd[j].sz /EDMA_LL_SZ ); + chan->ll_max = (chip->ll_region_rd[j].sz /EDMA_LL_SZ ); chan->ll_max -=1 ; dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
@@ -798,13 +798,13 @@ static int dw_edma_channel_setup(structvchan_init (&chan->vc, dma);if (write) { - dt_region->paddr = dw->dt_region_wr[j].paddr; - dt_region->vaddr = dw->dt_region_wr[j].vaddr; - dt_region->sz = dw->dt_region_wr[j].sz; + dt_region->paddr = chip->dt_region_wr[j].paddr; + dt_region->vaddr = chip->dt_region_wr[j].vaddr; + dt_region->sz = chip->dt_region_wr[j].sz; }else { - dt_region->paddr = dw->dt_region_rd[j].paddr; - dt_region->vaddr = dw->dt_region_rd[j].vaddr; - dt_region->sz = dw->dt_region_rd[j].sz; + dt_region->paddr = chip->dt_region_rd[j].paddr; + dt_region->vaddr = chip->dt_region_rd[j].vaddr; + dt_region->sz = chip->dt_region_rd[j].sz; }dw_edma_v0_core_device_config (chan);
@@ -858,11 +858,11 @@ static inline void dw_edma_add_irq_mask( (*mask)++; } -static int dw_edma_irq_request (struct dw_edma_chip *chip, +static int dw_edma_irq_request (struct dw_edma *dw,u32 *wr_alloc,u32 *rd_alloc) { -struct device *dev = chip->dev; -struct dw_edma *dw = chip->dw; +struct dw_edma_chip *chip = dw->chip; +struct device *dev = dw->chip->dev;u32 wr_mask =1 ;u32 rd_mask =1 ;int i, err =0 ;
@@ -871,12 +871,16 @@ static int dw_edma_irq_request(struct dw ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt; -if (dw->nr_irqs <1 ) +if (chip->nr_irqs <1 || !chip->ops->irq_vector)return -EINVAL ; -if (dw->nr_irqs ==1 ) { + dw->irq =devm_kcalloc (dev, chip->nr_irqs,sizeof (*dw->irq),GFP_KERNEL ); +if (!dw->irq) +return -ENOMEM ; + +if (chip->nr_irqs ==1 ) {/* Common IRQ shared among all channels */ - irq = dw->ops->irq_vector (dev,0 ); + irq = chip->ops->irq_vector (dev,0 ); err =request_irq (irq, dw_edma_interrupt_common,IRQF_SHARED , dw->name, &dw->irq[0 ]);if (err) {
@@ -886,9 +890,11 @@ static int dw_edma_irq_request(struct dwif (irq_get_msi_desc (irq))get_cached_msi_msg (irq, &dw->irq[0 ].msi); + + dw->nr_irqs =1 ; }else {/* Distribute IRQs equally among all channels */ -int tmp = dw->nr_irqs; +int tmp = chip->nr_irqs;while (tmp && (*wr_alloc + *rd_alloc) < ch_cnt) {dw_edma_dec_irq_alloc (&tmp, wr_alloc, dw->wr_ch_cnt);
@@ -899,7 +905,7 @@ static int dw_edma_irq_request(struct dwdw_edma_add_irq_mask (&rd_mask, *rd_alloc, dw->rd_ch_cnt);for (i =0 ; i < (*wr_alloc + *rd_alloc); i++) { - irq = dw->ops->irq_vector (dev, i); + irq = chip->ops->irq_vector (dev, i); err = request_irq(irq, i < *wr_alloc ? dw_edma_interrupt_write :
@@ -933,20 +939,22 @@ int dw_edma_probe(struct dw_edma_chip *creturn -EINVAL ; dev = chip->dev; -if (!dev) +if (!dev || !chip->ops)return -EINVAL ; - dw = chip->dw; -if (!dw || !dw->irq || !dw->ops || !dw->ops->irq_vector) -return -EINVAL ; + dw =devm_kzalloc (dev,sizeof (*dw),GFP_KERNEL ); +if (!dw) +return -ENOMEM ; + + dw->chip = chip;raw_spin_lock_init (&dw->lock); - dw->wr_ch_cnt =min_t (u16, dw->wr_ch_cnt, + dw->wr_ch_cnt =min_t (u16, chip->wr_ch_cnt,dw_edma_v0_core_ch_count (dw,EDMA_DIR_WRITE )); dw->wr_ch_cnt =min_t (u16, dw->wr_ch_cnt,EDMA_MAX_WR_CH ); - dw->rd_ch_cnt =min_t (u16, dw->rd_ch_cnt, + dw->rd_ch_cnt =min_t (u16, chip->rd_ch_cnt,dw_edma_v0_core_ch_count (dw,EDMA_DIR_READ )); dw->rd_ch_cnt =min_t (u16, dw->rd_ch_cnt,EDMA_MAX_RD_CH );
@@ -968,17 +976,17 @@ int dw_edma_probe(struct dw_edma_chip *cdw_edma_v0_core_off (dw);/* Request IRQs */ - err =dw_edma_irq_request (chip, &wr_alloc, &rd_alloc); + err =dw_edma_irq_request (dw, &wr_alloc, &rd_alloc);if (err)return err;/* Setup write channels */ - err =dw_edma_channel_setup (chip, true, wr_alloc, rd_alloc); + err =dw_edma_channel_setup (dw, true, wr_alloc, rd_alloc);if (err) goto err_irq_free;/* Setup read channels */ - err =dw_edma_channel_setup (chip, false, wr_alloc, rd_alloc); + err =dw_edma_channel_setup (dw, false, wr_alloc, rd_alloc);if (err) goto err_irq_free;
@@ -986,15 +994,15 @@ int dw_edma_probe(struct dw_edma_chip *cpm_runtime_enable (dev);/* Turn debugfs on */ -dw_edma_v0_core_debugfs_on (chip); +dw_edma_v0_core_debugfs_on (dw); + + chip->dw = dw;return 0 ; err_irq_free:for (i = (dw->nr_irqs -1 ); i >=0 ; i--) -free_irq (dw->ops->irq_vector (dev, i), &dw->irq[i]); - - dw->nr_irqs =0 ; +free_irq (chip->ops->irq_vector (dev, i), &dw->irq[i]);return err; }
@@ -1012,7 +1020,7 @@ int dw_edma_remove(struct dw_edma_chip */* Free irqs */ for (i = (dw->nr_irqs -1 ); i >=0 ; i--) -free_irq (dw->ops->irq_vector (dev, i), &dw->irq[i]); +free_irq (chip->ops->irq_vector (dev, i), &dw->irq[i]);/* Power management */ pm_runtime_disable (dev);
@@ -1033,7 +1041,7 @@ int dw_edma_remove(struct dw_edma_chip * }/* Turn debugfs off */ -dw_edma_v0_core_debugfs_off (chip); +dw_edma_v0_core_debugfs_off (dw);return 0 ; }
drivers/dma/dw-edma/dw-edma-core.h +3 -28
@@ -15,20 +15,12 @@#include "../virt-dma.h" #define EDMA_LL_SZ 24 -#define EDMA_MAX_WR_CH 8 -#define EDMA_MAX_RD_CH 8enum dw_edma_dir {EDMA_DIR_WRITE =0 ,EDMA_DIR_READ }; -enum dw_edma_map_format { -EDMA_MF_EDMA_LEGACY =0x0 , -EDMA_MF_EDMA_UNROLL =0x1 , -EDMA_MF_HDMA_COMPAT =0x5 -}; -enum dw_edma_request {EDMA_REQ_NONE =0 ,EDMA_REQ_STOP ,
@@ -57,12 +49,6 @@ struct dw_edma_burst {u32 sz; }; -struct dw_edma_region { -phys_addr_t paddr; -void __iomem *vaddr; -size_t sz; -}; -struct dw_edma_chunk {struct list_head list;struct dw_edma_chan *chan;
@@ -87,7 +73,7 @@ struct dw_edma_desc {struct dw_edma_chan {struct virt_dma_chan vc; -struct dw_edma_chip *chip; +struct dw_edma *dw;int id;enum dw_edma_dir dir;
@@ -109,10 +95,6 @@ struct dw_edma_irq {struct dw_edma *dw; }; -struct dw_edma_core_ops { -int (*irq_vector)(struct device *dev,unsigned int nr); -}; -struct dw_edma {char name[20 ];
@@ -122,21 +104,14 @@ struct dw_edma {struct dma_device rd_edma;u16 rd_ch_cnt; -struct dw_edma_region rg_region;/* Registers */ -struct dw_edma_region ll_region_wr[EDMA_MAX_WR_CH ]; -struct dw_edma_region ll_region_rd[EDMA_MAX_RD_CH ]; -struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH ]; -struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH ]; -struct dw_edma_irq *irq;int nr_irqs; -enum dw_edma_map_format mf; -struct dw_edma_chan *chan; -const struct dw_edma_core_ops *ops;raw_spinlock_t lock;/* Only for legacy */ + +struct dw_edma_chip *chip;#ifdef CONFIG_DEBUG_FS struct dentry *debugfs;#endif /* CONFIG_DEBUG_FS */
drivers/dma/dw-edma/dw-edma-pcie.c +34 -48
143 lines not on this page · raw message
drivers/dma/dw-edma/dw-edma-v0-core.c +16 -16
@@ -25,7 +25,7 @@ enum dw_edma_control {static inline struct dw_edma_v0_regs __iomem *__dw_regs (struct dw_edma *dw) { -return dw->rg_region.vaddr; +return dw->chip->rg_region.vaddr; }#define SET_32 (dw, name, value) \
@@ -96,7 +96,7 @@ static inline struct dw_edma_v0_regs __istatic inline struct dw_edma_v0_ch_regs __iomem *__dw_ch_regs (struct dw_edma *dw,enum dw_edma_dir dir,u16 ch) { -if (dw->mf ==EDMA_MF_EDMA_LEGACY ) +if (dw->chip->mf ==EDMA_MF_EDMA_LEGACY )return &(__dw_regs (dw)->type.legacy.ch);if (dir ==EDMA_DIR_WRITE )
@@ -108,7 +108,7 @@ __dw_ch_regs(struct dw_edma *dw, enum dwstatic inline void writel_ch (struct dw_edma *dw,enum dw_edma_dir dir,u16 ch,u32 value,void __iomem *addr) { -if (dw->mf ==EDMA_MF_EDMA_LEGACY ) { +if (dw->chip->mf ==EDMA_MF_EDMA_LEGACY ) {u32 viewport_sel;unsigned long flags;
@@ -133,7 +133,7 @@ static inline u32 readl_ch(struct dw_edm {u32 value; -if (dw->mf ==EDMA_MF_EDMA_LEGACY ) { +if (dw->chip->mf ==EDMA_MF_EDMA_LEGACY ) {u32 viewport_sel;unsigned long flags;
@@ -169,7 +169,7 @@ static inline u32 readl_ch(struct dw_edmstatic inline void writeq_ch (struct dw_edma *dw,enum dw_edma_dir dir,u16 ch,u64 value,void __iomem *addr) { -if (dw->mf ==EDMA_MF_EDMA_LEGACY ) { +if (dw->chip->mf ==EDMA_MF_EDMA_LEGACY ) {u32 viewport_sel;unsigned long flags;
@@ -194,7 +194,7 @@ static inline u64 readq_ch(struct dw_edm {u64 value; -if (dw->mf ==EDMA_MF_EDMA_LEGACY ) { +if (dw->chip->mf ==EDMA_MF_EDMA_LEGACY ) {u32 viewport_sel;unsigned long flags;
@@ -256,7 +256,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_eenum dma_status dw_edma_v0_core_ch_status (struct dw_edma_chan *chan) { -struct dw_edma *dw = chan->chip->dw; +struct dw_edma *dw = chan->dw;u32 tmp; tmp = FIELD_GET(EDMA_V0_CH_STATUS_MASK,
@@ -272,7 +272,7 @@ enum dma_status dw_edma_v0_core_ch_statuvoid dw_edma_v0_core_clear_done_int (struct dw_edma_chan *chan) { -struct dw_edma *dw = chan->chip->dw; +struct dw_edma *dw = chan->dw;SET_RW_32 (dw, chan->dir, int_clear,FIELD_PREP (EDMA_V0_DONE_INT_MASK ,BIT (chan->id)));
@@ -280,7 +280,7 @@ void dw_edma_v0_core_clear_done_int(struvoid dw_edma_v0_core_clear_abort_int (struct dw_edma_chan *chan) { -struct dw_edma *dw = chan->chip->dw; +struct dw_edma *dw = chan->dw;SET_RW_32 (dw, chan->dir, int_clear,FIELD_PREP (EDMA_V0_ABORT_INT_MASK ,BIT (chan->id)));
@@ -357,7 +357,7 @@ static void dw_edma_v0_core_write_chunk(void dw_edma_v0_core_start (struct dw_edma_chunk *chunk,bool first) {struct dw_edma_chan *chan = chunk->chan; -struct dw_edma *dw = chan->chip->dw; +struct dw_edma *dw = chan->dw;u32 tmp;dw_edma_v0_core_write_chunk (chunk);
@@ -365,7 +365,7 @@ void dw_edma_v0_core_start(struct dw_edmif (first) {/* Enable engine */ SET_RW_32 (dw, chan->dir, engine_en,BIT (0 )); -if (dw->mf ==EDMA_MF_HDMA_COMPAT ) { +if (dw->chip->mf ==EDMA_MF_HDMA_COMPAT ) {switch (chan->id) {case 0 : SET_RW_COMPAT(dw, chan->dir, ch0_pwr_en,
@@ -434,7 +434,7 @@ void dw_edma_v0_core_start(struct dw_edmint dw_edma_v0_core_device_config (struct dw_edma_chan *chan) { -struct dw_edma *dw = chan->chip->dw; +struct dw_edma *dw = chan->dw;u32 tmp =0 ;/* MSI done addr - low, high */
@@ -504,12 +504,12 @@ int dw_edma_v0_core_device_config(struct }/* eDMA debugfs callbacks */ -void dw_edma_v0_core_debugfs_on (struct dw_edma_chip *chip) +void dw_edma_v0_core_debugfs_on (struct dw_edma *dw) { -dw_edma_v0_debugfs_on (chip); +dw_edma_v0_debugfs_on (dw); } -void dw_edma_v0_core_debugfs_off (struct dw_edma_chip *chip) +void dw_edma_v0_core_debugfs_off (struct dw_edma *dw) { -dw_edma_v0_debugfs_off (chip); +dw_edma_v0_debugfs_off (dw); }
drivers/dma/dw-edma/dw-edma-v0-core.h +2 -2
@@ -22,7 +22,7 @@ u32 dw_edma_v0_core_status_abort_int(strvoid dw_edma_v0_core_start (struct dw_edma_chunk *chunk,bool first);int dw_edma_v0_core_device_config (struct dw_edma_chan *chan);/* eDMA debug fs callbacks */ -void dw_edma_v0_core_debugfs_on (struct dw_edma_chip *chip); -void dw_edma_v0_core_debugfs_off (struct dw_edma_chip *chip); +void dw_edma_v0_core_debugfs_on (struct dw_edma *dw); +void dw_edma_v0_core_debugfs_off (struct dw_edma *dw);#endif /* _DW_EDMA_V0_CORE_H */
drivers/dma/dw-edma/dw-edma-v0-debugfs.c +9 -9
@@ -54,7 +54,7 @@ struct debugfs_entries {static int dw_edma_debugfs_u32_get (void *data,u64 *val) {void __iomem *reg = (void __force __iomem *)data; -if (dw->mf ==EDMA_MF_EDMA_LEGACY && +if (dw->chip->mf ==EDMA_MF_EDMA_LEGACY && reg >= (void __iomem *)®s->type.legacy.ch) {void __iomem *ptr = ®s->type.legacy.ch;u32 viewport_sel =0 ;
@@ -173,7 +173,7 @@ static void dw_edma_debugfs_regs_wr(stru nr_entries =ARRAY_SIZE (debugfs_regs);dw_edma_debugfs_create_x32 (debugfs_regs, nr_entries, regs_dir); -if (dw->mf ==EDMA_MF_HDMA_COMPAT ) { +if (dw->chip->mf ==EDMA_MF_HDMA_COMPAT ) { nr_entries =ARRAY_SIZE (debugfs_unroll_regs);dw_edma_debugfs_create_x32 (debugfs_unroll_regs, nr_entries, regs_dir);
@@ -242,7 +242,7 @@ static void dw_edma_debugfs_regs_rd(stru nr_entries =ARRAY_SIZE (debugfs_regs);dw_edma_debugfs_create_x32 (debugfs_regs, nr_entries, regs_dir); -if (dw->mf ==EDMA_MF_HDMA_COMPAT ) { +if (dw->chip->mf ==EDMA_MF_HDMA_COMPAT ) { nr_entries =ARRAY_SIZE (debugfs_unroll_regs);dw_edma_debugfs_create_x32 (debugfs_unroll_regs, nr_entries, regs_dir);
@@ -282,13 +282,13 @@ static void dw_edma_debugfs_regs(void)dw_edma_debugfs_regs_rd (regs_dir); } -void dw_edma_v0_debugfs_on (struct dw_edma_chip *chip) +void dw_edma_v0_debugfs_on (struct dw_edma *_dw) { - dw = chip->dw; + dw = _dw;if (!dw)return ; - regs = dw->rg_region.vaddr; + regs = dw->chip->rg_region.vaddr;if (!regs)return ;
@@ -296,16 +296,16 @@ void dw_edma_v0_debugfs_on(struct dw_edmif (!dw->debugfs)return ; -debugfs_create_u32 ("mf" ,0444 , dw->debugfs, &dw->mf); +debugfs_create_u32 ("mf" ,0444 , dw->debugfs, &dw->chip->mf);debugfs_create_u16 ("wr_ch_cnt" ,0444 , dw->debugfs, &dw->wr_ch_cnt);debugfs_create_u16 ("rd_ch_cnt" ,0444 , dw->debugfs, &dw->rd_ch_cnt);dw_edma_debugfs_regs (); } -void dw_edma_v0_debugfs_off (struct dw_edma_chip *chip) +void dw_edma_v0_debugfs_off (struct dw_edma *_dw) { - dw = chip->dw; + dw = _dw;if (!dw)return ;
drivers/dma/dw-edma/dw-edma-v0-debugfs.h +4 -4
@@ -12,14 +12,14 @@#include <linux/dma/edma.h> #ifdef CONFIG_DEBUG_FS -void dw_edma_v0_debugfs_on (struct dw_edma_chip *chip); -void dw_edma_v0_debugfs_off (struct dw_edma_chip *chip); +void dw_edma_v0_debugfs_on (struct dw_edma *dw); +void dw_edma_v0_debugfs_off (struct dw_edma *dw);#else -static inline void dw_edma_v0_debugfs_on (struct dw_edma_chip *chip) +static inline void dw_edma_v0_debugfs_on (struct dw_edma *dw) { } -static inline void dw_edma_v0_debugfs_off (struct dw_edma_chip *chip) +static inline void dw_edma_v0_debugfs_off (struct dw_edma *dw) { }#endif /* CONFIG_DEBUG_FS */
include/linux/dma/edma.h +47 -1
64 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 117/272 dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Li <[email protected]>
[ Upstream commit 8ffba0171c6bbce5f093c6dba5a02c0805b31203 ]
The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA channels, and modifying them requires a read-modify-write sequence. Because this operation is not atomic, concurrent calls to dw_edma_v0_core_start() can introduce race conditions if two channels update these registers simultaneously.
Add a spinlock to serialize access to these registers and prevent race conditions.
--- drivers/dma/dw-edma/dw-edma-core.h | 2 +- drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-)
drivers/dma/dw-edma/dw-edma-core.h +1 -1
@@ -109,7 +109,7 @@ struct dw_edma {struct dw_edma_chan *chan; -raw_spinlock_t lock;/* Only for legacy */ +raw_spinlock_t lock;/* Protect v0 shared registers */ struct dw_edma_chip *chip;#ifdef CONFIG_DEBUG_FS
drivers/dma/dw-edma/dw-edma-v0-core.c +6 -0
@@ -358,6 +358,7 @@ void dw_edma_v0_core_start(struct dw_edm {struct dw_edma_chan *chan = chunk->chan;struct dw_edma *dw = chan->dw; +unsigned long flags;u32 tmp;dw_edma_v0_core_write_chunk (chunk);
@@ -402,6 +403,8 @@ void dw_edma_v0_core_start(struct dw_edm } }/* Interrupt unmask - done, abort */ +raw_spin_lock_irqsave (&dw->lock, flags); + tmp =GET_RW_32 (dw, chan->dir, int_mask); tmp &= ~FIELD_PREP (EDMA_V0_DONE_INT_MASK ,BIT (chan->id)); tmp &= ~FIELD_PREP (EDMA_V0_ABORT_INT_MASK ,BIT (chan->id));
@@ -410,6 +413,9 @@ void dw_edma_v0_core_start(struct dw_edm tmp =GET_RW_32 (dw, chan->dir, linked_list_err_en); tmp |=FIELD_PREP (EDMA_V0_LINKED_LIST_ERR_MASK ,BIT (chan->id));SET_RW_32 (dw, chan->dir, linked_list_err_en, tmp); + +raw_spin_unlock_irqrestore (&dw->lock, flags); +/* Channel control */ SET_CH_32 (dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS |DW_EDMA_V0_LLE ));
Greg Kroah-Hartman
maintainerPATCH 118/272 taskstats: fill_stats_for_tgid: use for_each_thread()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleg Nesterov <[email protected]>
[ Upstream commit ed5378a387fd7c382497f2abcf4605e030b64044 ]
do/while_each_thread should be avoided when possible.
Plus I _think_ this change allows to avoid lock_task_sighand() but I am not sure, I forgot everything about taskstats. In any case, this code does not look right in that the same thread can be accounted twice:taskstats_exit() can account the exiting thread in signal->stats and drop ->siglock but this thread is still on the thread-group list, so lock_task_sighand() can't help.
Stable-dep-of: b3e4fbb04220 ("taskstats: retain dead thread stats in TGID queries")
--- kernel/taskstats.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/taskstats.c +2 -3
@@ -211,9 +211,8 @@ static int fill_stats_for_tgid(pid_t tgielse memset (stats,0 ,sizeof (*stats)); - tsk = first; start_time =ktime_get_ns (); - do { +for_each_thread (first, tsk) {if (tsk->exit_state)continue ;/*
@@ -236,7 +235,7 @@ static int fill_stats_for_tgid(pid_t tgi stats->nvcsw += tsk->nvcsw; stats->nivcsw += tsk->nivcsw; - }while_each_thread (first, tsk); + }unlock_task_sighand (first, &flags); rc =0 ;
Greg Kroah-Hartman
maintainerPATCH 119/272 taskstats: retain dead thread stats in TGID queries
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yiyang Chen <[email protected]>
[ Upstream commit b3e4fbb04220efc3bc022bcf31b5689d39c6b111 ]
Patch series "taskstats: fix TGID dead-thread stat retention", v3.
This series fixes a taskstats TGID aggregation bug where fields added in the TGID query path were not preserved after thread exit, and adds a kselftest covering the regression.
The first patch keeps the cached TGID aggregate used for dead threads in step with the fields already accumulated for live threads, and also fixes the final TGID exit notification emitted when group_dead is true.
The second patch adds a kselftest that verifies TGID CPU stats do not regress after a worker thread exits and has been reaped.
This patch (of 2):
fill_stats_for_tgid() builds TGID stats from two sources: the cached aggregate in signal->stats and a scan of the live threads in the group.
However, fill_tgid_exit() only accumulates delay accounting into signal->stats. This means that once a thread exits, TGID queries lose the fields that fill_stats_for_tgid() adds for live threads.
This gap was introduced incrementally by two earlier changes that extended fill_stats_for_tgid() but did not make the corresponding update to fill_tgid_exit():
- commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") added ac_etime, ac_utime, and ac_stime to the TGID query path.
- commit b663a79c1915 ("taskstats: add context-switch counters") added nvcsw and nivcsw to the TGID query path.
As a result, those fields were accounted for live threads in TGID queries, but were dropped from the cached TGID aggregate after thread exit. The final TGID exit notification emitted when group_dead is true also copies that cached aggregate, so it loses the same fields.
Factor the per-task TGID accumulation into tgid_stats_add_task() and use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the cached aggregate used for dead threads aligned with the live-thread accumulation used by TGID queries.
--- kernel/taskstats.c | 62 ++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 29 deletions(-)
kernel/taskstats.c +33 -29
@@ -188,13 +188,39 @@ static int fill_stats_for_pid(pid_t pid,return 0 ; } +static void tgid_stats_add_task (struct taskstats *stats, +struct task_struct *tsk,u64 now_ns) +{ +u64 delta, utime, stime; + +/* +* Each accounting subsystem calls its functions here to +* accumulate its per-task stats for tsk, into the per-tgid structure +* +* per-task-foo(stats, tsk); +*/ +delayacct_add_tsk (stats, tsk); + +/* calculate task elapsed time in nsec */ + delta = now_ns - tsk->start_time; +/* Convert to micro seconds */ +do_div (delta,NSEC_PER_USEC ); + stats->ac_etime += delta; + +task_cputime (tsk, &utime, &stime); + stats->ac_utime +=div_u64 (utime,NSEC_PER_USEC ); + stats->ac_stime +=div_u64 (stime,NSEC_PER_USEC ); + + stats->nvcsw += tsk->nvcsw; + stats->nivcsw += tsk->nivcsw; +} +static int fill_stats_for_tgid (pid_t tgid,struct taskstats *stats) {struct task_struct *tsk, *first;unsigned long flags;int rc = -ESRCH ; -u64 delta, utime, stime; -u64 start_time; +u64 now_ns;/* * Add additional stats from live tasks except zombie thread group
@@ -211,30 +237,12 @@ static int fill_stats_for_tgid(pid_t tgielse memset (stats,0 ,sizeof (*stats)); - start_time =ktime_get_ns (); + now_ns =ktime_get_ns ();for_each_thread (first, tsk) {if (tsk->exit_state)continue ; -/* -* Accounting subsystem can call its functions here to -* fill in relevant parts of struct taskstsats as follows -* -* per-task-foo(stats, tsk); -*/ -delayacct_add_tsk (stats, tsk); - -/* calculate task elapsed time in nsec */ - delta = start_time - tsk->start_time; -/* Convert to micro seconds */ -do_div (delta,NSEC_PER_USEC ); - stats->ac_etime += delta; - -task_cputime (tsk, &utime, &stime); - stats->ac_utime +=div_u64 (utime,NSEC_PER_USEC ); - stats->ac_stime +=div_u64 (stime,NSEC_PER_USEC ); - stats->nvcsw += tsk->nvcsw; - stats->nivcsw += tsk->nivcsw; +tgid_stats_add_task (stats, tsk, now_ns); }unlock_task_sighand (first, &flags);
@@ -253,18 +261,14 @@ out:static void fill_tgid_exit (struct task_struct *tsk) {unsigned long flags; +u64 now_ns;spin_lock_irqsave (&tsk->sighand->siglock, flags);if (!tsk->signal->stats) goto ret; -/* -* Each accounting subsystem calls its functions here to -* accumalate its per-task stats for tsk, into the per-tgid structure -* -* per-task-foo(tsk->signal->stats, tsk); -*/ -delayacct_add_tsk (tsk->signal->stats, tsk); + now_ns =ktime_get_ns (); +tgid_stats_add_task (tsk->signal->stats, tsk, now_ns); ret:spin_unlock_irqrestore (&tsk->sighand->siglock, flags);return ;
Greg Kroah-Hartman
maintainerPATCH 120/272 smb: client: use kvzalloc() for megabyte buffer in simple fallocate
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fredric Cover <[email protected]>
[ Upstream commit 806c00c23e3ce8eae397a40ced536ef88ae4e012 ]
Currently in smb3_simple_fallocate_range(), a 1 MB buffer is allocated using kzalloc(). Under heavy memory fragmentation, a contiguous 1 MB block of physical memory (an order-8 allocation) may not be available, causing the allocation to fail.
This failure was observed during xfstests generic/013 on a 4GB RAM test machine running fsstress:
fsstress: page allocation failure: order:8,
mode:0x40dc0(GFP_KERNEL|__GFP_ZERO|__GFP_COMP),
nodemask=(null),cpuset=/,mems_allowed=0
Call Trace: <TASK> dump_stack_lvl+0x5d/0x80 warn_alloc+0x163/0x190 __alloc_pages_slowpath.constprop.0+0x71b/0x12f0 __alloc_frozen_pages_noprof+0x2f6/0x340 alloc_pages_mpol+0xb6/0x170 ___kmalloc_large_node+0xb3/0xd0 __kmalloc_large_noprof+0x1e/0xc0 smb3_simple_falloc.isra.0+0x62b/0x960 cifs_fallocate+0xed/0x180 vfs_fallocate+0x165/0x3c0 __x64_sys_fallocate+0x48/0xa0 do_syscall_64+0xe1/0x640 entry_SYSCALL_64_after_hwframe+0x76/0x7e </TASK>
Node 0 Normal: 3375*4kB ... 7*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB
Since this scratch buffer does not require physically contiguous memory, switch the allocation to kvzalloc(). This retains the performance benefits of kmalloc() under normal conditions, while gracefully falling back to virtually contiguous memory when physical allocation fails.
--- fs/cifs/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/cifs/smb2ops.c +2 -2
@@ -3736,7 +3736,7 @@ static int smb3_simple_fallocate_range(uif (rc) goto out; - buf =kzalloc (1024 *1024 ,GFP_KERNEL ); + buf =kvzalloc (1024 *1024 ,GFP_KERNEL );if (buf ==NULL ) { rc = -ENOMEM ; goto out;
@@ -3803,7 +3803,7 @@ static int smb3_simple_fallocate_range(u out:kfree (out_data); -kfree (buf); +kvfree (buf);return rc; }
Re: [PATCH 5.15 120/272] smb: client: use kvzalloc() for megabyte buffer in simple fallocate
Hi Greg/Sasha,
Quoted 58 lines · Greg Kroah-Hartman
5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fredric Cover <[email protected]>
[ Upstream commit
806c00c23e3ce8eae397a40ced536ef88ae4e012]Currently in
smb3_simple_fallocate_range(), a 1 MB buffer is allocated usingkzalloc(). Under heavy memory fragmentation, a contiguous 1 MB block of physical memory (an order-8 allocation) may not be available, causing the allocation to fail.This failure was observed during xfstests generic/013 on a 4GB RAM test machine running fsstress:
fsstress: page allocation failure: order:8,
mode:0x40dc0(GFP_KERNEL|__GFP_ZERO|__GFP_COMP),
nodemask=(null),cpuset=/,mems_allowed=0Call Trace: <TASK> dump_stack_lvl+0x5d/0x80 warn_alloc+0x163/0x190 __alloc_pages_slowpath.constprop.0+0x71b/0x12f0 __alloc_frozen_pages_noprof+0x2f6/0x340 alloc_pages_mpol+0xb6/0x170 ___kmalloc_large_node+0xb3/0xd0 __kmalloc_large_noprof+0x1e/0xc0 smb3_simple_falloc.isra.0+0x62b/0x960 cifs_fallocate+0xed/0x180 vfs_fallocate+0x165/0x3c0 __x64_sys_fallocate+0x48/0xa0 do_syscall_64+0xe1/0x640 entry_SYSCALL_64_after_hwframe+0x76/0x7e </TASK>Node 0 Normal: 3375*4kB ... 7*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB
Since this scratch buffer does not require physically contiguous memory, switch the allocation to
kvzalloc(). This retains the performance benefits ofkmalloc()under normal conditions, while gracefully falling back to virtually contiguous memory when physical allocation fails.Fixes 966a3cb7c7db ("cifs: improve fallocate emulation")Signed-off-by Fredric Cover <[email protected]>Tested-by Fredric Cover <[email protected]>Signed-off-by Steve French <[email protected]>Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- fs/cifs/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/cifs/smb2ops.c static int smb3_simple_fallocate_range(u · line 3739
Quoted 5 lines
fs/cifs/smb2ops.c +1
-1@@ -3736,7 +3736,7 @@ static int smb3_simple_fallocate_range(uif (rc) goto out; - buf =kzalloc (1024 *1024 ,GFP_KERNEL ); + buf =kvzalloc (1024 *1024 ,GFP_KERNEL );
I ran an AI-assisted backport review of d1bc345f6229 and checked the finding against the 5.15.217 SMB Direct path. The kvzalloc() fallback is not safe in that older transport implementation.
Upstream already maps KVEC iterators page by page with:
if (is_vmalloc_or_module_addr((void *)kaddr))
page = vmalloc_to_page((void *)kaddr);
else
page = virt_to_page((void *)kaddr);5.15 instead sends the write kvec through:
sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len);
sg_set_buf() uses virt_to_page(), so if kvzalloc() falls back to vmalloc
memory, an SMB Direct mount gets an invalid scatterlist page, and CONFIG_DEBUG_SG can BUG before DMA mapping. The vmalloc-aware path comes from e5fbdde430171175f32094c8059eea0d0e2487ae ("cifs: Add a function to
build an RDMA SGE list from an iterator") and 3d78fe73fa1239 ("cifs: Build the RDMA SGE list directly from an iterator"), neither of which is in 5.15.
We should probably revert this for 5.15.y. Thoughts ?
thanks,
Harshit
Quoted 15 lines
fs/cifs/smb2ops.c +1
-1if (buf ==NULL ) { rc = -ENOMEM ; goto out;@@ -3803,7 +3803,7 @@ static int smb3_simple_fallocate_range(u out:kfree (out_data); -kfree (buf); +kvfree (buf);return rc; }
Sasha Levin
maintainerRe: [PATCH 5.15 120/272] smb: client: use kvzalloc() for megabyte buffer in simple fallocate
Quoted 10 lines
I ran an AI-assisted backport review of
d1bc345f6229and checked the finding against the 5.15.217 SMB Direct path. Thekvzalloc()fallback is not safe in that older transport implementation.Upstream already maps KVEC iterators page by page with:
if (is_vmalloc_or_module_addr((void *)kaddr)) page = vmalloc_to_page((void *)kaddr); else page = virt_to_page((void *)kaddr);
Agreed. On 6.1/5.15/5.10 that buffer reaches sg_set_buf(), which is virt_to_page() based, so a kvzalloc() that falls back to vmalloc DMA-maps the wrong page. 6.6 and newer build the SGE list from an iterator and are fine.
Reverted on 6.1, 5.15 and 5.10, thanks.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 121/272 tpm: tpm_tis_spi: Use wait_woken() in wait_for_tmp_stat()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jarkko Sakkinen <[email protected]>
[ Upstream commit c0c9cfb3b75def8bf200a2d4db09015806acfeaf ]
wait_event_interruptible_timeout() evaluates its condition after setting the current task state to TASK_INTERRUPTIBLE.
With CONFIG_DEBUG_ATOMIC_SLEEP this triggers a warning when the IRQ wait path is used:
tpm_tis_status()
tpm_tis_spi_read_bytes()
tpm_tis_spi_transfer_full()
spi_bus_lock()
mutex_lock()Address this with the following measures:
1. Call wait_tpm_stat_cond() only while tasking is running.
2. Use wait_woken() to wait for changes.
--- drivers/char/tpm/tpm_tis_core.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-)
drivers/char/tpm/tpm_tis_core.c +24 -14
@@ -49,10 +49,11 @@ static int wait_for_tpm_stat(struct tpm_ bool check_cancel) {struct tpm_tis_data *priv =dev_get_drvdata (&chip->dev); +DEFINE_WAIT_FUNC (wait, woken_wake_function);unsigned long stop; -long rc;u8 status;bool canceled = false; +int ret;/* check current status */ status = chip->ops->status (chip);
@@ -62,23 +63,32 @@ static int wait_for_tpm_stat(struct tpm_ stop = jiffies + timeout;if (chip->flags &TPM_CHIP_FLAG_IRQ ) { + ret = -ETIME ; +add_wait_queue (queue, &wait); again: +if (wait_for_tpm_stat_cond (chip, mask, check_cancel, + &canceled)) { + ret = canceled ? -ECANCELED :0 ; + goto out; + } + timeout = stop - jiffies;if ((long )timeout <=0 ) -return -ETIME ; - rc =wait_event_interruptible_timeout (*queue, -wait_for_tpm_stat_cond (chip, mask, check_cancel, - &canceled), - timeout); -if (rc >0 ) { -if (canceled) -return -ECANCELED ; -return 0 ; - } -if (rc == -ERESTARTSYS &&freezing (current)) { -clear_thread_flag (TIF_SIGPENDING ); - goto again; + goto out; + +if (signal_pending (current)) { +if (freezing (current)) { +clear_thread_flag (TIF_SIGPENDING ); + goto again; + } + goto out; } + +wait_woken (&wait,TASK_INTERRUPTIBLE , timeout); + goto again; +out: +remove_wait_queue (queue, &wait); +return ret; } else { do { usleep_range(priv->timeout_min,
Greg Kroah-Hartman
maintainerPATCH 122/272 ksmbd: fix integer overflow in set_file_allocation_info()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
[ Upstream commit 1c0ae3df692ea2a4ce992f786346154e75a3f0d5 ]
set_file_allocation_info() converts the client-supplied FILE_ALLOCATION_INFORMATION::AllocationSize into a 512-byte block count with:
alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
AllocationSize is a fully client-controlled __le64 field; the only validation performed by the caller (smb2_set_info_file(), case FILE_ALLOCATION_INFORMATION) is that the fixed buffer is at least sizeof(struct smb2_file_alloc_info) == 8 bytes. The value itself is never range-checked before this arithmetic.
When AllocationSize is close to U64_MAX (e.g. 0xffffffffffffffff), "AllocationSize + 511" wraps around mod 2^64 to a small number (0xffffffffffffffff + 511 = 510), so alloc_blks becomes 0. Since any existing regular file has stat.blocks > 0, the function then takes the "shrink" branch and calls:
ksmbd_vfs_truncate(work, fp, alloc_blks * 512); /* == 0 */
silently truncating the file to size 0, even though the client asked to grow the allocation to (what looks like) the maximum possible size. The trailing "if (size < alloc_blks * 512) i_size_write(inode, size);" restore is guarded by a comparison that is never true once alloc_blks == 0, so the truncation is not undone. This lets an authenticated SMB client that already holds an open handle with FILE_WRITE_DATA on a file silently truncate that same file to size 0 via a single crafted SET_INFO(FILE_ALLOCATION_INFORMATION) request advertising a near-U64_MAX AllocationSize, even though the request asks to grow the file's allocation rather than shrink it. This is a functional/data-loss bug, not a privilege-boundary
violation: the same client could already truncate the file via FILE_END_OF_FILE_INFORMATION or a plain write.
Fix it by validating AllocationSize against MAX_LFS_FILESIZE, the same upper bound the VFS itself uses to reject unrepresentable file sizes, before doing the "+511" rounding, and rejecting oversized values with -EINVAL. Bounding AllocationSize to MAX_LFS_FILESIZE - 511 guarantees the "+511" addition cannot wrap, and that the subsequent "alloc_blks * 512" values passed to vfs_fallocate() and ksmbd_vfs_truncate() stay within a representable loff_t as well.
No legitimate SMB client asks for an allocation size anywhere near 2^64 bytes, so this only rejects a value that was previously silently misinterpreted as zero.
Runtime-verified on a v6.19 KASAN test stand: sending SET_INFO (FILE_ALLOCATION_INFORMATION) with AllocationSize = 0xffffffffffffffff against ksmbd now returns -EINVAL and leaves the target file's size unchanged, where the unpatched kernel truncated it from 4096 to 0 bytes.
--- fs/ksmbd/smb2pdu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
fs/ksmbd/smb2pdu.c +14 -1
@@ -5814,6 +5814,7 @@ static int set_file_allocation_info(stru*/ loff_t alloc_blks; +u64 alloc_size;struct inode *inode;struct kstat stat;int rc;
@@ -5826,7 +5827,19 @@ static int set_file_allocation_info(struif (rc)return rc; - alloc_blks = (le64_to_cpu (file_alloc_info->AllocationSize) +511 ) >>9 ; +/* +* AllocationSize is fully client-controlled (the caller only +* validates the fixed 8-byte buffer length). Reject values that +* would overflow the "round up to 512-byte blocks" conversion +* below instead of silently wrapping it to a tiny block count, +* which would truncate the file to a size the client never +* asked for. +*/ + alloc_size =le64_to_cpu (file_alloc_info->AllocationSize); +if (alloc_size >MAX_LFS_FILESIZE -511 ) +return -EINVAL ; + + alloc_blks = (alloc_size +511 ) >>9 ; inode =file_inode (fp->filp);if (alloc_blks > stat.blocks) {
Greg Kroah-Hartman
maintainerPATCH 123/272 i2c: imx: separate atomic, dma and non-dma use case
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Eichenberger <[email protected]>
[ Upstream commit b460b15b3cc23ef3639cc51043bf8b2a70ca1878 ]
Separate the atomic, dma and non-dma use case as a preparation step for moving the non-dma use case to the isr to avoid rescheduling while a transfer is in progress.
Stable-dep-of: cb2fc3785769 ("i2c: imx: fix locked bus on SMBus block-read of 0 (atomic)")
--- drivers/i2c/busses/i2c-imx.c | 107 ++++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 37 deletions(-)
drivers/i2c/busses/i2c-imx.c +70 -37
@@ -979,6 +979,43 @@ static int i2c_imx_dma_write(struct imx_return i2c_imx_acked (i2c_imx); } +static int i2c_imx_prepare_read (struct imx_i2c_struct *i2c_imx, +struct i2c_msg *msgs,bool atomic, +bool use_dma) +{ +int result; +unsigned int temp =0 ; + +/* write slave address */ +imx_i2c_write_reg (i2c_8bit_addr_from_msg (msgs), i2c_imx,IMX_I2C_I2DR ); + result =i2c_imx_trx_complete (i2c_imx, atomic); +if (result) +return result; + result =i2c_imx_acked (i2c_imx); +if (result) +return result; + +dev_dbg (&i2c_imx->adapter.dev,"<%s> setup bus\n" , __func__); + +/* setup bus to read data */ + temp =imx_i2c_read_reg (i2c_imx,IMX_I2C_I2CR ); + temp &= ~I2CR_MTX ; + +/* +* Reset the I2CR_TXAK flag initially for SMBus block read since the +* length is unknown +*/ +if (msgs->len -1 ) + temp &= ~I2CR_TXAK ; +if (use_dma) + temp |=I2CR_DMAEN ; + +imx_i2c_write_reg (temp, i2c_imx,IMX_I2C_I2CR ); +imx_i2c_read_reg (i2c_imx,IMX_I2C_I2DR );/* dummy read */ + +return 0 ; +} +static int i2c_imx_dma_read (struct imx_i2c_struct *i2c_imx,struct i2c_msg *msgs,bool is_lastmsg) {
@@ -989,6 +1026,11 @@ static int i2c_imx_dma_read(struct imx_istruct imx_i2c_dma *dma = i2c_imx->dma;struct device *dev = &i2c_imx->adapter.dev; + result =i2c_imx_prepare_read (i2c_imx, msgs, false, true); +if (result) +return result; + +dev_dbg (&i2c_imx->adapter.dev,"<%s> read data\n" , __func__); dma->chan_using = dma->chan_rx; dma->dma_transfer_dir =DMA_DEV_TO_MEM ;
@@ -1099,50 +1141,24 @@ static int i2c_imx_write(struct imx_i2c_return 0 ; } +static int i2c_imx_atomic_write (struct imx_i2c_struct *i2c_imx,struct i2c_msg *msgs) +{ +return i2c_imx_write (i2c_imx, msgs, true); +} +static int i2c_imx_read (struct imx_i2c_struct *i2c_imx,struct i2c_msg *msgs,bool is_lastmsg,bool atomic) {int i, result;unsigned int temp;int block_data = msgs->flags &I2C_M_RECV_LEN ; -int use_dma = i2c_imx->dma && msgs->flags &I2C_M_DMA_SAFE && - msgs->len >=DMA_THRESHOLD && !block_data; - -dev_dbg (&i2c_imx->adapter.dev, -"<%s> write slave address: addr=0x%x\n" , - __func__,i2c_8bit_addr_from_msg (msgs)); -/* write slave address */ -imx_i2c_write_reg (i2c_8bit_addr_from_msg (msgs), i2c_imx,IMX_I2C_I2DR ); - result =i2c_imx_trx_complete (i2c_imx, atomic); -if (result) -return result; - result =i2c_imx_acked (i2c_imx); + result =i2c_imx_prepare_read (i2c_imx, msgs, atomic, false);if (result)return result; -dev_dbg (&i2c_imx->adapter.dev,"<%s> setup bus\n" , __func__); - -/* setup bus to read data */ - temp =imx_i2c_read_reg (i2c_imx,IMX_I2C_I2CR ); - temp &= ~I2CR_MTX ; - -/* -* Reset the I2CR_TXAK flag initially for SMBus block read since the -* length is unknown -*/ -if ((msgs->len -1 ) || block_data) - temp &= ~I2CR_TXAK ; -if (use_dma) - temp |=I2CR_DMAEN ; -imx_i2c_write_reg (temp, i2c_imx,IMX_I2C_I2CR ); -imx_i2c_read_reg (i2c_imx,IMX_I2C_I2DR );/* dummy read */ -dev_dbg (&i2c_imx->adapter.dev,"<%s> read data\n" , __func__); -if (use_dma) -return i2c_imx_dma_read (i2c_imx, msgs, is_lastmsg); -/* read data */ for (i =0 ; i < msgs->len; i++) {u8 len =0 ;
@@ -1209,6 +1225,12 @@ static int i2c_imx_read(struct imx_i2c_sreturn 0 ; } +static int i2c_imx_atomic_read (struct imx_i2c_struct *i2c_imx,struct i2c_msg *msgs, +bool is_lastmsg) +{ +return i2c_imx_read (i2c_imx, msgs, is_lastmsg, true); +} +static int i2c_imx_xfer_common (struct i2c_adapter *adapter,struct i2c_msg *msgs,int num,bool atomic) {
@@ -1216,6 +1238,7 @@ static int i2c_imx_xfer_common(struct i2int result;bool is_lastmsg = false;struct imx_i2c_struct *i2c_imx =i2c_get_adapdata (adapter); +int use_dma =0 ;/* Start I2C transfer */ result =i2c_imx_start (i2c_imx, atomic);
@@ -1268,15 +1291,25 @@ static int i2c_imx_xfer_common(struct i2 (temp &I2SR_SRW ?1 :0 ), (temp &I2SR_IIF ?1 :0 ), (temp & I2SR_RXAK ? 1 : 0));#endif + + use_dma = i2c_imx->dma && msgs[i].len >=DMA_THRESHOLD && + msgs[i].flags &I2C_M_DMA_SAFE ;if (msgs[i].flags &I2C_M_RD ) { - result =i2c_imx_read (i2c_imx, &msgs[i], is_lastmsg, atomic); +int block_data = msgs->flags &I2C_M_RECV_LEN ; + +if (atomic) + result =i2c_imx_atomic_read (i2c_imx, &msgs[i], is_lastmsg); +else if (use_dma && !block_data) + result =i2c_imx_dma_read (i2c_imx, &msgs[i], is_lastmsg); +else + result =i2c_imx_read (i2c_imx, &msgs[i], is_lastmsg, false); }else { -if (!atomic && - i2c_imx->dma && msgs[i].len >=DMA_THRESHOLD && - msgs[i].flags &I2C_M_DMA_SAFE ) +if (atomic) + result =i2c_imx_atomic_write (i2c_imx, &msgs[i]); +else if (use_dma) result =i2c_imx_dma_write (i2c_imx, &msgs[i]);else - result =i2c_imx_write (i2c_imx, &msgs[i], atomic); + result =i2c_imx_write (i2c_imx, &msgs[i], false); }if (result) goto fail0;
Greg Kroah-Hartman
maintainerPATCH 124/272 i2c: imx: fix locked bus on SMBus block-read of 0 (atomic)
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Jardin <[email protected]>
[ Upstream commit cb2fc37857693b55909fb77dc2c87cfbc1cdc476 ]
SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the atomic (polling) path rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle for this i2c controller.
Reading I2DR to obtain the count likewise arms the next byte on the count > I2C_SMBUS_BLOCK_MAX path, which also returned -EPROTO directly and left the bus held.
Handle both: NACK the in-flight dummy byte (TXAK) and extend msgs->len so the existing last-byte handling emits STOP; the dummy byte is discarded. A count of 0 is a valid empty block read; a count above I2C_SMBUS_BLOCK_MAX is still reported as -EPROTO, but only after the bus has been released.
The interrupt-driven path has the same flaw from a later commit and is fixed separately, as it carries a different Fixes: tag and stable range.
--- drivers/i2c/busses/i2c-imx.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
drivers/i2c/busses/i2c-imx.c +16 -3
@@ -1152,6 +1152,7 @@ static int i2c_imx_read(struct imx_i2c_sint i, result;unsigned int temp;int block_data = msgs->flags &I2C_M_RECV_LEN ; +int block_err =0 ; result =i2c_imx_prepare_read (i2c_imx, msgs, atomic, false);if (result)
@@ -1173,8 +1174,20 @@ static int i2c_imx_read(struct imx_i2c_s*/ if ((!i) && block_data) { len =imx_i2c_read_reg (i2c_imx,IMX_I2C_I2DR ); -if ((len ==0 ) || (len >I2C_SMBUS_BLOCK_MAX )) -return -EPROTO ; +if ((len ==0 ) || (len >I2C_SMBUS_BLOCK_MAX )) { +/* +* SMBus 3.1 6.5.7: support count byte of 0. +* I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. +*/ +if (len >I2C_SMBUS_BLOCK_MAX ) + block_err = -EPROTO ; + temp =imx_i2c_read_reg (i2c_imx,IMX_I2C_I2CR ); + temp |=I2CR_TXAK ; +imx_i2c_write_reg (temp, i2c_imx,IMX_I2C_I2CR ); + msgs->buf[0 ] =0 ; + msgs->len =2 ; +continue ; + }dev_dbg (&i2c_imx->adapter.dev,"<%s> read length: 0x%X\n" , __func__, len);
@@ -1222,7 +1235,7 @@ static int i2c_imx_read(struct imx_i2c_s"<%s> read byte: B%d=0x%X\n" , __func__, i, msgs->buf[i]); } -return 0 ; +return block_err; } static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
Greg Kroah-Hartman
maintainerPATCH 125/272 can/esd_usb2: Rename esd_usb2.c to esd_usb.c
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Jungclaus <[email protected]>
[ Upstream commit 5e910bdedc84c1f196863cebdf27c1806449c27c ]
As suggested by Vincent, renaming of esd_usb2.c to esd_usb.c and according to that, adaption of Kconfig and Makfile, too.
Stable-dep-of: c43122fef328 ("can: esd_usb: kill anchored URBs before freeing netdevs")
---
drivers/net/can/usb/Kconfig | 15 +++++++++++----
drivers/net/can/usb/Makefile | 2 +-
drivers/net/can/usb/{esd_usb2.c => esd_usb.c} | 0
drivers/net/can/usb/Kconfig | 15
drivers/net/can/usb/Makefile | 2
drivers/net/can/usb/esd_usb.c | 1170 +++++++++++++++++++++++++++++++++++++++++
drivers/net/can/usb/esd_usb2.c | 1170 -----------------------------------------
4 files changed, 1182 insertions(+), 1175 deletions(-)
rename drivers/net/can/usb/{esd_usb2.c => esd_usb.c} (100%)drivers/net/can/usb/Kconfig +11 -4
@@ -14,11 +14,18 @@ config CAN_EMS_USB This driver is for the one channel CPC-USB/ARM7 CAN/USB interface from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). -config CAN_ESD_USB2 - tristate "ESD USB/2 CAN/USB interface" +config CAN_ESD_USB + tristate "esd electronics gmbh CAN/USB interfaces" help - This driver supports the CAN-USB/2 interface - from esd electronic system design gmbh (http://www.esd.eu). + This driver adds supports for several CAN/USB interfaces + from esd electronics gmbh (https://www.esd.eu). + + The drivers supports the following devices: + - esd CAN-USB/2 + - esd CAN-USB/Micro + + To compile this driver as a module, choose M here: the module + will be called esd_usb. config CAN_ETAS_ES58X tristate "ETAS ES58X CAN/USB interfaces"
drivers/net/can/usb/Makefile +1 -1
@@ -5,7 +5,7 @@ obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o -obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_ESD_USB) += esd_usb.o obj-$(CONFIG_CAN_ETAS_ES58X) += etas_es58x/ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/
drivers/net/can/usb/esd_usb.c new file +1170 -0
1,170 lines not on this page · raw message
drivers/net/can/usb/esd_usb2.c deleted +0 -1170
1,170 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 126/272 can: esd_usb: kill anchored URBs before freeing netdevs
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit c43122fef328a70045fe7621c06de6b2b8e19264 ]
esd_usb_disconnect() frees each CAN netdev with free_candev() inside its per-netdev loop and only calls unlink_all_urbs(dev) afterwards. The per-netdev private data (struct esd_usb_net_priv) is embedded in the net_device allocation returned by alloc_candev(), so once free_candev() has run, dev->nets[i] points to freed memory. unlink_all_urbs() then dereferences the freed dev->nets[i] to kill the per-netdev TX anchor (usb_kill_anchored_urbs(&priv->tx_submitted)), clear active_tx_jobs, and reset priv->tx_contexts[].
Reorder the teardown so the anchored URBs are killed before the netdevs are freed, matching other CAN/USB drivers in the same directory such as ems_usb, usb_8dev and mcba_usb, which unregister, then unlink, then
free: unregister the netdevs first (which stops their TX queues), call unlink_all_urbs(dev) once, then free the netdevs.
This issue was found by an in-house static analysis tool.
--- drivers/net/can/usb/esd_usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/can/usb/esd_usb.c +4 -1
@@ -1151,10 +1151,13 @@ static void esd_usb2_disconnect(struct uif (dev->nets[i]) { netdev = dev->nets[i]->netdev;unregister_netdev (netdev); -free_candev (netdev); } }unlink_all_urbs (dev); +for (i =0 ; i < dev->net_count; i++) { +if (dev->nets[i]) +free_candev (dev->nets[i]->netdev); + }kfree (dev); } }
Greg Kroah-Hartman
maintainerPATCH 127/272 thunderbolt: Remove usage of the deprecated ida_simple_xx() API
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christophe JAILLET <[email protected]>
[ Upstream commit dec6a613574cd3dea799170b7aaa8fd76e22f176 ]
ida_alloc() and ida_free() should be preferred to the deprecated ida_simple_get() and ida_simple_remove().
Note that the upper limit of ida_simple_get() is exclusive, but the one of ida_alloc_range()/ida_alloc_max() is inclusive. So a -1 has been added when needed.
Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
--- drivers/thunderbolt/domain.c | 6 +++--- drivers/thunderbolt/nhi.c | 6 +++--- drivers/thunderbolt/nvm.c | 4 ++-- drivers/thunderbolt/switch.c | 6 +++--- drivers/thunderbolt/xdomain.c | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-)
drivers/thunderbolt/domain.c +3 -3
@@ -329,7 +329,7 @@ static void tb_domain_release(struct devtb_ctl_free (tb->ctl);destroy_workqueue (tb->wq); -ida_simple_remove (&tb_domain_ida, tb->index); +ida_free (&tb_domain_ida, tb->index);mutex_destroy (&tb->lock);kfree (tb); }
@@ -397,7 +397,7 @@ struct tb *tb_domain_alloc(struct tb_nhi tb->nhi = nhi;mutex_init (&tb->lock); - tb->index =ida_simple_get (&tb_domain_ida,0 ,0 ,GFP_KERNEL ); + tb->index =ida_alloc (&tb_domain_ida,GFP_KERNEL );if (tb->index <0 ) goto err_free;
@@ -421,7 +421,7 @@ struct tb *tb_domain_alloc(struct tb_nhi err_destroy_wq:destroy_workqueue (tb->wq); err_remove_ida: -ida_simple_remove (&tb_domain_ida, tb->index); +ida_free (&tb_domain_ida, tb->index); err_free:kfree (tb);
drivers/thunderbolt/nhi.c +3 -3
@@ -458,7 +458,7 @@ static int ring_request_msix(struct tb_rif (!nhi->pdev->msix_enabled)return 0 ; - ret =ida_simple_get (&nhi->msix_ida,0 ,MSIX_MAX_VECS ,GFP_KERNEL ); + ret =ida_alloc_max (&nhi->msix_ida,MSIX_MAX_VECS -1 ,GFP_KERNEL );if (ret <0 )return ret;
@@ -478,7 +478,7 @@ static int ring_request_msix(struct tb_rreturn 0 ; err_ida_remove: -ida_simple_remove (&nhi->msix_ida, ring->vector); +ida_free (&nhi->msix_ida, ring->vector);return ret; }
@@ -489,7 +489,7 @@ static void ring_release_msix(struct tb_return ;free_irq (ring->irq, ring); -ida_simple_remove (&ring->nhi->msix_ida, ring->vector); +ida_free (&ring->nhi->msix_ida, ring->vector); ring->vector =0 ; ring->irq =0 ; }
drivers/thunderbolt/nvm.c +2 -2
@@ -30,7 +30,7 @@ struct tb_nvm *tb_nvm_alloc(struct devicif (!nvm)return ERR_PTR (-ENOMEM ); - ret =ida_simple_get (&nvm_ida,0 ,0 ,GFP_KERNEL ); + ret =ida_alloc (&nvm_ida,GFP_KERNEL );if (ret <0 ) {kfree (nvm);return ERR_PTR (ret);
@@ -159,7 +159,7 @@ void tb_nvm_free(struct tb_nvm *nvm)if (nvm->active)nvmem_unregister (nvm->active);vfree (nvm->buf); -ida_simple_remove (&nvm_ida, nvm->id); +ida_free (&nvm_ida, nvm->id); }kfree (nvm); }
drivers/thunderbolt/switch.c +3 -3
@@ -806,7 +806,7 @@ static int tb_port_alloc_hopid(struct tbif (max_hopid <0 || max_hopid > port_max_hopid) max_hopid = port_max_hopid; -return ida_simple_get (ida, min_hopid, max_hopid +1 ,GFP_KERNEL ); +return ida_alloc_range (ida, min_hopid, max_hopid,GFP_KERNEL ); }/**
@@ -844,7 +844,7 @@ int tb_port_alloc_out_hopid(struct tb_po*/ void tb_port_release_in_hopid (struct tb_port *port,int hopid) { -ida_simple_remove (&port->in_hopids, hopid); +ida_free (&port->in_hopids, hopid); }/**
@@ -854,7 +854,7 @@ void tb_port_release_in_hopid(struct tb_*/ void tb_port_release_out_hopid (struct tb_port *port,int hopid) { -ida_simple_remove (&port->out_hopids, hopid); +ida_free (&port->out_hopids, hopid); } static inline bool tb_switch_is_reachable(const struct tb_switch *parent,
drivers/thunderbolt/xdomain.c +2 -2
@@ -808,7 +808,7 @@ static void tb_service_release(struct destruct tb_xdomain *xd =tb_service_parent (svc);tb_service_debugfs_remove (svc); -ida_simple_remove (&xd->service_ids, svc->id); +ida_free (&xd->service_ids, svc->id);kfree (svc->key);kfree (svc); }
@@ -910,7 +910,7 @@ static void enumerate_services(struct tbbreak ; } - id =ida_simple_get (&xd->service_ids,0 ,0 ,GFP_KERNEL ); + id =ida_alloc (&xd->service_ids,GFP_KERNEL );if (id <0 ) {kfree (svc->key);kfree (svc);
Greg Kroah-Hartman
maintainerPATCH 128/272 thunderbolt: Update property.c function documentation
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alan Borzeszkowski <[email protected]>
[ Upstream commit d015642ad36d78e6eba12d8ab96cea6fd4602b49 ]
Make property.c function documentation compliant with current kernel-doc standards. No functional changes.
Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
--- drivers/thunderbolt/property.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-)
drivers/thunderbolt/property.c +28 -10
@@ -231,11 +231,13 @@ static struct tb_property_dir *__tb_prop* * This function parses the XDomain properties data block into format that * can be traversed using the helper functions provided by this module. -* Upon success returns the parsed directory. In case of error returns -* %NULL. The resulting &struct tb_property_dir needs to be released by +* +* The resulting &struct tb_property_dir needs to be released by * calling tb_property_free_dir() when not needed anymore. * * The @block is expected to be root directory. +* +* Return: Pointer to &struct tb_property_dir, %NULL in case of failure. */ struct tb_property_dir *tb_property_parse_dir (const u32 *block,size_t block_len)
@@ -258,6 +260,8 @@ struct tb_property_dir *tb_property_pars* * Creates new, empty property directory. If @uuid is %NULL then the * directory is assumed to be root directory. +* +* Return: Pointer to &struct tb_property_dir, %NULL in case of failure. */ struct tb_property_dir *tb_property_create_dir (const uuid_t *uuid) {
@@ -501,9 +505,11 @@ static ssize_t __tb_property_format_dir(* @block_len: Length of the property block * * This function formats the directory to the packed format that can be -* then send over the thunderbolt fabric to receiving host. Returns %0 in -* case of success and negative errno on faulure. Passing %NULL in @block -* returns number of entries the block takes. +* then sent over the thunderbolt fabric to receiving host. +* +* Passing %NULL in @block returns number of entries the block takes. +* +* Return: %0 on success, negative errno otherwise. */ ssize_t tb_property_format_dir (const struct tb_property_dir *dir,u32 *block,size_t block_len)
@@ -525,9 +531,9 @@ ssize_t tb_property_format_dir(const str* tb_property_copy_dir() - Take a deep copy of directory * @dir: Directory to copy * -* This function takes a deep copy of @dir and returns back the copy. In -* case of error returns %NULL. The resulting directory needs to be -* released by calling tb_property_free_dir(). +* The resulting directory needs to be released by calling tb_property_free_dir(). +* +* Return: Pointer to &struct tb_property_dir, %NULL in case of failure. */ struct tb_property_dir *tb_property_copy_dir (const struct tb_property_dir *dir) {
@@ -597,6 +603,8 @@ err_free:* @parent: Directory to add the property * @key: Key for the property * @value: Immediate value to store with the property +* +* Return: %0 on success, negative errno otherwise. */ int tb_property_add_immediate (struct tb_property_dir *parent,const char *key,u32 value)
@@ -626,6 +634,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_immedi* @buflen: Number of bytes in the data buffer * * Function takes a copy of @buf and adds it to the directory. +* +* Return: %0 on success, negative errno otherwise. */ int tb_property_add_data (struct tb_property_dir *parent,const char *key,const void *buf,size_t buflen)
@@ -662,6 +672,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_data);* @text: String to add * * Function takes a copy of @text and adds it to the directory. +* +* Return: %0 on success, negative errno otherwise. */ int tb_property_add_text (struct tb_property_dir *parent,const char *key,const char *text)
@@ -696,6 +708,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_text);* @parent: Directory to add the property * @key: Key for the property * @dir: Directory to add +* +* Return: %0 on success, negative errno otherwise. */ int tb_property_add_dir (struct tb_property_dir *parent,const char *key,struct tb_property_dir *dir)
@@ -736,8 +750,10 @@ EXPORT_SYMBOL_GPL(tb_property_remove);* @key: Key to look for * @type: Type of the property * -* Finds and returns property from the given directory. Does not recurse -* into sub-directories. Returns %NULL if the property was not found. +* Finds and returns property from the given directory. Does not +* recurse into sub-directories. +* +* Return: Pointer to &struct tb_property, %NULL if the property was not found. */ struct tb_property *tb_property_find (struct tb_property_dir *dir,const char *key,enum tb_property_type type)
@@ -757,6 +773,8 @@ EXPORT_SYMBOL_GPL(tb_property_find);* tb_property_get_next() - Get next property from directory * @dir: Directory holding properties * @prev: Previous property in the directory (%NULL returns the first) +* +* Return: Pointer to &struct tb_property, %NULL if property was not found. */ struct tb_property *tb_property_get_next (struct tb_property_dir *dir,struct tb_property *prev)
Greg Kroah-Hartman
maintainerPATCH 129/272 thunderbolt: Keep XDomain reference during the lifetime of a service
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mika Westerberg <[email protected]>
[ Upstream commit 8b4060998637f06975fceee9b73845d8672d411e ]
This is needed because we release the service ID in tb_service_release() and the ID array is owned by the parent XDomain.
Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
--- drivers/thunderbolt/xdomain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/thunderbolt/xdomain.c +2 -1
@@ -811,6 +811,7 @@ static void tb_service_release(struct deida_free (&xd->service_ids, svc->id);kfree (svc->key);kfree (svc); +tb_xdomain_put (xd); }struct device_type tb_service_type = {
@@ -919,7 +920,7 @@ static void enumerate_services(struct tb svc->id = id; svc->dev.bus = &tb_bus_type; svc->dev.type = &tb_service_type; - svc->dev.parent = &xd->dev; + svc->dev.parent =get_device (&xd->dev);dev_set_name (&svc->dev,"%s.%d" ,dev_name (&xd->dev), svc->id);tb_service_debugfs_init (svc);
Greg Kroah-Hartman
maintainerPATCH 130/272 thunderbolt: Remove service debugfs entries during unregister
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mika Westerberg <[email protected]>
[ Upstream commit 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 ]
We add them as part of the register path so to keep it symmetric remove them as part of the unregister path. This also removes them even if the service itself is not yet released (but is unregistered), thus allowing new register with the same service name to happen.
Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
--- drivers/thunderbolt/xdomain.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/thunderbolt/xdomain.c +11 -3
@@ -807,7 +807,6 @@ static void tb_service_release(struct de struct tb_service *svc = container_of(dev, struct tb_service, dev);struct tb_xdomain *xd =tb_service_parent (svc); -tb_service_debugfs_remove (svc);ida_free (&xd->service_ids, svc->id);kfree (svc->key);kfree (svc);
@@ -822,6 +821,14 @@ struct device_type tb_service_type = { };EXPORT_SYMBOL_GPL (tb_service_type); +static void __unregister_service (struct device *dev) +{ +struct tb_service *svc =tb_to_service (dev); + +tb_service_debugfs_remove (svc); +device_unregister (&svc->dev); +} +static int remove_missing_service (struct device *dev,void *data) {struct tb_xdomain *xd = data;
@@ -833,7 +840,7 @@ static int remove_missing_service(structif (!tb_property_find (xd->remote_properties, svc->key,TB_PROPERTY_TYPE_DIRECTORY )) -device_unregister (dev); +__unregister_service (dev);return 0 ; }
@@ -926,6 +933,7 @@ static void enumerate_services(struct tbtb_service_debugfs_init (svc);if (device_register (&svc->dev)) { +tb_service_debugfs_remove (svc);put_device (&svc->dev);break ; }
@@ -1462,7 +1470,7 @@ void tb_xdomain_add(struct tb_xdomain *xstatic int unregister_service (struct device *dev,void *data) { -device_unregister (dev); +__unregister_service (dev);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 131/272 thunderbolt: Remove XDomain from the bus without holding tb->lock
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mika Westerberg <[email protected]>
[ Upstream commit a8937f35cf39c39c64325aa84d0463d866850857 ]
Currently we call device_unregister() for services and the XDomain itself with tb->lock held. This prevents the service drivers from calling any functions that may take it. For this reason separate removing the XDomain from the topology data structures (where we need the lock) from unregistering the device from the bus (where remove callbacks of the drivers are being called).
Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
--- drivers/thunderbolt/domain.c | 30 +++++++++++++++++++++ drivers/thunderbolt/icm.c | 5 +++ drivers/thunderbolt/switch.c | 14 ++++++++++ drivers/thunderbolt/tb.c | 58 +++++++++++++++++++++--------------------- drivers/thunderbolt/tb.h | 2 + drivers/thunderbolt/xdomain.c | 51 +++++++++++++++++++++++------------- 6 files changed, 113 insertions(+), 47 deletions(-)
drivers/thunderbolt/domain.c +30 -0
@@ -874,6 +874,36 @@ int tb_domain_disconnect_all_paths(strucreturn bus_for_each_dev (&tb_bus_type,NULL , tb, disconnect_xdomain); } +struct unregister_context { +const struct tb *tb; +int n; +}; + +static int unregister_unplugged_xdomain (struct device *dev,void *data) +{ +struct unregister_context *ctx = data; +struct tb_xdomain *xd; + + xd =tb_to_xdomain (dev); +if (xd && xd->tb == ctx->tb && xd->is_unplugged) { +tb_xdomain_unregister (xd); + ctx->n++; + } +return 0 ; +} + +int tb_domain_unregister_unplugged_xdomains (struct tb *tb) +{ +struct unregister_context ctx; + + ctx.tb =tb_domain_get (tb); + ctx.n =0 ; +bus_for_each_dev (&tb_bus_type,NULL , &ctx, unregister_unplugged_xdomain); +tb_domain_put (tb); + +return ctx.n; +} +int tb_domain_init (void ) {int ret;
drivers/thunderbolt/icm.c +5 -0
@@ -715,6 +715,7 @@ static void remove_xdomain(struct tb_xdo sw =tb_to_switch (xd->dev.parent);tb_port_at (xd->route, sw)->xdomain =NULL ; + xd->is_unplugged = true;tb_xdomain_remove (xd); }
@@ -1730,6 +1731,8 @@ static void icm_handle_notification(strukfree (n->pkg);kfree (n); + +tb_domain_unregister_unplugged_xdomains (tb); } static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
@@ -2075,6 +2078,8 @@ static void icm_rescan_work(struct work_if (tb->root_switch)icm_free_unplugged_children (tb->root_switch);mutex_unlock (&tb->lock); + +tb_domain_unregister_unplugged_xdomains (tb); }static void icm_complete (struct tb *tb)
drivers/thunderbolt/switch.c +14 -0
@@ -3035,6 +3035,20 @@ int tb_switch_resume(struct tb_switch *stb_port_warn (port,"lost during suspend, disconnecting\n" );tb_sw_set_unplugged (port->remote->sw); + }else if (port->xdomain) { +/* +* If the user replaced the XDomain with +* another router, this will succeed in +* which case we must remove the XDomain +* before adding the new router. +*/ + err =tb_cfg_get_upstream_port (sw->tb->ctl, + port->xdomain->route); +if (err >0 ) { +tb_port_warn (port, +"XDomain was disconnected\n" ); + port->xdomain->is_unplugged = true; + } } } }
drivers/thunderbolt/tb.c +29 -29
100 lines not on this page · raw message
drivers/thunderbolt/tb.h +2 -0
@@ -700,6 +700,7 @@ int tb_domain_disconnect_xdomain_paths(s int transmit_path, int transmit_ring, int receive_path, int receive_ring);int tb_domain_disconnect_all_paths (struct tb *tb); +int tb_domain_unregister_unplugged_xdomains (struct tb *tb);static inline struct tb *tb_domain_get (struct tb *tb) {
@@ -1025,6 +1026,7 @@ struct tb_xdomain *tb_xdomain_alloc(stru const uuid_t *remote_uuid);void tb_xdomain_add (struct tb_xdomain *xd);void tb_xdomain_remove (struct tb_xdomain *xd); +void tb_xdomain_unregister (struct tb_xdomain *xd);struct tb_xdomain *tb_xdomain_find_by_link_depth (struct tb *tb,u8 link,u8 depth);
drivers/thunderbolt/xdomain.c +33 -18
70 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 132/272 thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
tb_xdp_handle_request() runs on system_wq and queues xd->state_work via queue_delayed_work() in three request handlers:PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake), and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues xd->properties_changed_work when local properties change.
Concurrently, tb_xdomain_remove() calls stop_handshake() which does cancel_delayed_work_sync() on both delayed works. Later, tb_xdomain_unregister() calls device_unregister() which eventually frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue") moved the request handler off tb->wq, the handler and the remove path are no longer serialized. If queue_delayed_work() executes after cancel_delayed_work_sync() but before the xdomain is freed, the delayed work fires on a freed object.
Add xd->removing that tb_xdomain_remove() sets under xd->lock before calling stop_handshake(). Each external queue site holds the same lock and checks removing before calling queue_delayed_work(). This provides the mutual exclusion needed:
either the queue site acquires the lock first and queues work that the subsequent cancel will see, or the remove path acquires the lock first and the queue site observes removing == true and skips the queue.
--- drivers/thunderbolt/xdomain.c | 22 +++++++++++++++++----- include/linux/thunderbolt.h | 3 +++ 2 files changed, 20 insertions(+), 5 deletions(-)
drivers/thunderbolt/xdomain.c +17 -5
@@ -630,9 +630,13 @@ static void tb_xdp_handle_request(struct* the xdomain related to this connection as well in * case there is a change in services it offers. */ -if (xd &&device_is_registered (&xd->dev)) { -queue_delayed_work (tb->wq, &xd->get_properties_work, -msecs_to_jiffies (50 )); +if (xd) { +mutex_lock (&xd->lock); +if (!xd->removing &&device_is_registered (&xd->dev)) +queue_delayed_work (tb->wq, + &xd->get_properties_work, +msecs_to_jiffies (50 )); +mutex_unlock (&xd->lock); }break ;
@@ -1486,6 +1490,10 @@ static int unregister_service(struct dev*/ void tb_xdomain_remove (struct tb_xdomain *xd) { +mutex_lock (&xd->lock); + xd->removing = true; +mutex_unlock (&xd->lock); +stop_handshake (xd);if (!device_is_registered (&xd->dev)) {
@@ -1885,8 +1893,12 @@ static int update_xdomain(struct device xd =tb_to_xdomain (dev);if (xd) { -queue_delayed_work (xd->tb->wq, &xd->properties_changed_work, -msecs_to_jiffies (50 )); +mutex_lock (&xd->lock); +if (!xd->removing) +queue_delayed_work (xd->tb->wq, + &xd->properties_changed_work, +msecs_to_jiffies (50 )); +mutex_unlock (&xd->lock); }return 0 ;
include/linux/thunderbolt.h +3 -0
@@ -188,6 +188,8 @@ void tb_unregister_property_dir(const ch* @link_speed: Speed of the link in Gb/s * @link_width: Width of the link (1 or 2) * @is_unplugged: The XDomain is unplugged +* @removing: Set by tb_xdomain_remove() under @lock to prevent +* concurrent delayed work queueing * @needs_uuid: If the XDomain does not have @remote_uuid it will be * queried first * @service_ids: Used to generate IDs for the services
@@ -235,6 +237,7 @@ struct tb_xdomain {unsigned int link_speed;unsigned int link_width;bool is_unplugged; +bool removing;bool needs_uuid;struct ida service_ids;struct ida in_hopids;
Greg Kroah-Hartman
maintainerPATCH 133/272 bpf,fork: wipe ->bpf_storage before bailouts that access it
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
[ Upstream commit 9b51a6155d14389876916726430da30eabb1d4ed ]
Currently, copy_process() can bail out to free_task() before p->bpf_storage has been initialized, with this call graph (shown here for the !CONFIG_MEMCG case):
copy_process
dup_task_struct
arch_dup_task_struct
[copies the entire task_struct, including ->bpf_storage member]
[RLIMIT_NPROC check fails]
delayed_free_task
free_task
bpf_task_storage_free
rcu_dereference(task->bpf_storage)
bpf_local_storage_destroyIn this case, the nascent task's ->bpf_storage member that bpf_local_storage_destroy() operates on is a plain copy of the parent's ->bpf_storage pointer, not a real initialized pointer.
This leads to badness (kernel hangs, UAF).
This is reachable as long as the process calling fork() has been inserted into a task storage map.
--- kernel/fork.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/fork.c +5 -4
@@ -983,6 +983,11 @@ static struct task_struct *dup_task_stru#ifdef CONFIG_MEMCG tsk->active_memcg =NULL ;#endif + +#ifdef CONFIG_BPF_SYSCALL +RCU_INIT_POINTER (tsk->bpf_storage,NULL ); + tsk->bpf_ctx =NULL ; +#endif return tsk; free_stack:
@@ -2252,10 +2257,6 @@ static __latent_entropy struct task_stru p->sequential_io =0 ; p->sequential_io_avg =0 ;#endif -#ifdef CONFIG_BPF_SYSCALL -RCU_INIT_POINTER (p->bpf_storage,NULL ); - p->bpf_ctx =NULL ; -#endif /* Perform scheduler related setup. Assign this task to a CPU. */ retval =sched_fork (clone_flags, p);
Greg Kroah-Hartman
maintainerPATCH 134/272 ovl: use linked upper dentry in copy-up tmpfile
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Souvik Banerjee <[email protected]>
[ Upstream commit e348eecd4d8fa8d18a5157ff59f7be1dc59c5928 ]
ovl_copy_up_tmpfile() stores the disconnected O_TMPFILE dentry as the overlay's upper dentry reference via ovl_inode_update(). vfs_tmpfile() allocated this dentry via d_alloc(parentpath->dentry, &slash_name), so d_name is "/" and d_parent is c->workdir. Local upper filesystems (ext4, btrfs, xfs, ...) immediately rename it to "#<inum>" via d_mark_tmpfile() inside their ->tmpfile() op; FUSE and virtiofs do not, so both fields stay that way. Neither identifies the destination directory and filename where ovl_do_link() actually linked the file.
When the upper filesystem implements ->d_revalidate() (e.g. FUSE or virtiofs), ovl_revalidate_real() calls it with the dentry's parent inode and a snapshot of d_name. The server tries to look up "/" inside c->workdir, fails, and overlayfs reports -ESTALE.
This causes persistent ESTALE errors for any file that was copied up via the tmpfile path, breaking dpkg, apt, and other tools that do rename-over-existing on overlayfs with a FUSE/virtiofs upper.
Before commit 6b52243f633e ("ovl: fold copy-up helpers into callers"), the tmpfile copy-up path used a dedicated helper ovl_link_tmpfile() that captured the linked destination dentry returned by ovl_do_link():
err = ovl_do_link(temp, udir, upper);
...
if (!err)
*newdentry = dget(upper);and published it via ovl_inode_update(d_inode(c->dentry), newdentry). The fold inlined ovl_do_link() into ovl_copy_up_tmpfile() but dropped the dget(upper) capture, and rewrote the publish line as ovl_inode_update(d_inode(c->dentry), dget(temp)) — where temp is the disconnected O_TMPFILE dentry.
Fix by keeping a reference to the linked destination dentry after ovl_do_link() succeeds, and publishing that dentry at the existing ovl_inode_update() call site. The non-tmpfile/workdir path continues to publish the renamed temporary dentry.
Reproducer:
- Mount overlayfs with virtiofs (or a FUSE fs whose server advertises FUSE_TMPFILE) as upper
- Run: dpkg -i <any .deb>
- Observe: "error installing new file '...': Stale file handle"
--- fs/overlayfs/copy_up.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
fs/overlayfs/copy_up.c +10 -4
@@ -730,7 +730,7 @@ static int ovl_copy_up_tmpfile(struct ov {struct ovl_fs *ofs =OVL_FS (c->dentry->d_sb);struct inode *udir =d_inode (c->destdir); -struct dentry *temp, *upper; +struct dentry *temp, *upper, *newdentry =NULL ;struct ovl_cu_creds cc;int err;
@@ -754,6 +754,14 @@ static int ovl_copy_up_tmpfile(struct ov err =PTR_ERR (upper);if (!IS_ERR (upper)) { err =ovl_do_link (ofs, temp, udir, upper); +if (!err) { +/* +* Record the linked dentry -- not the disconnected +* O_TMPFILE dentry -- so that ->d_revalidate() on +* the upper fs sees the real parent/name. +*/ + newdentry =dget (upper); + }dput (upper); }inode_unlock (udir);
@@ -763,9 +771,7 @@ static int ovl_copy_up_tmpfile(struct ovif (!c->metacopy)ovl_set_upperdata (d_inode (c->dentry)); -ovl_inode_update (d_inode (c->dentry), temp); - -return 0 ; +ovl_inode_update (d_inode (c->dentry), newdentry); out_dput:dput (temp);
Greg Kroah-Hartman
maintainerPATCH 135/272 scsi: lpfc: Fix memory leak in lpfc_sli4_driver_resource_setup()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
[ Upstream commit 1bd28625e25be549ee7c47532e7c3ef91c682410 ]
The memory allocated for mboxq using mempool_alloc() is not freed in some of the early exit error paths. Fix that by moving the mempool_free() call to an earlier point after last use.
--- drivers/scsi/lpfc/lpfc_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/scsi/lpfc/lpfc_init.c +1 -2
@@ -8010,6 +8010,7 @@ lpfc_sli4_driver_resource_setup(struct l goto out_free_bsmbx; } } +mempool_free (mboxq, phba->mbox_mem_pool);/* * 1 for cmd, 1 for rsp, NVME adds an extra one
@@ -8131,8 +8132,6 @@ lpfc_sli4_driver_resource_setup(struct lif (!phba->lpfc_cmd_rsp_buf_pool) goto out_free_sg_dma_buf; -mempool_free (mboxq, phba->mbox_mem_pool); -/* Verify OAS is supported */ lpfc_sli4_oas_verify (phba);
Greg Kroah-Hartman
maintainerPATCH 136/272 dm-integrity: dont increment hash_offset twice
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikulas Patocka <[email protected]>
[ Upstream commit edf025f083854f80032b73a1aad69a3c90db236f ]
hash_offset is already incremented in the loop "for (i = 0; i < to_copy; i++, ts--)". Do not increment it again.
--- drivers/md/dm-integrity.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/md/dm-integrity.c +0 -4
@@ -1474,10 +1474,6 @@ thorough_test: *metadata_offset =0 ; } -if (unlikely (!is_power_of_2 (ic->tag_size))) { - hash_offset = (hash_offset + to_copy) % ic->tag_size; - } - total_size -= to_copy; }while (unlikely (total_size));
Greg Kroah-Hartman
maintainerPATCH 137/272 dm-verity: make error counter atomic
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikulas Patocka <[email protected]>
[ Upstream commit 8ec4d9c5a5cf4b61fc087f871465b1f79b393325 ]
The error counter "v->corrupted_errs" was not atomic, thus it could be subject to race conditions. The call to dm_audit_log_target("max-corrupted-errors") may be skipped due to the races.
--- drivers/md/dm-verity-target.c | 12 +++++++----- drivers/md/dm-verity.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-)
drivers/md/dm-verity-target.c +7 -5
@@ -217,14 +217,16 @@ static int verity_handle_err(struct dm_vchar *envp[] = { verity_env,NULL };const char *type_str ="" ;struct mapped_device *md =dm_table_get_md (v->ti->table); +int ce;/* Corruption should be visible in device status in all modes */ v->hash_failed =1 ; -if (v->corrupted_errs >=DM_VERITY_MAX_CORRUPTED_ERRS ) - goto out; - - v->corrupted_errs++; + ce =atomic_read (&v->corrupted_errs); +do { +if (ce >=DM_VERITY_MAX_CORRUPTED_ERRS ) + goto out; + }while (!atomic_try_cmpxchg (&v->corrupted_errs, &ce, ce +1 ));switch (type) {case DM_VERITY_BLOCK_TYPE_DATA :
@@ -240,7 +242,7 @@ static int verity_handle_err(struct dm_vDMERR_LIMIT ("%s: %s block %llu is corrupted" , v->data_dev->name, type_str, block); -if (v->corrupted_errs ==DM_VERITY_MAX_CORRUPTED_ERRS ) +if (ce +1 ==DM_VERITY_MAX_CORRUPTED_ERRS )DMERR ("%s: reached maximum errors" , v->data_dev->name); snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
drivers/md/dm-verity.h +1 -1
@@ -55,7 +55,7 @@ struct dm_verity {unsigned int ahash_reqsize;/* the size of temporary space for crypto */ int hash_failed;/* set to 1 if hash of any block failed */ enum verity_mode mode;/* mode for handling verification errors */ -unsigned corrupted_errs;/* Number of errors for corrupted blocks */ +atomic_t corrupted_errs;/* Number of errors for corrupted blocks */ struct workqueue_struct *verify_wq;
Greg Kroah-Hartman
maintainerPATCH 138/272 firmware_loader: introduce __free() cleanup hanler
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
[ Upstream commit 8dde8fa0cc3edce73c050b9882d06c1a575f6402 ]
Define cleanup handler using facilities from linux/cleanup.h to simplify error handling in code using firmware loader. This will allow writing code like this:
int driver_update_firmware(...)
{
const struct firmware *fw_entry __free(firmware) = NULL;
int error;
...
error = request_firmware(&fw_entry, fw_name, dev);
if (error) {
dev_err(dev, "failed to request firmware %s: %d",
fw_name, error);
return error;
}
error = check_firmware_valid(fw_entry);
if (error)
return error;
guard(mutex)(&instance->lock);
error = use_firmware(instance, fw);
if (error)
return error;
return 0;
}Stable-dep-of: d48795b5cd68 ("Input: ims-pcu - fix firmware leak in async update")
--- include/linux/firmware.h | 3 +++ 1 file changed, 3 insertions(+)
include/linux/firmware.h +3 -0
@@ -4,6 +4,7 @@#include <linux/types.h> #include <linux/compiler.h> +#include <linux/cleanup.h> #include <linux/gfp.h> #define FW_ACTION_NOUEVENT 0
@@ -118,4 +119,6 @@ static inline int request_partial_firmwaint firmware_request_cache (struct device *device,const char *name); +DEFINE_FREE(firmware, struct firmware *, release_firmware(_T)) +#endif
Greg Kroah-Hartman
maintainerPATCH 139/272 Input: ims-pcu - fix firmware leak in async update
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
[ Upstream commit d48795b5cd6828d36b707e8d62fc9e5c90e004ab ]
The firmware object was not being released if validation failed. Use __free(firmware) to ensure the firmware is always released.
--- drivers/input/misc/ims-pcu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/input/misc/ims-pcu.c +2 -3
@@ -964,9 +964,10 @@ out:return retval; } -static void ims_pcu_process_async_firmware (const struct firmware *fw, +static void ims_pcu_process_async_firmware (const struct firmware *_fw,void *context) { + const struct firmware *fw __free(firmware) = _fw;struct ims_pcu *pcu = context;int error;
@@ -987,8 +988,6 @@ static void ims_pcu_process_async_firmwaims_pcu_handle_firmware_update (pcu, fw);mutex_unlock (&pcu->cmd_mutex); -release_firmware (fw); - out:complete (&pcu->async_firmware_done); }
Greg Kroah-Hartman
maintainerPATCH 140/272 mmc: vub300: fix use-after-free on disconnect
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
The vub300 driver maintains an explicit reference count for the controller and its driver data and the last reference can in theory be dropped after the driver has been unbound.
This specifically means that the controller allocation must not be device managed as that can lead to use-after-free.
Note that the lifetime is currently also incorrectly tied the parent USB device rather than interface, which can lead to memory leaks if the driver is unbound without its device being physically disconnected (e.g. on probe deferral).
Fix both issues by reverting to non-managed allocation of the controller.
--- drivers/mmc/host/vub300.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/mmc/host/vub300.c +6 -6
@@ -2281,7 +2281,7 @@ static int vub300_probe(struct usb_interdev_err (&vub300->udev->dev,"Could not find two sets of bulk-in/out endpoint pairs\n" ); retval = -EINVAL ; - goto error5; + goto err_free_host; } retval = usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
@@ -2290,14 +2290,14 @@ static int vub300_probe(struct usb_inter0x0000 ,0x0000 , &vub300->hc_info, sizeof(vub300->hc_info), 1000);if (retval <0 ) - goto error5; + goto err_free_host; retval =usb_control_msg (vub300->udev,usb_sndctrlpipe (vub300->udev,0 ),SET_ROM_WAIT_STATES ,USB_DIR_OUT |USB_TYPE_VENDOR |USB_RECIP_DEVICE , firmware_rom_wait_states,0x0000 ,NULL ,0 ,1000 );if (retval <0 ) - goto error5; + goto err_free_host; dev_info(&vub300->udev->dev, "operating_mode = %s %s %d MHz %s %d byte USB packets\n", (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL",
@@ -2312,7 +2312,7 @@ static int vub300_probe(struct usb_inter0x0000 ,0x0000 , &vub300->system_port_status, sizeof(vub300->system_port_status), 1000);if (retval <0 ) { - goto error5; + goto err_free_host; }else if (sizeof (vub300->system_port_status) == retval) { vub300->card_present = (0x0001 & vub300->system_port_status.port_flags) ?1 :0 ;
@@ -2320,7 +2320,7 @@ static int vub300_probe(struct usb_inter (0x0010 & vub300->system_port_status.port_flags) ?1 :0 ; }else { retval = -EINVAL ; - goto error5; + goto err_free_host; }usb_set_intfdata (interface, vub300);INIT_DELAYED_WORK (&vub300->pollwork, vub300_pollwork_thread);
@@ -2350,7 +2350,7 @@ static int vub300_probe(struct usb_interreturn 0 ; error6:del_timer_sync (&vub300->inactivity_timer); -error5: +err_free_host:mmc_free_host (mmc);/* * and hence also frees vub300
Greg Kroah-Hartman
maintainerPATCH 141/272 mmc: vub300: rename probe error labels
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
Error labels should be named after what they do.
Rename the probe error labels.
--- drivers/mmc/host/vub300.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
drivers/mmc/host/vub300.c +11 -9
@@ -2116,19 +2116,19 @@ static int vub300_probe(struct usb_inter command_out_urb =usb_alloc_urb (0 ,GFP_KERNEL );if (!command_out_urb) { retval = -ENOMEM ; - goto error0; + goto err_put_udev; } command_res_urb =usb_alloc_urb (0 ,GFP_KERNEL );if (!command_res_urb) { retval = -ENOMEM ; - goto error1; + goto err_free_out_urb; }/* this also allocates memory for our VUB300 mmc host device */ mmc =mmc_alloc_host (sizeof (struct vub300_mmc_host ), &udev->dev);if (!mmc) { retval = -ENOMEM ;dev_err (&udev->dev,"not enough memory for the mmc_host\n" ); - goto error4; + goto err_free_res_urb; }/* MMC core transfer sizes tunable parameters */ mmc->caps =0 ;
@@ -2345,23 +2345,25 @@ static int vub300_probe(struct usb_inter interface_to_InterfaceNumber(interface)); retval =mmc_add_host (mmc);if (retval) - goto error6; + goto err_delete_timer;return 0 ; -error6: -del_timer_sync (&vub300->inactivity_timer); + +err_delete_timer: +timer_delete_sync (&vub300->inactivity_timer); err_free_host:mmc_free_host (mmc);/* * and hence also frees vub300 * which is contained at the end of struct mmc */ -error4: +err_free_res_urb:usb_free_urb (command_res_urb); -error1: +err_free_out_urb:usb_free_urb (command_out_urb); -error0: +err_put_udev:usb_put_dev (udev); +return retval; }
Greg Kroah-Hartman
maintainerPATCH 142/272 mmc: vub300: fix use-after-free on probe failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
The vub300 driver lifetime-manages its controller state using vub300->kref, with vub300_delete() freeing the mmc host when the last reference is dropped. The probe error path after the inactivity timer has been armed still bypasses that lifetime rule, however, and falls through to mmc_free_host() directly if mmc_add_host() fails.
The race window is between arming the inactivity timer and reaching the probe error unwind after mmc_add_host() fails:
probe thread timer/workqueue
------------ ---------------
kref_init(&vub300->kref) ref = 1
kref_get(&vub300->kref) ref = 2, timer ref
add_timer(inactivity_timer) fires after one second
|
| race window
|<---------------------------------------------------->
| mmc_add_host(mmc)
inactivity timer fires vub300_queue_dead_work()
kref_get() ref = 3
queue_work(deadwork)
mmc_add_host() fails
timer_delete_sync()
mmc_free_host(mmc)
frees vub300
deadwork runs
use-after-free
The inactivity timeout is one second, so this would require mmc_add_host() to both fail and take more than one second to do so. This is unlikely to happen in practice, but the error path is still wrong.
timer_delete_sync() only waits for the timer callback itself. It does not flush deadwork that the callback may already have queued. As a result, queued deadwork can still hold a kref while the probe error path directly frees the backing mmc host, including the vub300 storage.
Fix this by using the same lifetime mechanism as disconnect. Clear vub300->interface so that the timer callback and any queued deadwork return early and drop their references, then drop the initial probe reference and return without falling through to err_free_host.
--- drivers/mmc/host/vub300.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/mmc/host/vub300.c +7 -3
@@ -2345,12 +2345,16 @@ static int vub300_probe(struct usb_inter interface_to_InterfaceNumber(interface)); retval =mmc_add_host (mmc);if (retval) - goto err_delete_timer; + goto err_stop_io;return 0 ; -err_delete_timer: -timer_delete_sync (&vub300->inactivity_timer); +err_stop_io: + vub300->interface =NULL ; +kref_put (&vub300->kref, vub300_delete); + +return retval; + err_free_host:mmc_free_host (mmc);/*
Greg Kroah-Hartman
maintainerPATCH 143/272 locking/rt: Fix the incorrect RCU protection in rt_spin_unlock()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
[ Upstream commit 89038cc87d80c77e7aa6f42a64b2573b74af339f ]
rt_spin_unlock() releases the RCU protection before unlocking the lock. That opens the door for the following UAF scenario:
T1 T2
spin_lock(&p->lock); rcu_read_lock();
invalidate(p); p = rcu_dereference(ptr);
rcu_assign_pointer(ptr, NULL); if (!p) return;
spin_unlock(&p->lock); spin_lock(&p->lock)
lock(&lock->lock); rcu_read_lock(); kfree_rcu(p); rcu_read_unlock(); .... spin_unlock(&p->lock) rcu_read_unlock(); // Ends grace period rcu_do_batch() kfree(p); UAF -> rt_mutex_cmpxchg_release(&lock->lock...)
Regular spinlocks keep preemption disabled accross the unlock operation, which provides full RCU protection, but the RT substitution fails to resemble that. Same applies for the rwlock substitution.
Move the rcu_read_unlock() invocation past the unlock operations to match the non-RT semantics. This makes it asymmetric vs. rt_xxx_lock(), but that's harmless as the caller needs to hold RCU read lock across the lock operation. The migrate_enable() call stays before the unlock operation because there is no per CPU operation in the unlock path which would require migration to be kept disabled.
--- kernel/locking/spinlock_rt.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-)
kernel/locking/spinlock_rt.c +24 -3
@@ -66,10 +66,27 @@ void __sched rt_spin_unlock(spinlock_t * {spin_release (&lock->dep_map, _RET_IP_);migrate_enable (); -rcu_read_unlock ();if (unlikely (!rt_mutex_cmpxchg_release (&lock->lock, current,NULL )))rt_mutex_slowunlock (&lock->lock); + +/* +* This must be last to prevent the following UAF: +* +* T1 T2 +* spin_lock(&p->lock); rcu_read_lock(); +* invalidate(p); p = rcu_dereference(ptr); +* rcu_assign_pointer(ptr, NULL); if (!p) return; +* spin_unlock(&p->lock); spin_lock(&p->lock); +* kfree_rcu(p); rcu_read_unlock(); +* .... +* spin_unlock(&p->lock) +* rcu_read_unlock(); // Ends grace period +* rcu_do_batch() +* kfree(p); +* UAF -> rt_mutex_cmpxchg_release(&p->lock.lock...) +*/ +rcu_read_unlock (); }EXPORT_SYMBOL (rt_spin_unlock);
@@ -232,17 +249,21 @@ void __sched rt_read_unlock(rwlock_t *rw {rwlock_release (&rwlock->dep_map, _RET_IP_);migrate_enable (); -rcu_read_unlock ();rwbase_read_unlock (&rwlock->rwbase,TASK_RTLOCK_WAIT ); + +/* This must be last. See comment in rt_spin_unlock() */ +rcu_read_unlock (); }EXPORT_SYMBOL (rt_read_unlock); void __sched rt_write_unlock(rwlock_t *rwlock) {rwlock_release (&rwlock->dep_map, _RET_IP_); -rcu_read_unlock ();migrate_enable ();rwbase_write_unlock (&rwlock->rwbase); + +/* This must be last. See comment in rt_spin_unlock() */ +rcu_read_unlock (); }EXPORT_SYMBOL (rt_write_unlock);
Greg Kroah-Hartman
maintainerPATCH 144/272 net: Add helper function to parse netlink msg of ip_tunnel_encap
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liu Jian <[email protected]>
[ Upstream commit 537dd2d9fb9f4aa7939fb4fcf552ebe4f497bd7e ]
Add ip_tunnel_netlink_encap_parms to parse netlink msg of ip_tunnel_encap. Reduces duplicate code, no actual functional changes.
Stable-dep-of: 8211a2632466 ("net: ipip: require CAP_NET_ADMIN in the device netns for changelink")
--- include/net/ip_tunnels.h | 3 +++ net/ipv4/ip_tunnel_core.c | 35 +++++++++++++++++++++++++++++++++++ net/ipv4/ipip.c | 38 ++------------------------------------ net/ipv6/ip6_tunnel.c | 37 ++----------------------------------- net/ipv6/sit.c | 38 ++------------------------------------ 5 files changed, 44 insertions(+), 107 deletions(-)
include/net/ip_tunnels.h +3 -0
@@ -296,6 +296,9 @@ int ip_tunnel_newlink(struct net_device struct ip_tunnel_parm *p, __u32 fwmark);void ip_tunnel_setup (struct net_device *dev,unsigned int net_id); +bool ip_tunnel_netlink_encap_parms (struct nlattr *data[], +struct ip_tunnel_encap *encap); +extern const struct header_ops ip_tunnel_header_ops;__be16 ip_tunnel_parse_protocol (const struct sk_buff *skb);
net/ipv4/ip_tunnel_core.c +35 -0
@@ -1091,3 +1091,38 @@ EXPORT_SYMBOL(ip_tunnel_parse_protocol);const struct header_ops ip_tunnel_header_ops = { .parse_protocol = ip_tunnel_parse_protocol };EXPORT_SYMBOL (ip_tunnel_header_ops); + +/* This function returns true when ENCAP attributes are present in the nl msg */ +bool ip_tunnel_netlink_encap_parms (struct nlattr *data[], +struct ip_tunnel_encap *encap) +{ +bool ret = false; + +memset (encap,0 ,sizeof (*encap)); + +if (!data) +return ret; + +if (data[IFLA_IPTUN_ENCAP_TYPE ]) { + ret = true; + encap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); + } + +if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { + ret = true; + encap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); + } + +if (data[IFLA_IPTUN_ENCAP_SPORT ]) { + ret = true; + encap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); + } + +if (data[IFLA_IPTUN_ENCAP_DPORT ]) { + ret = true; + encap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); + } + +return ret; +} +EXPORT_SYMBOL_GPL (ip_tunnel_netlink_encap_parms);
net/ipv4/ipip.c +2 -36
@@ -448,40 +448,6 @@ static void ipip_netlink_parms(struct nl *fwmark =nla_get_u32 (data[IFLA_IPTUN_FWMARK ]); } -/* This function returns true when ENCAP attributes are present in the nl msg */ -static bool ipip_netlink_encap_parms (struct nlattr *data[], -struct ip_tunnel_encap *ipencap) -{ -bool ret = false; - -memset (ipencap,0 ,sizeof (*ipencap)); - -if (!data) -return ret; - -if (data[IFLA_IPTUN_ENCAP_TYPE ]) { - ret = true; - ipencap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); - } - -if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { - ret = true; - ipencap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); - } - -if (data[IFLA_IPTUN_ENCAP_SPORT ]) { - ret = true; - ipencap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); - } - -if (data[IFLA_IPTUN_ENCAP_DPORT ]) { - ret = true; - ipencap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); - } - -return ret; -} -static int ipip_newlink (struct net *src_net,struct net_device *dev,struct nlattr *tb[],struct nlattr *data[],struct netlink_ext_ack *extack)
@@ -491,7 +457,7 @@ static int ipip_newlink(struct net *src_struct ip_tunnel_encap ipencap;__u32 fwmark =0 ; -if (ipip_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip_tunnel_encap_setup (t, &ipencap);if (err <0 )
@@ -512,7 +478,7 @@ static int ipip_changelink(struct net_debool collect_md;__u32 fwmark = t->fwmark; -if (ipip_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip_tunnel_encap_setup (t, &ipencap);if (err <0 )
net/ipv6/ip6_tunnel.c +2 -35
@@ -2008,39 +2008,6 @@ static void ip6_tnl_netlink_parms(struct parms->fwmark =nla_get_u32 (data[IFLA_IPTUN_FWMARK ]); } -static bool ip6_tnl_netlink_encap_parms (struct nlattr *data[], -struct ip_tunnel_encap *ipencap) -{ -bool ret = false; - -memset (ipencap,0 ,sizeof (*ipencap)); - -if (!data) -return ret; - -if (data[IFLA_IPTUN_ENCAP_TYPE ]) { - ret = true; - ipencap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); - } - -if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { - ret = true; - ipencap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); - } - -if (data[IFLA_IPTUN_ENCAP_SPORT ]) { - ret = true; - ipencap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); - } - -if (data[IFLA_IPTUN_ENCAP_DPORT ]) { - ret = true; - ipencap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); - } - -return ret; -} -static int ip6_tnl_newlink (struct net *src_net,struct net_device *dev,struct nlattr *tb[],struct nlattr *data[],struct netlink_ext_ack *extack)
@@ -2053,7 +2020,7 @@ static int ip6_tnl_newlink(struct net *s nt =netdev_priv (dev); -if (ip6_tnl_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) { err =ip6_tnl_encap_setup (nt, &ipencap);if (err <0 )return err;
@@ -2090,7 +2057,7 @@ static int ip6_tnl_changelink(struct netif (dev == ip6n->fb_tnl_dev)return -EINVAL ; -if (ip6_tnl_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip6_tnl_encap_setup (t, &ipencap);if (err <0 )
net/ipv6/sit.c +2 -36
@@ -1545,40 +1545,6 @@ static void ipip6_netlink_parms(struct n *fwmark =nla_get_u32 (data[IFLA_IPTUN_FWMARK ]); } -/* This function returns true when ENCAP attributes are present in the nl msg */ -static bool ipip6_netlink_encap_parms (struct nlattr *data[], -struct ip_tunnel_encap *ipencap) -{ -bool ret = false; - -memset (ipencap,0 ,sizeof (*ipencap)); - -if (!data) -return ret; - -if (data[IFLA_IPTUN_ENCAP_TYPE ]) { - ret = true; - ipencap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); - } - -if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { - ret = true; - ipencap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); - } - -if (data[IFLA_IPTUN_ENCAP_SPORT ]) { - ret = true; - ipencap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); - } - -if (data[IFLA_IPTUN_ENCAP_DPORT ]) { - ret = true; - ipencap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); - } - -return ret; -} -#ifdef CONFIG_IPV6_SIT_6RD /* This function returns true when 6RD attributes are present in the nl msg */ static bool ipip6_netlink_6rd_parms(struct nlattr *data[],
@@ -1630,7 +1596,7 @@ static int ipip6_newlink(struct net *src nt =netdev_priv (dev); -if (ipip6_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) { err =ip_tunnel_encap_setup (nt, &ipencap);if (err <0 )return err;
@@ -1685,7 +1651,7 @@ static int ipip6_changelink(struct net_dif (dev == sitn->fb_tunnel_dev)return -EINVAL ; -if (ipip6_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) { err =ip_tunnel_encap_setup (t, &ipencap);if (err <0 )return err;
Greg Kroah-Hartman
maintainerPATCH 145/272 net: ipip: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
[ Upstream commit 8211a26324667980a463c069469a818e71207e02 ]
ipip_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net.
Gate ipip_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed.
--- net/ipv4/ipip.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv4/ipip.c +3 -0
@@ -478,6 +478,9 @@ static int ipip_changelink(struct net_debool collect_md;__u32 fwmark = t->fwmark; +if (!rtnl_dev_link_net_capable (dev, t->net)) +return -EPERM ; +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip_tunnel_encap_setup (t, &ipencap);
Greg Kroah-Hartman
maintainerPATCH 146/272 net: ixp4xx_hss: fix duplicate HDLC netdev allocation
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit db818b0e8af7bac16860116a19c341a63d6677b4 ]
ixp4xx_hss_probe() allocates two HDLC netdevs. The first one is stored in ndev, initialized, and registered with register_hdlc_device(). The second one is stored in port->netdev and later used by the remove path for unregister_hdlc_device() and free_netdev().
This means that the registered netdev is not the same object that is unregistered and freed on remove. It also leaks the first allocation if the second alloc_hdlcdev() call fails, and the first allocation is not checked before ndev is used.
Older code allocated the HDLC netdev only once and stored the same object in both the local variable and port->netdev. The buggy conversion split this into two alloc_hdlcdev() calls. A later rename changed the local variable name to ndev, but the underlying mismatch remained.
Fix this by allocating the HDLC netdev only once and assigning the same object to port->netdev.
--- drivers/net/wan/ixp4xx_hss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/wan/ixp4xx_hss.c +2 -2
@@ -1353,11 +1353,11 @@ static int hss_init_one(struct platform_ } dev =alloc_hdlcdev (port); - port->netdev =alloc_hdlcdev (port); -if (!port->netdev) { +if (!dev) { err = -ENOMEM ; goto err_plat; } + port->netdev = dev;SET_NETDEV_DEV (dev, &pdev->dev); hdlc =dev_to_hdlc (dev);
Greg Kroah-Hartman
maintainerPATCH 147/272 net/sched: act_ct: preserve tc_skb_cb across defragmentation
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
[ Upstream commit 9092e15defbe6c7bc241c306093ca9d358a578e7 ]
tcf_ct_handle_fragments() calls nf_ct_handle_fragments() without saving and restoring skb->cb. The defrag helper clears IPCB/IP6CB, which aliases the tc_skb_cb/qdisc_skb_cb control buffer. Fragmented traffic through act_ct therefore loses qdisc metadata such as pkt_segs and can trigger WARN_ON_ONCE() in qdisc_pkt_segs() when panic_on_warn is enabled.
Save and restore the full tc_skb_cb around nf_ct_handle_fragments(), matching the pattern used by ovs_ct_handle_fragments().
--- net/sched/act_ct.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/sched/act_ct.c +5 -5
@@ -700,10 +700,10 @@ static int tcf_ct_handle_fragments(struc u8 family, u16 zone, bool *defrag) { enum ip_conntrack_info ctinfo; +struct tc_skb_cb cb;struct nf_conn *ct;int err =0 ;bool frag; -u16 mru;/* Previously seen (loopback)? Ignore. */ ct =nf_ct_get (skb, &ctinfo);
@@ -717,7 +717,7 @@ static int tcf_ct_handle_fragments(strucif (err || !frag)return err; - mru =tc_skb_cb (skb)->mru; + cb = *tc_skb_cb (skb);if (family ==NFPROTO_IPV4 ) {enum ip_defrag_users user =IP_DEFRAG_CONNTRACK_IN + zone;
@@ -731,7 +731,7 @@ static int tcf_ct_handle_fragments(strucif (!err) { *defrag = true; - mru =IPCB (skb)->frag_max_size; + cb.mru =IPCB (skb)->frag_max_size; } }else {/* NFPROTO_IPV6 */ #if IS_ENABLED (CONFIG_NF_DEFRAG_IPV6 )
@@ -744,7 +744,7 @@ static int tcf_ct_handle_fragments(strucif (!err) { *defrag = true; - mru =IP6CB (skb)->frag_max_size; + cb.mru =IP6CB (skb)->frag_max_size; }#else err = -EOPNOTSUPP ;
@@ -753,7 +753,7 @@ static int tcf_ct_handle_fragments(struc }if (err != -EINPROGRESS ) -tc_skb_cb (skb)->mru = mru; + *tc_skb_cb (skb) = cb;skb_clear_hash (skb); skb->ignore_df =1 ;return err;
Greg Kroah-Hartman
maintainerPATCH 148/272 net: mana: Validate the packet length reported by the NIC
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dexuan Cui <[email protected]>
[ Upstream commit 2e2a83b4998af4384e677d3b2ac08565274279bf ]
Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted.
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/net/ethernet/microsoft/mana/mana_en.c +13 -0
@@ -1021,6 +1021,19 @@ static void mana_process_rx_cqe(struct m rxbuf_oob = &rxq->rx_oobs[curr];WARN_ON_ONCE (rxbuf_oob->wqe_inf.wqe_size_in_bu !=1 ); +if (unlikely (pktlen > rxq->datasize)) { +/* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; + + ++ndev->stats.rx_dropped; +netdev_warn_once (ndev, +"Dropped oversized RX packet: len=%u, datasize=%u\n" , + pktlen, rxq->datasize); + +/* Reuse the RX buffer since rxbuf_oob is unchanged. */ + goto drop; + } + new_page =alloc_page (GFP_ATOMIC );if (new_page) {
Greg Kroah-Hartman
maintainerPATCH 149/272 net: ip6_tunnel: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
[ Upstream commit 2496fa0b7d180b3ad356b514e7ff93bb14e6140a ]
ip6_tnl_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net.
Gate ip6_tnl_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed.
--- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv6/ip6_tunnel.c +3 -0
@@ -2054,6 +2054,9 @@ static int ip6_tnl_changelink(struct netstruct ip6_tnl_net *ip6n =net_generic (net, ip6_tnl_net_id);struct ip_tunnel_encap ipencap; +if (!rtnl_dev_link_net_capable (dev, net)) +return -EPERM ; +if (dev == ip6n->fb_tnl_dev)return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 150/272 octeontx2-af: cn10k: restrict VF LMTLINE sharing to its own PF
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junrui Luo <[email protected]>
[ Upstream commit 8cdcf3d2caacdee7ddd363705fb4d93b0c1a0915 ]
rvu_mbox_handler_lmtst_tbl_setup() uses req->base_pcifunc as a direct index into the LMT map table to read another function's LMTLINE physical base address and copy it into the caller's own LMT map table entry. The mailbox dispatcher authenticates req->hdr.pcifunc from the IRQ source, but req->base_pcifunc is a separate payload field and is not sanitized.
Reject the request with -EPERM when a VF caller's base_pcifunc is not a valid function under its own PF. is_pf_func_valid() bounds the FUNC field to the PF's configured VF count, keeping the computed index inside the caller's own slot block.
--- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 2 +- drivers/net/ethernet/marvell/octeontx2/af/rvu.h | 1 + drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +1 -1
@@ -441,7 +441,7 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvureturn &rvu->pf[rvu_get_pf (pcifunc)]; } -static bool is_pf_func_valid (struct rvu *rvu,u16 pcifunc) +bool is_pf_func_valid (struct rvu *rvu,u16 pcifunc) {int pf, vf, nvfs;u64 cfg;
drivers/net/ethernet/marvell/octeontx2/af/rvu.h +1 -0
@@ -693,6 +693,7 @@ int rvu_get_pf(u16 pcifunc);struct rvu_pfvf *rvu_get_pfvf (struct rvu *rvu,int pcifunc);void rvu_get_pf_numvfs (struct rvu *rvu,int pf,int *numvfs,int *hwvf);bool is_block_implemented (struct rvu_hwinfo *hw,int blkaddr); +bool is_pf_func_valid (struct rvu *rvu,u16 pcifunc);bool is_pffunc_map_valid (struct rvu *rvu,u16 pcifunc,int blktype);int rvu_get_lf (struct rvu *rvu,struct rvu_block *block,u16 pcifunc,u16 slot);int rvu_lf_reset (struct rvu *rvu,struct rvu_block *block,int lf);
drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c +9 -0
@@ -178,6 +178,15 @@ int rvu_mbox_handler_lmtst_tbl_setup(str* pcifunc (will be the one who is calling this mailbox). */ if (req->base_pcifunc) { +/* A VF is untrusted and must not redirect its LMTLINE to +* another PF's region, so confine VF callers to their own PF. +*/ +if (is_vf (req->hdr.pcifunc) && + (!is_pf_func_valid (rvu, req->base_pcifunc) || +rvu_get_pf (req->hdr.pcifunc) != +rvu_get_pf (req->base_pcifunc))) +return -EPERM ; +/* Calculating the LMT table index equivalent to primary * pcifunc. */
Greg Kroah-Hartman
maintainerPATCH 151/272 treewide: rename pinctrl_gpio_direction_output_new()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bartosz Golaszewski <[email protected]>
[ Upstream commit b679d6c06b2b29187374f9f4da7eea1961c2eeaa ]
Now that pinctrl_gpio_direction_output() is no longer used, let's drop the '_new' suffix from its improved variant.
Stable-dep-of: d3e91a95b2b0 ("gpio: tegra: do not call pinctrl for GPIO direction")
--- drivers/gpio/gpio-mvebu.c | 4 ++-- drivers/gpio/gpio-pxa.c | 4 ++-- drivers/gpio/gpio-rockchip.c | 4 ++-- drivers/gpio/gpio-tegra.c | 4 ++-- drivers/gpio/gpio-vf610.c | 4 ++-- drivers/pinctrl/bcm/pinctrl-bcm2835.c | 4 ++-- drivers/pinctrl/cirrus/pinctrl-lochnagar.c | 2 +- drivers/pinctrl/core.c | 15 +++++++++------ drivers/pinctrl/intel/pinctrl-cherryview.c | 4 ++-- drivers/pinctrl/intel/pinctrl-intel.c | 4 ++-- drivers/pinctrl/intel/pinctrl-lynxpoint.c | 4 ++-- drivers/pinctrl/mediatek/pinctrl-moore.c | 4 ++-- drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 4 ++-- drivers/pinctrl/mediatek/pinctrl-paris.c | 4 ++-- drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c | 4 ++-- drivers/pinctrl/pinctrl-as3722.c | 4 ++-- drivers/pinctrl/pinctrl-axp209.c | 2 +- drivers/pinctrl/pinctrl-ingenic.c | 11 ++++++++--- drivers/pinctrl/pinctrl-ocelot.c | 4 ++-- drivers/pinctrl/pinctrl-rk805.c | 4 ++-- drivers/pinctrl/pinctrl-st.c | 4 ++-- drivers/pinctrl/renesas/gpio.c | 4 ++-- drivers/pinctrl/stm32/pinctrl-stm32.c | 4 ++-- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 4 ++-- drivers/pinctrl/vt8500/pinctrl-wmt.c | 4 ++-- include/linux/pinctrl/consumer.h | 13 +++++++++---- 26 files changed, 70 insertions(+), 57 deletions(-)
drivers/gpio/gpio-mvebu.c +2 -2
@@ -348,7 +348,7 @@ static int mvebu_gpio_direction_input(st* Check with the pinctrl driver whether this pin is usable as * an input GPIO */ - ret =pinctrl_gpio_direction_input (chip->base + pin); + ret =pinctrl_gpio_direction_input (chip, pin);if (ret)return ret;
@@ -368,7 +368,7 @@ static int mvebu_gpio_direction_output(s* Check with the pinctrl driver whether this pin is usable as * an output GPIO */ - ret =pinctrl_gpio_direction_output (chip->base + pin); + ret =pinctrl_gpio_direction_output (chip, pin);if (ret)return ret;
drivers/gpio/gpio-pxa.c +2 -2
@@ -266,7 +266,7 @@ static int pxa_gpio_direction_input(struint ret;if (pxa_gpio_has_pinctrl ()) { - ret =pinctrl_gpio_direction_input (chip->base + offset); + ret =pinctrl_gpio_direction_input (chip, offset);if (ret)return ret; }
@@ -295,7 +295,7 @@ static int pxa_gpio_direction_output(strwritel_relaxed (mask, base + (value ?GPSR_OFFSET :GPCR_OFFSET ));if (pxa_gpio_has_pinctrl ()) { - ret =pinctrl_gpio_direction_output (chip->base + offset); + ret =pinctrl_gpio_direction_output (chip, offset);if (ret)return ret; }
drivers/gpio/gpio-rockchip.c +2 -2
@@ -158,9 +158,9 @@ static int rockchip_gpio_set_direction(sif (input) -pinctrl_gpio_direction_input (bank->pin_base + offset); +pinctrl_gpio_direction_input (chip, offset);else -pinctrl_gpio_direction_output (bank->pin_base + offset); +pinctrl_gpio_direction_output (chip, offset);raw_spin_lock_irqsave (&bank->slock, flags);rockchip_gpio_writel_bit (bank, offset, data, bank->gpio_regs->port_ddr);
drivers/gpio/gpio-tegra.c +2 -2
@@ -179,7 +179,7 @@ static int tegra_gpio_direction_input(sttegra_gpio_mask_write (tgi,GPIO_MSK_OE (tgi, offset), offset,0 );tegra_gpio_enable (tgi, offset); - ret =pinctrl_gpio_direction_input (chip->base + offset); + ret =pinctrl_gpio_direction_input (chip, offset);if (ret <0 ) dev_err(tgi->dev, "Failed to set pinctrl input direction of GPIO %d: %d",
@@ -199,7 +199,7 @@ static int tegra_gpio_direction_output(stegra_gpio_mask_write (tgi,GPIO_MSK_OE (tgi, offset), offset,1 );tegra_gpio_enable (tgi, offset); - ret =pinctrl_gpio_direction_output (chip->base + offset); + ret =pinctrl_gpio_direction_output (chip, offset);if (ret <0 ) dev_err(tgi->dev, "Failed to set pinctrl output direction of GPIO %d: %d",
drivers/gpio/gpio-vf610.c +2 -2
@@ -117,7 +117,7 @@ static int vf610_gpio_direction_input(stvf610_gpio_writel (val, port->gpio_base +GPIO_PDDR ); } -return pinctrl_gpio_direction_input (chip->base + gpio); +return pinctrl_gpio_direction_input (chip, gpio); } static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
@@ -135,7 +135,7 @@ static int vf610_gpio_direction_output(svf610_gpio_writel (val, port->gpio_base +GPIO_PDDR ); } -return pinctrl_gpio_direction_output (chip->base + gpio); +return pinctrl_gpio_direction_output (chip, gpio); }static void vf610_gpio_irq_handler (struct irq_desc *desc)
drivers/pinctrl/bcm/pinctrl-bcm2835.c +2 -2
@@ -312,7 +312,7 @@ static inline void bcm2835_pinctrl_fsel_static int bcm2835_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int bcm2835_gpio_get (struct gpio_chip *chip,unsigned offset)
@@ -348,7 +348,7 @@ static int bcm2835_gpio_direction_output unsigned offset, int value) {bcm2835_gpio_set (chip ,offset ,value ); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); } static int bcm2835_of_gpio_ranges_fallback(struct gpio_chip *gc,
drivers/pinctrl/cirrus/pinctrl-lochnagar.c +1 -1
@@ -1096,7 +1096,7 @@ static int lochnagar_gpio_direction_out( {lochnagar_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static int lochnagar_fill_func_groups (struct lochnagar_pin_priv *priv)
drivers/pinctrl/core.c +9 -6
@@ -22,6 +22,7 @@#include <linux/list.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/gpio/driver.h> #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/machine.h>
@@ -864,29 +865,31 @@ static int pinctrl_gpio_direction(unsign/** * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode -* @gpio: the GPIO pin number from the GPIO subsystem number space +* @gc: GPIO chip structure from the GPIO subsystem +* @offset: hardware offset of the GPIO relative to the controller * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_direction_input() semantics, platforms and individual * drivers shall *NOT* touch pin control GPIO calls. */ -int pinctrl_gpio_direction_input (unsigned gpio) +int pinctrl_gpio_direction_input (struct gpio_chip *gc,unsigned int offset) { -return pinctrl_gpio_direction (gpio, true); +return pinctrl_gpio_direction (gc->base + offset, true); }EXPORT_SYMBOL_GPL (pinctrl_gpio_direction_input);/** * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode -* @gpio: the GPIO pin number from the GPIO subsystem number space +* @gc: GPIO chip structure from the GPIO subsystem +* @offset: hardware offset of the GPIO relative to the controller * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_direction_output() semantics, platforms and individual * drivers shall *NOT* touch pin control GPIO calls. */ -int pinctrl_gpio_direction_output (unsigned gpio) +int pinctrl_gpio_direction_output (struct gpio_chip *gc,unsigned int offset) { -return pinctrl_gpio_direction (gpio, false); +return pinctrl_gpio_direction (gc->base + offset, false); }EXPORT_SYMBOL_GPL (pinctrl_gpio_direction_output);
drivers/pinctrl/intel/pinctrl-cherryview.c +2 -2
@@ -1220,14 +1220,14 @@ static int chv_gpio_get_direction(structstatic int chv_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int chv_gpio_direction_output (struct gpio_chip *chip,unsigned int offset,int value) {chv_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static const struct gpio_chip chv_gpio_chip = {
drivers/pinctrl/intel/pinctrl-intel.c +2 -2
@@ -1000,14 +1000,14 @@ static int intel_gpio_get_direction(strustatic int intel_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int intel_gpio_direction_output (struct gpio_chip *chip,unsigned int offset,int value) {intel_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static const struct gpio_chip intel_gpio_chip = {
drivers/pinctrl/intel/pinctrl-lynxpoint.c +2 -2
@@ -614,7 +614,7 @@ static void lp_gpio_set(struct gpio_chipstatic int lp_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); } static int lp_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
@@ -622,7 +622,7 @@ static int lp_gpio_direction_output(stru {lp_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static int lp_gpio_get_direction (struct gpio_chip *chip,unsigned int offset)
drivers/pinctrl/mediatek/pinctrl-moore.c +2 -2
@@ -471,7 +471,7 @@ static void mtk_gpio_set(struct gpio_chistatic int mtk_gpio_direction_input (struct gpio_chip *chip,unsigned int gpio) { -return pinctrl_gpio_direction_input (chip->base + gpio); +return pinctrl_gpio_direction_input (chip, gpio); } static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,
@@ -479,7 +479,7 @@ static int mtk_gpio_direction_output(str {mtk_gpio_set (chip, gpio, value); -return pinctrl_gpio_direction_output (chip->base + gpio); +return pinctrl_gpio_direction_output (chip, gpio); }static int mtk_gpio_to_irq (struct gpio_chip *chip,unsigned int offset)
drivers/pinctrl/mediatek/pinctrl-mtk-common.c +2 -2
@@ -778,14 +778,14 @@ static const struct pinmux_ops mtk_pmx_ostatic int mtk_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int mtk_gpio_direction_output (struct gpio_chip *chip,unsigned offset,int value) {mtk_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static int mtk_gpio_get_direction (struct gpio_chip *chip,unsigned offset)
drivers/pinctrl/mediatek/pinctrl-paris.c +2 -2
@@ -786,7 +786,7 @@ static int mtk_gpio_direction_input(struif (gpio >= hw->soc->npins)return -EINVAL ; -return pinctrl_gpio_direction_input (chip->base + gpio); +return pinctrl_gpio_direction_input (chip, gpio); } static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,
@@ -799,7 +799,7 @@ static int mtk_gpio_direction_output(strmtk_gpio_set (chip, gpio, value); -return pinctrl_gpio_direction_output (chip->base + gpio); +return pinctrl_gpio_direction_output (chip, gpio); }static int mtk_gpio_to_irq (struct gpio_chip *chip,unsigned int offset)
drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c +2 -2
@@ -168,7 +168,7 @@ static int npcmgpio_direction_input(strustruct npcm7xx_gpio *bank =gpiochip_get_data (chip);int ret; - ret =pinctrl_gpio_direction_input (offset + chip->base); + ret =pinctrl_gpio_direction_input (chip, offset);if (ret)return ret;
@@ -185,7 +185,7 @@ static int npcmgpio_direction_output(strdev_dbg (chip->parent,"gpio_direction_output: offset%d = %x\n" , offset, value); - ret =pinctrl_gpio_direction_output (offset + chip->base); + ret =pinctrl_gpio_direction_output (chip, offset);if (ret)return ret;
drivers/pinctrl/pinctrl-as3722.c +2 -2
@@ -514,14 +514,14 @@ static void as3722_gpio_set(struct gpio_static int as3722_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int as3722_gpio_direction_output (struct gpio_chip *chip,unsigned offset,int value) {as3722_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static int as3722_gpio_to_irq (struct gpio_chip *chip,unsigned offset)
drivers/pinctrl/pinctrl-axp209.c +1 -1
@@ -112,7 +112,7 @@ static int axp20x_gpio_get_reg(unsignedstatic int axp20x_gpio_input (struct gpio_chip *chip,unsigned int offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int axp20x_gpio_get (struct gpio_chip *chip,unsigned int offset)
drivers/pinctrl/pinctrl-ingenic.c +8 -3
@@ -130,6 +130,8 @@ struct ingenic_pinctrl {struct pinctrl_pin_desc *pdesc;const struct ingenic_chip_info *info; + +struct gpio_chip *gc; };struct ingenic_gpio_chip {
@@ -3530,14 +3532,14 @@ static int ingenic_gpio_get(struct gpio_static int ingenic_gpio_direction_input (struct gpio_chip *gc,unsigned int offset) { -return pinctrl_gpio_direction_input (gc->base + offset); +return pinctrl_gpio_direction_input (gc, offset); }static int ingenic_gpio_direction_output (struct gpio_chip *gc,unsigned int offset,int value) {ingenic_gpio_set (gc, offset, value); -return pinctrl_gpio_direction_output (gc->base + offset); +return pinctrl_gpio_direction_output (gc, offset); } static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
@@ -3998,7 +4000,8 @@ static int ingenic_pinconf_set(struct pibreak ;case PIN_CONFIG_OUTPUT : - ret =pinctrl_gpio_direction_output (pin); + ret =pinctrl_gpio_direction_output (jzpc->gc, + pin - jzpc->gc->base);if (ret)return ret;
@@ -4118,6 +4121,8 @@ static int __init ingenic_gpio_probe(strif (!jzgc)return -ENOMEM ; + jzpc->gc = &jzgc->gc; + jzgc->jzpc = jzpc; jzgc->reg_base = bank * jzpc->info->reg_offset;
drivers/pinctrl/pinctrl-ocelot.c +2 -2
@@ -1174,7 +1174,7 @@ static int ocelot_gpio_get_direction(strstatic int ocelot_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); } static int ocelot_gpio_direction_output(struct gpio_chip *chip,
@@ -1190,7 +1190,7 @@ static int ocelot_gpio_direction_output(regmap_write (info->map,REG (OCELOT_GPIO_OUT_CLR , info, offset), pin); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static const struct gpio_chip ocelot_gpiolib_chip = {
drivers/pinctrl/pinctrl-rk805.c +2 -2
@@ -166,14 +166,14 @@ static void rk805_gpio_set(struct gpio_cstatic int rk805_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int rk805_gpio_direction_output (struct gpio_chip *chip,unsigned int offset,int value) {rk805_gpio_set (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static int rk805_gpio_get_direction (struct gpio_chip *chip,unsigned int offset)
drivers/pinctrl/pinctrl-st.c +2 -2
@@ -715,7 +715,7 @@ static void st_gpio_set(struct gpio_chipstatic int st_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -pinctrl_gpio_direction_input (chip->base + offset); +pinctrl_gpio_direction_input (chip, offset);return 0 ; }
@@ -726,7 +726,7 @@ static int st_gpio_direction_output(strustruct st_gpio_bank *bank =gpiochip_get_data (chip);__st_gpio_set (bank, offset, value); -pinctrl_gpio_direction_output (chip->base + offset); +pinctrl_gpio_direction_output (chip, offset);return 0 ; }
drivers/pinctrl/renesas/gpio.c +2 -2
@@ -165,7 +165,7 @@ static void gpio_pin_set_value(struct shstatic int gpio_pin_direction_input (struct gpio_chip *gc,unsigned offset) { -return pinctrl_gpio_direction_input (offset); +return pinctrl_gpio_direction_input (gc, offset); } static int gpio_pin_direction_output(struct gpio_chip *gc, unsigned offset,
@@ -173,7 +173,7 @@ static int gpio_pin_direction_output(str {gpio_pin_set_value (gpiochip_get_data (gc), offset, value); -return pinctrl_gpio_direction_output (offset); +return pinctrl_gpio_direction_output (gc, offset); }static int gpio_pin_get (struct gpio_chip *gc,unsigned offset)
drivers/pinctrl/stm32/pinctrl-stm32.c +2 -2
@@ -255,7 +255,7 @@ static void stm32_gpio_set(struct gpio_cstatic int stm32_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); } static int stm32_gpio_direction_output(struct gpio_chip *chip,
@@ -264,7 +264,7 @@ static int stm32_gpio_direction_output(sstruct stm32_gpio_bank *bank =gpiochip_get_data (chip);__stm32_gpio_set (bank, offset, value); -pinctrl_gpio_direction_output (chip->base + offset); +pinctrl_gpio_direction_output (chip, offset);return 0 ; }
drivers/pinctrl/sunxi/pinctrl-sunxi.c +2 -2
@@ -850,7 +850,7 @@ static const struct pinmux_ops sunxi_pmxstatic int sunxi_pinctrl_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int sunxi_pinctrl_gpio_get (struct gpio_chip *chip,unsigned offset)
@@ -901,7 +901,7 @@ static int sunxi_pinctrl_gpio_direction_ unsigned offset, int value) {sunxi_pinctrl_gpio_set (chip ,offset ,value ); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); } static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc,
drivers/pinctrl/vt8500/pinctrl-wmt.c +2 -2
@@ -528,14 +528,14 @@ static void wmt_gpio_set_value(struct gpstatic int wmt_gpio_direction_input (struct gpio_chip *chip,unsigned offset) { -return pinctrl_gpio_direction_input (chip->base + offset); +return pinctrl_gpio_direction_input (chip, offset); }static int wmt_gpio_direction_output (struct gpio_chip *chip,unsigned offset,int value) {wmt_gpio_set_value (chip, offset, value); -return pinctrl_gpio_direction_output (chip->base + offset); +return pinctrl_gpio_direction_output (chip, offset); }static const struct gpio_chip wmt_gpio_chip = {
include/linux/pinctrl/consumer.h +9 -4
@@ -20,6 +20,7 @@struct pinctrl ;struct pinctrl_state ;struct device ; +struct gpio_chip ;#ifdef CONFIG_PINCTRL
@@ -27,8 +28,10 @@ struct device;extern bool pinctrl_gpio_can_use_line (unsigned gpio);extern int pinctrl_gpio_request (unsigned gpio);extern void pinctrl_gpio_free (unsigned gpio); -extern int pinctrl_gpio_direction_input (unsigned gpio); -extern int pinctrl_gpio_direction_output (unsigned gpio); +extern int pinctrl_gpio_direction_input (struct gpio_chip *gc, +unsigned int offset); +extern int pinctrl_gpio_direction_output (struct gpio_chip *gc, +unsigned int offset);extern int pinctrl_gpio_set_config (unsigned gpio,unsigned long config);extern struct pinctrl *__must_check pinctrl_get (struct device *dev);
@@ -77,12 +80,14 @@ static inline void pinctrl_gpio_free(uns { } -static inline int pinctrl_gpio_direction_input (unsigned gpio) +static inline int pinctrl_gpio_direction_input (struct gpio_chip *gc, +unsigned int offset) {return 0 ; } -static inline int pinctrl_gpio_direction_output (unsigned gpio) +static inline int pinctrl_gpio_direction_output (struct gpio_chip *gc, +unsigned int offset) {return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 152/272 gpio: tegra: do not call pinctrl for GPIO direction
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 ]
tegra_gpio_direction_input() and tegra_gpio_direction_output() already program the GPIO controller direction registers directly. The additional pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl operation, because the Tegra pinmux ops provide GPIO request/free handling but no gpio_set_direction hook.
The extra call still enters the pinctrl core and takes pctldev->mutex. Shared GPIO users can call the direction path while holding their per-line spinlock, so this otherwise redundant pinctrl direction call can sleep in an atomic context.
This was found by our static analysis tool and then confirmed by manual review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the Tegra pinctrl ops. The reviewed path has a default non-sleeping struct gpio_chip while the direction callback still enters the pinctrl mutex path.
A directed runtime validation kept the same non-sleeping chip registration and drove:
gpio_shared_proxy_direction_output()
gpiod_direction_output_raw_commit()
tegra_gpio_direction_output()
pinctrl_gpio_direction_output()
Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output() on the stack.
Do not mark the whole chip as can_sleep to paper over this: can_sleep describes whether get()/set() may sleep, and Tegra value access is MMIO. Remove the redundant pinctrl direction calls and keep pinctrl involvement in the existing request/free path.
--- drivers/gpio/gpio-tegra.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-)
drivers/gpio/gpio-tegra.c +2 -16
@@ -174,18 +174,11 @@ static int tegra_gpio_direction_input(st unsigned int offset) {struct tegra_gpio_info *tgi =gpiochip_get_data (chip); -int ret;tegra_gpio_mask_write (tgi,GPIO_MSK_OE (tgi, offset), offset,0 );tegra_gpio_enable (tgi, offset); - ret =pinctrl_gpio_direction_input (chip, offset); -if (ret <0 ) -dev_err (tgi->dev, -"Failed to set pinctrl input direction of GPIO %d: %d" , - chip->base + offset, ret); - -return ret; +return 0 ; } static int tegra_gpio_direction_output(struct gpio_chip *chip,
@@ -193,19 +186,12 @@ static int tegra_gpio_direction_output(s int value) {struct tegra_gpio_info *tgi =gpiochip_get_data (chip); -int ret;tegra_gpio_set (chip, offset, value);tegra_gpio_mask_write (tgi,GPIO_MSK_OE (tgi, offset), offset,1 );tegra_gpio_enable (tgi, offset); - ret =pinctrl_gpio_direction_output (chip, offset); -if (ret <0 ) -dev_err (tgi->dev, -"Failed to set pinctrl output direction of GPIO %d: %d" , - chip->base + offset, ret); - -return ret; +return 0 ; } static int tegra_gpio_get_direction(struct gpio_chip *chip,
Greg Kroah-Hartman
maintainerPATCH 153/272 net/sched: taprio: avoid calling child->ops->dequeue(child) twice
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladimir Oltean <[email protected]>
[ Upstream commit 4c22942734f0814d3c928c25a80f48df0a6ce45e ]
Simplify taprio_dequeue_from_txq() by noticing that we can goto one call earlier than the previous skb_found label. This is possible because we've unified the treatment of the child->ops->dequeue(child) return call, we always try other TXQs now, instead of abandoning the root dequeue completely if we failed in the peek() case.
Stable-dep-of: e056e1dfcddc ("net/sched: sch_taprio: Replace direct dequeue call with peek and qdisc_dequeue_peeked")
--- net/sched/sch_taprio.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
net/sched/sch_taprio.c +3 -7
@@ -557,12 +557,8 @@ static struct sk_buff *taprio_dequeue_frif (unlikely (!child))return NULL ; -if (TXTIME_ASSIST_IS_ENABLED (q->flags)) { - skb = child->ops->dequeue (child); -if (!skb) -return NULL ; - goto skb_found; - } +if (TXTIME_ASSIST_IS_ENABLED (q->flags)) + goto skip_peek_checks; skb = child->ops->peek (child);if (!skb)
@@ -589,11 +585,11 @@ static struct sk_buff *taprio_dequeue_fr atomic_sub_return(len, &entry->budget) < 0)return NULL ; +skip_peek_checks: skb = child->ops->dequeue (child);if (unlikely (!skb))return NULL ; -skb_found:qdisc_bstats_update (sch, skb);qdisc_qstats_backlog_dec (sch, skb); sch->q.qlen--;
Greg Kroah-Hartman
maintainerPATCH 154/272 net/sched: sch_taprio: Replace direct dequeue call with peek and qdisc_dequeue_peeked
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
[ Upstream commit e056e1dfcddca877dd46d704e8ec9860cfc9ec44 ]
When taprio's software path peeks a non-work-conserving child qdisc, the child stashes the peeked skb in its gso_skb; taprio_dequeue_from_txq() then takes the packet with a direct child ->dequeue() call, which ignores that stash, orphans the peeked skb and desyncs the child's qlen/backlog. With a qfq child this re-enters the child on an emptied list and dereferences NULL, panicking the kernel from softirq on ordinary egress.
Take the packet through qdisc_dequeue_peeked(), as sch_red and sch_sfb now do. The helper returns the child's stashed skb first and is a no-op when there is none, so a work-conserving child is unaffected and the gated path now consumes the skb whose length was charged to the budget.
--- net/sched/sch_taprio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_taprio.c +1 -1
@@ -586,7 +586,7 @@ static struct sk_buff *taprio_dequeue_frreturn NULL ; skip_peek_checks: - skb = child->ops->dequeue (child); + skb =qdisc_dequeue_peeked (child);if (unlikely (!skb))return NULL ;
Greg Kroah-Hartman
maintainerPATCH 155/272 bootconfig: do not put quotes on cmdline items unless necessary
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rasmus Villemoes <[email protected]>
[ Upstream commit 212f863fa8811c780abacc1d0404c573fdc0a2de ]
When trying to migrate to using bootconfig to embed the kernel's and PID1's command line with the kernel image itself, and so allowing changing that without modifying the bootloader, I noticed that /proc/cmdline changed from e.g.
console=ttymxc0,115200n8 cma=128M quiet -- --log-level=notice
to
console="ttymxc0,115200n8" cma="128M" quiet -- --log-level="notice"
The kernel parameters are parsed just fine, and the quotes are indeed stripped from the actual argv[] given to PID1. However, the quoting doesn't really serve any purpose and looks excessive, and might confuse some (naive) userspace tool trying to parse /proc/cmdline. So do not quote the value unless it contains whitespace.
Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()")
--- init/main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
init/main.c +9 -3
@@ -328,7 +328,7 @@ static int __init xbc_snprint_cmdline(ch {struct xbc_node *knode, *vnode;char *end = buf + size; -const char *val; +const char *val, *q;int ret;xbc_node_for_each_key_value (root, knode, val) {
@@ -346,8 +346,14 @@ static int __init xbc_snprint_cmdline(chcontinue ; }xbc_array_for_each_value (vnode, val) { - ret =snprintf (buf,rest (buf, end),"%s=\"%s\" " , - xbc_namebuf, val); +/* +* For prettier and more readable /proc/cmdline, only +* quote the value when necessary, i.e. when it contains +* whitespace. +*/ + q =strpbrk (val," \t\r\n" ) ?"\"" :"" ; + ret =snprintf (buf,rest (buf, end),"%s=%s%s%s " , + xbc_namebuf, q, val, q);if (ret <0 )return ret; buf += ret;
Greg Kroah-Hartman
maintainerPATCH 156/272 bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 5a643e4623238e14b03d75ca0d4eda0645720cee ]
Move xbc_snprint_cmdline() from init/main.c to lib/bootconfig.c so the function (and its xbc_namebuf scratch buffer) becomes part of the shared parser library. tools/bootconfig already compiles lib/bootconfig.c directly, which lets a follow-up patch reuse the same renderer in the userspace tool to convert a bootconfig file into a flat cmdline string at build time.
No functional change.
Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()")
--- include/linux/bootconfig.h | 3 ++ init/main.c | 45 ------------------------------------ lib/bootconfig.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 45 deletions(-)
include/linux/bootconfig.h +3 -0
@@ -254,6 +254,9 @@ static inline struct xbc_node * __init xint __init xbc_node_compose_key_after (struct xbc_node *root,struct xbc_node *node,char *buf,size_t size); +/* Render key/value pairs under @root as a flat cmdline string */ +int __init xbc_snprint_cmdline (char *buf,size_t size,struct xbc_node *root); +/** * xbc_node_compose_key() - Compose full key string of the XBC node * @node: An XBC node.
init/main.c +0 -45
51 lines not on this page · raw message
lib/bootconfig.c +56 -0
62 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 157/272 bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit dec4d8118c179b3d12bca7e609054c6011c4f2ce ]
xbc_snprint_cmdline() is meant to be called twice: first with buf=NULL, size=0 to probe the rendered length, then with a real buffer to fill it (the standard snprintf() two-pass pattern). The probe call makes the function compute "buf + size" (NULL + 0) and, on every iteration, advance "buf += ret" from that NULL base and pass the result back into snprintf().
Pointer arithmetic on a NULL pointer is undefined behavior. It is harmless in the in-kernel callers today, but the follow-up patches run this same code in the userspace tools/bootconfig parser at kernel build time, where host UBSan / FORTIFY_SOURCE abort the build.
Track a running written length (size_t) instead of mutating @buf, and only form "buf + len" when @buf is non-NULL. snprintf(NULL, 0, ...) is itself well defined and returns the would-be length, so the two-pass "probe then fill" usage returns identical byte counts.
--- lib/bootconfig.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
lib/bootconfig.c +16 -7
@@ -358,10 +358,18 @@ static char xbc_namebuf[XBC_KEYLEN_MAX]int __init xbc_snprint_cmdline (char *buf,size_t size,struct xbc_node *root) {struct xbc_node *knode, *vnode; -char *end = buf + size;const char *val, *q; +size_t len =0 ;int ret; +/* +* Track the running written length rather than advancing @buf, so we +* never form "buf + size" or "buf += ret" while @buf is NULL (the +* size-probe call passes buf=NULL, size=0). NULL pointer arithmetic +* is undefined behavior and trips host UBSan / FORTIFY_SOURCE when +* this renderer runs at kernel build time. snprintf(NULL, 0, ...) +* itself is well defined and returns the would-be length. +*/ xbc_node_for_each_key_value (root, knode, val) { ret =xbc_node_compose_key_after (root, knode, xbc_namebuf,XBC_KEYLEN_MAX );
@@ -370,10 +378,11 @@ int __init xbc_snprint_cmdline(char *buf vnode =xbc_node_get_child (knode);if (!vnode) { - ret =snprintf (buf,rest (buf, end),"%s " , xbc_namebuf); + ret =snprintf (buf ? buf + len :NULL ,rest (len, size), +"%s " , xbc_namebuf);if (ret <0 )return ret; - buf += ret; + len += ret;continue ; }xbc_array_for_each_value (vnode, val) {
@@ -383,15 +392,15 @@ int __init xbc_snprint_cmdline(char *buf* whitespace. */ q =strpbrk (val," \t\r\n" ) ?"\"" :"" ; - ret =snprintf (buf,rest (buf, end),"%s=%s%s%s " , - xbc_namebuf, q, val, q); + ret =snprintf (buf ? buf + len :NULL ,rest (len, size), +"%s=%s%s%s " , xbc_namebuf, q, val, q);if (ret <0 )return ret; - buf += ret; + len += ret; } } -return buf - (end - size); +return len; }#undef rest
Greg Kroah-Hartman
maintainerPATCH 158/272 espintcp: use sk_msg_free_partial to fix partial send
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sabrina Dubroca <[email protected]>
[ Upstream commit 007800408002d871f5699bdb944f985896730b8f ]
sk_msg_free_partial() ensures consistency of the skmsg at every iteration, without having to manually handle uncharges and offsets. This simplifies the code, and fixes some bugs in skmsg accounting when we don't send the full contents.
--- net/xfrm/espintcp.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-)
net/xfrm/espintcp.c +6 -26
@@ -210,41 +210,21 @@ static int espintcp_sendskmsg_locked(str {struct sk_msg *skmsg = &emsg->skmsg;struct scatterlist *sg; -int done =0 ;int ret; flags |=MSG_SENDPAGE_NOTLAST ; - sg = &skmsg->sg.data[skmsg->sg.start];do { -size_t size = sg->length - emsg->offset; -int offset = sg->offset + emsg->offset; -struct page *p; - - emsg->offset =0 ; - + sg = &skmsg->sg.data[skmsg->sg.start];if (sg_is_last (sg)) flags &= ~MSG_SENDPAGE_NOTLAST ; - p =sg_page (sg); -retry: - ret =do_tcp_sendpages (sk, p, offset, size, flags); -if (ret <0 ) { - emsg->offset = offset - sg->offset; - skmsg->sg.start += done; + ret =do_tcp_sendpages (sk,sg_page (sg), sg->offset, + sg->length, flags); +if (ret <0 )return ret; - } - -if (ret != size) { - offset += ret; - size -= ret; - goto retry; - } - done++; -put_page (p); -sk_mem_uncharge (sk, sg->length); - sg =sg_next (sg); - }while (sg); +sk_msg_free_partial (sk, skmsg, ret); + }while (skmsg->sg.size);memset (emsg,0 ,sizeof (*emsg));
Greg Kroah-Hartman
maintainerPATCH 159/272 net: ipa: fix SMEM state handle leaks in SMP2P init
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit 96ca1e658ae459276292bd6d971ab5d8c7e0379a ]
ipa_smp2p_init() acquires two Qualcomm SMEM state handles with qcom_smem_state_get(). However, neither the init error paths nor ipa_smp2p_exit() release them.
Release both handles with qcom_smem_state_put() in the init error paths and in ipa_smp2p_exit().
--- drivers/net/ipa/ipa_smp2p.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-)
drivers/net/ipa/ipa_smp2p.c +22 -8
@@ -233,19 +233,27 @@ int ipa_smp2p_init(struct ipa *ipa, bool &valid_bit);if (IS_ERR (valid_state))return PTR_ERR (valid_state); -if (valid_bit >=32 )/* BITS_PER_U32 */ -return -EINVAL ; +if (valid_bit >=32 ) {/* BITS_PER_U32 */ + ret = -EINVAL ; + goto err_valid_state_put; + } enabled_state =qcom_smem_state_get (dev,"ipa-clock-enabled" , &enabled_bit); -if (IS_ERR (enabled_state)) -return PTR_ERR (enabled_state); -if (enabled_bit >=32 )/* BITS_PER_U32 */ -return -EINVAL ; +if (IS_ERR (enabled_state)) { + ret =PTR_ERR (enabled_state); + goto err_valid_state_put; + } +if (enabled_bit >=32 ) {/* BITS_PER_U32 */ + ret = -EINVAL ; + goto err_enabled_state_put; + } smp2p =kzalloc (sizeof (*smp2p),GFP_KERNEL ); -if (!smp2p) -return -ENOMEM ; +if (!smp2p) { + ret = -ENOMEM ; + goto err_enabled_state_put; + } smp2p->ipa = ipa;
@@ -290,6 +298,10 @@ err_null_smp2p: ipa->smp2p =NULL ;mutex_destroy (&smp2p->mutex);kfree (smp2p); +err_enabled_state_put: +qcom_smem_state_put (enabled_state); +err_valid_state_put: +qcom_smem_state_put (valid_state);return ret; }
@@ -306,6 +318,8 @@ void ipa_smp2p_exit(struct ipa *ipa)ipa_smp2p_power_release (ipa); ipa->smp2p =NULL ;mutex_destroy (&smp2p->mutex); +qcom_smem_state_put (smp2p->enabled_state); +qcom_smem_state_put (smp2p->valid_state);kfree (smp2p); }
Greg Kroah-Hartman
maintainerPATCH 160/272 octeontx2-pf: fix SQB pointer leak on init failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
[ Upstream commit 62e7df6d042aeebd5efb581074e28865c04477be ]
otx2_init_hw_resources() initializes SQ aura and pool resources before several later setup steps. On failure, err_free_sq_ptrs only frees SQB pages, leaving the per-SQ sqb_ptrs arrays behind.
Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs even when sq->sqe has not been allocated yet.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1.
An x86_64 allyesconfig build showed no new warnings. As we do not have an OcteonTX2 PF device and the corresponding AF mailbox setup to test with, no runtime testing was able to be performed.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +1 -2
@@ -1484,14 +1484,13 @@ static int otx2_init_hw_resources(structreturn err; err_free_nix_queues: -otx2_free_sq_res (pf);otx2_free_cq_res (pf);otx2_ctx_disable (mbox,NIX_AQ_CTYPE_RQ , false); err_free_txsch:if (otx2_txschq_stop (pf))dev_err (pf->dev,"%s failed to stop TX schedulers\n" , __func__); err_free_sq_ptrs: -otx2_sq_free_sqbs (pf); +otx2_free_sq_res (pf); err_free_rq_ptrs:otx2_free_aura_ptr (pf,AURA_NIX_RQ );otx2_ctx_disable (mbox,NPA_AQ_CTYPE_POOL , true);
Greg Kroah-Hartman
maintainerPATCH 161/272 fs/resctrl: Fix double-add of pseudo-locked regions RMID to free list
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Reinette Chatre <[email protected]>
[ Upstream commit b9f089723aee892efc77c349ae47a6b452b293c4 ]
A pseudo-locked group's RMID is freed when it is created. On unmount rmdir_all_sub() unconditionally frees all RMID of all groups, resulting in a double-free of the pseudo-locked group's RMID. The consequence of this is that the original free results in the pseudo-locked group's RMID being added to the rmid_free_lru linked list and the second free then attempts to add the same RMID entry to the rmid_free_lru again.
Do not double-free a pseudo-locked group's RMID.
--- arch/x86/kernel/cpu/resctrl/rdtgroup.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
arch/x86/kernel/cpu/resctrl/rdtgroup.c +7 -5
@@ -2439,10 +2439,6 @@ static void rmdir_all_sub(void)if (rdtgrp == &rdtgroup_default)continue ; -if (rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKSETUP || - rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKED ) -rdtgroup_pseudo_lock_remove (rdtgrp); -/* * Give any CPUs back to the default group. We cannot copy * cpu_online_mask because a CPU might have executed the
@@ -2451,7 +2447,13 @@ static void rmdir_all_sub(void)cpumask_or (&rdtgroup_default.cpu_mask, &rdtgroup_default.cpu_mask, &rdtgrp->cpu_mask); -free_rmid (rdtgrp->mon.rmid); +if (rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKSETUP || + rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKED ) { +rdtgroup_pseudo_lock_remove (rdtgrp); + }else { +/* Pseudo-locked group's RMID is freed during setup. */ +free_rmid (rdtgrp->mon.rmid); + }kernfs_remove (rdtgrp->kn);list_del (&rdtgrp->rdtgroup_list);
Greg Kroah-Hartman
maintainerPATCH 162/272 KVM: x86/mmu: Directly "destroy" PTE list when recycling rmaps
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit a42989e7fbb0186d9fee05b29e0ea9cb639d0bd3 ]
Use pte_list_destroy() directly when recycling rmaps instead of bouncing through kvm_unmap_rmapp() and kvm_zap_rmapp(). Calling kvm_unmap_rmapp() is unnecessary and odd as it requires passing dummy parameters; passing NULL for @slot when __rmap_add() already has a valid slot is especially weird and confusing.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: 2abd5287f083 ("KVM: x86: Check for invalid/obsolete root *after* making MMU pages available")
--- arch/x86/kvm/mmu/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kvm/mmu/mmu.c +1 -1
@@ -1639,7 +1639,7 @@ static void rmap_add(struct kvm_vcpu *vc rmap_count =pte_list_add (vcpu, spte, rmap_head);if (rmap_count >RMAP_RECYCLE_THRESHOLD ) { -kvm_unmap_rmapp (vcpu->kvm, rmap_head,NULL , gfn, sp->role.level,__pte (0 )); +pte_list_destroy (vcpu->kvm, rmap_head);kvm_flush_remote_tlbs_with_address ( vcpu->kvm, sp->gfn,KVM_PAGES_PER_HPAGE (sp->role.level)); }
Greg Kroah-Hartman
maintainerPATCH 163/272 KVM: x86/mmu: Rename pte_list_{destroy,remove}() to show they zap SPTEs
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit 9202aee816c84d69179f94193c5dd321bb0e8530 ]
Rename pte_list_remove() and pte_list_destroy() to kvm_zap_one_rmap_spte() and kvm_zap_all_rmap_sptes() respectively to document that (a) they zap SPTEs and (b) to better document how they differ (remove vs. destroy does not exactly scream "one vs. all").
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: 2abd5287f083 ("KVM: x86: Check for invalid/obsolete root *after* making MMU pages available")
--- arch/x86/kvm/mmu/mmu.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
arch/x86/kvm/mmu/mmu.c +9 -8
@@ -999,15 +999,16 @@ static void __pte_list_remove(u64 *spte, } } -static void pte_list_remove (struct kvm *kvm,struct kvm_rmap_head *rmap_head, -u64 *sptep) +static void kvm_zap_one_rmap_spte (struct kvm *kvm, +struct kvm_rmap_head *rmap_head,u64 *sptep) {mmu_spte_clear_track_bits (kvm, sptep);__pte_list_remove (sptep, rmap_head); } -/* Return true if rmap existed, false otherwise */ -static bool pte_list_destroy (struct kvm *kvm,struct kvm_rmap_head *rmap_head) +/* Return true if at least one SPTE was zapped, false otherwise */ +static bool kvm_zap_all_rmap_sptes (struct kvm *kvm, +struct kvm_rmap_head *rmap_head) {struct pte_list_desc *desc, *next;int i;
@@ -1434,7 +1435,7 @@ static bool rmap_write_protect(struct kvstatic bool kvm_zap_rmapp (struct kvm *kvm,struct kvm_rmap_head *rmap_head,const struct kvm_memory_slot *slot) { -return pte_list_destroy (kvm, rmap_head); +return kvm_zap_all_rmap_sptes (kvm, rmap_head); } static bool kvm_unmap_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
@@ -1465,7 +1466,7 @@ restart: need_flush =1 ;if (pte_write (pte)) { -pte_list_remove (kvm, rmap_head, sptep); +kvm_zap_one_rmap_spte (kvm, rmap_head, sptep); goto restart; }else { new_spte = kvm_mmu_changed_pte_notifier_make_spte(
@@ -1639,7 +1640,7 @@ static void rmap_add(struct kvm_vcpu *vc rmap_count =pte_list_add (vcpu, spte, rmap_head);if (rmap_count >RMAP_RECYCLE_THRESHOLD ) { -pte_list_destroy (vcpu->kvm, rmap_head); +kvm_zap_all_rmap_sptes (vcpu->kvm, rmap_head);kvm_flush_remote_tlbs_with_address ( vcpu->kvm, sp->gfn,KVM_PAGES_PER_HPAGE (sp->role.level)); }
@@ -5855,7 +5856,7 @@ restart: !kvm_is_reserved_pfn(pfn) && sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn, pfn, PG_LEVEL_NUM)) { -pte_list_remove (kvm, rmap_head, sptep); +kvm_zap_one_rmap_spte (kvm, rmap_head, sptep);if (kvm_available_flush_tlb_with_range ()) kvm_flush_remote_tlbs_with_address(kvm, sp->gfn,
Greg Kroah-Hartman
maintainerPATCH 164/272 KVM: x86/mmu: Document the "rules" for using host_pfn_mapping_level()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit 65e3b446bcceaac7448cb25a2a5bf4adbcf25fe6 ]
Add a comment to document how host_pfn_mapping_level() can be used safely, as the line between safe and dangerous is quite thin. E.g. if KVM were to ever support in-place promotion to create huge pages, consuming the level is safe if the caller holds mmu_lock and checks that there's an existing _leaf_ SPTE, but unsafe if the caller only checks that there's a non-leaf SPTE.
Opportunistically tweak the existing comments to explicitly document why KVM needs to use READ_ONCE().
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: 2abd5287f083 ("KVM: x86: Check for invalid/obsolete root *after* making MMU pages available")
--- arch/x86/kvm/mmu/mmu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
arch/x86/kvm/mmu/mmu.c +25 -0
@@ -2869,6 +2869,31 @@ static void direct_pte_prefetch(struct k__direct_pte_prefetch (vcpu, sp, sptep); } +/* +* Lookup the mapping level for @gfn in the current mm. +* +* WARNING! Use of host_pfn_mapping_level() requires the caller and the end +* consumer to be tied into KVM's handlers for MMU notifier events! +* +* There are several ways to safely use this helper: +* +* - Check mmu_notifier_retry_hva() after grabbing the mapping level, before +* consuming it. In this case, mmu_lock doesn't need to be held during the +* lookup, but it does need to be held while checking the MMU notifier. +* +* - Hold mmu_lock AND ensure there is no in-progress MMU notifier invalidation +* event for the hva. This can be done by explicit checking the MMU notifier +* or by ensuring that KVM already has a valid mapping that covers the hva. +* +* - Do not use the result to install new mappings, e.g. use the host mapping +* level only to decide whether or not to zap an entry. In this case, it's +* not required to hold mmu_lock (though it's highly likely the caller will +* want to hold mmu_lock anyways, e.g. to modify SPTEs). +* +* Note! The lookup can still race with modifications to host page tables, but +* the above "rules" ensure KVM will not _consume_ the result of the walk if a +* race with the primary MMU occurs. +*/ static int host_pfn_mapping_level (struct kvm *kvm,gfn_t gfn,kvm_pfn_t pfn,const struct kvm_memory_slot *slot) {
Greg Kroah-Hartman
maintainerPATCH 165/272 KVM: Rename mmu_notifier_* to mmu_invalidate_*
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Peng <[email protected]>
[ Upstream commit 20ec3ebd707c77fb9b11b37193449193d4649f33 ]
The motivation of this renaming is to make these variables and related helper functions less mmu_notifier bound and can also be used for non mmu_notifier based page invalidation. mmu_invalidate_* was chosen to better describe the purpose of 'invalidating' a page that those variables are used for.
- mmu_notifier_seq/range_start/range_end are renamed to mmu_invalidate_seq/range_start/range_end.
- mmu_notifier_retry{_hva} helper functions are renamed to mmu_invalidate_retry{_hva}.
- mmu_notifier_count is renamed to mmu_invalidate_in_progress to avoid confusion with mn_active_invalidate_count.
- While here, also update kvm_inc/dec_notifier_count() to kvm_mmu_invalidate_begin/end() to match the change for mmu_notifier_count.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: 2abd5287f083 ("KVM: x86: Check for invalid/obsolete root *after* making MMU pages available")
--- arch/arm64/kvm/mmu.c | 10 ++--- arch/mips/kvm/mmu.c | 12 +++--- arch/powerpc/include/asm/kvm_book3s_64.h | 2 - arch/powerpc/kvm/book3s_64_mmu_host.c | 4 +- arch/powerpc/kvm/book3s_64_mmu_hv.c | 4 +- arch/powerpc/kvm/book3s_64_mmu_radix.c | 6 +-- arch/powerpc/kvm/book3s_64_vio_hv.c | 2 - arch/powerpc/kvm/book3s_hv_nested.c | 2 - arch/powerpc/kvm/book3s_hv_rm_mmu.c | 8 ++-- arch/powerpc/kvm/e500_mmu_host.c | 4 +- arch/x86/kvm/mmu/mmu.c | 14 +++---- arch/x86/kvm/mmu/paging_tmpl.h | 6 +-- include/linux/kvm_host.h | 60 ++++++++++++++++--------------- virt/kvm/kvm_main.c | 49 ++++++++++++------------- 14 files changed, 93 insertions(+), 90 deletions(-)
arch/arm64/kvm/mmu.c +5 -5
@@ -881,7 +881,7 @@ transparent_hugepage_adjust(struct kvm ** THP doesn't start to split while we are adjusting the * refcounts. * -* We are sure this doesn't happen, because mmu_notifier_retry +* We are sure this doesn't happen, because mmu_invalidate_retry * was successful and we are holding the mmu_lock, so if this * THP is trying to split, it will be blocked in the mmu * notifier before touching any of the pages, specifically
@@ -1074,14 +1074,14 @@ static int user_mem_abort(struct kvm_vcp gfn = fault_ipa >>PAGE_SHIFT ;/* -* Read mmu_notifier_seq so that KVM can detect if the results of +* Read mmu_invalidate_seq so that KVM can detect if the results of * vma_lookup() or __gfn_to_pfn_memslot() become stale prior to * acquiring kvm->mmu_lock. * * Rely on mmap_read_unlock() for an implicit smp_rmb(), which pairs -* with the smp_wmb() in kvm_dec_notifier_count(). +* with the smp_wmb() in kvm_mmu_invalidate_end(). */ - mmu_seq = vcpu->kvm->mmu_notifier_seq; + mmu_seq = vcpu->kvm->mmu_invalidate_seq;mmap_read_unlock (current->mm); pfn = __gfn_to_pfn_memslot(memslot, gfn, false, NULL,
@@ -1118,7 +1118,7 @@ static int user_mem_abort(struct kvm_vcpspin_lock (&kvm->mmu_lock); pgt = vcpu->arch.hw_mmu->pgt; -if (mmu_notifier_retry (kvm, mmu_seq)) +if (mmu_invalidate_retry (kvm, mmu_seq)) goto out_unlock;/*
arch/mips/kvm/mmu.c +6 -6
@@ -615,17 +615,17 @@ retry:* Used to check for invalidations in progress, of the pfn that is * returned by pfn_to_pfn_prot below. */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;/* -* Ensure the read of mmu_notifier_seq isn't reordered with PTE reads in -* gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't +* Ensure the read of mmu_invalidate_seq isn't reordered with PTE reads +* in gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't * risk the page we get a reference to getting unmapped before we have a -* chance to grab the mmu_lock without mmu_notifier_retry() noticing. +* chance to grab the mmu_lock without mmu_invalidate_retry() noticing. * * This smp_rmb() pairs with the effective smp_wmb() of the combination * of the pte_unmap_unlock() after the PTE is zapped, and the * spin_lock() in kvm_mmu_notifier_invalidate_<page|range_end>() before -* mmu_notifier_seq is incremented. +* mmu_invalidate_seq is incremented. */ smp_rmb ();
@@ -638,7 +638,7 @@ retry:spin_lock (&kvm->mmu_lock);/* Check if an invalidation has taken place since we got pfn */ -if (mmu_notifier_retry (kvm, mmu_seq)) { +if (mmu_invalidate_retry (kvm, mmu_seq)) {/* * This can happen when mappings are changed asynchronously, but * also synchronously if a COW is triggered by
arch/powerpc/include/asm/kvm_book3s_64.h +1 -1
@@ -673,7 +673,7 @@ static inline pte_t *find_kvm_host_pte(sVM_WARN (!spin_is_locked (&kvm->mmu_lock),"%s called with kvm mmu_lock not held \n" , __func__); -if (mmu_notifier_retry (kvm, mmu_seq)) +if (mmu_invalidate_retry (kvm, mmu_seq))return NULL ; pte =__find_linux_pte (kvm->mm->pgd, ea,NULL , hshift);
arch/powerpc/kvm/book3s_64_mmu_host.c +2 -2
@@ -90,7 +90,7 @@ int kvmppc_mmu_map_page(struct kvm_vcpuunsigned long pfn;/* used to check for invalidations in progress */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();/* Get host physical address for gpa */
@@ -151,7 +151,7 @@ int kvmppc_mmu_map_page(struct kvm_vcpu cpte =kvmppc_mmu_hpte_cache_next (vcpu);spin_lock (&kvm->mmu_lock); -if (!cpte ||mmu_notifier_retry (kvm, mmu_seq)) { +if (!cpte ||mmu_invalidate_retry (kvm, mmu_seq)) { r = -EAGAIN ; goto out_unlock; }
arch/powerpc/kvm/book3s_64_mmu_hv.c +2 -2
@@ -570,7 +570,7 @@ int kvmppc_book3s_hv_page_fault(struct kreturn -EFAULT ;/* used to check for invalidations in progress */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb (); ret = -EFAULT ;
@@ -685,7 +685,7 @@ int kvmppc_book3s_hv_page_fault(struct k/* Check if we might have been invalidated; let the guest retry if so */ ret =RESUME_GUEST ; -if (mmu_notifier_retry (vcpu->kvm, mmu_seq)) { +if (mmu_invalidate_retry (vcpu->kvm, mmu_seq)) {unlock_rmap (rmap); goto out_unlock; }
arch/powerpc/kvm/book3s_64_mmu_radix.c +3 -3
@@ -634,7 +634,7 @@ int kvmppc_create_pte(struct kvm *kvm, p/* Check if we might have been invalidated; let the guest retry if so */ spin_lock (&kvm->mmu_lock); ret = -EAGAIN ; -if (mmu_notifier_retry (kvm, mmu_seq)) +if (mmu_invalidate_retry (kvm, mmu_seq)) goto out_unlock;/* Now traverse again under the lock and change the tree */
@@ -824,7 +824,7 @@ int kvmppc_book3s_instantiate_page(strucbool large_enable;/* used to check for invalidations in progress */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();/*
@@ -1185,7 +1185,7 @@ void kvmppc_radix_flush_memslot(struct k* Increase the mmu notifier sequence number to prevent any page * fault that read the memslot earlier from writing a PTE. */ - kvm->mmu_notifier_seq++; + kvm->mmu_invalidate_seq++;spin_unlock (&kvm->mmu_lock); }
arch/powerpc/kvm/book3s_64_vio_hv.c +1 -1
@@ -499,7 +499,7 @@ long kvmppc_rm_h_put_tce_indirect(struct/* * used to check for invalidations in progress */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb (); stt =kvmppc_find_table (vcpu->kvm, liobn);
arch/powerpc/kvm/book3s_hv_nested.c +1 -1
@@ -1574,7 +1574,7 @@ static long int __kvmhv_nested_page_faul/* 2. Find the host pte for this L1 guest real address */ /* Used to check for invalidations in progress */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();/* See if can find translation in our partition scoped tables for L1 */
arch/powerpc/kvm/book3s_hv_rm_mmu.c +4 -4
@@ -216,7 +216,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, g_ptel = ptel;/* used later to detect if we might have been invalidated */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();/* Find the memslot (if any) for this address */
@@ -363,7 +363,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, rmap =real_vmalloc_addr (rmap);lock_rmap (rmap);/* Check for pending invalidations under the rmap chain lock */ -if (mmu_notifier_retry (kvm, mmu_seq)) { +if (mmu_invalidate_retry (kvm, mmu_seq)) {/* inval in progress, write a non-present HPTE */ pteh |=HPTE_V_ABSENT ; pteh &= ~HPTE_V_VALID ;
@@ -929,7 +929,7 @@ static long kvmppc_do_h_page_init_zero(sint i;/* Used later to detect if we might have been invalidated */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();arch_spin_lock (&kvm->mmu_lock.rlock.raw_lock);
@@ -957,7 +957,7 @@ static long kvmppc_do_h_page_init_copy(slong ret =H_SUCCESS ;/* Used later to detect if we might have been invalidated */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();arch_spin_lock (&kvm->mmu_lock.rlock.raw_lock);
arch/powerpc/kvm/e500_mmu_host.c +2 -2
@@ -339,7 +339,7 @@ static inline int kvmppc_e500_shadow_mapunsigned long flags;/* used to check for invalidations in progress */ - mmu_seq = kvm->mmu_notifier_seq; + mmu_seq = kvm->mmu_invalidate_seq;smp_rmb ();/*
@@ -460,7 +460,7 @@ static inline int kvmppc_e500_shadow_map }spin_lock (&kvm->mmu_lock); -if (mmu_notifier_retry (kvm, mmu_seq)) { +if (mmu_invalidate_retry (kvm, mmu_seq)) { ret = -EAGAIN ; goto out; }
arch/x86/kvm/mmu/mmu.c +7 -7
@@ -2863,7 +2863,7 @@ static void direct_pte_prefetch(struct k* If addresses are being invalidated, skip prefetching to avoid * accidentally prefetching those addresses. */ -if (unlikely (vcpu->kvm->mmu_notifier_count)) +if (unlikely (vcpu->kvm->mmu_invalidate_in_progress))return ;__direct_pte_prefetch (vcpu, sp, sptep);
@@ -2877,7 +2877,7 @@ static void direct_pte_prefetch(struct k* * There are several ways to safely use this helper: * -* - Check mmu_notifier_retry_hva() after grabbing the mapping level, before +* - Check mmu_invalidate_retry_hva() after grabbing the mapping level, before * consuming it. In this case, mmu_lock doesn't need to be held during the * lookup, but it does need to be held while checking the MMU notifier. *
@@ -2972,7 +2972,7 @@ int kvm_mmu_hugepage_adjust(struct kvm_vreturn PG_LEVEL_4K ;/* -* mmu_notifier_retry() was successful and mmu_lock is held, so +* mmu_invalidate_retry() was successful and mmu_lock is held, so * the pmd can't be split from under us. */ mask =KVM_PAGES_PER_HPAGE (level) -1 ;
@@ -4018,7 +4018,7 @@ static int direct_page_fault(struct kvm_if (r)return r; - mmu_seq = vcpu->kvm->mmu_notifier_seq; + mmu_seq = vcpu->kvm->mmu_invalidate_seq;smp_rmb (); if (kvm_faultin_pfn(vcpu, prefault, gfn, gpa, &pfn, &hva,
@@ -4035,7 +4035,7 @@ static int direct_page_fault(struct kvm_else write_lock (&vcpu->kvm->mmu_lock); -if (!is_noslot_pfn (pfn) &&mmu_notifier_retry_hva (vcpu->kvm, mmu_seq, hva)) +if (!is_noslot_pfn (pfn) &&mmu_invalidate_retry_hva (vcpu->kvm, mmu_seq, hva)) goto out_unlock;if (is_tdp_mmu_fault) {
@@ -5767,7 +5767,7 @@ void kvm_zap_gfn_range(struct kvm *kvm,write_lock (&kvm->mmu_lock); -kvm_inc_notifier_count (kvm, gfn_start, gfn_end); +kvm_mmu_invalidate_begin (kvm, gfn_start, gfn_end);if (kvm_memslots_have_rmaps (kvm)) {for (i =0 ; i <KVM_ADDRESS_SPACE_NUM ; i++) {
@@ -5802,7 +5802,7 @@ void kvm_zap_gfn_range(struct kvm *kvm,kvm_flush_remote_tlbs_with_address (kvm, gfn_start, gfn_end - gfn_start); -kvm_dec_notifier_count (kvm, gfn_start, gfn_end); +kvm_mmu_invalidate_end (kvm, gfn_start, gfn_end);write_unlock (&kvm->mmu_lock); }
arch/x86/kvm/mmu/paging_tmpl.h +3 -3
@@ -634,7 +634,7 @@ static void FNAME(pte_prefetch)(struct k* If addresses are being invalidated, skip prefetching to avoid * accidentally prefetching those addresses. */ -if (unlikely (vcpu->kvm->mmu_notifier_count)) +if (unlikely (vcpu->kvm->mmu_invalidate_in_progress))return ;if (sp->role.direct)
@@ -894,7 +894,7 @@ static int FNAME(page_fault)(struct kvm_else max_level = walker.level; - mmu_seq = vcpu->kvm->mmu_notifier_seq; + mmu_seq = vcpu->kvm->mmu_invalidate_seq;smp_rmb (); if (kvm_faultin_pfn(vcpu, prefault, walker.gfn, addr, &pfn, &hva,
@@ -925,7 +925,7 @@ static int FNAME(page_fault)(struct kvm_ r =RET_PF_RETRY ;write_lock (&vcpu->kvm->mmu_lock); -if (!is_noslot_pfn (pfn) &&mmu_notifier_retry_hva (vcpu->kvm, mmu_seq, hva)) +if (!is_noslot_pfn (pfn) &&mmu_invalidate_retry_hva (vcpu->kvm, mmu_seq, hva)) goto out_unlock;kvm_mmu_audit (vcpu,AUDIT_PRE_PAGE_FAULT );
include/linux/kvm_host.h +31 -29
93 lines not on this page · raw message
virt/kvm/kvm_main.c +25 -24
115 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 166/272 KVM: x86/mmu: Split out TDP MMU page fault handling
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Matlack <[email protected]>
[ Upstream commit 9aa8ab43b38146029de807a8ff2696f51e15b226 ]
Split out the page fault handling for the TDP MMU to a separate function. This creates some duplicate code, but makes the TDP MMU fault handler simpler to read by eliminating branches and will enable future cleanups by allowing the TDP MMU and non-TDP MMU fault paths to diverge.
Only compile in the TDP MMU fault handler for 64-bit builds since kvm_tdp_mmu_map() does not exist in 32-bit builds.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: 2abd5287f083 ("KVM: x86: Check for invalid/obsolete root *after* making MMU pages available")
--- arch/x86/kvm/mmu/mmu.c | 84 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 20 deletions(-)
arch/x86/kvm/mmu/mmu.c +64 -20
@@ -3997,7 +3997,6 @@ out_retry:static int direct_page_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code,bool prefault,int max_level,bool is_tdp) { -bool is_tdp_mmu_fault =is_tdp_mmu (vcpu->arch.mmu);bool write = error_code &PFERR_WRITE_MASK ;bool map_writable;
@@ -4029,31 +4028,20 @@ static int direct_page_fault(struct kvm_return r; r =RET_PF_RETRY ; - -if (is_tdp_mmu_fault) -read_lock (&vcpu->kvm->mmu_lock); -else -write_lock (&vcpu->kvm->mmu_lock); +write_lock (&vcpu->kvm->mmu_lock);if (!is_noslot_pfn (pfn) &&mmu_invalidate_retry_hva (vcpu->kvm, mmu_seq, hva)) goto out_unlock; -if (is_tdp_mmu_fault) { - r =kvm_tdp_mmu_map (vcpu, gpa, error_code, map_writable, max_level, - pfn, prefault); - }else { - r =make_mmu_pages_available (vcpu); -if (r) - goto out_unlock; - r =__direct_map (vcpu, gpa, error_code, map_writable, max_level, pfn, - prefault, is_tdp); - } + r =make_mmu_pages_available (vcpu); +if (r) + goto out_unlock; + + r =__direct_map (vcpu, gpa, error_code, map_writable, max_level, pfn, + prefault, is_tdp); out_unlock: -if (is_tdp_mmu_fault) -read_unlock (&vcpu->kvm->mmu_lock); -else -write_unlock (&vcpu->kvm->mmu_lock); +write_unlock (&vcpu->kvm->mmu_lock);kvm_release_pfn_clean (pfn);return r; }
@@ -4101,6 +4089,56 @@ int kvm_handle_page_fault(struct kvm_vcp }EXPORT_SYMBOL_GPL (kvm_handle_page_fault); +#ifdef CONFIG_X86_64 +static int kvm_tdp_mmu_page_fault (struct kvm_vcpu *vcpu,gpa_t gpa, +u32 error_code,bool prefault,int max_level) +{ +bool write = error_code &PFERR_WRITE_MASK ; +bool map_writable; + +gfn_t gfn = gpa >>PAGE_SHIFT ; +unsigned long mmu_seq; +kvm_pfn_t pfn; +hva_t hva; +int r; + +if (page_fault_handle_page_track (vcpu, error_code, gfn)) +return RET_PF_EMULATE ; + + r =fast_page_fault (vcpu, gpa, error_code); +if (r !=RET_PF_INVALID ) +return r; + + r =mmu_topup_memory_caches (vcpu, false); +if (r) +return r; + + mmu_seq = vcpu->kvm->mmu_invalidate_seq; +smp_rmb (); + +if (kvm_faultin_pfn (vcpu, prefault, gfn, gpa, &pfn, &hva, + write, &map_writable, &r)) +return r; + +if (handle_abnormal_pfn (vcpu,0 , gfn, pfn,ACC_ALL , &r)) +return r; + + r =RET_PF_RETRY ; +read_lock (&vcpu->kvm->mmu_lock); + +if (!is_noslot_pfn (pfn) &&mmu_invalidate_retry_hva (vcpu->kvm, mmu_seq, hva)) + goto out_unlock; + + r =kvm_tdp_mmu_map (vcpu, gpa, error_code, map_writable, max_level, + pfn, prefault); + +out_unlock: +read_unlock (&vcpu->kvm->mmu_lock); +kvm_release_pfn_clean (pfn); +return r; +} +#endif +int kvm_tdp_page_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code,bool prefault) {
@@ -4116,6 +4154,12 @@ int kvm_tdp_page_fault(struct kvm_vcpu *break ; } +#ifdef CONFIG_X86_64 +if (is_tdp_mmu (vcpu->arch.mmu)) +return kvm_tdp_mmu_page_fault (vcpu, gpa, error_code, prefault, + max_level); +#endif +return direct_page_fault (vcpu, gpa, error_code, prefault, max_level, true); }
Greg Kroah-Hartman
maintainerPATCH 167/272 KVM: x86/mmu: Rename __direct_map() to direct_map()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Matlack <[email protected]>
[ Upstream commit 6c882ef4fc7bd99b67ad152e75428b669281c521 ]
Rename __direct_map() to direct_map() since the leading underscores are unnecessary. This also makes the page fault handler names more
consistent: kvm_tdp_mmu_page_fault() calls kvm_tdp_mmu_map() and direct_page_fault() calls direct_map().
Opportunistically make some trivial cleanups to comments that had to be modified anyway since they mentioned __direct_map(). Specifically, use "()" when referring to functions, and include kvm_tdp_mmu_map() among the various callers of disallowed_hugepage_adjust().
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: 2abd5287f083 ("KVM: x86: Check for invalid/obsolete root *after* making MMU pages available")
--- arch/x86/kvm/mmu/mmu.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
arch/x86/kvm/mmu/mmu.c +10 -10
@@ -2991,11 +2991,11 @@ void disallowed_hugepage_adjust(u64 spte is_shadow_present_pte(spte) && !is_large_pte(spte)) {/* -* A small SPTE exists for this pfn, but FNAME(fetch) -* and __direct_map would like to create a large PTE -* instead: just force them to go down another level, -* patching back for them into pfn the next 9 bits of -* the address. +* A small SPTE exists for this pfn, but FNAME(fetch), +* direct_map(), or kvm_tdp_mmu_map() would like to create a +* large PTE instead: just force them to go down another level, +* patching back for them into pfn the next 9 bits of the +* address. */ u64 page_mask =KVM_PAGES_PER_HPAGE (level) -KVM_PAGES_PER_HPAGE (level -1 );
@@ -3004,9 +3004,9 @@ void disallowed_hugepage_adjust(u64 spte } } -static int __direct_map (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code, -int map_writable,int max_level,kvm_pfn_t pfn, -bool prefault,bool is_tdp) +static int direct_map (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code, +int map_writable,int max_level,kvm_pfn_t pfn, +bool prefault,bool is_tdp) {bool nx_huge_page_workaround_enabled =is_nx_huge_page_enabled ();bool write = error_code &PFERR_WRITE_MASK ;
@@ -4037,8 +4037,8 @@ static int direct_page_fault(struct kvm_if (r) goto out_unlock; - r =__direct_map (vcpu, gpa, error_code, map_writable, max_level, pfn, - prefault, is_tdp); + r =direct_map (vcpu, gpa, error_code, map_writable, max_level, pfn, + prefault, is_tdp); out_unlock:write_unlock (&vcpu->kvm->mmu_lock);
Greg Kroah-Hartman
maintainerPATCH 168/272 drm/dp/mst: fix buffer overflows in sideband chunk accumulation
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
[ Upstream commit 55bd5e685bda455b9b50c835f8c8442d52a344a3 ]
drm_dp_sideband_append_payload() has three related bugs when processing device-provided sideband reply data:
1. Zero-length curchunk_len underflow: msg_len is a 6-bit field taken directly from the DP sideband header. If a device sends msg_len=0, curchunk_len is set to zero. The condition (curchunk_idx >= curchunk_len) is immediately true, and curchunk_len-1 wraps to 255 (u8 underflow). drm_dp_msg_data_crc4() reads 255 bytes from chunk[48], then memcpy() writes 255 bytes into msg[], both far out of bounds.
2. chunk[48] overflow: curchunk_len can reach 63 (6-bit field). chunk[] is only 48 bytes. Multi-iteration payload assembly appends 16-byte blocks until curchunk_idx reaches curchunk_len, writing up to 15 bytes past the end of chunk[] into msg[].
3. msg[256] overflow: each chunk contributes (curchunk_len-1) bytes to msg[]. No check ensures curlen + (curchunk_len-1) stays within msg[256], so the memcpy can spill into adjacent struct fields.
All three are reachable from any DP MST device that can forge sideband reply messages on a physical connection.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/gpu/drm/drm_dp_mst_topology.c +9 -0
@@ -779,6 +779,12 @@ static bool drm_dp_sideband_append_paylo {u8 crc4; +/* curchunk_len must be >= 1 (min 1 CRC byte) and fit in chunk[] */ +if (!msg->curchunk_len || + msg->curchunk_len >ARRAY_SIZE (msg->chunk) || + msg->curchunk_idx + replybuflen >ARRAY_SIZE (msg->chunk)) +return false; +memcpy (&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); msg->curchunk_idx += replybuflen;
@@ -789,6 +795,9 @@ static bool drm_dp_sideband_append_payloprint_hex_dump (KERN_DEBUG ,"wrong crc" ,DUMP_PREFIX_NONE ,16 ,1 , msg->chunk, msg->curchunk_len, false); +/* Guard against accumulated msg[] overflow */ +if (msg->curlen + msg->curchunk_len -1 >ARRAY_SIZE (msg->msg)) +return false;/* copy chunk into bigger msg */ memcpy (&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len -1 ); msg->curlen += msg->curchunk_len -1 ;
Greg Kroah-Hartman
maintainerPATCH 169/272 usb: gadget: f_tcm: synchronize delayed set_alt with teardown
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 79e2d75725c85607f8a9d87ae9cace62a19f767d ]
The f_tcm set_alt() path defers endpoint setup to a work item and completes the delayed status response from process context. The delayed work uses f_tcm private state and may complete the setup request after disconnect or function teardown has already moved on.
Cancel and drain the delayed set_alt work when the function is unbound or freed. For disable paths, which are reached under the composite device lock, use a small state machine and a non-sleeping cancellation path instead of cancel_work_sync(). If the work is already running, mark it cancelled and let the worker own the cleanup; otherwise tcm_disable() can cancel the queued work and clean up immediately.
Also serialize the final delayed-status completion with the cancellation check while holding the composite device lock. This prevents a disconnect from clearing delayed_status while the worker is about to complete the control request.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0 Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? tcm_delayed_set_alt+0x6c/0xef0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? tcm_delayed_set_alt+0x6c/0xef0 kasan_report+0xe0/0x110 ? tcm_delayed_set_alt+0x6c/0xef0 tcm_delayed_set_alt+0x6c/0xef0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? process_one_work+0x4cb/0xb90 ? rcu_is_watching+0x20/0x50 ? tcm_delayed_set_alt+0x9/0xef0 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 544: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x8f/0xa0 tcm_alloc+0x68/0x180 usb_get_function+0x36/0x60 config_usb_cfg_link+0x125/0x1b0 configfs_symlink+0x322/0x890 vfs_symlink+0xc2/0x270 filename_symlinkat+0x295/0x2f0 __x64_sys_symlinkat+0x62/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 661: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kfree+0x2f9/0x530 config_usb_cfg_unlink+0x173/0x1e0 configfs_unlink+0x1fa/0x340 vfs_unlink+0x15c/0x510 filename_unlinkat+0x2ba/0x450 __x64_sys_unlinkat+0x63/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f
--- drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++++++++------ drivers/usb/gadget/function/tcm.h | 13 ++ 2 files changed, 177 insertions(+), 28 deletions(-)
drivers/usb/gadget/function/f_tcm.c +164 -28
@@ -2047,31 +2047,158 @@ ep_fail:return -ENOTSUPP ; } -struct guas_setup_wq { -struct work_struct work; -struct f_uas *fu; -unsigned int alt; -}; +static void tcm_cleanup_old_alt (struct f_uas *fu) +{ +if (fu->flags &USBG_IS_UAS ) +uasp_cleanup_old_alt (fu); +else if (fu->flags &USBG_IS_BOT ) +bot_cleanup_old_alt (fu); + fu->flags =0 ; +} + +static void tcm_delayed_set_alt_done (struct f_uas *fu) +{ +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +} + +static bool tcm_delayed_set_alt_cancelled (struct f_uas *fu) +{ +bool cancelled; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + cancelled = fu->delayed_set_alt_cancel; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +return cancelled; +} + +static bool tcm_complete_delayed_status (struct f_uas *fu) +{ +struct usb_composite_dev *cdev = fu->function.config->cdev; +struct usb_request *req = cdev->req; +unsigned long cdev_flags; +bool cancelled; +int ret; + +spin_lock_irqsave (&cdev->lock, cdev_flags); +spin_lock (&fu->delayed_set_alt_lock); + cancelled = fu->delayed_set_alt_cancel; +if (!cancelled) { + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; + } +spin_unlock (&fu->delayed_set_alt_lock); + +if (cancelled) { +spin_unlock_irqrestore (&cdev->lock, cdev_flags); +return false; + } + +if (cdev->delayed_status ==0 ) { +WARN (cdev,"%s: Unexpected call\n" , __func__); + }else if (--cdev->delayed_status ==0 ) { + req->length =0 ; + req->context = cdev; + ret =usb_ep_queue (cdev->gadget->ep0, req,GFP_ATOMIC ); +if (ret ==0 ) { + cdev->setup_pending = true; + }else { + req->status =0 ; + req->complete (cdev->gadget->ep0, req); + } + } + +spin_unlock_irqrestore (&cdev->lock, cdev_flags); + +return true; +} + +static bool tcm_cancel_delayed_set_alt (struct f_uas *fu) +{ +bool cleanup = false; +bool cancel = false; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +switch (fu->delayed_set_alt_state) { +case USBG_DELAYED_SET_ALT_IDLE : + cleanup = true; +break ; +case USBG_DELAYED_SET_ALT_QUEUED : +case USBG_DELAYED_SET_ALT_RUNNING : + fu->delayed_set_alt_cancel = true; + cancel = true; +break ; + } +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +if (cancel &&cancel_work (&fu->delayed_set_alt)) { +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state ==USBG_DELAYED_SET_ALT_QUEUED ) { + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; + cleanup = true; + } +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + } + +return cleanup; +} + +static void tcm_cancel_delayed_set_alt_sync (struct f_uas *fu) +{ +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_IDLE ) + fu->delayed_set_alt_cancel = true; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +cancel_work_sync (&fu->delayed_set_alt); + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +}static void tcm_delayed_set_alt (struct work_struct *wq) { - struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, - work); -struct f_uas *fu = work->fu; -int alt = work->alt; + struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt); +unsigned long flags; +unsigned int alt; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_QUEUED ) { +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +return ; + } + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_RUNNING ; + alt = fu->delayed_alt; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); -kfree (work); +tcm_cleanup_old_alt (fu); -if (fu->flags &USBG_IS_BOT ) -bot_cleanup_old_alt (fu); -if (fu->flags &USBG_IS_UAS ) -uasp_cleanup_old_alt (fu); +if (tcm_delayed_set_alt_cancelled (fu)) + goto out_done;if (alt ==USB_G_ALT_INT_BBB )bot_set_alt (fu);else if (alt ==USB_G_ALT_INT_UAS )uasp_set_alt (fu); -usb_composite_setup_continue (fu->function.config->cdev); + +if (tcm_complete_delayed_status (fu)) +return ; + +tcm_cleanup_old_alt (fu); +out_done: +tcm_delayed_set_alt_done (fu); }static int tcm_get_alt (struct usb_function *f,unsigned intf)
@@ -2097,15 +2224,20 @@ static int tcm_set_alt(struct usb_functireturn -EOPNOTSUPP ;if ((alt ==USB_G_ALT_INT_BBB ) || (alt ==USB_G_ALT_INT_UAS )) { -struct guas_setup_wq *work; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_IDLE ) { +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, + flags); +return -EBUSY ; + } + fu->delayed_alt = alt; + fu->delayed_set_alt_cancel = false; + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_QUEUED ; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); - work =kmalloc (sizeof (*work),GFP_ATOMIC ); -if (!work) -return -ENOMEM ; -INIT_WORK (&work->work, tcm_delayed_set_alt); - work->fu = fu; - work->alt = alt; -schedule_work (&work->work); +schedule_work (&fu->delayed_set_alt);return USB_GADGET_DELAYED_STATUS ; }return -EOPNOTSUPP ;
@@ -2115,11 +2247,8 @@ static void tcm_disable(struct usb_funct {struct f_uas *fu =to_f_uas (f); -if (fu->flags &USBG_IS_UAS ) -uasp_cleanup_old_alt (fu); -else if (fu->flags &USBG_IS_BOT ) -bot_cleanup_old_alt (fu); - fu->flags =0 ; +if (tcm_cancel_delayed_set_alt (fu)) +tcm_cleanup_old_alt (fu); } static int tcm_setup(struct usb_function *f,
@@ -2267,11 +2396,16 @@ static void tcm_free(struct usb_function {struct f_uas *tcm =to_f_uas (f); +tcm_cancel_delayed_set_alt_sync (tcm);kfree (tcm); }static void tcm_unbind (struct usb_configuration *c,struct usb_function *f) { +struct f_uas *fu =to_f_uas (f); + +tcm_cancel_delayed_set_alt_sync (fu); +tcm_cleanup_old_alt (fu);usb_free_all_descriptors (f); }
@@ -2304,6 +2438,8 @@ static struct usb_function *tcm_alloc(st fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; +INIT_WORK (&fu->delayed_set_alt, tcm_delayed_set_alt); +spin_lock_init (&fu->delayed_set_alt_lock);mutex_unlock (&tpg_instances_lock);return &fu->function;
drivers/usb/gadget/function/tcm.h +13 -0
@@ -3,6 +3,7 @@#define __TARGET_USB_GADGET_H__#include <linux/kref.h> +#include <linux/spinlock.h> /* #include <linux/usb/uas.h> */ #include <linux/usb/composite.h> #include <linux/usb/uas.h>
@@ -26,6 +27,12 @@ enum {#define USB_G_DEFAULT_SESSION_TAGS 128 +enum { +USBG_DELAYED_SET_ALT_IDLE =0 , +USBG_DELAYED_SET_ALT_QUEUED , +USBG_DELAYED_SET_ALT_RUNNING , +}; +struct tcm_usbg_nexus {struct se_session *tvn_se_sess; };
@@ -117,6 +124,12 @@ struct f_uas {#define USBG_IS_BOT (1 << 3)#define USBG_BOT_CMD_PEND (1 << 4) +struct work_struct delayed_set_alt; +spinlock_t delayed_set_alt_lock;/* protects delayed_set_alt_* */ +unsigned int delayed_alt; +unsigned int delayed_set_alt_state; +bool delayed_set_alt_cancel; +struct usbg_cdb cmd;struct usb_ep *ep_in;struct usb_ep *ep_out;
Greg Kroah-Hartman
maintainerPATCH 170/272 drm/dp/mst: fix OOB reads in remote DPCD/I2C sideband reply parsers
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
[ Upstream commit 1a8f537f5a1eeac941f262fe73078d6b08ba83c0 ]
drm_dp_sideband_parse_remote_dpcd_read() reads num_bytes from the raw message and then unconditionally does:
memcpy(bytes, &raw->msg[idx], num_bytes);
without checking that idx + num_bytes <= raw->curlen. raw->msg[] is 256 bytes; if a malicious or misbehaving MST hub sets num_bytes larger than the remaining payload, the memcpy reads past the received data into whatever follows in raw->msg[].
drm_dp_sideband_parse_remote_i2c_read_ack() has the same flaw (noted with a /* TODO check */ comment since the code was introduced).
Fix both functions by using a single combined check
(idx + num_bytes > curlen) before each memcpy. Since num_bytes is u8, it is always >= 0, so this strictly subsumes the simpler idx > curlen form and no separate step is needed.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +4 -2
@@ -870,7 +870,7 @@ static bool drm_dp_sideband_parse_remote goto fail_len; repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; idx++; -if (idx > raw->curlen) +if (idx + repmsg->u.remote_dpcd_read_ack.num_bytes > raw->curlen) goto fail_len;memcpy (repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
@@ -906,7 +906,9 @@ static bool drm_dp_sideband_parse_remote goto fail_len; repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx]; idx++; -/* TODO check */ +if (idx + repmsg->u.remote_i2c_read_ack.num_bytes > raw->curlen) + goto fail_len; +memcpy (repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);return true; fail_len:
Greg Kroah-Hartman
maintainerPATCH 171/272 drm/dp/mst: fix OOB reads on 2-byte fields in sideband reply parsers
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
[ Upstream commit 6b89ba3dba2f583626fb693e47e951ffb8bf591f ]
Three sideband reply parsers read 16-bit fields as:
val = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
and check bounds only after the fact. When idx == raw->curlen, raw->msg[idx+1] reads one byte past the received message data into the following struct fields (curchunk_len, curchunk_idx, curlen).
Affected functions:
- drm_dp_sideband_parse_enum_path_resources_ack() full_payload_bw_number and avail_payload_bw_number fields
- drm_dp_sideband_parse_allocate_payload_ack() allocated_pbn field
- drm_dp_sideband_parse_query_payload_ack() allocated_pbn field
Fix by using a single combined check (idx + 2 > curlen) before each 2-byte read. Since the check is strictly tighter than idx > curlen, no separate step is needed.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +4 -13
@@ -924,16 +924,13 @@ static bool drm_dp_sideband_parse_enum_p repmsg->u.path_resources.port_number = (raw->msg[idx] >>4 ) &0xf ; repmsg->u.path_resources.fec_capable = raw->msg[idx] &0x1 ; idx++; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] <<8 ) | (raw->msg[idx+1 ]); idx +=2 ; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] <<8 ) | (raw->msg[idx+1 ]); - idx +=2 ; -if (idx > raw->curlen) - goto fail_len;return true; fail_len:DRM_DEBUG_KMS ("enum resource parse length fail %d %d\n" , idx, raw->curlen);
@@ -951,12 +948,9 @@ static bool drm_dp_sideband_parse_alloca goto fail_len; repmsg->u.allocate_payload.vcpi = raw->msg[idx]; idx++; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] <<8 ) | (raw->msg[idx+1 ]); - idx +=2 ; -if (idx > raw->curlen) - goto fail_len;return true; fail_len:DRM_DEBUG_KMS ("allocate payload parse length fail %d %d\n" , idx, raw->curlen);
@@ -970,12 +964,9 @@ static bool drm_dp_sideband_parse_query_ repmsg->u.query_payload.port_number = (raw->msg[idx] >>4 ) &0xf ; idx++; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] <<8 ) | (raw->msg[idx +1 ]); - idx +=2 ; -if (idx > raw->curlen) - goto fail_len;return true; fail_len:DRM_DEBUG_KMS ("query payload parse length fail %d %d\n" , idx, raw->curlen);
Greg Kroah-Hartman
maintainerPATCH 172/272 dma-buf/drivers: make reserving a shared slot mandatory v4
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian König <[email protected]>
[ Upstream commit c8d4c18bfbc4ab467188dbe45cc8155759f49d9e ]
Audit all the users of dma_resv_add_excl_fence() and make sure they reserve a shared slot also when only trying to add an exclusive fence.
This is the next step towards handling the exclusive fence like a shared one.
v2: fix missed case in amdgpu
v3: and two more radeon, rename function
v4: add one more case to TTM, fix i915 after rebase
Stable-dep-of: a48bbcc7ac73 ("drm/virtio: use uninterruptible resv lock for plane updates")
--- drivers/dma-buf/dma-resv.c | 10 +++++----- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 4 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 4 ++-- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 4 ++-- drivers/gpu/drm/i915/i915_vma.c | 2 +- drivers/gpu/drm/lima/lima_gem.c | 2 +- drivers/gpu/drm/msm/msm_gem_submit.c | 2 +- drivers/gpu/drm/nouveau/nouveau_fence.c | 2 +- drivers/gpu/drm/qxl/qxl_release.c | 2 +- drivers/gpu/drm/radeon/radeon_vm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 4 ++-- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 4 ++-- drivers/gpu/drm/vc4/vc4_gem.c | 2 +- drivers/gpu/drm/vgem/vgem_fence.c | 2 +- drivers/gpu/drm/virtio/virtgpu_gem.c | 9 +++++++++ include/linux/dma-resv.h | 2 +- 18 files changed, 35 insertions(+), 26 deletions(-)
drivers/dma-buf/dma-resv.c +5 -5
@@ -131,7 +131,7 @@ void dma_resv_fini(struct dma_resv *obj)EXPORT_SYMBOL (dma_resv_fini);/** -* dma_resv_reserve_shared - Reserve space to add shared fences to +* dma_resv_reserve_fences - Reserve space to add shared fences to * a dma_resv. * @obj: reservation object * @num_fences: number of fences we want to add
@@ -142,7 +142,7 @@ EXPORT_SYMBOL(dma_resv_fini);* RETURNS * Zero for success, or -errno */ -int dma_resv_reserve_shared (struct dma_resv *obj,unsigned int num_fences) +int dma_resv_reserve_fences (struct dma_resv *obj,unsigned int num_fences) {struct dma_resv_list *old, *new;unsigned int i, j, k, max;
@@ -205,7 +205,7 @@ int dma_resv_reserve_shared(struct dma_rreturn 0 ; } -EXPORT_SYMBOL (dma_resv_reserve_shared); +EXPORT_SYMBOL (dma_resv_reserve_fences);#ifdef CONFIG_DEBUG_MUTEXES /**
@@ -213,7 +213,7 @@ EXPORT_SYMBOL(dma_resv_reserve_shared);* @obj: the dma_resv object to reset * * Reset the number of pre-reserved shared slots to test that drivers do -* correct slot allocation using dma_resv_reserve_shared(). See also +* correct slot allocation using dma_resv_reserve_fences(). See also * &dma_resv_list.shared_max. */ void dma_resv_reset_shared_max (struct dma_resv *obj)
@@ -235,7 +235,7 @@ EXPORT_SYMBOL(dma_resv_reset_shared_max)* @fence: the shared fence to add * * Add a fence to a shared slot, obj->lock must be held, and -* dma_resv_reserve_shared() has been called. +* dma_resv_reserve_fences() has been called. */ void dma_resv_add_shared_fence (struct dma_resv *obj,struct dma_fence *fence) {
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +2 -2
@@ -1236,7 +1236,7 @@ static int init_kfd_vm(struct amdgpu_vm AMDGPU_FENCE_OWNER_KFD, false);if (ret) goto wait_pd_fail; - ret =dma_resv_reserve_shared (vm->root.bo->tbo.base.resv,1 ); + ret =dma_resv_reserve_fences (vm->root.bo->tbo.base.resv,1 );if (ret) goto reserve_shared_fail; amdgpu_bo_fence(vm->root.bo,
@@ -2461,7 +2461,7 @@ int amdgpu_amdkfd_add_gws_to_process(voi* Add process eviction fence to bo so they can * evict each other. */ - ret =dma_resv_reserve_shared (gws_bo->tbo.base.resv,1 ); + ret =dma_resv_reserve_fences (gws_bo->tbo.base.resv,1 );if (ret) goto reserve_shared_fail;amdgpu_bo_fence (gws_bo, &process_info->eviction_fence->base, true);
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +2 -2
@@ -3008,7 +3008,7 @@ int amdgpu_vm_init(struct amdgpu_deviceif (r) goto error_free_root; - r =dma_resv_reserve_shared (root_bo->tbo.base.resv,1 ); + r =dma_resv_reserve_fences (root_bo->tbo.base.resv,1 );if (r) goto error_unreserve;
@@ -3459,7 +3459,7 @@ bool amdgpu_vm_handle_fault(struct amdgp value =0 ; } - r =dma_resv_reserve_shared (root->tbo.base.resv,1 ); + r =dma_resv_reserve_fences (root->tbo.base.resv,1 );if (r) {pr_debug ("failed %d to reserve fence slot\n" , r); goto error_unlock;
drivers/gpu/drm/amd/amdkfd/kfd_svm.c +1 -1
@@ -512,7 +512,7 @@ svm_range_vram_node_new(struct amdgpu_de goto reserve_bo_failed; } - r =dma_resv_reserve_shared (bo->tbo.base.resv,1 ); + r =dma_resv_reserve_fences (bo->tbo.base.resv,1 );if (r) {pr_debug ("failed %d to reserve bo\n" , r);amdgpu_bo_unreserve (bo);
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +1 -1
@@ -180,7 +180,7 @@ static int submit_fence_sync(struct etnastruct dma_resv *robj = bo->obj->base.resv;if (!(bo->flags &ETNA_SUBMIT_BO_WRITE )) { - ret =dma_resv_reserve_shared (robj,1 ); + ret =dma_resv_reserve_fences (robj,1 );if (ret)return ret; }
drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +2 -2
@@ -958,7 +958,7 @@ static int eb_validate_vmas(struct i915_ }if (!(ev->flags &EXEC_OBJECT_WRITE )) { - err =dma_resv_reserve_shared (vma->resv,1 ); + err =dma_resv_reserve_fences (vma->resv,1 );if (err)return err; }
@@ -2160,7 +2160,7 @@ static int eb_parse(struct i915_execbuff goto err_trampoline; } - err =dma_resv_reserve_shared (shadow->resv,1 ); + err =dma_resv_reserve_fences (shadow->resv,1 );if (err) goto err_trampoline;
drivers/gpu/drm/i915/i915_vma.c +1 -1
@@ -1278,7 +1278,7 @@ int i915_vma_move_to_active(struct i915_ obj->read_domains =0 ; }else {if (!(flags & __EXEC_OBJECT_NO_RESERVE)) { - err =dma_resv_reserve_shared (vma->resv,1 ); + err =dma_resv_reserve_fences (vma->resv,1 );if (unlikely (err))return err; }
drivers/gpu/drm/lima/lima_gem.c +1 -1
@@ -263,7 +263,7 @@ static int lima_gem_sync_bo(struct lima_int err =0 ;if (!write) { - err =dma_resv_reserve_shared (lima_bo_resv (bo),1 ); + err =dma_resv_reserve_fences (lima_bo_resv (bo),1 );if (err)return err; }
drivers/gpu/drm/msm/msm_gem_submit.c +1 -1
@@ -349,7 +349,7 @@ static int submit_fence_sync(struct msm_* strange place to call it. OTOH this is a * convenient can-fail point to hook it in. */ - ret =dma_resv_reserve_shared (obj->resv,1 ); + ret =dma_resv_reserve_fences (obj->resv,1 );if (ret)return ret; }
drivers/gpu/drm/nouveau/nouveau_fence.c +1 -1
@@ -349,7 +349,7 @@ nouveau_fence_sync(struct nouveau_bo *nvint ret =0 , i;if (!exclusive) { - ret =dma_resv_reserve_shared (resv,1 ); + ret =dma_resv_reserve_fences (resv,1 );if (ret)return ret;
drivers/gpu/drm/qxl/qxl_release.c +1 -1
@@ -200,7 +200,7 @@ static int qxl_release_validate_bo(strucreturn ret; } - ret =dma_resv_reserve_shared (bo->tbo.base.resv,1 ); + ret =dma_resv_reserve_fences (bo->tbo.base.resv,1 );if (ret)return ret;
drivers/gpu/drm/radeon/radeon_vm.c +1 -1
@@ -831,7 +831,7 @@ static int radeon_vm_update_ptes(structint r;radeon_sync_resv (rdev, &ib->sync, pt->tbo.base.resv, true); - r =dma_resv_reserve_shared (pt->tbo.base.resv,1 ); + r =dma_resv_reserve_fences (pt->tbo.base.resv,1 );if (r)return r;
drivers/gpu/drm/ttm/ttm_bo.c +2 -2
@@ -767,7 +767,7 @@ static int ttm_bo_add_move_fence(structdma_resv_add_shared_fence (bo->base.resv, fence); - ret =dma_resv_reserve_shared (bo->base.resv,1 ); + ret =dma_resv_reserve_fences (bo->base.resv,1 );if (unlikely (ret)) {dma_fence_put (fence);return ret;
@@ -826,7 +826,7 @@ int ttm_bo_mem_space(struct ttm_buffer_obool type_found = false;int i, ret; - ret =dma_resv_reserve_shared (bo->base.resv,1 ); + ret =dma_resv_reserve_fences (bo->base.resv,1 );if (unlikely (ret))return ret;
drivers/gpu/drm/ttm/ttm_execbuf_util.c +2 -2
@@ -104,7 +104,7 @@ int ttm_eu_reserve_buffers(struct ww_acqif (!entry->num_shared)continue ; - ret =dma_resv_reserve_shared (bo->base.resv, + ret =dma_resv_reserve_fences (bo->base.resv, entry->num_shared);if (!ret)continue ;
@@ -121,7 +121,7 @@ int ttm_eu_reserve_buffers(struct ww_acq }if (!ret && entry->num_shared) - ret =dma_resv_reserve_shared (bo->base.resv, + ret =dma_resv_reserve_fences (bo->base.resv, entry->num_shared);if (unlikely (ret !=0 )) {
drivers/gpu/drm/vc4/vc4_gem.c +1 -1
@@ -644,7 +644,7 @@ retry:for (i =0 ; i < exec->bo_count; i++) { bo = &exec->bo[i]->base; - ret =dma_resv_reserve_shared (bo->resv,1 ); + ret =dma_resv_reserve_fences (bo->resv,1 );if (ret) {vc4_unlock_bo_reservations (dev, exec, acquire_ctx);return ret;
drivers/gpu/drm/vgem/vgem_fence.c +1 -1
@@ -161,7 +161,7 @@ int vgem_fence_attach_ioctl(struct drm_ddma_resv_lock (resv,NULL );if (arg->flags &VGEM_FENCE_WRITE )dma_resv_add_excl_fence (resv, fence); -else if ((ret =dma_resv_reserve_shared (resv,1 )) ==0 ) +else if ((ret =dma_resv_reserve_fences (resv,1 )) ==0 )dma_resv_add_shared_fence (resv, fence);dma_resv_unlock (resv);
drivers/gpu/drm/virtio/virtgpu_gem.c +9 -0
@@ -214,6 +214,7 @@ void virtio_gpu_array_add_obj(struct virint virtio_gpu_array_lock_resv (struct virtio_gpu_object_array *objs) { +unsigned int i;int ret;if (objs->nents ==1 ) {
@@ -222,6 +223,14 @@ int virtio_gpu_array_lock_resv(struct vi ret =drm_gem_lock_reservations (objs->objs, objs->nents, &objs->ticket); } +if (ret) +return ret; + +for (i =0 ; i < objs->nents; ++i) { + ret =dma_resv_reserve_fences (objs->objs[i]->resv,1 ); +if (ret) +return ret; + }return ret; }
include/linux/dma-resv.h +1 -1
@@ -364,7 +364,7 @@ static inline struct dma_resv_list *dma_void dma_resv_init (struct dma_resv *obj);void dma_resv_fini (struct dma_resv *obj); -int dma_resv_reserve_shared (struct dma_resv *obj,unsigned int num_fences); +int dma_resv_reserve_fences (struct dma_resv *obj,unsigned int num_fences);void dma_resv_add_shared_fence (struct dma_resv *obj,struct dma_fence *fence);void dma_resv_add_excl_fence (struct dma_resv *obj,struct dma_fence *fence); int dma_resv_get_fences(struct dma_resv *obj, struct dma_fence **pfence_excl,
Greg Kroah-Hartman
maintainerPATCH 173/272 drm/virtio: use uninterruptible resv lock for plane updates
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
[ Upstream commit a48bbcc7ac739e93562d6148c6fa504c2e9f22f8 ]
virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush() lock the framebuffer BO's dma_resv via virtio_gpu_array_lock_resv() and ignore its return value. The function can fail with -EINTR from dma_resv_lock_interruptible() (signal during lock wait) or with -ENOMEM from dma_resv_reserve_fences() (fence slot allocation), leaving the resv lock not held. The queue path then walks the object array and calls dma_resv_add_fence(), which requires the lock held; with lockdep enabled this trips dma_resv_assert_held():
WARNING: drivers/dma-buf/dma-resv.c:296 at dma_resv_add_fence+0x71e/0x840 Call Trace: virtio_gpu_array_add_fence virtio_gpu_queue_ctrl_sgs virtio_gpu_queue_fenced_ctrl_buffer virtio_gpu_cursor_plane_update drm_atomic_helper_commit_planes drm_atomic_helper_commit_tail commit_tail drm_atomic_helper_commit drm_atomic_commit drm_atomic_helper_update_plane __setplane_atomic drm_mode_cursor_universal drm_mode_cursor_common drm_mode_cursor_ioctl drm_ioctl __x64_sys_ioctl
Beyond the WARN, mutating the dma_resv fence list without the lock races with concurrent readers/writers and can corrupt the list.
Both call sites run inside the .atomic_update plane callback, which DRM atomic helpers do not allow to fail (by the time it runs, the commit has been signed off to userspace and there is no clean rollback path). Moving the lock acquisition to .prepare_fb was rejected because the broader lock scope deadlocks against other BO locking paths in the same atomic commit.
Introduce virtio_gpu_lock_one_resv_uninterruptible() that uses dma_resv_lock() instead of dma_resv_lock_interruptible(). This eliminates the -EINTR failure mode -- the realistic syzbot trigger -- without extending the lock hold across the commit. The helper locks a single BO and rejects nents > 1 with -EINVAL; both fix sites lock exactly one BO.
Use it from virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush(); check the return value to handle the remaining -ENOMEM case from dma_resv_reserve_fences() by freeing the objs and skipping the plane update for that frame. The framebuffer BOs touched here are not shared with other contexts and lock contention is expected to be brief, so the loss of signal-interruptibility is acceptable.
Other callers of virtio_gpu_array_lock_resv() (the ioctl paths) continue to use the interruptible variant.
The bug was reported by syzbot, triggered via fault injection (fail_nth) on the DRM_IOCTL_MODE_CURSOR path, which forces the -ENOMEM branch in dma_resv_reserve_fences().
--- drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + drivers/gpu/drm/virtio/virtgpu_gem.c | 17 +++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_plane.c | 10 ++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-)
drivers/gpu/drm/virtio/virtgpu_drv.h +1 -0
@@ -288,6 +288,7 @@ virtio_gpu_array_from_handles(struct drmvoid virtio_gpu_array_add_obj (struct virtio_gpu_object_array *objs,struct drm_gem_object *obj);int virtio_gpu_array_lock_resv (struct virtio_gpu_object_array *objs); +int virtio_gpu_lock_one_resv_uninterruptible (struct virtio_gpu_object_array *objs);void virtio_gpu_array_unlock_resv (struct virtio_gpu_object_array *objs);void virtio_gpu_array_add_fence (struct virtio_gpu_object_array *objs,struct dma_fence *fence);
drivers/gpu/drm/virtio/virtgpu_gem.c +17 -0
@@ -234,6 +234,23 @@ int virtio_gpu_array_lock_resv(struct vireturn ret; } +int virtio_gpu_lock_one_resv_uninterruptible (struct virtio_gpu_object_array *objs) +{ +int ret; + +if (objs->nents !=1 ) +return -EINVAL ; + +dma_resv_lock (objs->objs[0 ]->resv,NULL ); + + ret =dma_resv_reserve_fences (objs->objs[0 ]->resv,1 ); +if (ret) { +virtio_gpu_array_unlock_resv (objs); +return ret; + } +return 0 ; +} +void virtio_gpu_array_unlock_resv (struct virtio_gpu_object_array *objs) {if (objs->nents ==1 ) {
drivers/gpu/drm/virtio/virtgpu_plane.c +8 -2
@@ -147,7 +147,10 @@ static void virtio_gpu_resource_flush(stif (!objs)return ;virtio_gpu_array_add_obj (objs, vgfb->base.obj[0 ]); -virtio_gpu_array_lock_resv (objs); +if (virtio_gpu_lock_one_resv_uninterruptible (objs)) { +virtio_gpu_array_put_free (objs); +return ; + }virtio_gpu_cmd_resource_flush (vgdev, bo->hw_res_handle, x, y, width, height, objs, vgfb->fence);virtio_gpu_notify (vgdev);
@@ -314,7 +317,10 @@ static void virtio_gpu_cursor_plane_updaif (!objs)return ;virtio_gpu_array_add_obj (objs, vgfb->base.obj[0 ]); -virtio_gpu_array_lock_resv (objs); +if (virtio_gpu_lock_one_resv_uninterruptible (objs)) { +virtio_gpu_array_put_free (objs); +return ; + } virtio_gpu_cmd_transfer_to_host_2d (vgdev, 0, plane->state->crtc_w,
Greg Kroah-Hartman
maintainerPATCH 174/272 drm/displayid: fix Tiled Display Topology ID size
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit 90c0486a82e27393f9eaf3bb350f51a0bd38cb6b ]
The Tiled Display Topology ID of a DisplayID Tiled Display Topology Data Block consists of three fields:
- Tiled Display Manufacturer/Vendor ID Field (3 bytes)
- Tiled Display Product ID Code Field (2 bytes)
- Tiled Display Serial Number Field (4 bytes)
i.e. a total of 9 bytes, not 8.
The DisplayID Tiled Display Topology ID is used as the tile group identifier.
Update both struct displayid_tiled_block topology_id member and struct drm_tile_group group_data member to full 9 bytes.
The group data was missing the last byte of the serial number. I don't know whether there are known bug reports that might be linked to this, but it's plausible the last byte could be the differentiating part for the tile groups, and fewer tile groups might have been created than intended.
--- drivers/gpu/drm/drm_connector.c | 12 ++++++------ include/drm/drm_connector.h | 6 +++--- include/drm/drm_displayid.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-)
drivers/gpu/drm/drm_connector.c +6 -6
@@ -2594,7 +2594,7 @@ EXPORT_SYMBOL(drm_mode_put_tile_group);/** * drm_mode_get_tile_group - get a reference to an existing tile group * @dev: DRM device -* @topology: 8-bytes unique per monitor. +* @topology_id: 9-byte unique ID per monitor. * * Use the unique bytes to get a reference to an existing tile group. *
@@ -2602,14 +2602,14 @@ EXPORT_SYMBOL(drm_mode_put_tile_group);* tile group or NULL if not found. */ struct drm_tile_group *drm_mode_get_tile_group (struct drm_device *dev, -const char topology[8 ]) +const char topology_id[9 ]) {struct drm_tile_group *tg;int id;mutex_lock (&dev->mode_config.idr_mutex);idr_for_each_entry (&dev->mode_config.tile_idr, tg, id) { -if (!memcmp (tg->group_data, topology,8 )) { +if (!memcmp (tg->group_data, topology_id,sizeof (tg->group_data))) {if (!kref_get_unless_zero (&tg->refcount)) tg =NULL ;mutex_unlock (&dev->mode_config.idr_mutex);
@@ -2624,7 +2624,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group);/** * drm_mode_create_tile_group - create a tile group from a displayid description * @dev: DRM device -* @topology: 8-bytes unique per monitor. +* @topology_id: 9-byte unique ID per monitor. * * Create a tile group for the unique monitor, and get a unique * identifier for the tile group.
@@ -2633,7 +2633,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group);* new tile group or NULL. */ struct drm_tile_group *drm_mode_create_tile_group (struct drm_device *dev, -const char topology[8 ]) +const char topology_id[9 ]) {struct drm_tile_group *tg;int ret;
@@ -2643,7 +2643,7 @@ struct drm_tile_group *drm_mode_create_treturn NULL ;kref_init (&tg->refcount); -memcpy (tg->group_data, topology,8 ); +memcpy (tg->group_data, topology_id,sizeof (tg->group_data)); tg->dev = dev;mutex_lock (&dev->mode_config.idr_mutex);
include/drm/drm_connector.h +3 -3
@@ -1728,13 +1728,13 @@ struct drm_tile_group {struct kref refcount;struct drm_device *dev;int id; -u8 group_data[8 ]; +u8 group_data[9 ]; };struct drm_tile_group *drm_mode_create_tile_group (struct drm_device *dev, -const char topology[8 ]); +const char topology_id[9 ]);struct drm_tile_group *drm_mode_get_tile_group (struct drm_device *dev, -const char topology[8 ]); +const char topology_id[9 ]);void drm_mode_put_tile_group (struct drm_device *dev,struct drm_tile_group *tg);
include/drm/drm_displayid.h +1 -1
@@ -75,7 +75,7 @@ struct displayid_tiled_block {u8 topo[3 ];u8 tile_size[4 ];u8 tile_pixel_bezel[5 ]; -u8 topology_id[8 ]; +u8 topology_id[9 ]; } __packed;struct displayid_detailed_timings_1 {
Greg Kroah-Hartman
maintainerPATCH 175/272 drm/tegra: fbdev: Remove offset into framebuffer memory
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit a18b6e30ecd69096beda4a0c96d2570900c3879a ]
The screen_buffer field in struct fb_info contains the kernel address of the first byte of framebuffer memory. Do not add the display offset. This offset only describes scrolling during scanout.
--- drivers/gpu/drm/tegra/fb.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
drivers/gpu/drm/tegra/fb.c +2 -6
@@ -221,7 +221,6 @@ static int tegra_fbdev_probe(struct drm_struct drm_mode_fb_cmd2 cmd = {0 };unsigned int bytes_per_pixel;struct drm_framebuffer *fb; -unsigned long offset;struct fb_info *info;struct tegra_bo *bo;size_t size;
@@ -267,9 +266,6 @@ static int tegra_fbdev_probe(struct drm_drm_fb_helper_fill_info (info, helper, sizes); - offset = info->var.xoffset * bytes_per_pixel + - info->var.yoffset * fb->pitches[0 ]; -if (bo->pages) { bo->vaddr =vmap (bo->pages, bo->num_pages,VM_MAP ,pgprot_writecombine (PAGE_KERNEL ));
@@ -281,9 +277,9 @@ static int tegra_fbdev_probe(struct drm_ } drm->mode_config.fb_base = (resource_size_t )bo->iova; - info->screen_base = (void __iomem *)bo->vaddr + offset; + info->screen_base = (void __iomem *)bo->vaddr; info->screen_size = size; - info->fix.smem_start = (unsigned long )(bo->iova + offset); + info->fix.smem_start = (unsigned long )(bo->iova); info->fix.smem_len = size;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 176/272 drm/amdgpu: Fix amdgpu_bo_move() when old_mem and new_mem are both GTT
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
[ Upstream commit ee94a65f192c05c543b4d3ad7137cd696b5c18fc ]
The UVD code relies on GTT to GTT moves in order to ensure that its BOs don't cross 256M segments.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +17 -0
@@ -472,6 +472,15 @@ static int amdgpu_bo_move(struct ttm_bufif (new_mem->mem_type ==TTM_PL_TT || new_mem->mem_type ==AMDGPU_PL_PREEMPT ) { +if (old_mem && (old_mem->mem_type ==TTM_PL_TT || + old_mem->mem_type ==AMDGPU_PL_PREEMPT )) { + r =ttm_bo_wait_ctx (bo, ctx); +if (r) +return r; + +amdgpu_ttm_backend_unbind (bo->bdev, bo->ttm); + } + r =amdgpu_ttm_backend_bind (bo->bdev, bo->ttm, new_mem);if (r)return r;
@@ -505,6 +514,14 @@ static int amdgpu_bo_move(struct ttm_bufttm_resource_free (bo, &bo->resource);ttm_bo_assign_mem (bo, new_mem); goto out; + } +if ((old_mem->mem_type ==TTM_PL_TT || + old_mem->mem_type ==AMDGPU_PL_PREEMPT ) && + (new_mem->mem_type ==TTM_PL_TT || + new_mem->mem_type ==AMDGPU_PL_PREEMPT )) { +ttm_resource_free (bo, &bo->resource); +ttm_bo_assign_mem (bo, new_mem); + goto out; } if (old_mem->mem_type == AMDGPU_PL_GDS ||
Greg Kroah-Hartman
maintainerPATCH 177/272 drm/virtio: Return proper error codes instead of -1
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Osipenko <[email protected]>
[ Upstream commit 4c703f5d6f776eaa6a98611c9b5dfe800fbeb0c8 ]
Don't return -1 in error cases, return proper error code. The returned error codes propagate to error messages and to userspace and it's always good to have a meaningful error number for debugging purposes.
Stable-dep-of: 4e1a53892ba7 ("drm/virtio: bound EDID block reads to the response buffer")
--- drivers/gpu/drm/virtio/virtgpu_vq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/virtio/virtgpu_vq.c +3 -3
@@ -329,7 +329,7 @@ static int virtio_gpu_queue_ctrl_sgs(strif (fence && vbuf->objs)virtio_gpu_array_unlock_resv (vbuf->objs);free_vbuf (vgdev, vbuf); -return -1 ; +return -ENODEV ; }if (vgdev->has_indirect)
@@ -393,7 +393,7 @@ static int virtio_gpu_queue_fenced_ctrl_if (!sgt) {if (fence && vbuf->objs)virtio_gpu_array_unlock_resv (vbuf->objs); -return -1 ; +return -ENOMEM ; } elemcnt += sg_ents;
@@ -728,7 +728,7 @@ static int virtio_get_edid_block(void *dsize_t start = block *EDID_LENGTH ;if (start + len >le32_to_cpu (resp->size)) -return -1 ; +return -EINVAL ;memcpy (buf, resp->edid + start, len);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 178/272 drm/virtio: bound EDID block reads to the response buffer
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
[ Upstream commit 4e1a53892ba7f8a3e1da6bfc53c83ae7c812dccd ]
virtio_get_edid_block() validates the read offset only against the device-supplied resp->size field, never against the fixed-size resp->edid array. The EDID block index is driven by the device-supplied extension count, so a malicious virtio-gpu backend can advertise a large size together with a high block count and read far past the array into adjacent kernel memory, which is then surfaced in the parsed EDID (an out-of-bounds read / info leak).
Also reject any read whose end exceeds the size of the edid array. Conforming EDID responses stay within the array and are unaffected.
--- drivers/gpu/drm/virtio/virtgpu_vq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/virtio/virtgpu_vq.c +2 -1
@@ -727,7 +727,8 @@ static int virtio_get_edid_block(void *dstruct virtio_gpu_resp_edid *resp = data;size_t start = block *EDID_LENGTH ; -if (start + len >le32_to_cpu (resp->size)) +if (start + len >le32_to_cpu (resp->size) || + start + len >sizeof (resp->edid))return -EINVAL ;memcpy (buf, resp->edid + start, len);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 179/272 media: aspeed: fix missing of_reserved_mem_device_release() on probe failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
[ Upstream commit 253c8ef7d57da0c74db251f385324faaa5ae2257 ]
aspeed_video_init() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. When aspeed_video_setup_video() subsequently fails in aspeed_video_probe(), the error path frees the JPEG buffer and unprepares the clocks but does not release the reserved memory association, leaking the rmem_assigned_device entry on the global list.
The normal remove path already calls of_reserved_mem_device_release() correctly; only the probe error path was missing it.
Add the missing of_reserved_mem_device_release() call to the aspeed_video_setup_video() failure cleanup.
--- drivers/media/platform/aspeed-video.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/platform/aspeed-video.c +1 -0
@@ -1724,6 +1724,7 @@ static int aspeed_video_probe(struct pla rc =aspeed_video_setup_video (video);if (rc) {aspeed_video_free_buf (video, &video->jpeg); +of_reserved_mem_device_release (&pdev->dev);clk_unprepare (video->vclk);clk_unprepare (video->eclk);return rc;
Greg Kroah-Hartman
maintainerPATCH 180/272 drm/i915/vrr: require valid min/max vfreq for VRR
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit f8a9262c7a6fc2de9802e14b0228114f0333869e ]
Ensure the EDID provided min/max vfreq are valid. Most scenarios are already covered (by coincidence) through the checks in intel_vrr_is_capable() and intel_vrr_is_in_range(), but be more explicit about it. At worst, a zero min_vfreq could lead to a division by zero in intel_vrr_compute_vmax().
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_vrr.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/gpu/drm/i915/display/intel_vrr.c +4 -0
@@ -26,6 +26,10 @@ bool intel_vrr_is_capable(struct drm_con* EDID monitor range also should be atleast 10 for reasonable * Adaptive Sync or Variable Refresh Rate end user experience. */ +if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq || + info->monitor_range.min_vfreq > info->monitor_range.max_vfreq) +return false; +return HAS_VRR (i915) &&drm_dp_sink_can_do_video_without_timing_msa (intel_dp->dpcd) && info->monitor_range.max_vfreq - info->monitor_range.min_vfreq >10 ;
Greg Kroah-Hartman
maintainerPATCH 181/272 drm/i915/hdcp: check streams[] bounds before overflow
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit bbb15a6b042d02e5508a02b4847e02d2579ee7bc ]
The data->streams[] overflow check is done after the buffer overflow has already happened. Move the overflow check before the write.
Side note, emitting a warning splat with a backtrace might be overkill here, but prefer not changing the behaviour other than not doing the overrun.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_hdcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +4 -1
@@ -89,6 +89,9 @@ intel_hdcp_required_content_stream(strucif (!enforce_type0 && !intel_streams_type1_capable (connector)) enforce_type0 = true; +if (drm_WARN_ON (&i915->drm, data->k >=INTEL_NUM_PIPES (i915))) +return -EINVAL ; + data->streams[data->k].stream_id =intel_conn_to_vcpi (connector); data->k++;
@@ -98,7 +101,7 @@ intel_hdcp_required_content_stream(struc }drm_connector_list_iter_end (&conn_iter); -if (drm_WARN_ON (&i915->drm, data->k >INTEL_NUM_PIPES (i915) || data->k ==0 )) +if (drm_WARN_ON (&i915->drm, !data->k))return -EINVAL ;/*
Greg Kroah-Hartman
maintainerPATCH 182/272 drm/i915/hdcp: require monotonically increasing seq_num_v
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit db9e64c983dcb07ff256bd455f258c44aa530ff8 ]
The HDCP 2.2 specification requires the seq_num_v to be monotonically increasing, and repeated seq_num_v needs to be treated as an integrity failure. Make it so.
For the first message, seq_num_v must be zero, and is already checked. We can only check for less-than-or-equal for the subsequent messages, where hdcp2_encrypted is true.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_hdcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +4 -3
@@ -1645,9 +1645,10 @@ int hdcp2_authenticate_repeater_topologyreturn -EINVAL ; } -if (seq_num_v < hdcp->seq_num_v) { -/* Roll over of the seq_num_v from repeater. Reauthenticate. */ -drm_dbg_kms (&dev_priv->drm,"Seq_num_v roll over.\n" ); +if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { +/* Reauthenticate on Seq_num_v repeat or rollover */ +drm_dbg_kms (&dev_priv->drm,"Seq_num_v %s\n" , + seq_num_v == hdcp->seq_num_v ?"repeat" :"rollover" );return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 183/272 media: marvell-cam: fix missing pci_disable_device() on remove
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit 033ff0420e4c9c240ae5523fff39770298efa964 ]
During manual code audit, we found that cafe_pci_probe() enables the PCI device with pci_enable_device(), and its probe error path properly calls pci_disable_device() on failure.
However, cafe_pci_remove() tears down the controller and frees the driver data without disabling the PCI device, leaving the remove path inconsistent with probe cleanup.
Add the missing pci_disable_device() call to cafe_pci_remove().
--- drivers/media/platform/marvell-ccic/cafe-driver.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/platform/marvell-ccic/cafe-driver.c +1 -0
@@ -610,6 +610,7 @@ static void cafe_pci_remove(struct pci_dreturn ; }cafe_shutdown (cam); +pci_disable_device (pdev);kfree (cam); }
Greg Kroah-Hartman
maintainerPATCH 184/272 media: i2c: imx219: Drop IMX219_VTS_* macros
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit 5ebbdd7aab3321e60a8be23aac1fee4f16644021 ]
The IMX219_VTS_* macros define default VTS values for the modes supported by the driver. They are used in a single place, and hinder readability compared to using the value directly as a decimal number. Drop them.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-)
drivers/media/i2c/imx219.c +7 -17
@@ -49,20 +49,10 @@/* V_TIMING internal */ #define IMX219_REG_VTS 0x0160 -#define IMX219_VTS_15FPS 0x0dc6 -#define IMX219_VTS_30FPS_1080P 0x06e3 -#define IMX219_VTS_30FPS_BINNED 0x06e3 -#define IMX219_VTS_30FPS_640x480 0x06e3 -#define IMX219_VTS_MAX 0xffff +#define IMX219_FLL_MAX 0xffff#define IMX219_VBLANK_MIN 4 -/*Frame Length Line*/ -#define IMX219_FLL_MIN 0x08a6 -#define IMX219_FLL_MAX 0xffff -#define IMX219_FLL_STEP 1 -#define IMX219_FLL_DEFAULT 0x0c98 -/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
@@ -386,7 +376,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = IMX219_VTS_15FPS, + .vts_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs,
@@ -403,7 +393,7 @@ static const struct imx219_mode supporte .width = 1920, .height =1080 }, - .vts_def = IMX219_VTS_30FPS_1080P, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs,
@@ -420,7 +410,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = IMX219_VTS_30FPS_BINNED, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs,
@@ -437,7 +427,7 @@ static const struct imx219_mode supporte .width = 1280, .height =960 }, - .vts_def = IMX219_VTS_30FPS_640x480, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs,
@@ -841,7 +831,7 @@ static int imx219_set_pad_format(struct imx219->mode = mode;/* Update limits and set FPS to default */ __v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - mode->height,1 , +IMX219_FLL_MAX - mode->height,1 , mode->vts_def - mode->height);__v4l2_ctrl_s_ctrl (imx219->vblank, mode->vts_def - mode->height);
@@ -1261,7 +1251,7 @@ static int imx219_init_controls(struct i/* Initial vblank/hblank/exposure parameters based on current mode */ imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_VBLANK ,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - height,1 , +IMX219_FLL_MAX - height,1 , imx219->mode->vts_def - height); hblank =IMX219_PPL_DEFAULT - imx219->mode->width; imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
Greg Kroah-Hartman
maintainerPATCH 185/272 media: i2c: imx219: Correct the minimum vblanking value
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Plowman <[email protected]>
[ Upstream commit e3b82d49bf676f3c873e642038765eac32ab6d39 ]
The datasheet for this sensor documents the minimum vblanking as being 32 lines. It does fix some problems with occasional black lines at the bottom of images (tested on Raspberry Pi).
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx219.c +1 -1
@@ -51,7 +51,7 @@#define IMX219_REG_VTS 0x0160#define IMX219_FLL_MAX 0xffff -#define IMX219_VBLANK_MIN 4 +#define IMX219_VBLANK_MIN 32/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
Greg Kroah-Hartman
maintainerPATCH 186/272 media: i2c: imx219: Rename VTS to FRM_LENGTH
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jai Luthra <[email protected]>
[ Upstream commit 04f78503f99ae7e9887c7fe5e4bc54a7cfb10fe0 ]
The IMX219 datasheet refers to the vertical length + blanking as FRM_LENGTH instead of VTS.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-)
drivers/media/i2c/imx219.c +16 -17
@@ -47,12 +47,6 @@#define IMX219_DEFAULT_LINK_FREQ 456000000 -/* V_TIMING internal */ -#define IMX219_REG_VTS 0x0160 -#define IMX219_FLL_MAX 0xffff - -#define IMX219_VBLANK_MIN 32 -/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
@@ -77,6 +71,11 @@#define IMX219_DGTL_GAIN_DEFAULT 0x0100#define IMX219_DGTL_GAIN_STEP 1 +/* V_TIMING internal */ +#define IMX219_REG_FRM_LENGTH_A 0x0160 +#define IMX219_FLL_MAX 0xffff +#define IMX219_VBLANK_MIN 32 +#define IMX219_REG_ORIENTATION 0x0172/* Binning Mode */
@@ -135,7 +134,7 @@ struct imx219_mode {struct v4l2_rect crop;/* V-timing */ -unsigned int vts_def; +unsigned int fll_def;/* Default register values */ struct imx219_reg_list reg_list;
@@ -376,7 +375,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = 3526, + .fll_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs,
@@ -393,7 +392,7 @@ static const struct imx219_mode supporte .width = 1920, .height =1080 }, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs,
@@ -410,7 +409,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs,
@@ -427,7 +426,7 @@ static const struct imx219_mode supporte .width = 1280, .height =960 }, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs,
@@ -665,7 +664,7 @@ static int imx219_set_ctrl(struct v4l2_c imx219->vflip->val << 1);break ;case V4L2_CID_VBLANK : - ret =imx219_write_reg (imx219,IMX219_REG_VTS , + ret =imx219_write_reg (imx219,IMX219_REG_FRM_LENGTH_A ,IMX219_REG_VALUE_16BIT , imx219->mode->height + ctrl->val);break ;
@@ -832,11 +831,11 @@ static int imx219_set_pad_format(struct/* Update limits and set FPS to default */ __v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN ,IMX219_FLL_MAX - mode->height,1 , - mode->vts_def - mode->height); + mode->fll_def - mode->height);__v4l2_ctrl_s_ctrl (imx219->vblank, - mode->vts_def - mode->height); + mode->fll_def - mode->height);/* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def -4 ; + exposure_max = mode->fll_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; __v4l2_ctrl_modify_range(imx219->exposure,
@@ -1252,14 +1251,14 @@ static int imx219_init_controls(struct i imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_VBLANK ,IMX219_VBLANK_MIN ,IMX219_FLL_MAX - height,1 , - imx219->mode->vts_def - height); + imx219->mode->fll_def - height); hblank =IMX219_PPL_DEFAULT - imx219->mode->width; imx219->hblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_HBLANK , hblank, hblank,1 , hblank);if (imx219->hblank) imx219->hblank->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - exposure_max = imx219->mode->vts_def -4 ; + exposure_max = imx219->mode->fll_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
Greg Kroah-Hartman
maintainerPATCH 187/272 media: imx219: Fix maximum frame length in lines
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sakari Ailus <[email protected]>
[ Upstream commit 2c4f1ba7354312ad2d6e34e70a518a51a9344715 ]
The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it.
--- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx219.c +1 -1
@@ -73,7 +73,7 @@/* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A 0x0160 -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe#define IMX219_VBLANK_MIN 32#define IMX219_REG_ORIENTATION 0x0172
Greg Kroah-Hartman
maintainerPATCH 188/272 wifi: ath6kl: fix use-after-free in aggr_reset_state()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Hodges <[email protected]>
[ Upstream commit ba7debb4dd6427386862220e8335a53a4bfc235d ]
The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy().
If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy().
Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition.
Use timer_delete_sync() instead to ensure any running timer callback has completed before returning.
--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/ath/ath6kl/txrx.c +1 -1
@@ -1829,7 +1829,7 @@ void aggr_reset_state(struct aggr_info_creturn ;if (aggr_conn->timer_scheduled) { -del_timer (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer); aggr_conn->timer_scheduled = false; }
Greg Kroah-Hartman
maintainerPATCH 189/272 media: v4l: async: Set owner for async sub-devices
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sakari Ailus <[email protected]>
[ Upstream commit 8a718752f5c339137c5b05e54f116cd26d5a4143 ]
Set the owner field of the async sub-devices by making v4l2_async_register_subdev() a macro and obtaining THIS_MODULE that way.
Stable-dep-of: 06cb687a5132 ("media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()")
--- drivers/media/v4l2-core/v4l2-async.c | 8 ++++++-- include/media/v4l2-async.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-)
drivers/media/v4l2-core/v4l2-async.c +6 -2
@@ -705,7 +705,7 @@ __v4l2_async_notifier_add_i2c_subdev(str }EXPORT_SYMBOL_GPL (__v4l2_async_notifier_add_i2c_subdev); -int v4l2_async_register_subdev (struct v4l2_subdev *sd) +int __v4l2_async_register_subdev (struct v4l2_subdev *sd,struct module *module) {struct v4l2_async_notifier *subdev_notifier;struct v4l2_async_notifier *notifier;
@@ -719,6 +719,8 @@ int v4l2_async_register_subdev(struct v4if (!sd->fwnode && sd->dev) sd->fwnode =dev_fwnode (sd->dev); + sd->owner = module; +mutex_lock (&list_lock);INIT_LIST_HEAD (&sd->async_list);
@@ -769,9 +771,11 @@ err_unbind:mutex_unlock (&list_lock); + sd->owner =NULL ; +return ret; } -EXPORT_SYMBOL (v4l2_async_register_subdev); +EXPORT_SYMBOL (__v4l2_async_register_subdev);void v4l2_async_unregister_subdev (struct v4l2_subdev *sd) {
include/media/v4l2-async.h +3 -1
@@ -283,7 +283,9 @@ void v4l2_async_notifier_cleanup(struct* * @sd: pointer to &struct v4l2_subdev */ -int v4l2_async_register_subdev (struct v4l2_subdev *sd); +#define v4l2_async_register_subdev (sd) \ + __v4l2_async_register_subdev(sd, THIS_MODULE) +int __v4l2_async_register_subdev (struct v4l2_subdev *sd,struct module *module);/** * v4l2_async_register_subdev_sensor - registers a sensor sub-device to the
Greg Kroah-Hartman
maintainerPATCH 190/272 media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mirela Rabulea <[email protected]>
[ Upstream commit 06cb687a5132fcffe624c0070576ab852ac6b568 ]
The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL.
This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver).
Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro:
#define v4l2_async_register_subdev_sensor(sd) \
__v4l2_async_register_subdev_sensor(sd, THIS_MODULE)This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal.
--- drivers/media/v4l2-core/v4l2-fwnode.c | 6 +++--- include/media/v4l2-async.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-)
drivers/media/v4l2-core/v4l2-fwnode.c +3 -3
@@ -1318,7 +1318,7 @@ v4l2_async_notifier_parse_fwnode_sensor(return 0 ; } -int v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd,struct module *module) {struct v4l2_async_notifier *notifier;int ret;
@@ -1340,7 +1340,7 @@ int v4l2_async_register_subdev_sensor(stif (ret <0 ) goto out_cleanup; - ret =v4l2_async_register_subdev (sd); + ret =__v4l2_async_register_subdev (sd, module);if (ret <0 ) goto out_unregister;
@@ -1357,7 +1357,7 @@ out_cleanup:return ret; } -EXPORT_SYMBOL_GPL (v4l2_async_register_subdev_sensor); +EXPORT_SYMBOL_GPL (__v4l2_async_register_subdev_sensor);MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Sakari Ailus <[email protected]>" );
include/media/v4l2-async.h +3 -1
@@ -306,8 +306,10 @@ int __v4l2_async_register_subdev(struct* An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor (sd) \ + __v4l2_async_register_subdev_sensor(sd, THIS_MODULE)int __must_check -v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd,struct module *module);/** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
Greg Kroah-Hartman
maintainerPATCH 191/272 ALSA: seq: close a re-opened queue timer in the destructor
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
[ Upstream commit 2c4dc0ed50b05cd847a4b34b8cebf0775f19aeb9 ]
queue_delete() closes the queue timer, then frees it. snd_seq_timer_close() clears q->timer->timeri. snd_use_lock_sync() then drains borrowers, and snd_seq_timer_delete() frees q->timer.
A borrower can re-open the timer inside that window. A SET_QUEUE_CLIENT that took a queueptr() use_lock reference before the queue was unlinked runs snd_seq_timer_open() after the close. Open refuses re-open only while timeri is set, and the close just cleared it, so it re-opens timeri.
snd_seq_timer_delete() does not close that instance. Its snd_seq_timer_stop() is a no-op, because running was cleared first. So it frees q->timer with the instance still live. The queue is freed next.
The instance stays on the global timer with callback_data pointing at the freed queue. A non-owner START on the unlocked queue arms it. The next tick derefs the freed queue in snd_seq_timer_interrupt().
Reachable by an unprivileged user with access to /dev/snd/seq. No CAP and no queue ownership required.
Close any lingering instance in the destructor. There, ->timeri can no longer change: the queue is unlinked and all use_lock borrowers have drained, so no snd_seq_queue_use() can re-open it. Close it before clearing q->timer. snd_timer_close() waits for any in-flight snd_seq_timer_interrupt() to finish, and that callback still reads q->timer (via snd_seq_check_queue()), so q->timer must stay valid until it drains.
--- sound/core/seq/seq_timer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
sound/core/seq/seq_timer.c +12 -1
@@ -58,12 +58,23 @@ struct snd_seq_timer *snd_seq_timer_new(void snd_seq_timer_delete (struct snd_seq_timer **tmr) {struct snd_seq_timer *t = *tmr; - *tmr =NULL ; +struct snd_timer_instance *ti;if (t ==NULL ) {pr_debug ("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n" );return ; } + +spin_lock_irq (&t->lock); + ti = t->timeri; + t->timeri =NULL ; +spin_unlock_irq (&t->lock); +if (ti) { +snd_timer_close (ti); +snd_timer_instance_free (ti); + } + + *tmr =NULL ; t->running =0 ;/* reset time */
Greg Kroah-Hartman
maintainerPATCH 192/272 wifi: brcmfmac: drain bus_reset work on device removal
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit 43b25879f004c98defa2776bedc6ca4763c51945 ]
brcmf_fw_crashed() and the debugfs "reset" entry both schedule drvr->bus_reset, whose callback recovers drvr through container_of() and dereferences it. The removal path frees drvr (brcmf_free -> wiphy_free) without draining the work, so a bus_reset callback pending or running during removal can outlive drvr.
Cancellation cannot live in brcmf_detach() or brcmf_free(): the work callback reaches teardown through the bus .reset op (PCIe brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for the running work and deadlock.
Add a per-bus mutex (bus_reset_lock) and route all arming through brcmf_bus_schedule_reset(), which under the lock skips when the bus is marked removing. Each bus remove entry calls brcmf_bus_cancel_reset_work(), which under the same lock sets removing and cancels the work. Holding the mutex across cancel_work_sync() makes the set-removing + drain step atomic. Every producer reaches the arming path from process context -- the PCIe firmware-halt notification runs in the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail path runs from the data workqueue -- so the mutex is taken only in sleepable contexts. Where applicable the remove entry first stops the firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; on SDIO unregister the bus interrupt and cancel the data worker, which also reports firmware halts through brcmf_fw_crashed(). The mutex is initialized at bus allocation. The SDIO suspend power-off path frees drvr through the same brcmf_sdiod_remove() and takes the same lock; resume re-allows the work only on a successful re-probe.
Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire before brcmf_attach() wires up drvr, and it dereferences drvr (bphy_err/brcmf_dev_coredump) before reaching the arming gate.
The bus_reset work is shared across buses, so the drain is applied to every remove path: PCIe (the .reset op introduced by the Fixes commit), SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the debugfs "reset" entry). cancel_work_sync() drains a running or pending bus_reset work item before removal frees drvr, and patch 1/2 makes the scratch-buffer release safe when reset teardown has already released those DMA buffers.
This patch fixes the lifetime of the bus_reset work item itself. It does not attempt to address the separate, pre-existing lifetime of the asynchronous firmware completion started by the PCIe reset path. That callback needs its own lifetime/ownership protocol and is being tracked separately.
This issue was found by an in-house static analysis tool.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 ++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 45 +++++++++++++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 3 7 files changed, 77 insertions(+), 3 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +13 -0
@@ -1043,6 +1043,7 @@ static int brcmf_ops_sdio_probe(struct s bus_if =kzalloc (sizeof (struct brcmf_bus ),GFP_KERNEL );if (!bus_if)return -ENOMEM ; +mutex_init (&bus_if->bus_reset_lock); sdiodev =kzalloc (sizeof (struct brcmf_sdio_dev ),GFP_KERNEL );if (!sdiodev) {kfree (bus_if);
@@ -1102,6 +1103,14 @@ static void brcmf_ops_sdio_remove(structif (func->num !=1 )return ; +/* Drain bus_reset before the shared brcmf_sdiod_remove() +* teardown, which the SDIO reset callback also reaches. The +* data worker can arm bus_reset via brcmf_fw_crashed(); cancel +* it first. +*/ +brcmf_sdio_cancel_datawork (sdiodev->bus); +brcmf_bus_cancel_reset_work (bus_if); +/* only proceed with rest of cleanup if func 1 */ brcmf_sdiod_remove (sdiodev);
@@ -1163,6 +1172,8 @@ static int brcmf_ops_sdio_suspend(struct }else {/* power will be cut so remove device, probe again in resume */ brcmf_sdiod_intr_unregister (sdiodev); +brcmf_sdio_cancel_datawork (sdiodev->bus); +brcmf_bus_cancel_reset_work (bus_if); ret =brcmf_sdiod_remove (sdiodev);if (ret)brcmf_err ("Failed to remove device on suspend\n" );
@@ -1188,6 +1199,8 @@ static int brcmf_ops_sdio_resume(struct ret =brcmf_sdiod_probe (sdiodev);if (ret)brcmf_err ("Failed to probe device on resume\n" ); +else +brcmf_bus_allow_reset_work (bus_if); }else {if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +6 -0
@@ -6,6 +6,7 @@#ifndef BRCMFMAC_BUS_H #define BRCMFMAC_BUS_H +#include <linux/mutex.h> #include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */
@@ -149,11 +150,16 @@ struct brcmf_bus {u32 chiprev;bool always_use_fws_queue;bool wowl_supported; +bool removing;/* device removal in progress; quiesce async work */ +struct mutex bus_reset_lock;const struct brcmf_bus_ops *ops;struct brcmf_bus_msgbuf *msgbuf; }; +void brcmf_bus_cancel_reset_work (struct brcmf_bus *bus_if); +void brcmf_bus_allow_reset_work (struct brcmf_bus *bus_if); +/* * callback wrappers */
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +42 -3
68 lines not on this page · raw message
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +6 -0
@@ -1901,6 +1901,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, c ret = -ENOMEM ; goto fail; } +mutex_init (&bus->bus_reset_lock); bus->msgbuf =kzalloc (sizeof (*bus->msgbuf),GFP_KERNEL );if (!bus->msgbuf) { ret = -ENOMEM ;
@@ -1972,6 +1973,11 @@ brcmf_pcie_remove(struct pci_dev *pdev)if (devinfo->ci)brcmf_pcie_intr_disable (devinfo); +if (devinfo->irq_allocated) +synchronize_irq (pdev->irq); + +brcmf_bus_cancel_reset_work (bus); +brcmf_detach (&pdev->dev);brcmf_free (&pdev->dev);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +6 -0
@@ -4543,6 +4543,12 @@ fail:return NULL ; } +void brcmf_sdio_cancel_datawork (struct brcmf_sdio *bus) +{ +if (bus) +cancel_work_sync (&bus->datawork); +} +/* Detach and free everything */ void brcmf_sdio_remove (struct brcmf_sdio *bus) {
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +1 -0
@@ -373,6 +373,7 @@ int brcmf_sdiod_remove(struct brcmf_sdiostruct brcmf_sdio *brcmf_sdio_probe (struct brcmf_sdio_dev *sdiodev);void brcmf_sdio_remove (struct brcmf_sdio *bus);void brcmf_sdio_isr (struct brcmf_sdio *bus,bool in_isr); +void brcmf_sdio_cancel_datawork (struct brcmf_sdio *bus);void brcmf_sdio_wd_timer (struct brcmf_sdio *bus,bool active);void brcmf_sdio_wowl_config (struct device *dev,bool enabled);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +3 -0
@@ -1273,6 +1273,7 @@ static int brcmf_usb_probe_cb(struct brc ret = -ENOMEM ; goto fail; } +mutex_init (&bus->bus_reset_lock); bus->dev = dev; bus_pub->bus = bus;
@@ -1338,6 +1339,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbreturn ;brcmf_dbg (USB ,"Enter, bus_pub %p\n" , devinfo); +brcmf_bus_cancel_reset_work (devinfo->bus_pub.bus); +brcmf_detach (devinfo->dev);brcmf_free (devinfo->dev);kfree (devinfo->bus_pub.bus);
Greg Kroah-Hartman
maintainerPATCH 193/272 serial: 8250_mid: Remove unneeded test for ->setup() presence
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 324facd1ccb353a213ea2c2785604f2507f79297 ]
All supported platforms by this driver require ->setup() and ->exit(). Remove unneeded test for ->setup() presence.
Stable-dep-of: 7fb13fd7e9a5 ("serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms")
--- drivers/tty/serial/8250/8250_mid.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_mid.c +3 -5
@@ -319,11 +319,9 @@ static int mid8250_probe(struct pci_devif (!uart.port.membase)return -ENOMEM ; -if (mid->board->setup) { - ret = mid->board->setup (mid, &uart.port); -if (ret) -return ret; - } + ret = mid->board->setup (mid, &uart.port); +if (ret) +return ret; ret =mid8250_dma_setup (mid, &uart);if (ret)
Greg Kroah-Hartman
maintainerPATCH 194/272 serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
[ Upstream commit 7fb13fd7e9a59a37cd911efff83abe19e3ee029d ]
Commit b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") replaced the dnv_board setup and exit callbacks with PTR_IF(false, ...), which evaluates to NULL. However, the three call sites in mid8250_probe() and mid8250_remove() unconditionally dereference these function pointers without NULL checks, causing a NULL pointer dereference (kernel oops) on any Denverton (DNV), Ice Lake Xeon D (ICX-D/CDF), or Snowridge (SNR) platform.
Fix this by adding the missing NULL checks before calling the setup and exit callbacks.
--- drivers/tty/serial/8250/8250_mid.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_mid.c +9 -5
@@ -319,9 +319,11 @@ static int mid8250_probe(struct pci_devif (!uart.port.membase)return -ENOMEM ; - ret = mid->board->setup (mid, &uart.port); -if (ret) -return ret; +if (mid->board->setup) { + ret = mid->board->setup (mid, &uart.port); +if (ret) +return ret; + } ret =mid8250_dma_setup (mid, &uart);if (ret)
@@ -337,7 +339,8 @@ static int mid8250_probe(struct pci_devreturn 0 ; err: - mid->board->exit (mid); +if (mid->board->exit) + mid->board->exit (mid);return ret; }
@@ -347,7 +350,8 @@ static void mid8250_remove(struct pci_deserial8250_unregister_port (mid->line); - mid->board->exit (mid); +if (mid->board->exit) + mid->board->exit (mid); }static const struct mid8250_board pnw_board = {
Greg Kroah-Hartman
maintainerPATCH 195/272 wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gokul Sivakumar <[email protected]>
[ Upstream commit 74e2ef72bd4b25ce21c8f309d4f5b91b5df9ff5b ]
Cypress(Infineon) is not the vendor for this 43752 SDIO WLAN chip, and so has not officially released any firmware binary for it. It is incorrect to maintain this WLAN chip with firmware vendor ID as "CYW". So relabel the chip's firmware Vendor ID as "WCC" as suggested by the maintainer.
Stable-dep-of: 29ab31f3f271 ("wifi: brcmfmac: set F2 blocksize to 256 for BCM43752")
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 +- include/linux/mmc/sdio_ids.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +1 -1
@@ -989,9 +989,9 @@ static const struct sdio_device_id brcmfBRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_4354 ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_4356 ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_4359 ), +BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_43752 ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 ), -BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 ), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359), { /* end: all zeroes */ } };
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +1 -1
@@ -4270,8 +4270,8 @@ static void brcmf_sdio_firmware_callback bus->hostintmask, NULL);switch (sdiod->func1->device) { +case SDIO_DEVICE_ID_BROADCOM_43752 :case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 : -case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 :brcmf_dbg (INFO ,"set F2 watermark to 0x%x*4 bytes\n" ,CY_4373_F2_WATERMARK ); brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
include/linux/mmc/sdio_ids.h +1 -1
@@ -75,7 +75,7 @@#define SDIO_DEVICE_ID_BROADCOM_43364 0xa9a4#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf -#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 0xaae8 +#define SDIO_DEVICE_ID_BROADCOM_43752 0xaae8#define SDIO_VENDOR_ID_MARVELL 0x02df#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
Greg Kroah-Hartman
maintainerPATCH 196/272 wifi: brcmfmac: set F2 blocksize to 256 for BCM43752
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: LiangCheng Wang <[email protected]>
[ Upstream commit 29ab31f3f27157648f2f7e6d5e1fd9792fdf0614 ]
The BCM43752 is not reliable with the default 512-byte SDIO function 2 block size: on an i.MX8MP board with an AMPAK AP6275S module at SDR104 / 200 MHz, an iperf TX stress test kills WLAN within seconds:
mmc_submit_one: CMD53 sg block write failed -84
brcmf_sdio_dpc: failed backplane access over SDIO, halting operation
Commit d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") set up the 43752 like the 4373 for the F2 watermark but missed the F2 block size, which the 4373 limits to 256 bytes. The vendor driver (bcmdhd) also programs a 256-byte F2 block size for this chip and runs the same hardware without errors.
Group the 43752 with the 4373, matching the F2 watermark handling. With this change a 10-minute bidirectional iperf3 soak completes with zero SDIO errors at ~270 Mbit/s in each direction.
Backporting note: kernels before v6.18 name this id SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, so on those trees the case label added by this patch must be adjusted to that name. Cherry-picking the rename commit 74e2ef72bd4b ("wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)") first is not a clean
alternative: on trees before v6.17 its context collides with the 43751 additions, and trees before v6.2 lack the FWVID framework it touches.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +1 -0
@@ -914,6 +914,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_ goto out; }switch (sdiodev->func2->device) { +case SDIO_DEVICE_ID_BROADCOM_43752 :case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 : f2_blksz =SDIO_4373_FUNC2_BLOCKSIZE ;break ;
Greg Kroah-Hartman
maintainerPATCH 197/272 sc16is7xx: Properly resume TX after stop
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tomasz Moń <[email protected]>
[ Upstream commit cc4c1d05eb10c3ad4c6315f1897bc56b1e7429aa ]
sc16is7xx_stop_tx() clears THRI bit and thus disables THRI interrupt. This makes it possible for transmission to cease indefinitely when more than 64 characters are being sent.
The sc16is7xx_handle_tx() call executed by sc16is7xx_tx_proc() can send up to FIFO length (64) characters. If more characters are written to the output buffer, then the THRI interrupt is needed.
Solve the issue by enabling THRI interrupt in sc16is7xx_tx_proc().
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 47 +++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-)
drivers/tty/serial/sc16is7xx.c +41 -6
@@ -307,7 +307,8 @@ struct sc16is7xx_devtype {struct sc16is7xx_one_config {unsigned int flags; -u8 ier_clear; +u8 ier_mask; +u8 ier_val; };struct sc16is7xx_one {
@@ -341,6 +342,9 @@ static struct uart_driver sc16is7xx_uart .nr = SC16IS7XX_MAX_DEVS, }; +static void sc16is7xx_ier_set (struct uart_port *port,u8 bit); +static void sc16is7xx_stop_tx (struct uart_port *port); +#define to_sc16is7xx_port (p,e) ((container_of((p), struct sc16is7xx_port, e)))#define to_sc16is7xx_one (p,e) ((container_of((p), struct sc16is7xx_one, e)))
@@ -650,6 +654,7 @@ static void sc16is7xx_handle_tx(struct ustruct sc16is7xx_port *s =dev_get_drvdata (port->dev);struct circ_buf *xmit = &port->state->xmit;unsigned int txlen, to_send, i; +unsigned long flags;if (unlikely (port->x_char)) {sc16is7xx_port_write (port,SC16IS7XX_THR_REG , port->x_char);
@@ -658,8 +663,12 @@ static void sc16is7xx_handle_tx(struct ureturn ; } -if (uart_circ_empty (xmit) ||uart_tx_stopped (port)) +if (uart_circ_empty (xmit) ||uart_tx_stopped (port)) { +spin_lock_irqsave (&port->lock, flags); +sc16is7xx_stop_tx (port); +spin_unlock_irqrestore (&port->lock, flags);return ; + }/* Get length of data pending in circular buffer */ to_send =uart_circ_chars_pending (xmit);
@@ -686,8 +695,13 @@ static void sc16is7xx_handle_tx(struct usc16is7xx_fifo_write (port, to_send); } +spin_lock_irqsave (&port->lock, flags);if (uart_circ_chars_pending (xmit) <WAKEUP_CHARS )uart_write_wakeup (port); + +if (uart_circ_empty (xmit)) +sc16is7xx_stop_tx (port); +spin_unlock_irqrestore (&port->lock, flags); }static bool sc16is7xx_port_irq (struct sc16is7xx_port *s,int portno)
@@ -762,6 +776,7 @@ static void sc16is7xx_tx_proc(struct kth {struct uart_port *port = &(to_sc16is7xx_one (ws, tx_work)->port);struct sc16is7xx_port *s =dev_get_drvdata (port->dev); +unsigned long flags;if ((port->rs485.flags &SER_RS485_ENABLED ) && (port->rs485.delay_rts_before_send >0 ))
@@ -770,6 +785,10 @@ static void sc16is7xx_tx_proc(struct kthmutex_lock (&s->efr_lock);sc16is7xx_handle_tx (port);mutex_unlock (&s->efr_lock); + +spin_lock_irqsave (&port->lock, flags); +sc16is7xx_ier_set (port,SC16IS7XX_IER_THRI_BIT ); +spin_unlock_irqrestore (&port->lock, flags); }static void sc16is7xx_reconf_rs485 (struct uart_port *port)
@@ -819,7 +838,7 @@ static void sc16is7xx_reg_proc(struct kt }if (config.flags &SC16IS7XX_RECONF_IER )sc16is7xx_port_update (&one->port,SC16IS7XX_IER_REG , - config.ier_clear,0 ); + config.ier_mask, config.ier_val);if (config.flags &SC16IS7XX_RECONF_RS485 )sc16is7xx_reconf_rs485 (&one->port);
@@ -830,8 +849,24 @@ static void sc16is7xx_ier_clear(struct ustruct sc16is7xx_port *s =dev_get_drvdata (port->dev);struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); +lockdep_assert_held_once (&port->lock); + + one->config.flags |=SC16IS7XX_RECONF_IER ; + one->config.ier_mask |= bit; + one->config.ier_val &= ~bit; +kthread_queue_work (&s->kworker, &one->reg_work); +} + +static void sc16is7xx_ier_set (struct uart_port *port,u8 bit) +{ +struct sc16is7xx_port *s =dev_get_drvdata (port->dev); +struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); + +lockdep_assert_held_once (&port->lock); + one->config.flags |=SC16IS7XX_RECONF_IER ; - one->config.ier_clear |= bit; + one->config.ier_mask |= bit; + one->config.ier_val |= bit;kthread_queue_work (&s->kworker, &one->reg_work); }
@@ -1059,8 +1094,8 @@ static int sc16is7xx_startup(struct uartSC16IS7XX_EFCR_TXDISABLE_BIT , 0); -/* Enable RX, TX interrupts */ - val =SC16IS7XX_IER_RDI_BIT |SC16IS7XX_IER_THRI_BIT ; +/* Enable RX interrupt */ + val =SC16IS7XX_IER_RDI_BIT ;sc16is7xx_port_write (port,SC16IS7XX_IER_REG , val);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 198/272 serial: sc16is7xx: Fill in rs485_supported
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 267913ecf73745ca3e8fc8282671b0b4f24df5fe ]
Add information on supported serial_rs485 features.
This driver does not support delay_rts_after_send but the pre-existing behavior is to return -EINVAL if delay_rts_after_send is non-zero. In contrast, other drivers that do not support delay_rts_after_send either zero delay_rts_after_send or do not care (leave the inaccurate value). As changing this would cause userspace visible impact, the change is not attempted here. But perhaps it should be still tried (maybe nobody finds that kind of API oddity significant)?
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/tty/serial/sc16is7xx.c +7 -0
@@ -1239,6 +1239,12 @@ static int sc16is7xx_gpio_direction_outp }#endif +static const struct serial_rs485 sc16is7xx_rs485_supported = { + .flags =SER_RS485_ENABLED |SER_RS485_RTS_AFTER_SEND , + .delay_rts_before_send =1 , + .delay_rts_after_send =1 ,/* Not supported but keep returning -EINVAL */ +}; +static int sc16is7xx_probe (struct device *dev,const struct sc16is7xx_devtype *devtype,struct regmap *regmap,int irq)
@@ -1328,6 +1334,7 @@ static int sc16is7xx_probe(struct device s->p[i].port.iotype =UPIO_PORT ; s->p[i].port.uartclk = freq; s->p[i].port.rs485_config = sc16is7xx_config_rs485; + s->p[i].port.rs485_supported = &sc16is7xx_rs485_supported; s->p[i].port.ops = &sc16is7xx_ops; s->p[i].port.line =sc16is7xx_alloc_line ();if (s->p[i].port.line >=SC16IS7XX_MAX_DEVS ) {
Greg Kroah-Hartman
maintainerPATCH 199/272 serial: sc16is7xx: remove obsolete out_thread label
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit dabc54a45711fe77674a6c0348231e00e66bd567 ]
Commit c8f71b49ee4d ("serial: sc16is7xx: setup GPIO controller later in probe") moved GPIO setup code later in probe function. Doing so also required to move ports cleanup code (out_ports label) after the GPIO cleanup code.
After these moves, the out_thread label becomes misplaced and makes part of the cleanup code illogical.
This patch remove the now obsolete out_thread label and make GPIO setup code jump to out_ports label if it fails.
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/tty/serial/sc16is7xx.c +1 -3
@@ -1399,7 +1399,7 @@ static int sc16is7xx_probe(struct device s->gpio.can_sleep =1 ; ret =gpiochip_add_data (&s->gpio, s);if (ret) - goto out_thread; + goto out_ports; }#endif
@@ -1425,8 +1425,6 @@ static int sc16is7xx_probe(struct device#ifdef CONFIG_GPIOLIB if (devtype->nr_gpio)gpiochip_remove (&s->gpio); - -out_thread:#endif out_ports:
Greg Kroah-Hartman
maintainerPATCH 200/272 serial: sc16is7xx: fix regression with GPIO configuration
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit 0499942928341d572a42199580433c2b0725211e ]
Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function.
As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected.
Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message.
Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B).
Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch).
When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property.
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 124 ++++++++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 19 deletions(-)
drivers/tty/serial/sc16is7xx.c +105 -19
@@ -237,7 +237,8 @@/* IOControl register bits (Only 750/760) */ #define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0)/* Enable input latching */ -#define SC16IS7XX_IOCONTROL_MODEM_BIT (1 << 1)/* Enable GPIO[7:4] as modem pins */ +#define SC16IS7XX_IOCONTROL_MODEM_A_BIT (1 << 1)/* Enable GPIO[7:4] as modem A pins */ +#define SC16IS7XX_IOCONTROL_MODEM_B_BIT (1 << 2)/* Enable GPIO[3:0] as modem B pins */ #define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3)/* Software Reset */ /* EFCR register bits */
@@ -294,6 +295,7 @@/* Misc definitions */ #define SC16IS7XX_FIFO_SIZE (64)#define SC16IS7XX_REG_SHIFT 2 +#define SC16IS7XX_GPIOS_PER_BANK 4struct sc16is7xx_devtype {char name[10 ];
@@ -326,7 +328,9 @@ struct sc16is7xx_port {struct clk *clk;#ifdef CONFIG_GPIOLIB struct gpio_chip gpio; +unsigned long gpio_valid_mask;#endif +u8 mctrl_mask;unsigned char buf[SC16IS7XX_FIFO_SIZE ];struct kthread_worker kworker;struct task_struct *kworker_task;
@@ -1237,8 +1241,98 @@ static int sc16is7xx_gpio_direction_outpreturn 0 ; } + +static int sc16is7xx_gpio_init_valid_mask (struct gpio_chip *chip, +unsigned long *valid_mask, +unsigned int ngpios) +{ +struct sc16is7xx_port *s =gpiochip_get_data (chip); + + *valid_mask = s->gpio_valid_mask; + +return 0 ; +} + +static int sc16is7xx_setup_gpio_chip (struct sc16is7xx_port *s) +{ +struct device *dev = s->p[0 ].port.dev; + +if (!s->devtype->nr_gpio) +return 0 ; + +switch (s->mctrl_mask) { +case 0 : + s->gpio_valid_mask =GENMASK (7 ,0 ); +break ; +case SC16IS7XX_IOCONTROL_MODEM_A_BIT : + s->gpio_valid_mask =GENMASK (3 ,0 ); +break ; +case SC16IS7XX_IOCONTROL_MODEM_B_BIT : + s->gpio_valid_mask =GENMASK (7 ,4 ); +break ; +default : +break ; + } + +if (s->gpio_valid_mask ==0 ) +return 0 ; + + s->gpio.owner =THIS_MODULE ; + s->gpio.parent = dev; + s->gpio.label =dev_name (dev); + s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base =-1 ; + s->gpio.ngpio = s->devtype->nr_gpio; + s->gpio.can_sleep =1 ; + +return gpiochip_add_data (&s->gpio, s); +}#endif +/* +* Configure ports designated to operate as modem control lines. +*/ +static int sc16is7xx_setup_mctrl_ports (struct sc16is7xx_port *s) +{ +int i; +int ret; +int count; +u32 mctrl_port[2 ]; +struct device *dev = s->p[0 ].port.dev; + + count =device_property_count_u32 (dev,"nxp,modem-control-line-ports" ); +if (count <0 || count >ARRAY_SIZE (mctrl_port)) +return 0 ; + + ret =device_property_read_u32_array (dev,"nxp,modem-control-line-ports" , + mctrl_port, count); +if (ret) +return ret; + + s->mctrl_mask =0 ; + +for (i =0 ; i < count; i++) { +/* Use GPIO lines as modem control lines */ +if (mctrl_port[i] ==0 ) + s->mctrl_mask |=SC16IS7XX_IOCONTROL_MODEM_A_BIT ; +else if (mctrl_port[i] ==1 ) + s->mctrl_mask |=SC16IS7XX_IOCONTROL_MODEM_B_BIT ; + } + +if (s->mctrl_mask) +regmap_update_bits ( + s->regmap, +SC16IS7XX_IOCONTROL_REG <<SC16IS7XX_REG_SHIFT , +SC16IS7XX_IOCONTROL_MODEM_A_BIT | +SC16IS7XX_IOCONTROL_MODEM_B_BIT , s->mctrl_mask); + +return 0 ; +} +static const struct serial_rs485 sc16is7xx_rs485_supported = { .flags =SER_RS485_ENABLED |SER_RS485_RTS_AFTER_SEND , .delay_rts_before_send =1 ,
@@ -1348,6 +1442,7 @@ static int sc16is7xx_probe(struct devicesc16is7xx_port_write (&s->p[i].port,SC16IS7XX_EFCR_REG ,SC16IS7XX_EFCR_RXDISABLE_BIT |SC16IS7XX_EFCR_TXDISABLE_BIT ); +/* Initialize kthread work structs */ kthread_init_work (&s->p[i].tx_work, sc16is7xx_tx_proc);kthread_init_work (&s->p[i].reg_work, sc16is7xx_reg_proc);
@@ -1384,23 +1479,14 @@ static int sc16is7xx_probe(struct device s->p[u].irda_mode = true; } + ret =sc16is7xx_setup_mctrl_ports (s); +if (ret) + goto out_ports; +#ifdef CONFIG_GPIOLIB -if (devtype->nr_gpio) { -/* Setup GPIO cotroller */ - s->gpio.owner =THIS_MODULE ; - s->gpio.parent = dev; - s->gpio.label =dev_name (dev); - s->gpio.direction_input = sc16is7xx_gpio_direction_input; - s->gpio.get = sc16is7xx_gpio_get; - s->gpio.direction_output = sc16is7xx_gpio_direction_output; - s->gpio.set = sc16is7xx_gpio_set; - s->gpio.base =-1 ; - s->gpio.ngpio = devtype->nr_gpio; - s->gpio.can_sleep =1 ; - ret =gpiochip_add_data (&s->gpio, s); -if (ret) - goto out_ports; - } + ret =sc16is7xx_setup_gpio_chip (s); +if (ret) + goto out_ports;#endif /*
@@ -1423,7 +1509,7 @@ static int sc16is7xx_probe(struct devicereturn 0 ;#ifdef CONFIG_GPIOLIB -if (devtype->nr_gpio) +if (s->gpio_valid_mask)gpiochip_remove (&s->gpio);#endif
@@ -1447,7 +1533,7 @@ static int sc16is7xx_remove(struct devicint i;#ifdef CONFIG_GPIOLIB -if (s->devtype->nr_gpio) +if (s->gpio_valid_mask)gpiochip_remove (&s->gpio);#endif
Greg Kroah-Hartman
maintainerPATCH 201/272 serial: sc16is7xx: implement gpio get_direction() callback
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit af071d9e07e57cfff239e8d09d2f3b05ebc9c667 ]
It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio.
--- drivers/tty/serial/sc16is7xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/tty/serial/sc16is7xx.c +12 -0
@@ -1204,6 +1204,17 @@ static void sc16is7xx_gpio_set(struct gp val ? BIT(offset) : 0); } +static int sc16is7xx_gpio_get_direction (struct gpio_chip *chip,unsigned int offset) +{ +struct sc16is7xx_port *s =gpiochip_get_data (chip); +struct uart_port *port = &s->p[0 ].port; +unsigned int val; + + val =sc16is7xx_port_read (port,SC16IS7XX_IODIR_REG ); + +return val &BIT (offset) ?GPIO_LINE_DIRECTION_OUT :GPIO_LINE_DIRECTION_IN ; +} +static int sc16is7xx_gpio_direction_input (struct gpio_chip *chip,unsigned offset) {
@@ -1281,6 +1292,7 @@ static int sc16is7xx_setup_gpio_chip(str s->gpio.parent = dev; s->gpio.label =dev_name (dev); s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.get_direction = sc16is7xx_gpio_get_direction; s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; s->gpio.direction_output = sc16is7xx_gpio_direction_output;
Greg Kroah-Hartman
maintainerPATCH 202/272 mptcp: cleanup MPJ subflow list handling
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 3e5014909b5661b3da59990d72a317a45ba3b284 ]
We can simplify the join list handling leveraging the mptcp_release_cb(): if we can acquire the msk socket lock at mptcp_finish_join time, move the new subflow directly into the conn_list, otherwise place it on join_list and let the release_cb process such list.
Since pending MPJ connection are now always processed in a timely way, we can avoid flushing the join list every time we have to process all the current subflows.
Additionally we can now use the mptcp data lock to protect the join_list, removing the additional spin lock.
Finally, the MPJ handshake is now always finalized under the msk socket lock, we can drop the additional synchronization between mptcp_finish_join() and mptcp_close().
Stable-dep-of: f3ca0ee2cc30 ("mptcp: decrement subflows counter on failed passive join")
--- net/mptcp/pm_netlink.c | 4 - net/mptcp/protocol.c | 120 ++++++++++++++++++++----------------------------- net/mptcp/protocol.h | 15 ------ net/mptcp/sockopt.c | 24 ++------- net/mptcp/subflow.c | 5 -- 5 files changed, 61 insertions(+), 107 deletions(-)
net/mptcp/pm_netlink.c +0 -4
@@ -174,7 +174,6 @@ select_local_address(const struct pm_nl_msk_owned_by_me (msk);rcu_read_lock (); -__mptcp_flush_join_list (msk);list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) {if (!(entry->flags &MPTCP_PM_ADDR_FLAG_SUBFLOW ))continue ;
@@ -619,7 +618,6 @@ static unsigned int fill_local_addressesmptcp_local_address ((struct sock_common *)msk, &mpc_addr);rcu_read_lock (); -__mptcp_flush_join_list (msk);list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) {if (!(entry->flags &MPTCP_PM_ADDR_FLAG_FULLMESH ))continue ;
@@ -658,7 +656,6 @@ static unsigned int fill_local_addressesunsigned int local_addr_max =mptcp_pm_get_local_addr_max (msk);rcu_read_lock (); -__mptcp_flush_join_list (msk);list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) {if (!(entry->flags &MPTCP_PM_ADDR_FLAG_SUBFLOW ))continue ;
@@ -796,7 +793,6 @@ void mptcp_pm_nl_addr_send_ack_avoid_lis !mptcp_pm_should_rm_signal(msk))return ; -__mptcp_flush_join_list (msk);mptcp_for_each_subflow (msk, subflow) {if (!__mptcp_subflow_active (subflow))continue ;
net/mptcp/protocol.c +51 -69
218 lines not on this page · raw message
net/mptcp/protocol.h +2 -13
@@ -121,7 +121,7 @@#define MPTCP_CLEAN_UNA 7#define MPTCP_ERROR_REPORT 8#define MPTCP_RETRANSMIT 9 -#define MPTCP_WORK_SYNC_SETSOCKOPT 10 +#define MPTCP_FLUSH_JOIN_LIST 10#define MPTCP_CONNECTED 11static inline bool before64 (__u64 seq1,__u64 seq2)
@@ -252,7 +252,6 @@ struct mptcp_sock {bool use_64bit_ack;/* Set when we received a 64-bit DSN */ bool csum_enabled;bool allow_infinite_fallback; -spinlock_t join_list_lock;int keepalive_cnt;int keepalive_idle;int keepalive_intvl;
@@ -508,15 +507,6 @@ mptcp_subflow_get_mapped_dsn(const strucreturn subflow->map_seq +mptcp_subflow_get_map_offset (subflow); } -static inline void mptcp_add_pending_subflow (struct mptcp_sock *msk, -struct mptcp_subflow_context *subflow) -{ -sock_hold (mptcp_subflow_tcp_sock (subflow)); -spin_lock_bh (&msk->join_list_lock); -list_add_tail (&subflow->node, &msk->join_list); -spin_unlock_bh (&msk->join_list_lock); -} -void mptcp_subflow_process_delegated (struct sock *ssk);static inline void mptcp_subflow_delegate (struct mptcp_subflow_context *subflow,int action)
@@ -684,7 +674,6 @@ void __mptcp_data_acked(struct sock *sk)void __mptcp_error_report (struct sock *sk);void mptcp_subflow_eof (struct sock *sk);bool mptcp_update_rcv_data_fin (struct mptcp_sock *msk,u64 data_fin_seq,bool use_64bit); -void __mptcp_flush_join_list (struct mptcp_sock *msk);static inline bool mptcp_data_fin_enabled (const struct mptcp_sock *msk) { return READ_ONCE(msk->snd_data_fin_enable) &&
@@ -859,7 +848,7 @@ static inline bool mptcp_pm_add_addr_c_f }void mptcp_sockopt_sync (struct mptcp_sock *msk,struct sock *ssk); -void mptcp_sockopt_sync_all (struct mptcp_sock *msk); +void mptcp_sockopt_sync_locked (struct mptcp_sock *msk,struct sock *ssk);static inline struct mptcp_ext *mptcp_get_ext (const struct sk_buff *skb) {
net/mptcp/sockopt.c +6 -18
@@ -904,27 +904,15 @@ void mptcp_sockopt_sync(struct mptcp_soc } } -void mptcp_sockopt_sync_all (struct mptcp_sock *msk) +void mptcp_sockopt_sync_locked (struct mptcp_sock *msk,struct sock *ssk) { -struct mptcp_subflow_context *subflow; -struct sock *sk = (struct sock *)msk; -u32 seq; +struct mptcp_subflow_context *subflow =mptcp_subflow_ctx (ssk); - seq =sockopt_seq_reset (sk); +msk_owned_by_me (msk); -mptcp_for_each_subflow (msk, subflow) { -struct sock *ssk =mptcp_subflow_tcp_sock (subflow); -u32 sseq =READ_ONCE (subflow->setsockopt_seq); +if (READ_ONCE (subflow->setsockopt_seq) != msk->setsockopt_seq) { +sync_socket_options (msk, ssk); -if (sseq != msk->setsockopt_seq) { -__mptcp_sockopt_sync (msk, ssk); -WRITE_ONCE (subflow->setsockopt_seq, seq); - }else if (sseq != seq) { -WRITE_ONCE (subflow->setsockopt_seq, seq); - } - -cond_resched (); + subflow->setsockopt_seq = msk->setsockopt_seq; } - - msk->setsockopt_seq = seq; }
net/mptcp/subflow.c +2 -3
@@ -1490,7 +1490,8 @@ int __mptcp_subflow_connect(struct sock subflow->request_bkup = !!(flags &MPTCP_PM_ADDR_FLAG_BACKUP );mptcp_info2sockaddr (remote, &addr, ssk->sk_family); -mptcp_add_pending_subflow (msk, subflow); +sock_hold (ssk); +list_add_tail (&subflow->node, &msk->conn_list);mptcp_sockopt_sync (msk, ssk); err =kernel_connect (sf, (struct sockaddr *)&addr, addrlen,O_NONBLOCK );if (err && err != -EINPROGRESS )
@@ -1503,9 +1504,7 @@ int __mptcp_subflow_connect(struct sockreturn err; failed_unlink: -spin_lock_bh (&msk->join_list_lock);list_del (&subflow->node); -spin_unlock_bh (&msk->join_list_lock);sock_put (mptcp_subflow_tcp_sock (subflow)); failed:
Greg Kroah-Hartman
maintainerPATCH 203/272 mptcp: fix subflow accounting on close
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 95d686517884a403412b000361cee2b08b2ed1e6 ]
If the PM closes a fully established MPJ subflow or the subflow creation errors out in it's early stage the subflows counter is not bumped accordingly.
This change adds the missing accounting, additionally taking care of updating accordingly the 'accept_subflow' flag.
Stable-dep-of: f3ca0ee2cc30 ("mptcp: decrement subflows counter on failed passive join")
--- net/mptcp/protocol.h | 14 ++++++++++++++ net/mptcp/subflow.c | 12 +++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-)
net/mptcp/protocol.h +14 -0
@@ -839,6 +839,20 @@ unsigned int mptcp_pm_get_add_addr_accepunsigned int mptcp_pm_get_subflows_max (const struct mptcp_sock *msk);unsigned int mptcp_pm_get_local_addr_max (const struct mptcp_sock *msk); +/* called under PM lock */ +static inline void __mptcp_pm_close_subflow (struct mptcp_sock *msk) +{ +if (--msk->pm.subflows <mptcp_pm_get_subflows_max (msk)) +WRITE_ONCE (msk->pm.accept_subflow, true); +} + +static inline void mptcp_pm_close_subflow (struct mptcp_sock *msk) +{ +spin_lock_bh (&msk->pm.lock); +__mptcp_pm_close_subflow (msk); +spin_unlock_bh (&msk->pm.lock); +} +static inline bool mptcp_pm_add_addr_c_flag_case (struct mptcp_sock *msk) { return READ_ONCE(msk->pm.remote_deny_join_id0) &&
net/mptcp/subflow.c +9 -3
@@ -1434,20 +1434,20 @@ int __mptcp_subflow_connect(struct sockstruct sockaddr_storage addr;int remote_id = remote->id;int local_id = loc->id; +int err = -ENOTCONN ;struct socket *sf;struct sock *ssk;u32 remote_token;int addrlen;int ifindex;u8 flags; -int err;if (!mptcp_is_fully_established (sk)) -return -ENOTCONN ; + goto err_out; err =mptcp_subflow_create_socket (sk, &sf);if (err) -return err; + goto err_out; ssk = sf->sk; subflow =mptcp_subflow_ctx (ssk);
@@ -1510,6 +1510,12 @@ failed_unlink: failed: subflow->disposable =1 ;sock_release (sf); + +err_out: +/* we account subflows before the creation, and this failures will not +* be caught by sk_state_change() +*/ +mptcp_pm_close_subflow (msk);return err; }
Greg Kroah-Hartman
maintainerPATCH 204/272 mptcp: decrement subflows counter on failed passive join
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit f3ca0ee2cc308e33896536789cbc5f3a12ca7b30 ]
mptcp_pm_allow_new_subflow() increments extra_subflows before __mptcp_finish_join() on the passive MP_JOIN path.
In case of race conditions, the subflow is dropped without calling mptcp_close_ssk(), so the counter is not rolled back.
Call mptcp_pm_close_subflow() when the join completion fails to decrement the subflows counter.
--- net/mptcp/protocol.c | 1 + 1 file changed, 1 insertion(+)
net/mptcp/protocol.c +1 -0
@@ -3361,6 +3361,7 @@ bool mptcp_finish_join(struct sock *ssk)mptcp_data_unlock (parent);if (!ret) { +mptcp_pm_close_subflow (msk); err_prohibited: subflow->reset_reason =MPTCP_RST_EPROHIBIT ;return false;
Greg Kroah-Hartman
maintainerPATCH 205/272 sctp: avoid auth_enable sysctl UAF during netns teardown
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
[ Upstream commit f8d5e7846025f4ab15a461235f8ebae9094a361a ]
proc_sctp_do_auth() updates the SCTP control socket after changing net.sctp.auth_enable. The handler gets the per-net SCTP state from ctl->data, so an already opened sysctl file can still target a network namespace while that namespace is being torn down.
SCTP previously registered its per-net sysctls from sctp_defaults_init(), while the control socket is created later from sctp_ctrlsock_init(). This exposed a window during initialization where auth_enable was writable before net->sctp.ctl_sock existed, and a teardown window where auth_enable stayed writable after inet_ctl_sock_destroy() had released the control socket.
Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after sctp_ctl_sock_init() succeeds, and unregister the sysctl table before destroying the control socket in sctp_ctrlsock_exit(). If sysctl registration fails after the control socket was created, destroy the control socket in the same init path.
Make sctp_sysctl_net_unregister() tolerate a missing header and clear the saved pointer so init-error and exit paths can safely share the unregister helper.
--- net/sctp/protocol.c | 20 ++++++++++++-------- net/sctp/sysctl.c | 9 +++++++-- 2 files changed, 19 insertions(+), 10 deletions(-)
net/sctp/protocol.c +12 -8
@@ -1386,10 +1386,6 @@ static int __net_init sctp_defaults_init/* Initialize maximum autoclose timeout. */ net->sctp.max_autoclose =INT_MAX /HZ ; - status =sctp_sysctl_net_register (net); -if (status) - goto err_sysctl_register; -/* Allocate and initialise sctp mibs. */ status =init_sctp_mibs (net);if (status)
@@ -1423,8 +1419,6 @@ err_init_proc:cleanup_sctp_mibs (net);#endif err_init_mibs: -sctp_sysctl_net_unregister (net); -err_sysctl_register:return status; }
@@ -1439,7 +1433,6 @@ static void __net_exit sctp_defaults_exi net->sctp.proc_net_sctp =NULL ;#endif cleanup_sctp_mibs (net); -sctp_sysctl_net_unregister (net); }static struct pernet_operations sctp_defaults_ops = {
@@ -1453,16 +1446,27 @@ static int __net_init sctp_ctrlsock_init/* Initialize the control inode/socket for handling OOTB packets. */ status =sctp_ctl_sock_init (net); -if (status) +if (status) {pr_err ("Failed to initialize the SCTP control sock\n" ); +return status; + } + + status =sctp_sysctl_net_register (net); +if (status) { +inet_ctl_sock_destroy (net->sctp.ctl_sock); + net->sctp.ctl_sock =NULL ; + }return status; } static void __net_exit sctp_ctrlsock_exit(struct net *net) { +sctp_sysctl_net_unregister (net); +/* Free the control endpoint. */ inet_ctl_sock_destroy (net->sctp.ctl_sock); + net->sctp.ctl_sock =NULL ; }static struct pernet_operations sctp_ctrlsock_ops = {
net/sctp/sysctl.c +7 -2
@@ -617,11 +617,16 @@ int sctp_sysctl_net_register(struct netvoid sctp_sysctl_net_unregister (struct net *net) { +struct ctl_table_header *header = net->sctp.sysctl_header;struct ctl_table *table; - table = net->sctp.sysctl_header->ctl_table_arg; -unregister_net_sysctl_table (net->sctp.sysctl_header); +if (!header) +return ; + + table = header->ctl_table_arg; +unregister_net_sysctl_table (header);kfree (table); + net->sctp.sysctl_header =NULL ; }static struct ctl_table_header *sctp_sysctl_header;
Greg Kroah-Hartman
maintainerPATCH 206/272 ceph: avoid fs reclaim while using current->journal_info
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 5b602344a49e039e792ce5a8923bcc61412ee134 ]
handle_reply() stores a ceph_mds_request pointer in current->journal_info while filling the inode and dentry cache from an MDS reply.
An allocation in this section can enter direct reclaim and prune dentries from another filesystem. If this dirties an ext4 inode, ext4 starts a JBD2 transaction. JBD2 interprets the Ceph request in current->journal_info as a journal handle and dereferences the request's r_tid as h_transaction, causing a kernel crash, e.g.:
Unable to handle kernel paging request at virtual address 00000000077b4818 [...] Internal error: Oops: 0000000096000004 [#1] SMP Modules linked in: CPU: 6 UID: 0 PID: 2699135 Comm: kworker/6:3 Tainted: G W 6.18.38-i3 #1113 NONE [...] Workqueue: ceph-msgr ceph_con_workfn pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : jbd2__journal_start+0x2c/0x208 lr : __ext4_journal_start_sb+0x100/0x178 [...] Call trace: jbd2__journal_start+0x2c/0x208 (P) __ext4_journal_start_sb+0x100/0x178 ext4_dirty_inode+0x3c/0x90 __mark_inode_dirty+0x58/0x400 iput.part.0+0x2b0/0x370 iput+0x18/0x30 dentry_unlink_inode+0xc0/0x158 __dentry_kill+0x80/0x250 shrink_dentry_list+0x90/0x130 prune_dcache_sb+0x60/0x98 super_cache_scan+0xe8/0x190 do_shrink_slab+0x174/0x388 shrink_slab+0xd8/0x4c0 shrink_node+0x31c/0x908 do_try_to_free_pages+0xd0/0x508 try_to_free_pages+0x11c/0x238 __alloc_frozen_pages_noprof+0x4d0/0xdd0 __folio_alloc_noprof+0x18/0x70 __filemap_get_folio+0x248/0x440 ceph_readdir_prepopulate+0x570/0x9e8 mds_dispatch+0x1424/0x1ba0 ceph_con_process_message+0x74/0xa0 ceph_con_v1_try_read+0x3a0/0x1510 ceph_con_workfn+0x260/0x460
Enter a scoped NOFS allocation context and leave it after clearing journal_info. This prevents filesystem reclaim from recursing into another filesystem while the field contains Ceph-private data.
--- fs/ceph/mds_client.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/ceph/mds_client.c +11 -0
@@ -6,6 +6,7 @@#include <linux/slab.h> #include <linux/gfp.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/ratelimit.h>
@@ -3158,6 +3159,7 @@ static void handle_reply(struct ceph_mdsstruct ceph_mds_reply_head *head = msg->front.iov_base;struct ceph_mds_reply_info_parsed *rinfo;/* parsed reply info */ struct ceph_snap_realm *realm; +unsigned int nofs_flags;u64 tid;int err, result;int mds = session->s_mds;
@@ -3307,6 +3309,14 @@ static void handle_reply(struct ceph_mds/* insert trace into our cache */ mutex_lock (&req->r_fill_mutex); + +/* disable fs reclaim while we are using current->journal_info +* for our own purposes, or else shrinkers of other +* filesystems might dereference this pointer as a different +* type +*/ + nofs_flags =memalloc_nofs_save (); + current->journal_info = req; err =ceph_fill_trace (mdsc->fsc->sb, req);if (err ==0 ) {
@@ -3315,6 +3325,7 @@ static void handle_reply(struct ceph_mdsceph_readdir_prepopulate (req, req->r_session); } current->journal_info =NULL ; +memalloc_nofs_restore (nofs_flags);mutex_unlock (&req->r_fill_mutex);up_read (&mdsc->snap_rwsem);
Greg Kroah-Hartman
maintainerPATCH 207/272 libceph: Amend checking to fix `make W=1` build breakage
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 04d8712b079327409b09dee628378f9583e2e035 ]
In a few cases the code compares 32-bit value to a SIZE_MAX derived constant which is much higher than that value on 64-bit platforms, Clang, in particular, is not happy about this
net/ceph/osdmap.c:1441:10: error: result of comparison of constant 4611686018427387891 with expression of type 'u32' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
1441 | if (len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
| ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/ceph/osdmap.c:1624:10: error: result of comparison of constant 2305843009213693945 with expression of type 'u32' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
1624 | if (len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
| ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Fix this by casting to size_t. Note, that possible replacement of SIZE_MAX by U32_MAX may lead to the behaviour changes on the corner cases.
Stable-dep-of: 9f00f9cf2be2 ("libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE")
--- net/ceph/osdmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +2 -2
@@ -1424,7 +1424,7 @@ static struct ceph_pg_mapping *__decode_ceph_decode_32_safe (p, end, len, e_inval);if (len ==0 && incremental)return NULL ;/* new_pg_temp: [] to remove */ -if (len > (SIZE_MAX -sizeof (*pg)) /sizeof (u32)) +if ((size_t )len > (SIZE_MAX -sizeof (*pg)) /sizeof (u32))return ERR_PTR (-EINVAL );ceph_decode_need (p, end, len *sizeof (u32), e_inval);
@@ -1607,7 +1607,7 @@ static struct ceph_pg_mapping *__decode_u32 len, i;ceph_decode_32_safe (p, end, len, e_inval); -if (len > (SIZE_MAX -sizeof (*pg)) / (2 *sizeof (u32))) +if ((size_t )len > (SIZE_MAX -sizeof (*pg)) / (2 *sizeof (u32)))return ERR_PTR (-EINVAL );ceph_decode_need (p, end,2 * len *sizeof (u32), e_inval);
Greg Kroah-Hartman
maintainerPATCH 208/272 libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 9f00f9cf2be293efe899db67dc5272e3a9c62717 ]
__decode_pg_temp() decodes an user-controlled length but only rejects values large enough to overflow the allocation; it does not bound it to CEPH_PG_MAX_SIZE. The helper backs both pg_temp and pg_upmap decoding, and apply_upmap()/get_temp_osds() later copy the decoded list into the fixed-size on-stack array struct ceph_osds.osds[CEPH_PG_MAX_SIZE]. A monitor that sends an OSDMap with a pg_temp/pg_upmap entry longer than 32 thus causes a stack out-of-bounds write.
An OSD set for a single PG can never exceed CEPH_PG_MAX_SIZE, so reject longer entries at decode time. The bound is well below the old overflow threshold, so it also covers the allocation-size overflow the previous check guarded against.
BUG: KASAN: stack-out-of-bounds in ceph_pg_to_up_acting_osds Write of size 4 ... by task exploit kasan_report (mm/kasan/report.c:595) ceph_pg_to_up_acting_osds (net/ceph/osdmap.c:2617 net/ceph/osdmap.c:2833) calc_target (net/ceph/osd_client.c:1638) __submit_request (net/ceph/osd_client.c:2394) ceph_osdc_start_request (net/ceph/osd_client.c:2490) ceph_osdc_call (net/ceph/osd_client.c:5164) rbd_dev_image_probe (drivers/block/rbd.c:6899) do_rbd_add (drivers/block/rbd.c:7138) ... kernel BUG at net/ceph/osdmap.c:2670!
[ idryomov: do the same in __decode_pg_upmap_items() ]
--- net/ceph/osdmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +2 -2
@@ -1424,7 +1424,7 @@ static struct ceph_pg_mapping *__decode_ceph_decode_32_safe (p, end, len, e_inval);if (len ==0 && incremental)return NULL ;/* new_pg_temp: [] to remove */ -if ((size_t )len > (SIZE_MAX -sizeof (*pg)) /sizeof (u32)) +if (len >CEPH_PG_MAX_SIZE )return ERR_PTR (-EINVAL );ceph_decode_need (p, end, len *sizeof (u32), e_inval);
@@ -1607,7 +1607,7 @@ static struct ceph_pg_mapping *__decode_u32 len, i;ceph_decode_32_safe (p, end, len, e_inval); -if ((size_t )len > (SIZE_MAX -sizeof (*pg)) / (2 *sizeof (u32))) +if (len >CEPH_PG_MAX_SIZE )return ERR_PTR (-EINVAL );ceph_decode_need (p, end,2 * len *sizeof (u32), e_inval);
Greg Kroah-Hartman
maintainerPATCH 209/272 libceph: add doutc and *_client debug macros support
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 5c5f0d2b5f92c47baf82b9b211e27edd7d195158 ]
This will help print the fsid and client's global_id in debug logs, and also print the function names.
[ idryomov: %lld -> %llu, leading space for doutc(), don't include
__func__ in pr_*() variants ]
Stable-dep-of: 50958bb928ba ("ceph: fix hanging __ceph_get_caps() with stale mds_wanted")
--- include/linux/ceph/ceph_debug.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
include/linux/ceph/ceph_debug.h +38 -0
@@ -19,12 +19,25 @@pr_debug ("%.*s %12.12s:%-4d : " fmt, \8 - (int )sizeof (KBUILD_MODNAME )," " , \ kbasename(__FILE__), __LINE__, ##__VA_ARGS__) +# define doutc (client, fmt, ...) \ + pr_debug("%.*s %12.12s:%-4d : [%pU %llu] " fmt, \ + 8 - (int)sizeof(KBUILD_MODNAME), " ", \ + kbasename(__FILE__), __LINE__, \ + &client->fsid, client->monc.auth->global_id, \ + ##__VA_ARGS__)# else /* faux printk call just to see any compiler warnings. */ # define dout (fmt, ...) do { \ if (0) \ printk(KERN_DEBUG fmt, ##__VA_ARGS__); \ } while (0) +# define doutc (client, fmt, ...) do { \ + if (0) \ + printk(KERN_DEBUG "[%pU %llu] " fmt, \ + &client->fsid, \ + client->monc.auth->global_id, \ + ##__VA_ARGS__); \ + } while (0)# endif #else
@@ -33,7 +46,32 @@* or, just wrap pr_debug */ # define dout (fmt, ...) pr_debug(" " fmt, ##__VA_ARGS__) +# define doutc (client, fmt, ...) \ + pr_debug(" [%pU %llu] %s: " fmt, &client->fsid, \ + client->monc.auth->global_id, __func__, ##__VA_ARGS__)#endif +#define pr_notice_client (client, fmt, ...) \ + pr_notice("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_info_client (client, fmt, ...) \ + pr_info("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_client (client, fmt, ...) \ + pr_warn("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_once_client (client, fmt, ...) \ + pr_warn_once("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_err_client (client, fmt, ...) \ + pr_err("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_ratelimited_client (client, fmt, ...) \ + pr_warn_ratelimited("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_err_ratelimited_client (client, fmt, ...) \ + pr_err_ratelimited("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#endif
Greg Kroah-Hartman
maintainerPATCH 210/272 ceph: rename _to_client() to _to_fs_client()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 5995d90d2d19f337df6a50bcf4699ef053214dac ]
We need to covert the inode to ceph_client in the following commit, and will add one new helper for that, here we rename the old helper to _fs_client().
Stable-dep-of: 50958bb928ba ("ceph: fix hanging __ceph_get_caps() with stale mds_wanted")
--- fs/ceph/addr.c | 20 ++++++++++---------- fs/ceph/cache.c | 2 +- fs/ceph/caps.c | 36 ++++++++++++++++++------------------ fs/ceph/dir.c | 20 ++++++++++---------- fs/ceph/export.c | 10 +++++----- fs/ceph/file.c | 24 ++++++++++++------------ fs/ceph/inode.c | 14 +++++++------- fs/ceph/ioctl.c | 8 ++++---- fs/ceph/snap.c | 2 +- fs/ceph/super.c | 22 +++++++++++----------- fs/ceph/super.h | 8 ++++---- fs/ceph/xattr.c | 12 ++++++------ 12 files changed, 89 insertions(+), 89 deletions(-)
fs/ceph/addr.c +10 -10
@@ -196,7 +196,7 @@ static void ceph_netfs_expand_readahead(static bool ceph_netfs_clamp_length (struct netfs_read_subrequest *subreq) {struct inode *inode = subreq->rreq->inode; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_inode_info *ci =ceph_inode (inode);u64 objno, objoff;u32 xlen;
@@ -210,7 +210,7 @@ static bool ceph_netfs_clamp_length(strustatic void finish_netfs_read (struct ceph_osd_request *req) { -struct ceph_fs_client *fsc =ceph_inode_to_client (req->r_inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (req->r_inode);struct ceph_osd_data *osd_data =osd_req_op_extent_osd_data (req,0 );struct netfs_read_subrequest *subreq = req->r_priv;int num_pages;
@@ -243,7 +243,7 @@ static void ceph_netfs_issue_op(struct nstruct netfs_read_request *rreq = subreq->rreq;struct inode *inode = rreq->inode;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_request *req;struct ceph_vino vino =ceph_vino (inode);struct iov_iter iter;
@@ -483,7 +483,7 @@ static int writepage_nounlock(struct pag {struct inode *inode = page->mapping->host;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_snap_context *snapc, *oldest;loff_t page_off =page_offset (page);int err;
@@ -625,7 +625,7 @@ static void writepages_finish(struct cepint rc = req->r_result;struct ceph_snap_context *snapc = req->r_snapc;struct address_space *mapping = inode->i_mapping; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);unsigned int len =0 ;bool remove_page;
@@ -707,7 +707,7 @@ static int ceph_writepages_start(struct {struct inode *inode = mapping->host;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_vino vino =ceph_vino (inode);pgoff_t index, start_index, end =-1 ;struct ceph_snap_context *snapc =NULL , *last_snapc =NULL , *pgsnapc;
@@ -1144,7 +1144,7 @@ static struct ceph_snap_context * ceph_find_incompatible(struct page *page) {struct inode *inode = page->mapping->host; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_inode_info *ci =ceph_inode (inode);if (READ_ONCE (fsc->mount_state) >=CEPH_MOUNT_SHUTDOWN ) {
@@ -1594,7 +1594,7 @@ int ceph_uninline_data(struct file *filp {struct inode *inode =file_inode (filp);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_request *req;struct page *page =NULL ;u64 len, inline_version;
@@ -1751,7 +1751,7 @@ enum {static int __ceph_pool_perm_get (struct ceph_inode_info *ci,s64 pool,struct ceph_string *pool_ns) { -struct ceph_fs_client *fsc =ceph_inode_to_client (&ci->vfs_inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (&ci->vfs_inode);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_osd_request *rd_req =NULL , *wr_req =NULL ;struct rb_node **p, *parent;
@@ -1944,7 +1944,7 @@ int ceph_pool_perm_check(struct inode *ireturn 0 ; } -if (ceph_test_mount_opt (ceph_inode_to_client (inode), +if (ceph_test_mount_opt (ceph_inode_to_fs_client (inode),NOPOOLPERM ))return 0 ;
fs/ceph/cache.c +1 -1
@@ -138,7 +138,7 @@ static const struct fscache_cookie_def cvoid ceph_fscache_register_inode_cookie (struct inode *inode) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_aux_inode aux;/* No caching for filesystem */
fs/ceph/caps.c +18 -18
@@ -633,7 +633,7 @@ void ceph_add_cap(struct inode *inode, unsigned seq, unsigned mseq, u64 realmino, int flags, struct ceph_cap **new_cap) { - struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_cap *cap;int mds = session->s_mds;
@@ -919,7 +919,7 @@ int __ceph_caps_issued_mask(struct ceph_int __ceph_caps_issued_mask_metric (struct ceph_inode_info *ci,int mask,int touch) { -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);int r; r =__ceph_caps_issued_mask (ci, mask, touch);
@@ -993,7 +993,7 @@ int __ceph_caps_file_wanted(struct ceph_const int WR_SHIFT =ffs (CEPH_FILE_MODE_WR );const int LAZY_SHIFT =ffs (CEPH_FILE_MODE_LAZY );struct ceph_mount_options *opt = -ceph_inode_to_client (&ci->vfs_inode)->mount_options; +ceph_inode_to_fs_client (&ci->vfs_inode)->mount_options;unsigned long used_cutoff = jiffies - opt->caps_wanted_delay_max *HZ ;unsigned long idle_cutoff = jiffies - opt->caps_wanted_delay_min *HZ ;
@@ -1118,7 +1118,7 @@ void __ceph_remove_cap(struct ceph_cap *dout ("__ceph_remove_cap %p from %p\n" , cap, &ci->vfs_inode); - mdsc =ceph_inode_to_client (&ci->vfs_inode)->mdsc; + mdsc =ceph_inode_to_fs_client (&ci->vfs_inode)->mdsc;/* remove from inode's cap rbtree, and clear auth cap */ rb_erase (&cap->ci_node, &ci->i_caps);
@@ -1188,7 +1188,7 @@ void ceph_remove_cap(struct ceph_cap *calockdep_assert_held (&ci->i_ceph_lock); - fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); + fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb); WARN_ON_ONCE(ci->i_auth_cap == cap && !list_empty(&ci->i_dirty_item) && !fsc->blocklisted &&
@@ -1623,7 +1623,7 @@ void ceph_flush_snaps(struct ceph_inode_ struct ceph_mds_session **psession) {struct inode *inode = &ci->vfs_inode; -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;struct ceph_mds_session *session =NULL ;bool need_put = false;int mds;
@@ -1688,7 +1688,7 @@ int __ceph_mark_dirty_caps(struct ceph_i struct ceph_cap_flush **pcf) {struct ceph_mds_client *mdsc = -ceph_sb_to_client (ci->vfs_inode.i_sb)->mdsc; +ceph_sb_to_fs_client (ci->vfs_inode.i_sb)->mdsc;struct inode *inode = &ci->vfs_inode;int was = ci->i_dirty_caps;int dirty =0 ;
@@ -1811,7 +1811,7 @@ static u64 __mark_caps_flushing(struct i struct ceph_mds_session *session, bool wake, u64 *oldest_flush_tid) { - struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_cap_flush *cf =NULL ;int flushing;
@@ -2143,7 +2143,7 @@ ack:*/ static int try_flush_caps (struct inode *inode,u64 *ptid) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);int flushing =0 ;u64 flush_tid =0 , oldest_flush_tid =0 ;
@@ -2221,7 +2221,7 @@ static int caps_are_flushed(struct inode*/ static int unsafe_request_wait (struct inode *inode) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req1 =NULL , *req2 =NULL ;int ret, err =0 ;
@@ -2400,7 +2400,7 @@ int ceph_write_inode(struct inode *inode caps_are_flushed(inode, flush_tid)); }else {struct ceph_mds_client *mdsc = -ceph_sb_to_client (inode->i_sb)->mdsc; +ceph_sb_to_fs_client (inode->i_sb)->mdsc;spin_lock (&ci->i_ceph_lock);if (__ceph_caps_dirty (ci))
@@ -2649,7 +2649,7 @@ static int try_get_cap_refs(struct inode loff_t endoff, int flags, int *got) { struct ceph_inode_info *ci = ceph_inode(inode); -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;int ret =0 ;int have, implemented;bool snap_rwsem_locked = false;
@@ -2864,7 +2864,7 @@ int ceph_get_caps(struct file *filp, intstruct ceph_file_info *fi = filp->private_data;struct inode *inode =file_inode (filp);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);int ret, _got, flags; ret =ceph_pool_perm_check (inode, need);
@@ -3593,7 +3593,7 @@ static void handle_cap_flush_ack(struct __releases(ci->i_ceph_lock) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_cap_flush *cf, *tmp_cf;LIST_HEAD (to_remove);unsigned seq =le32_to_cpu (m->seq);
@@ -3699,7 +3699,7 @@ void __ceph_remove_capsnap(struct inode bool *wake_ci, bool *wake_mdsc) { struct ceph_inode_info *ci = ceph_inode(inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;bool ret;lockdep_assert_held (&ci->i_ceph_lock);
@@ -3743,7 +3743,7 @@ static void handle_cap_flushsnap_ack(str struct ceph_mds_session *session) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;u64 follows =le64_to_cpu (m->snap_follows);struct ceph_cap_snap *capsnap;bool flushed = false;
@@ -3827,7 +3827,7 @@ static void handle_cap_export(struct inostruct ceph_mds_cap_peer *ph, struct ceph_mds_session *session) { - struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc;struct ceph_mds_session *tsession =NULL ;struct ceph_cap *cap, *tcap, *new_cap =NULL ;struct ceph_inode_info *ci =ceph_inode (inode);
@@ -4442,7 +4442,7 @@ int ceph_drop_caps_for_unlink(struct inoif (__ceph_caps_dirty (ci)) {struct ceph_mds_client *mdsc = -ceph_inode_to_client (inode)->mdsc; +ceph_inode_to_fs_client (inode)->mdsc;__cap_delay_requeue_front (mdsc, ci); } }
fs/ceph/dir.c +10 -10
@@ -307,7 +307,7 @@ static int ceph_readdir(struct file *filstruct ceph_dir_file_info *dfi = file->private_data;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_mds_client *mdsc = fsc->mdsc;int i;int err;
@@ -679,7 +679,7 @@ out:struct dentry *ceph_handle_snapdir (struct ceph_mds_request *req,struct dentry *dentry) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dentry->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dentry->d_sb);struct inode *parent =d_inode (dentry->d_parent);/* we hold i_mutex */ /* .snap dir? */
@@ -748,7 +748,7 @@ static bool is_root_ceph_dentry(struct istatic struct dentry *ceph_lookup (struct inode *dir,struct dentry *dentry,unsigned int flags) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dir->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dir->i_sb);struct ceph_mds_client *mdsc =ceph_sb_to_mdsc (dir->i_sb);struct ceph_mds_request *req;int op;
@@ -1147,7 +1147,7 @@ static int get_caps_for_async_unlink(str*/ static int ceph_unlink (struct inode *dir,struct dentry *dentry) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dir->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dir->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct inode *inode =d_inode (dentry);struct ceph_inode_info *ci =ceph_inode (inode);
@@ -1308,7 +1308,7 @@ void __ceph_dentry_lease_touch(struct cereturn ; } - mdsc =ceph_sb_to_client (dn->d_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (dn->d_sb)->mdsc;spin_lock (&mdsc->dentry_list_lock);list_move_tail (&di->lease_list, &mdsc->dentry_leases);spin_unlock (&mdsc->dentry_list_lock);
@@ -1355,7 +1355,7 @@ void __ceph_dentry_dir_lease_touch(strucreturn ; } - mdsc =ceph_sb_to_client (dn->d_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (dn->d_sb)->mdsc;spin_lock (&mdsc->dentry_list_lock);__dentry_dir_lease_touch (mdsc, di),spin_unlock (&mdsc->dentry_list_lock);
@@ -1369,7 +1369,7 @@ static void __dentry_lease_unlist(structif (list_empty (&di->lease_list))return ; - mdsc =ceph_sb_to_client (di->dentry->d_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (di->dentry->d_sb)->mdsc;spin_lock (&mdsc->dentry_list_lock);list_del_init (&di->lease_list);spin_unlock (&mdsc->dentry_list_lock);
@@ -1722,7 +1722,7 @@ static int ceph_d_revalidate(struct dentdout ("d_revalidate %p '%pd' inode %p offset 0x%llx\n" , dentry, dentry, inode,ceph_dentry (dentry)->offset); - mdsc =ceph_sb_to_client (dir->i_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (dir->i_sb)->mdsc;/* always trust cached snapped dentries, snapdir dentry */ if (ceph_snap (dir) !=CEPH_NOSNAP ) {
@@ -1829,7 +1829,7 @@ static int ceph_d_delete(const struct destatic void ceph_d_release (struct dentry *dentry) {struct ceph_dentry_info *di =ceph_dentry (dentry); -struct ceph_fs_client *fsc =ceph_sb_to_client (dentry->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dentry->d_sb);dout ("d_release %p\n" , dentry);
@@ -1898,7 +1898,7 @@ static ssize_t ceph_read_dir(struct fileint left;const int bufsize =1024 ; -if (!ceph_test_mount_opt (ceph_sb_to_client (inode->i_sb),DIRSTAT )) +if (!ceph_test_mount_opt (ceph_sb_to_fs_client (inode->i_sb),DIRSTAT ))return -EISDIR ;if (!dfi->dir_info) {
fs/ceph/export.c +5 -5
@@ -122,7 +122,7 @@ static int ceph_encode_fh(struct inode *static struct inode *__lookup_inode (struct super_block *sb,u64 ino) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (sb)->mdsc;struct inode *inode;struct ceph_vino vino;int err;
@@ -198,7 +198,7 @@ static struct dentry *__snapfh_to_dentry struct ceph_nfs_snapfh *sfh, bool want_parent) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (sb)->mdsc;struct ceph_mds_request *req;struct inode *inode;struct ceph_vino vino;
@@ -305,7 +305,7 @@ static struct dentry *ceph_fh_to_dentry(static struct dentry *__get_parent (struct super_block *sb,struct dentry *child,u64 ino) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (sb)->mdsc;struct ceph_mds_request *req;struct inode *inode;int mask;
@@ -427,7 +427,7 @@ static int __get_snap_name(struct dentry {struct inode *inode =d_inode (child);struct inode *dir =d_inode (parent); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_mds_request *req =NULL ;char *last_name =NULL ;unsigned next_offset =2 ;
@@ -530,7 +530,7 @@ static int ceph_get_name(struct dentry *if (ceph_snap (inode) !=CEPH_NOSNAP )return __get_snap_name (parent, name, child); - mdsc =ceph_inode_to_client (inode)->mdsc; + mdsc =ceph_inode_to_fs_client (inode)->mdsc; req =ceph_mdsc_create_request (mdsc,CEPH_MDS_OP_LOOKUPNAME ,USE_ANY_MDS );if (IS_ERR (req))
fs/ceph/file.c +12 -12
@@ -233,7 +233,7 @@ static int ceph_init_file_info(struct inspin_lock_init (&fi->rw_contexts_lock);INIT_LIST_HEAD (&fi->rw_contexts); - fi->filp_gen =READ_ONCE (ceph_inode_to_client (inode)->filp_gen); + fi->filp_gen =READ_ONCE (ceph_inode_to_fs_client (inode)->filp_gen);return 0 ; }
@@ -338,7 +338,7 @@ out:int ceph_open (struct inode *inode,struct file *file) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_sb_to_client (inode->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (inode->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_mds_request *req;struct ceph_file_info *fi = file->private_data;
@@ -687,7 +687,7 @@ static int ceph_finish_async_create(struint ceph_atomic_open (struct inode *dir,struct dentry *dentry,struct file *file,unsigned flags,umode_t mode) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dir->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dir->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_mds_request *req;struct dentry *dn;
@@ -875,7 +875,7 @@ static ssize_t ceph_sync_read(struct kiostruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_client *osdc = &fsc->client->osdc;ssize_t ret;u64 off = iocb->ki_pos;
@@ -1085,7 +1085,7 @@ static void ceph_aio_complete_req(structif (aio_work) {INIT_WORK (&aio_work->work, ceph_aio_retry_work); aio_work->req = req; -queue_work (ceph_inode_to_client (inode)->inode_wq, +queue_work (ceph_inode_to_fs_client (inode)->inode_wq, &aio_work->work);return ; }
@@ -1213,7 +1213,7 @@ ceph_direct_read_write(struct kiocb *iocstruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_client_metric *metric = &fsc->mdsc->metric;struct ceph_vino vino;struct ceph_osd_request *req;
@@ -1423,7 +1423,7 @@ ceph_sync_write(struct kiocb *iocb, strustruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_vino vino;struct ceph_osd_request *req;struct page **pages;
@@ -1702,7 +1702,7 @@ static ssize_t ceph_write_iter(struct kistruct ceph_file_info *fi = file->private_data;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_client *osdc = &fsc->client->osdc;struct ceph_cap_flush *prealloc_cf;ssize_t count, written =0 ;
@@ -1895,7 +1895,7 @@ out_unlocked:static loff_t ceph_llseek (struct file *file,loff_t offset,int whence) {struct inode *inode = file->f_mapping->host; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);loff_t i_size;loff_t ret;
@@ -1988,7 +1988,7 @@ static int ceph_zero_partial_object(stru loff_t offset, loff_t *length) { struct ceph_inode_info *ci = ceph_inode(inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_request *req;struct ceph_snap_context *snapc;int ret =0 ;
@@ -2332,7 +2332,7 @@ static ssize_t __ceph_copy_file_range(ststruct ceph_inode_info *src_ci =ceph_inode (src_inode);struct ceph_inode_info *dst_ci =ceph_inode (dst_inode);struct ceph_cap_flush *prealloc_cf; -struct ceph_fs_client *src_fsc =ceph_inode_to_client (src_inode); +struct ceph_fs_client *src_fsc =ceph_inode_to_fs_client (src_inode);loff_t size;ssize_t ret = -EIO , bytes;u64 src_objnum, dst_objnum, src_objoff, dst_objoff;
@@ -2340,7 +2340,7 @@ static ssize_t __ceph_copy_file_range(stint src_got =0 , dst_got =0 , err, dirty;if (src_inode->i_sb != dst_inode->i_sb) { -struct ceph_fs_client *dst_fsc =ceph_inode_to_client (dst_inode); +struct ceph_fs_client *dst_fsc =ceph_inode_to_fs_client (dst_inode);if (ceph_fsid_compare (&src_fsc->client->fsid, &dst_fsc->client->fsid)) {
fs/ceph/inode.c +7 -7
@@ -1272,7 +1272,7 @@ int ceph_fill_trace(struct super_block *struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;struct inode *in =NULL ;struct ceph_vino tvino, dvino; -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);int err =0 ; dout("fill_trace %p is_dentry %d is_target %d\n", req,
@@ -1828,7 +1828,7 @@ bool ceph_inode_set_size(struct inode *ivoid ceph_queue_inode_work (struct inode *inode,int work_bit) { -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_inode_info *ci =ceph_inode (inode);set_bit (work_bit, &ci->i_work_mask);
@@ -1845,7 +1845,7 @@ void ceph_queue_inode_work(struct inodestatic void ceph_do_invalidate_pages (struct inode *inode) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);u32 orig_gen;int check =0 ;
@@ -2000,7 +2000,7 @@ int __ceph_setattr(struct inode *inode,struct ceph_inode_info *ci =ceph_inode (inode);unsigned int ia_valid = attr->ia_valid;struct ceph_mds_request *req; -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_cap_flush *prealloc_cf;int issued;int release =0 , dirtied =0 ;
@@ -2215,7 +2215,7 @@ int ceph_setattr(struct user_namespace * struct iattr *attr) {struct inode *inode =d_inode (dentry); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);int err;if (ceph_snap (inode) !=CEPH_NOSNAP )
@@ -2248,7 +2248,7 @@ int ceph_setattr(struct user_namespace *int __ceph_do_getattr (struct inode *inode,struct page *locked_page,int mask,bool force) { -struct ceph_fs_client *fsc =ceph_sb_to_client (inode->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (inode->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_mds_request *req;int mode;
@@ -2378,7 +2378,7 @@ int ceph_getattr(struct user_namespace * stat->dev = ci->i_snapid_map ? ci->i_snapid_map->dev :0 ;if (S_ISDIR (inode->i_mode)) { -if (ceph_test_mount_opt (ceph_sb_to_client (inode->i_sb), +if (ceph_test_mount_opt (ceph_sb_to_fs_client (inode->i_sb),RBYTES )) stat->size = ci->i_rbytes;else
fs/ceph/ioctl.c +4 -4
@@ -64,7 +64,7 @@ static long __validate_layout(struct cepstatic long ceph_ioctl_set_layout (struct file *file,void __user *arg) {struct inode *inode =file_inode (file); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_mds_request *req;struct ceph_ioctl_layout l;struct ceph_inode_info *ci =ceph_inode (file_inode (file));
@@ -139,7 +139,7 @@ static long ceph_ioctl_set_layout_policystruct ceph_mds_request *req;struct ceph_ioctl_layout l;int err; -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;/* copy and validate */ if (copy_from_user (&l, arg,sizeof (l)))
@@ -182,7 +182,7 @@ static long ceph_ioctl_get_dataloc(strucstruct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_osd_client *osdc = - &ceph_sb_to_client (inode->i_sb)->client->osdc; + &ceph_sb_to_fs_client (inode->i_sb)->client->osdc;struct ceph_object_locator oloc;CEPH_DEFINE_OID_ONSTACK (oid);u32 xlen;
@@ -243,7 +243,7 @@ static long ceph_ioctl_lazyio(struct filstruct ceph_file_info *fi = file->private_data;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;if ((fi->fmode &CEPH_FILE_MODE_LAZY ) ==0 ) {spin_lock (&ci->i_ceph_lock);
fs/ceph/snap.c +1 -1
@@ -875,7 +875,7 @@ static void flush_snaps(struct ceph_mds_void ceph_change_snap_realm (struct inode *inode,struct ceph_snap_realm *realm) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;struct ceph_snap_realm *oldrealm = ci->i_snap_realm;lockdep_assert_held (&ci->i_ceph_lock);
fs/ceph/super.c +11 -11
@@ -41,7 +41,7 @@ static LIST_HEAD(ceph_fsc_list);*/ static void ceph_put_super (struct super_block *s) { -struct ceph_fs_client *fsc =ceph_sb_to_client (s); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (s);dout ("put_super\n" );ceph_mdsc_close_sessions (fsc->mdsc);
@@ -49,7 +49,7 @@ static void ceph_put_super(struct super_static int ceph_statfs (struct dentry *dentry,struct kstatfs *buf) { -struct ceph_fs_client *fsc =ceph_inode_to_client (d_inode (dentry)); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (d_inode (dentry));struct ceph_mon_client *monc = &fsc->client->monc;struct ceph_statfs st;int i, err;
@@ -112,7 +112,7 @@ static int ceph_statfs(struct dentry *destatic int ceph_sync_fs (struct super_block *sb,int wait) { -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);if (!wait) {dout ("sync_fs (non-blocking)\n" );
@@ -529,7 +529,7 @@ static int compare_mount_options(struct*/ static int ceph_show_options (struct seq_file *m,struct dentry *root) { -struct ceph_fs_client *fsc =ceph_sb_to_client (root->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (root->d_sb);struct ceph_mount_options *fsopt = fsc->mount_options;size_t pos;int ret;
@@ -848,7 +848,7 @@ static void __ceph_umount_begin(struct c*/ static void ceph_umount_begin (struct super_block *sb) { -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);dout ("ceph_umount_begin - starting forced umount\n" );if (!fsc)
@@ -1006,7 +1006,7 @@ static int ceph_compare_super(struct supstruct ceph_fs_client *new = fc->s_fs_info;struct ceph_mount_options *fsopt = new->mount_options;struct ceph_options *opt = new->client->options; -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);dout ("ceph_compare_super %p\n" , sb);
@@ -1099,9 +1099,9 @@ static int ceph_get_tree(struct fs_conte goto out; } -if (ceph_sb_to_client (sb) != fsc) { +if (ceph_sb_to_fs_client (sb) != fsc) {destroy_fs_client (fsc); - fsc =ceph_sb_to_client (sb); + fsc =ceph_sb_to_fs_client (sb);dout ("get_sb got existing client %p\n" , fsc); }else {dout ("get_sb using new client %p\n" , fsc);
@@ -1152,7 +1152,7 @@ static int ceph_reconfigure_fc(struct fs {struct ceph_parse_opts_ctx *pctx = fc->fs_private;struct ceph_mount_options *fsopt = pctx->opts; -struct ceph_fs_client *fsc =ceph_sb_to_client (fc->root->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (fc->root->d_sb);if (fsopt->flags &CEPH_MOUNT_OPT_ASYNC_DIROPS )ceph_set_mount_opt (fsc,ASYNC_DIROPS );
@@ -1223,7 +1223,7 @@ nomem:static void ceph_kill_sb (struct super_block *s) { -struct ceph_fs_client *fsc =ceph_sb_to_client (s); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (s);dout ("kill_sb %p\n" , s);
@@ -1261,7 +1261,7 @@ MODULE_ALIAS_FS("ceph");int ceph_force_reconnect (struct super_block *sb) { -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);int err =0 ; fsc->mount_state =CEPH_MOUNT_RECOVER ;
fs/ceph/super.h +4 -4
@@ -439,13 +439,13 @@ ceph_inode(const struct inode *inode) }static inline struct ceph_fs_client * -ceph_inode_to_client (const struct inode *inode) +ceph_inode_to_fs_client (const struct inode *inode) {return (struct ceph_fs_client *)inode->i_sb->s_fs_info; }static inline struct ceph_fs_client * -ceph_sb_to_client (const struct super_block *sb) +ceph_sb_to_fs_client (const struct super_block *sb) {return (struct ceph_fs_client *)sb->s_fs_info; }
@@ -453,7 +453,7 @@ ceph_sb_to_client(const struct super_blostatic inline struct ceph_mds_client *ceph_sb_to_mdsc (const struct super_block *sb) { -return (struct ceph_mds_client *)ceph_sb_to_client (sb)->mdsc; +return (struct ceph_mds_client *)ceph_sb_to_fs_client (sb)->mdsc; } static inline struct ceph_vino
@@ -509,7 +509,7 @@ static inline u64 ceph_snap(struct inode*/ static inline u64 ceph_present_ino (struct super_block *sb,u64 ino) { -if (unlikely (ceph_test_mount_opt (ceph_sb_to_client (sb),INO32 ))) +if (unlikely (ceph_test_mount_opt (ceph_sb_to_fs_client (sb),INO32 )))return ceph_ino_to_ino32 (ino);return ino; }
fs/ceph/xattr.c +6 -6
@@ -57,7 +57,7 @@ static bool ceph_vxattrcb_layout_exists(static ssize_t ceph_vxattrcb_layout (struct ceph_inode_info *ci,char *val,size_t size) { -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);struct ceph_osd_client *osdc = &fsc->client->osdc;struct ceph_string *pool_ns;s64 pool = ci->i_layout.pool_id;
@@ -161,7 +161,7 @@ static ssize_t ceph_vxattrcb_layout_pool char *val, size_t size) { ssize_t ret; -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);struct ceph_osd_client *osdc = &fsc->client->osdc;s64 pool = ci->i_layout.pool_id;const char *pool_name;
@@ -313,7 +313,7 @@ static ssize_t ceph_vxattrcb_snap_btime(static ssize_t ceph_vxattrcb_cluster_fsid (struct ceph_inode_info *ci,char *val,size_t size) { -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);return ceph_fmt_xattr (val, size,"%pU" , &fsc->client->fsid); }
@@ -321,7 +321,7 @@ static ssize_t ceph_vxattrcb_cluster_fsistatic ssize_t ceph_vxattrcb_client_id (struct ceph_inode_info *ci,char *val,size_t size) { -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);return ceph_fmt_xattr (val, size,"client%lld" ,ceph_client_gid (fsc->client));
@@ -1052,7 +1052,7 @@ out:static int ceph_sync_setxattr (struct inode *inode,const char *name,const char *value,size_t size,int flags) { -struct ceph_fs_client *fsc =ceph_sb_to_client (inode->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (inode->i_sb);struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req;struct ceph_mds_client *mdsc = fsc->mdsc;
@@ -1122,7 +1122,7 @@ int __ceph_setxattr(struct inode *inode, {struct ceph_vxattr *vxattr;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_cap_flush *prealloc_cf =NULL ;struct ceph_buffer *old_blob =NULL ;int issued;
Greg Kroah-Hartman
maintainerPATCH 211/272 ceph: print cluster fsid and client global_id in all debug logs
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 38d46409c4639a1d659ebfa70e27a8bed6b8ee1d ]
Multiple CephFS mounts on a host is increasingly common so disambiguating messages like this is necessary and will make it easier to debug issues.
At the same this will improve the debug logs to make them easier to troubleshooting issues, such as print the ino# instead only printing the memory addresses of the corresponding inodes and print the dentry names instead of the corresponding memory addresses for the dentry,etc.
Stable-dep-of: 50958bb928ba ("ceph: fix hanging __ceph_get_caps() with stale mds_wanted")
--- fs/ceph/acl.c | 6 ++++-- fs/ceph/file.c | 9 ++++++--- fs/ceph/metric.c | 5 +++-- fs/ceph/super.h | 6 ++++++ 4 files changed, 19 insertions(+), 7 deletions(-)
fs/ceph/acl.c +4 -2
@@ -15,6 +15,7 @@#include <linux/slab.h> #include "super.h" +#include "mds_client.h" static inline void ceph_set_cached_acl (struct inode *inode,int type,struct posix_acl *acl)
@@ -31,6 +32,7 @@ static inline void ceph_set_cached_acl(sstruct posix_acl *ceph_get_acl (struct inode *inode,int type,bool rcu) { +struct ceph_client *cl =ceph_inode_to_client (inode);int size;unsigned int retry_cnt =0 ;const char *name;
@@ -72,8 +74,8 @@ retry: }else if (size == -ENODATA || size ==0 ) { acl =NULL ; }else { -pr_err_ratelimited ("get acl %llx.%llx failed, err=%d\n" , -ceph_vinop (inode), size); +pr_err_ratelimited_client (cl,"%llx.%llx failed, err=%d\n" , +ceph_vinop (inode), size); acl =ERR_PTR (-EIO ); }
fs/ceph/file.c +6 -3
@@ -282,6 +282,7 @@ static int ceph_init_file(struct inode *int ceph_renew_caps (struct inode *inode,int fmode) {struct ceph_mds_client *mdsc =ceph_sb_to_mdsc (inode->i_sb); +struct ceph_client *cl = mdsc->fsc->client;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req;int err, flags, wanted;
@@ -293,8 +294,9 @@ int ceph_renew_caps(struct inode *inode, (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) {int issued =__ceph_caps_issued (ci,NULL );spin_unlock (&ci->i_ceph_lock); -dout ("renew caps %p want %s issued %s updating mds_wanted\n" , - inode,ceph_cap_string (wanted),ceph_cap_string (issued)); +doutc (cl,"%p %llx.%llx want %s issued %s updating mds_wanted\n" , + inode,ceph_vinop (inode),ceph_cap_string (wanted), +ceph_cap_string (issued));ceph_check_caps (ci,0 ,NULL );return 0 ; }
@@ -325,7 +327,8 @@ int ceph_renew_caps(struct inode *inode, err =ceph_mdsc_do_request (mdsc,NULL , req);ceph_mdsc_put_request (req); out: -dout ("renew caps %p open result=%d\n" , inode, err); +doutc (cl,"%p %llx.%llx open result=%d\n" , inode,ceph_vinop (inode), + err);return err <0 ? err :0 ; }
fs/ceph/metric.c +3 -2
@@ -25,6 +25,7 @@ static bool ceph_mdsc_send_metrics(strucstruct ceph_client_metric *m = &mdsc->metric;u64 nr_caps =atomic64_read (&m->total_caps);u32 header_len =sizeof (struct ceph_metric_header ); +struct ceph_client *cl = mdsc->fsc->client;struct ceph_msg *msg;struct timespec64 ts;s64 sum;
@@ -38,8 +39,8 @@ static bool ceph_mdsc_send_metrics(struc msg =ceph_msg_new (CEPH_MSG_CLIENT_METRICS , len,GFP_NOFS , true);if (!msg) { -pr_err ("send metrics to mds%d, failed to allocate message\n" , - s->s_mds); +pr_err_client (cl,"to mds%d, failed to allocate message\n" , + s->s_mds);return false; }
fs/ceph/super.h +6 -0
@@ -456,6 +456,12 @@ ceph_sb_to_mdsc(const struct super_blockreturn (struct ceph_mds_client *)ceph_sb_to_fs_client (sb)->mdsc; } +static inline struct ceph_client * +ceph_inode_to_client (const struct inode *inode) +{ +return (struct ceph_client *)ceph_inode_to_fs_client (inode)->client; +} +static inline struct ceph_vino ceph_vino (const struct inode *inode) {
Greg Kroah-Hartman
maintainerPATCH 212/272 ceph: fix hanging __ceph_get_caps() with stale mds_wanted
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 50958bb928bad3bdba9e5d1b7ff4bbadcf6951e6 ]
A reader can hang forever in __ceph_get_caps() when the client no longer holds FILE_RD, but local cap state still says that the capability is already wanted (via mds_wanted).
One way to trigger this is through MDS cap revocation. If another client performs a conflicting operation, the MDS can revoke FILE_RD from the reader; the next read then has to reacquire FILE_RD. If the cap update that should request FILE_RD never reaches the MDS after cap->mds_wanted was raised, the reader is left holding only non-file caps while local mds_wanted still includes the file read caps.
In that state, try_get_cap_refs() sees need <= mds_wanted and returns 0, so __ceph_get_caps() just waits on i_cap_wq. If the cap update that was supposed to request FILE_RD never reaches the MDS after cap->mds_wanted was` raised, no further request is sent and the waiter can sleep indefinitely until unrelated cap traffic happens to wake it up.
The ordering issue is that cap->mds_wanted is updated in __prep_cap() before the CEPH_MSG_CLIENT_CAPS message is actually queued for send. That makes one field serve two different meanings at
once: what this client wants, and what the client believes the MDS already knows it wants.
A proper fix would be to split those states and track whether a cap update is actually in flight or has been observed by the MDS. However, simply moving the cap->mds_wanted assignment later would not be sufficient: queueing the message in the messenger does not guarantee that the MDS processed that specific wanted set, and reconnect or message loss can still invalidate that assumption. Fixing that properly would require a larger rework of the cap state machine.
To allow simpler backports to stable kernels, this patch implements a simpler workaround:
- stop waiting forever in __ceph_get_caps(); after a bounded wait, fall back to the renew path
- make ceph_renew_caps() issue a synchronous OPEN request whenever the inode still does not actually hold the wanted caps, instead of only calling ceph_check_caps()
The extra issued-vs-wanted check in ceph_renew_caps() is necessary because the previous test only checked whether the inode still had any real caps at all. That is not enough after revocation: the client can still hold something like pLs and yet be missing FILE_RD completely. In that case, falling back to ceph_check_caps() is not sufficient, because it still trusts cap->mds_wanted and may resend nothing. By requiring (issued & wanted) == wanted before taking the asynchronous path, the code only uses ceph_check_caps() when the wanted caps are already actually issued. Otherwise, it sends the synchronous OPEN renew.
This preserves the existing asynchronous fast path when the wanted caps are already issued, avoids changing cap-state semantics, and fixes the hang by guaranteeing that a stalled waiter eventually retries through a path that does not rely on the stale mds_wanted state.
[ idryomov: move CEPH_GET_CAPS_WAIT_TIMEOUT from libceph.h to
mds_client.h, formatting ]
--- fs/ceph/caps.c | 17 +++++++++++++++-- fs/ceph/file.c | 9 +++++---- fs/ceph/mds_client.h | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-)
fs/ceph/caps.c +15 -2
@@ -2910,7 +2910,19 @@ int ceph_get_caps(struct file *filp, int ret = -ERESTARTSYS ;break ; } -wait_woken (&wait,TASK_INTERRUPTIBLE ,MAX_SCHEDULE_TIMEOUT ); + +/* +* If a cap update is lost after +* mds_wanted was raised, waiting +* forever will never make progress. +* Retry the renew path periodically +* so we can resend synchronously. +*/ +if (!wait_woken (&wait,TASK_INTERRUPTIBLE , +CEPH_GET_CAPS_WAIT_TIMEOUT )) { + ret = -ESTALE ; +break ; + } }remove_wait_queue (&ci->i_cap_wq, &wait);
@@ -2942,7 +2954,8 @@ int ceph_get_caps(struct file *filp, intcontinue ; }if (ret == -ESTALE ) { -/* session was killed, try renew caps */ +/* session was killed or a waited cap +* request needs a retry */ ret =ceph_renew_caps (inode, flags);if (ret ==0 )continue ;
fs/ceph/file.c +5 -4
@@ -277,7 +277,7 @@ static int ceph_init_file(struct inode * }/* -* try renew caps after session gets killed. +* Retry cap acquisition after a stale session or a lost cap update. */ int ceph_renew_caps (struct inode *inode,int fmode) {
@@ -285,14 +285,15 @@ int ceph_renew_caps(struct inode *inode,struct ceph_client *cl = mdsc->fsc->client;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req; -int err, flags, wanted; +int err, flags, wanted, issued;spin_lock (&ci->i_ceph_lock);__ceph_touch_fmode (ci, mdsc, fmode); wanted =__ceph_caps_file_wanted (ci); + issued =__ceph_caps_issued (ci,NULL );if (__ceph_is_any_real_caps (ci) && - (!(wanted &CEPH_CAP_ANY_WR ) || ci->i_auth_cap)) { -int issued =__ceph_caps_issued (ci,NULL ); + (!(wanted &CEPH_CAP_ANY_WR ) || ci->i_auth_cap) && + (issued & wanted) == wanted) {spin_unlock (&ci->i_ceph_lock); doutc(cl, "%p %llx.%llx want %s issued %s updating mds_wanted\n", inode, ceph_vinop(inode), ceph_cap_string(wanted),
fs/ceph/mds_client.h +2 -0
@@ -61,6 +61,8 @@ enum ceph_feature_type {struct ceph_fs_client ;struct ceph_cap ; +#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ) +/* * parsed info about a single inode. pointers are into the encoded * on-wire structures within the mds reply message payload.
Greg Kroah-Hartman
maintainerPATCH 213/272 libceph: fix two unsafe bare decodes in decode_lockers()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavitra Jha <[email protected]>
[ Upstream commit a109a556115271ca7896dcda7b4b7e45e156c227 ]
decode_lockers() in cls_lock_client.c contains two bare decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads:
1. ceph_decode_32(p) at the num_lockers field has no preceding bounds check. ceph_start_decoding() accepts struct_len=0 as valid -- the internal ceph_decode_need(p, end, 0, bad) always passes -- so when an OSD sends struct_len=0, ceph_start_decoding() returns success with p == end. The immediately following bare ceph_decode_32(p) then reads 4 bytes past the validated buffer boundary. The garbage value is passed directly to kzalloc_objs() as the locker count.
The sibling function decode_watchers() in osd_client.c already uses ceph_decode_32_safe() after its own ceph_start_decoding() call. decode_lockers() was the only site using the bare variant.
2. ceph_decode_8(p) after the decode_locker() loop has no preceding bounds check. If an OSD crafts num_lockers such that the loop advances p exactly to end, the subsequent bare ceph_decode_8(p) reads one byte past the validated buffer boundary. The result is passed directly into *type, which is used as a lock type discriminator by callers, giving an OSD-controlled one-byte OOB read with direct influence over the lock type field.
Fix both by replacing bare operations with their safe variants:
ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers,
err_inval)
ceph_decode_8(p) -> ceph_decode_8_safe(p, end, *type,
err_free_lockers)
The goto targets differ intentionally:
err_inval: is a new label returning -EINVAL directly. It is used for the pre-allocation failure path where *lockers is not yet allocated and must not be passed to ceph_free_lockers().
err_free_lockers: is the existing label. It is used for the post-allocation failure path where *lockers is allocated and must be freed.
ret is set to -EINVAL before ceph_decode_8_safe() so that err_free_lockers returns the correct error code on bounds violation. Without this, err_free_lockers would return a stale ret value (0 from the successful decode_locker() loop), silently swallowing the error.
-EINVAL is correct for both failure paths. The data received from the OSD is structurally malformed. -ENOMEM would misrepresent the failure class to callers and to stable@ backporters triaging error paths.
Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition).
[ idryomov: trim changelog, formatting ]
--- net/ceph/cls_lock_client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/ceph/cls_lock_client.c +6 -2
@@ -304,7 +304,7 @@ static int decode_lockers(void **p, voidif (ret)return ret; - *num_lockers =ceph_decode_32 (p); +ceph_decode_32_safe (p, end, *num_lockers, err_inval); *lockers =kcalloc (*num_lockers,sizeof (**lockers),GFP_NOIO );if (!*lockers)return -ENOMEM ;
@@ -315,7 +315,8 @@ static int decode_lockers(void **p, void goto err_free_lockers; } - *type =ceph_decode_8 (p); + ret = -EINVAL ; +ceph_decode_8_safe (p, end, *type, err_free_lockers); s =ceph_extract_encoded_string (p, end,NULL ,GFP_NOIO );if (IS_ERR (s)) { ret =PTR_ERR (s);
@@ -325,6 +326,9 @@ static int decode_lockers(void **p, void *tag = s;return 0 ; +err_inval: +return -EINVAL ; + err_free_lockers:ceph_free_lockers (*lockers, *num_lockers);return ret;
Greg Kroah-Hartman
maintainerPATCH 214/272 ksmbd: defer destroy_previous_session() until after NTLM authentication
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Montgomery <[email protected]>
[ Upstream commit c74801ee524f477c174a1899782b6c3b6918d407 ]
In ntlm_authenticate(), destroy_previous_session() is called using a user pointer resolved from the client-supplied NTLM blob username field before the NTLMv2 response is validated. An authenticated attacker can set the NTLM blob username to match a victim account and set PreviousSessionId to the victim's session ID; destroy_previous_session() destroys the victim's session while ksmbd_decode_ntlmssp_auth_blob() subsequently rejects the request with -EPERM.
Move destroy_previous_session() and the prev_id assignment to after ksmbd_decode_ntlmssp_auth_blob() returns success and use sess->user rather than the pre-authentication lookup result. This matches the ordering already used by krb5_authenticate(), where destroy_previous_session() is called only after ksmbd_krb5_authenticate() returns success.
--- fs/ksmbd/smb2pdu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
fs/ksmbd/smb2pdu.c +4 -5
@@ -1495,11 +1495,6 @@ static int ntlm_authenticate(struct ksmbreturn -EPERM ; } -/* Check for previous session */ - prev_id =le64_to_cpu (req->PreviousSessionId); -if (prev_id && prev_id != sess->id) -destroy_previous_session (conn, user, prev_id); -if (sess->state ==SMB2_SESSION_VALID ) {/* * Reuse session if anonymous try to connect
@@ -1537,6 +1532,10 @@ static int ntlm_authenticate(struct ksmb } } + prev_id =le64_to_cpu (req->PreviousSessionId); +if (prev_id && prev_id != sess->id) +destroy_previous_session (conn, sess->user, prev_id); +/* * If session state is SMB2_SESSION_VALID, We can assume * that it is reauthentication. And the user/password
Greg Kroah-Hartman
maintainerPATCH 215/272 net/sched: serialize qdisc_rtab_list against concurrent get/put
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aldo Ariel Panzardo <[email protected]>
[ Upstream commit f43ee0c0730d6191629b5ee1ceae27b1ebfdc047 ]
qdisc_get_rtab() and qdisc_put_rtab() mutate the process-global singly linked list qdisc_rtab_list and a plain non-atomic 'int refcnt' with no lock. This was only safe because every caller historically held the RTNL mutex, which serialized all rate-table lookups, inserts and frees.
That invariant no longer holds. cls_flower sets TCF_PROTO_OPS_DOIT_UNLOCKED, so tc_new_tfilter() keeps rtnl_held == false for it and sets TCA_ACT_FLAGS_NO_RTNL. That flag propagates through tcf_exts_validate_ex() -> tcf_action_init() -> tcf_action_init_1() -> tcf_police_init(), which calls qdisc_get_rtab()/qdisc_put_rtab() with the RTNL mutex NOT held. Two RTM_NEWTFILTER requests on different CPUs, each adding a flower filter with a police action carrying the same rate, then race on qdisc_rtab_list and on the non-atomic refcnt, leading to a use-after-free / double-free of the kmalloc-2k struct qdisc_rate_table. qdisc_rtab_list is a single global (not per-netns), so the corrupted object is shared system-wide.
BUG: KASAN: slab-use-after-free in qdisc_put_rtab+0x12f/0x160 qdisc_put_rtab+0x12f/0x160 tcf_police_init+0xda9/0x1590 tcf_action_init_1+0x460/0x6b0 tcf_action_init+0x439/0xa40 tcf_exts_validate_ex+0x42d/0x550 fl_change+0xddd/0x7da0 tc_new_tfilter+0xaa7/0x2420 rtnetlink_rcv_msg+0x95e/0xe90 which belongs to the cache kmalloc-2k of size 2048
Protect qdisc_rtab_list and the refcount with a dedicated spinlock. The (sleeping, GFP_KERNEL) allocation in qdisc_get_rtab() is performed before taking the lock; if a concurrent inserter added an identical table in the meantime the freshly allocated one is freed under the lock, so no duplicate is leaked. qdisc_put_rtab() now decrements the refcount and unlinks under the same lock.
--- net/sched/sch_api.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
net/sched/sch_api.c +19 -5
@@ -410,12 +410,13 @@ static __u8 __detect_linklayer(struct tc }static struct qdisc_rate_table *qdisc_rtab_list; +static DEFINE_SPINLOCK (qdisc_rtab_lock);struct qdisc_rate_table *qdisc_get_rtab (struct tc_ratespec *r,struct nlattr *tab,struct netlink_ext_ack *extack) { -struct qdisc_rate_table *rtab; +struct qdisc_rate_table *rtab, *new_rtab; if (tab == NULL || r->rate == 0 || r->cell_log == 0 || r->cell_log >= 32 ||
@@ -424,15 +425,20 @@ struct qdisc_rate_table *qdisc_get_rtab(return NULL ; } + new_rtab =kmalloc (sizeof (*new_rtab),GFP_KERNEL ); + +spin_lock (&qdisc_rtab_lock);for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {if (!memcmp (&rtab->rate, r,sizeof (struct tc_ratespec )) && !memcmp (&rtab->data,nla_data (tab),1024 )) { rtab->refcnt++; +spin_unlock (&qdisc_rtab_lock); +kfree (new_rtab);return rtab; } } - rtab =kmalloc (sizeof (*rtab),GFP_KERNEL ); + rtab = new_rtab;if (rtab) { rtab->rate = *r; rtab->refcnt =1 ;
@@ -444,6 +450,7 @@ struct qdisc_rate_table *qdisc_get_rtab( }else {NL_SET_ERR_MSG (extack,"Failed to allocate new qdisc rate table" ); } +spin_unlock (&qdisc_rtab_lock);return rtab; }EXPORT_SYMBOL (qdisc_get_rtab);
@@ -452,18 +459,25 @@ void qdisc_put_rtab(struct qdisc_rate_ta {struct qdisc_rate_table *rtab, **rtabp; -if (!tab || --tab->refcnt) +if (!tab) +return ; + +spin_lock (&qdisc_rtab_lock); +if (--tab->refcnt) { +spin_unlock (&qdisc_rtab_lock);return ; + }for (rtabp = &qdisc_rtab_list; (rtab = *rtabp) !=NULL ; rtabp = &rtab->next) {if (rtab == tab) { *rtabp = rtab->next; -kfree (rtab); -return ; +break ; } } +spin_unlock (&qdisc_rtab_lock); +kfree (tab); }EXPORT_SYMBOL (qdisc_put_rtab);
Greg Kroah-Hartman
maintainerPATCH 216/272 ftrace: Add global mutex to serialize trace_parser access
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tengda Wu <[email protected]>
[ Upstream commit 7720b63bcef3f54c7fe288774b720a227d54a306 ]
In ftrace, the trace_parser structure is allocated and initialized when a trace file is opened, and is subsequently used across write and release handlers to parse user input.
The affected handler paths and their specific functions are:
- Open paths: ftrace_regex_open(), ftrace_graph_open()
- Write paths: ftrace_regex_write(), ftrace_graph_write()
- Release paths: ftrace_regex_release(), ftrace_graph_release()
If userspace opens a trace file descriptor and shares it across multiple threads, concurrent write calls will race on the parser's internal state, specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted input or undefined behavior.
Fix this by adding a global mutex, parser_lock, to serialize all access to trace_parser across write and release paths, preventing concurrent corruption of parser state.
--- kernel/trace/ftrace.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
kernel/trace/ftrace.c +15 -0
@@ -1043,6 +1043,12 @@ struct ftrace_ops global_ops = { };/* +* parser_lock - Protects trace_parser state against concurrent operations. +* Held across trace_get_user() and subsequent buffer parsing to prevent races. +*/ +static DEFINE_MUTEX (parser_lock); + +/* * Used by the stack unwinder to know about dynamic ftrace trampolines. */ struct ftrace_ops *ftrace_ops_trampoline (unsigned long addr)
@@ -4928,6 +4934,8 @@ ftrace_regex_write(struct file *file, co/* iter->hash is a local copy, so we don't need regex_lock */ parser = &iter->parser; + +mutex_lock (&parser_lock); read =trace_get_user (parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) &&
@@ -4941,6 +4949,7 @@ ftrace_regex_write(struct file *file, co ret = read; out: +mutex_unlock (&parser_lock);return ret; }
@@ -5675,12 +5684,14 @@ int ftrace_regex_release(struct inode *i iter = file->private_data; parser = &iter->parser; +mutex_lock (&parser_lock);if (trace_parser_loaded (parser)) {int enable = !(iter->flags &FTRACE_ITER_NOTRACE );ftrace_process_regex (iter, parser->buffer, parser->idx, enable); } +mutex_unlock (&parser_lock);trace_parser_put (parser);
@@ -5998,10 +6009,12 @@ ftrace_graph_release(struct inode *inode parser = &fgd->parser; +mutex_lock (&parser_lock);if (trace_parser_loaded ((parser))) { ret =ftrace_graph_set_hash (fgd->new_hash, parser->buffer); } +mutex_unlock (&parser_lock);trace_parser_put (parser);
@@ -6121,6 +6134,7 @@ ftrace_graph_write(struct file *file, co parser = &fgd->parser; +mutex_lock (&parser_lock); read =trace_get_user (parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) &&
@@ -6134,6 +6148,7 @@ ftrace_graph_write(struct file *file, coif (!ret) ret = read; +mutex_unlock (&parser_lock);return ret; }