5.5.3-stable review
Subsystems (3)
Greg Kroah-Hartman
maintainerPATCH 138/367 of: Add OF_DMA_DEFAULT_COHERENT & select it on powerpc
In reply to Greg Kroah-HartmanFrom: Michael Ellerman <[email protected]>
commit dabf6b36b83a18d57e3d4b9d50544ed040d86255 upstream.
There's an OF helper called of_dma_is_coherent(), which checks if a device has a "dma-coherent" property to see if the device is coherent for DMA.
But on some platforms devices are coherent by default, and on some platforms it's not possible to update existing device trees to add the "dma-coherent" property.
So add a Kconfig symbol to allow arch code to tell of_dma_is_coherent() that devices are coherent by default, regardless of the presence of the property.
Select that symbol on powerpc when NOT_COHERENT_CACHE is not set, ie. when the system has a coherent cache.
--- arch/powerpc/Kconfig | 1 + drivers/of/Kconfig | 4 ++++ drivers/of/address.c | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-)
arch/powerpc/Kconfig +1 -0
@@ -238,6 +238,7 @@ config PPC select NEED_DMA_MAP_STATE if PPC64 || NOT_COHERENT_CACHE select NEED_SG_DMA_LENGTH select OF + select OF_DMA_DEFAULT_COHERENT if !NOT_COHERENT_CACHE select OF_EARLY_FLATTREE select OLD_SIGACTION if PPC32 select OLD_SIGSUSPEND
drivers/of/Kconfig +4 -0
@@ -103,4 +103,8 @@ config OF_OVERLAY config OF_NUMA bool +config OF_DMA_DEFAULT_COHERENT + # arches should select this if DMA is coherent by default for OF devices + bool + endif # OF
drivers/of/address.c +5 -1
@@ -995,12 +995,16 @@ out:* @np: device node * * It returns true if "dma-coherent" property was found -* for this device in DT. +* for this device in the DT, or if DMA is coherent by +* default for OF devices on the current platform. */ bool of_dma_is_coherent (struct device_node *np) {struct device_node *node =of_node_get (np); +if (IS_ENABLED (CONFIG_OF_DMA_DEFAULT_COHERENT )) +return true; +while (node) {if (of_property_read_bool (node,"dma-coherent" )) {of_node_put (node);
Greg Kroah-Hartman
maintainerPATCH 139/367 ARM: dma-api: fix max_pfn off-by-one error in __dma_supported()
In reply to Greg Kroah-HartmanFrom: Chen-Yu Tsai <[email protected]>
commit f3cc4e1d44a813a0685f2e558b78ace3db559722 upstream.
max_pfn, as set in arch/arm/mm/init.c:
static void __init find_limits(unsigned long *min,
unsigned long *max_low,
unsigned long *max_high)
{
*max_low = PFN_DOWN(memblock_get_current_limit());
*min = PFN_UP(memblock_start_of_DRAM());
*max_high = PFN_DOWN(memblock_end_of_DRAM());
}with memblock_end_of_DRAM() pointing to the next byte after DRAM. As such, max_pfn points to the PFN after the end of DRAM.
Thus when using max_pfn to check DMA masks, we should subtract one when checking DMA ranges against it.
Commit 8bf1268f48ad ("ARM: dma-api: fix off-by-one error in __dma_supported()") fixed the same issue, but missed this spot.
This issue was found while working on the sun4i-csi v4l2 driver on the Allwinner R40 SoC. On Allwinner SoCs, DRAM is offset at 0x40000000, and we are starting to use of_dma_configure() with the "dma-ranges" property in the device tree to have the DMA API handle the offset.
In this particular instance, dma-ranges was set to the same range as the actual available (2 GiB) DRAM. The following error appeared when the driver attempted to allocate a buffer:
sun4i-csi 1c09000.csi: Coherent DMA mask 0x7fffffff (pfn 0x40000-0xc0000)
covers a smaller range of system memory than the DMA zone pfn 0x0-0xc0001
sun4i-csi 1c09000.csi: dma_alloc_coherent of size 307200 failedFixing the off-by-one error makes things work.
--- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/mm/dma-mapping.c +1 -1
@@ -221,7 +221,7 @@ EXPORT_SYMBOL(arm_coherent_dma_ops);static int __dma_supported (struct device *dev,u64 mask,bool warn) { -unsigned long max_dma_pfn =min (max_pfn, arm_dma_pfn_limit); +unsigned long max_dma_pfn =min (max_pfn -1 , arm_dma_pfn_limit);/* * Translate the device's DMA mask to a PFN limit. This
Greg Kroah-Hartman
maintainerPATCH 140/367 dm zoned: support zone sizes smaller than 128MiB
In reply to Greg Kroah-HartmanFrom: Dmitry Fomichev <[email protected]>
commit b39962950339912978484cdac50069258545d753 upstream.
dm-zoned is observed to log failed kernel assertions and not work correctly when operating against a device with a zone size smaller than 128MiB (e.g. 32768 bits per 4K block). The reason is that the bitmap size per zone is calculated as zero with such a small zone size. Fix this problem and also make the code related to zone bitmap management be able to handle per zone bitmaps smaller than a single block.
A dm-zoned-tools patch is required to properly format dm-zoned devices with zone sizes smaller than 128MiB.
--- drivers/md/dm-zoned-metadata.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
drivers/md/dm-zoned-metadata.c +14 -9
@@ -134,6 +134,7 @@ struct dmz_metadata {sector_t zone_bitmap_size;unsigned int zone_nr_bitmap_blocks; +unsigned int zone_bits_per_mblk;unsigned int nr_bitmap_blocks;unsigned int nr_map_blocks;
@@ -1161,7 +1162,10 @@ static int dmz_init_zones(struct dmz_met/* Init */ zmd->zone_bitmap_size = dev->zone_nr_blocks >>3 ; - zmd->zone_nr_bitmap_blocks = zmd->zone_bitmap_size >>DMZ_BLOCK_SHIFT ; + zmd->zone_nr_bitmap_blocks = +max_t (sector_t,1 , zmd->zone_bitmap_size >>DMZ_BLOCK_SHIFT ); + zmd->zone_bits_per_mblk =min_t (sector_t, dev->zone_nr_blocks, +DMZ_BLOCK_SIZE_BITS );/* Allocate zone array */ zmd->zones =kcalloc (dev->nr_zones,sizeof (struct dm_zone ),GFP_KERNEL );
@@ -1956,7 +1960,7 @@ int dmz_copy_valid_blocks(struct dmz_metdmz_release_mblock (zmd, to_mblk);dmz_release_mblock (zmd, from_mblk); - chunk_block +=DMZ_BLOCK_SIZE_BITS ; + chunk_block += zmd->zone_bits_per_mblk; } to_zone->weight = from_zone->weight;
@@ -2017,7 +2021,7 @@ int dmz_validate_blocks(struct dmz_metad/* Set bits */ bit = chunk_block &DMZ_BLOCK_MASK_BITS ; - nr_bits =min (nr_blocks,DMZ_BLOCK_SIZE_BITS - bit); + nr_bits =min (nr_blocks, zmd->zone_bits_per_mblk - bit); count =dmz_set_bits ((unsigned long *)mblk->data, bit, nr_bits);if (count) {
@@ -2096,7 +2100,7 @@ int dmz_invalidate_blocks(struct dmz_met/* Clear bits */ bit = chunk_block &DMZ_BLOCK_MASK_BITS ; - nr_bits =min (nr_blocks,DMZ_BLOCK_SIZE_BITS - bit); + nr_bits =min (nr_blocks, zmd->zone_bits_per_mblk - bit); count =dmz_clear_bits ((unsigned long *)mblk->data, bit, nr_bits);
@@ -2156,6 +2160,7 @@ static int dmz_to_next_set_block(struct {struct dmz_mblock *mblk;unsigned int bit, set_bit, nr_bits; +unsigned int zone_bits = zmd->zone_bits_per_mblk;unsigned long *bitmap;int n =0 ;
@@ -2170,15 +2175,15 @@ static int dmz_to_next_set_block(struct/* Get offset */ bitmap = (unsigned long *) mblk->data; bit = chunk_block &DMZ_BLOCK_MASK_BITS ; - nr_bits =min (nr_blocks,DMZ_BLOCK_SIZE_BITS - bit); + nr_bits =min (nr_blocks, zone_bits - bit);if (set) - set_bit =find_next_bit (bitmap,DMZ_BLOCK_SIZE_BITS , bit); + set_bit =find_next_bit (bitmap, zone_bits, bit);else - set_bit =find_next_zero_bit (bitmap,DMZ_BLOCK_SIZE_BITS , bit); + set_bit =find_next_zero_bit (bitmap, zone_bits, bit);dmz_release_mblock (zmd, mblk); n += set_bit - bit; -if (set_bit <DMZ_BLOCK_SIZE_BITS ) +if (set_bit < zone_bits)break ; nr_blocks -= nr_bits;
@@ -2281,7 +2286,7 @@ static void dmz_get_zone_weight(struct d/* Count bits in this block */ bitmap = mblk->data; bit = chunk_block &DMZ_BLOCK_MASK_BITS ; - nr_bits =min (nr_blocks,DMZ_BLOCK_SIZE_BITS - bit); + nr_bits =min (nr_blocks, zmd->zone_bits_per_mblk - bit); n +=dmz_count_bits (bitmap, bit, nr_bits);dmz_release_mblock (zmd, mblk);
Greg Kroah-Hartman
maintainerPATCH 141/367 dm space map common: fix to ensure new block isnt already in use
In reply to Greg Kroah-HartmanFrom: Joe Thornber <[email protected]>
commit 4feaef830de7ffdd8352e1fe14ad3bf13c9688f8 upstream.
The space-maps track the reference counts for disk blocks allocated by both the thin-provisioning and cache targets. There are variants for tracking metadata blocks and data blocks.
Transactionality is implemented by never touching blocks from the previous transaction, so we can rollback in the event of a crash.
When allocating a new block we need to ensure the block is free (has reference count of 0) in both the current and previous transaction. Prior to this fix we were doing this by searching for a free block in the previous transaction, and relying on a 'begin' counter to track where the last allocation in the current transaction was. This 'begin' field was not being updated in all code paths (eg, increment of a data block reference count due to breaking sharing of a neighbour block in the same btree leaf).
This fix keeps the 'begin' field, but now it's just a hint to speed up the search. Instead the current transaction is searched for a free block, and then the old transaction is double checked to ensure it's free. Much simpler.
This fixes reports of sm_disk_new_block()'s BUG_ON() triggering when DM thin-provisioning's snapshots are heavily used.
--- drivers/md/persistent-data/dm-space-map-common.c | 27 +++++++++++++++++++++ drivers/md/persistent-data/dm-space-map-common.h | 2 + drivers/md/persistent-data/dm-space-map-disk.c | 6 +++- drivers/md/persistent-data/dm-space-map-metadata.c | 5 +++ 4 files changed, 37 insertions(+), 3 deletions(-)
drivers/md/persistent-data/dm-space-map-common.c +27 -0
@@ -380,6 +380,33 @@ int sm_ll_find_free_block(struct ll_diskreturn -ENOSPC ; } +int sm_ll_find_common_free_block (struct ll_disk *old_ll,struct ll_disk *new_ll, +dm_block_t begin,dm_block_t end,dm_block_t *b) +{ +int r; +uint32_t count; + +do { + r =sm_ll_find_free_block (new_ll, begin, new_ll->nr_blocks, b); +if (r) +break ; + +/* double check this block wasn't used in the old transaction */ +if (*b >= old_ll->nr_blocks) + count =0 ; +else { + r =sm_ll_lookup (old_ll, *b, &count); +if (r) +break ; + +if (count) + begin = *b +1 ; + } + }while (count); + +return r; +} +static int sm_ll_mutate (struct ll_disk *ll,dm_block_t b,int (*mutator)(void *context,uint32_t old,uint32_t *new),void *context,enum allocation_event *ev)
drivers/md/persistent-data/dm-space-map-common.h +2 -0
@@ -109,6 +109,8 @@ int sm_ll_lookup_bitmap(struct ll_disk *int sm_ll_lookup (struct ll_disk *ll,dm_block_t b,uint32_t *result);int sm_ll_find_free_block (struct ll_disk *ll,dm_block_t begin,dm_block_t end,dm_block_t *result); +int sm_ll_find_common_free_block (struct ll_disk *old_ll,struct ll_disk *new_ll, +dm_block_t begin,dm_block_t end,dm_block_t *result);int sm_ll_insert (struct ll_disk *ll,dm_block_t b,uint32_t ref_count,enum allocation_event *ev);int sm_ll_inc (struct ll_disk *ll,dm_block_t b,enum allocation_event *ev);int sm_ll_dec (struct ll_disk *ll,dm_block_t b,enum allocation_event *ev);
drivers/md/persistent-data/dm-space-map-disk.c +4 -2
@@ -167,8 +167,10 @@ static int sm_disk_new_block(struct dm_senum allocation_event ev; struct sm_disk *smd = container_of(sm, struct sm_disk, sm); -/* FIXME: we should loop round a couple of times */ - r =sm_ll_find_free_block (&smd->old_ll, smd->begin, smd->old_ll.nr_blocks, b); +/* +* Any block we allocate has to be free in both the old and current ll. +*/ + r =sm_ll_find_common_free_block (&smd->old_ll, &smd->ll, smd->begin, smd->ll.nr_blocks, b);if (r)return r;
drivers/md/persistent-data/dm-space-map-metadata.c +4 -1
@@ -448,7 +448,10 @@ static int sm_metadata_new_block_(structenum allocation_event ev; struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); - r =sm_ll_find_free_block (&smm->old_ll, smm->begin, smm->old_ll.nr_blocks, b); +/* +* Any block we allocate has to be free in both the old and current ll. +*/ + r =sm_ll_find_common_free_block (&smm->old_ll, &smm->ll, smm->begin, smm->ll.nr_blocks, b);if (r)return r;
Greg Kroah-Hartman
maintainerPATCH 142/367 dm writecache: fix incorrect flush sequence when doing SSD mode commit
In reply to Greg Kroah-HartmanFrom: Mikulas Patocka <[email protected]>
commit aa9509209c5ac2f0b35d01a922bf9ae072d0c2fc upstream.
When committing state, the function writecache_flush does the following:
1. write metadata (writecache_commit_flushed)
2. flush disk cache (writecache_commit_flushed)
3. wait for data writes to complete (writecache_wait_for_ios)
4. increase superblock seq_count
5. write the superblock
6. flush disk cache
It may happen that at step 3, when we wait for some write to finish, the disk may report the write as finished, but the write only hit the disk cache and it is not yet stored in persistent storage. At step 5 we write the superblock - it may happen that the superblock is written before the write that we waited for in step 3. If the machine crashes, it may result in incorrect data being returned after reboot.
In order to fix the bug, we must swap steps 2 and 3 in the above sequence, so that we first wait for writes to complete and then flush the disk cache.
--- drivers/md/dm-writecache.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-)
drivers/md/dm-writecache.c +21 -21
@@ -442,7 +442,13 @@ static void writecache_notify_io(unsignecomplete (&endio->c); } -static void ssd_commit_flushed (struct dm_writecache *wc) +static void writecache_wait_for_ios (struct dm_writecache *wc,int direction) +{ +wait_event (wc->bio_in_progress_wait[direction], + !atomic_read (&wc->bio_in_progress[direction])); +} + +static void ssd_commit_flushed (struct dm_writecache *wc,bool wait_for_ios) {struct dm_io_region region;struct dm_io_request req;
@@ -488,17 +494,20 @@ static void ssd_commit_flushed(struct dmwritecache_notify_io (0 , &endio);wait_for_completion_io (&endio.c); +if (wait_for_ios) +writecache_wait_for_ios (wc,WRITE ); +writecache_disk_flush (wc, wc->ssd_dev);memset (wc->dirty_bitmap,0 , wc->dirty_bitmap_size); } -static void writecache_commit_flushed (struct dm_writecache *wc) +static void writecache_commit_flushed (struct dm_writecache *wc,bool wait_for_ios) {if (WC_MODE_PMEM (wc))wmb ();else -ssd_commit_flushed (wc); +ssd_commit_flushed (wc, wait_for_ios); }static void writecache_disk_flush (struct dm_writecache *wc,struct dm_dev *dev)
@@ -522,12 +531,6 @@ static void writecache_disk_flush(structwritecache_error (wc, r,"error flushing metadata: %d" , r); } -static void writecache_wait_for_ios (struct dm_writecache *wc,int direction) -{ -wait_event (wc->bio_in_progress_wait[direction], - !atomic_read (&wc->bio_in_progress[direction])); -} -#define WFE_RETURN_FOLLOWING 1#define WFE_LOWEST_SEQ 2
@@ -724,15 +727,12 @@ static void writecache_flush(struct dm_w e = e2;cond_resched (); } -writecache_commit_flushed (wc); - -if (!WC_MODE_PMEM (wc)) -writecache_wait_for_ios (wc,WRITE ); +writecache_commit_flushed (wc, true); wc->seq_count++;pmem_assign (sb (wc)->seq_count,cpu_to_le64 (wc->seq_count));writecache_flush_region (wc, &sb (wc)->seq_count,sizeof sb (wc)->seq_count); -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false); wc->overwrote_committed = false;
@@ -756,7 +756,7 @@ static void writecache_flush(struct dm_w }if (need_flush_after_free) -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false); }static void writecache_flush_work (struct work_struct *work)
@@ -809,7 +809,7 @@ static void writecache_discard(struct dm }if (discarded_something) -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false); }static bool writecache_wait_for_writeback (struct dm_writecache *wc)
@@ -958,7 +958,7 @@ erase_this:if (need_flush) {writecache_flush_all_metadata (wc); -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false); }wc_unlock (wc);
@@ -1342,7 +1342,7 @@ static void __writecache_endio_pmem(stru wc->writeback_size--; n_walked++;if (unlikely (n_walked >=ENDIO_LATENCY )) { -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false);wc_unlock (wc);wc_lock (wc); n_walked =0 ;
@@ -1423,7 +1423,7 @@ pop_from_list:writecache_wait_for_ios (wc,READ ); } -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false);wc_unlock (wc); }
@@ -1766,10 +1766,10 @@ static int init_memory(struct dm_writecawrite_original_sector_seq_count (wc, &wc->entries[b],-1 ,-1 );writecache_flush_all_metadata (wc); -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false);pmem_assign (sb (wc)->magic,cpu_to_le32 (MEMORY_SUPERBLOCK_MAGIC ));writecache_flush_region (wc, &sb (wc)->magic,sizeof sb (wc)->magic); -writecache_commit_flushed (wc); +writecache_commit_flushed (wc, false);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 143/367 dm crypt: fix GFP flags passed to skcipher_request_alloc()
In reply to Greg Kroah-HartmanFrom: Mikulas Patocka <[email protected]>
commit 9402e959014a18b4ebf7558733076875808dd66c upstream.
GFP_KERNEL is not supposed to be or'd with GFP_NOFS (the result is equivalent to GFP_KERNEL). Also, we use GFP_NOIO instead of GFP_NOFS because we don't want any I/O being submitted in the direct reclaim path.
--- drivers/md/dm-crypt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/dm-crypt.c +1 -1
@@ -717,7 +717,7 @@ static int crypt_iv_eboiv_gen(struct crystruct crypto_wait wait;int err; - req =skcipher_request_alloc (any_tfm (cc),GFP_KERNEL |GFP_NOFS ); + req =skcipher_request_alloc (any_tfm (cc),GFP_NOIO );if (!req)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 144/367 dm crypt: fix benbi IV constructor crash if used in authenticated mode
In reply to Greg Kroah-HartmanFrom: Milan Broz <[email protected]>
commit 4ea9471fbd1addb25a4d269991dc724e200ca5b5 upstream.
If benbi IV is used in AEAD construction, for example:
cryptsetup luksFormat <device> --cipher twofish-xts-benbi --key-size 512 --integrity=hmac-sha256
the constructor uses wrong skcipher function and crashes:
BUG: kernel NULL pointer dereference, address: 00000014 ... EIP: crypt_iv_benbi_ctr+0x15/0x70 [dm_crypt] Call Trace: ? crypt_subkey_size+0x20/0x20 [dm_crypt] crypt_ctr+0x567/0xfc0 [dm_crypt] dm_table_add_target+0x15f/0x340 [dm_mod]
Fix this by properly using crypt_aead_blocksize() in this case.
--- drivers/md/dm-crypt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/md/dm-crypt.c +8 -2
@@ -331,8 +331,14 @@ static int crypt_iv_essiv_gen(struct crystatic int crypt_iv_benbi_ctr (struct crypt_config *cc,struct dm_target *ti,const char *opts) { -unsigned bs =crypto_skcipher_blocksize (any_tfm (cc)); -int log =ilog2 (bs); +unsigned bs; +int log; + +if (test_bit (CRYPT_MODE_INTEGRITY_AEAD , &cc->cipher_flags)) + bs =crypto_aead_blocksize (any_tfm_aead (cc)); +else + bs =crypto_skcipher_blocksize (any_tfm (cc)); + log =ilog2 (bs);/* we need to calculate how far we must shift the sector count * to get the cipher block count, we use this shift in _gen */
Greg Kroah-Hartman
maintainerPATCH 145/367 dm thin metadata: use pool locking at end of dm_pool_metadata_close
In reply to Greg Kroah-HartmanFrom: Mike Snitzer <[email protected]>
commit 44d8ebf436399a40fcd10dd31b29d37823d62fcc upstream.
Ensure that the pool is locked during calls to __commit_transaction and __destroy_persistent_data_objects. Just being consistent with locking, but reality is dm_pool_metadata_close is called once pool is being destroyed so access to pool shouldn't be contended.
Also, use pmd_write_lock_in_core rather than __pmd_write_lock in dm_pool_commit_metadata and rename __pmd_write_lock to pmd_write_lock_in_core -- there was no need for the alias.
In addition, verify that the pool is locked in __commit_transaction().
--- drivers/md/dm-thin-metadata.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/md/dm-thin-metadata.c +6 -4
@@ -387,16 +387,15 @@ static int subtree_equal(void *context,* Variant that is used for in-core only changes or code that * shouldn't put the pool in service on its own (e.g. commit). */ -static inline void __pmd_write_lock (struct dm_pool_metadata *pmd) +static inline void pmd_write_lock_in_core (struct dm_pool_metadata *pmd) __acquires(pmd->root_lock) {down_write (&pmd->root_lock); } -#define pmd_write_lock_in_core (pmd) __pmd_write_lock((pmd))static inline void pmd_write_lock (struct dm_pool_metadata *pmd) { -__pmd_write_lock (pmd); +pmd_write_lock_in_core (pmd);if (unlikely (!pmd->in_service)) pmd->in_service = true; }
@@ -831,6 +830,7 @@ static int __commit_transaction(struct d* We need to know if the thin_disk_superblock exceeds a 512-byte sector. */ BUILD_BUG_ON (sizeof (struct thin_disk_superblock ) >512 ); +BUG_ON (!rwsem_is_locked (&pmd->root_lock));if (unlikely (!pmd->in_service))return 0 ;
@@ -953,6 +953,7 @@ int dm_pool_metadata_close(struct dm_pooreturn -EBUSY ; } +pmd_write_lock_in_core (pmd);if (!dm_bm_is_read_only (pmd->bm) && !pmd->fail_io) { r =__commit_transaction (pmd);if (r <0 )
@@ -961,6 +962,7 @@ int dm_pool_metadata_close(struct dm_poo }if (!pmd->fail_io)__destroy_persistent_data_objects (pmd); +pmd_write_unlock (pmd);kfree (pmd);return 0 ;
@@ -1841,7 +1843,7 @@ int dm_pool_commit_metadata(struct dm_po* Care is taken to not have commit be what * triggers putting the thin-pool in-service. */ -__pmd_write_lock (pmd); +pmd_write_lock_in_core (pmd);if (pmd->fail_io) goto out;
Greg Kroah-Hartman
maintainerPATCH 146/367 dm thin: fix use-after-free in metadata_pre_commit_callback
In reply to Greg Kroah-HartmanFrom: Mike Snitzer <[email protected]>
commit a4a8d286586d4b28c8517a51db8d86954aadc74b upstream.
dm-thin uses struct pool to hold the state of the pool. There may be multiple pool_c's pointing to a given pool, each pool_c represents a loaded target. pool_c's may be created and destroyed arbitrarily and the pool contains a reference count of pool_c's pointing to it.
Since commit 694cfe7f31db3 ("dm thin: Flush data device before committing metadata") a pointer to pool_c is passed to dm_pool_register_pre_commit_callback and this function stores it in pmd->pre_commit_context. If this pool_c is freed, but pool is not (because there is another pool_c referencing it), we end up in a situation where pmd->pre_commit_context structure points to freed pool_c. It causes a crash in metadata_pre_commit_callback.
Fix this by moving the dm_pool_register_pre_commit_callback() from pool_ctr() to pool_preresume(). This way the in-core thin-pool metadata is only ever armed with callback data whose lifetime matches the active thin-pool target.
In should be noted that this fix preserves the ability to load a thin-pool table that uses a different data block device (that contains the same data) -- though it is unclear if that capability is still useful and/or needed.
--- drivers/md/dm-thin.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/md/dm-thin.c +3 -4
@@ -3408,10 +3408,6 @@ static int pool_ctr(struct dm_target *tiif (r) goto out_flags_changed; -dm_pool_register_pre_commit_callback (pt->pool->pmd, - metadata_pre_commit_callback, - pt); - pt->callbacks.congested_fn = pool_is_congested;dm_table_add_target_callbacks (ti->table, &pt->callbacks);
@@ -3574,6 +3570,9 @@ static int pool_preresume(struct dm_targif (r)return r; +dm_pool_register_pre_commit_callback (pool->pmd, + metadata_pre_commit_callback, pt); + r =maybe_resize_data_dev (ti, &need_commit1);if (r)return r;
Greg Kroah-Hartman
maintainerPATCH 147/367 dm: fix potential for q->make_request_fn NULL pointer
In reply to Greg Kroah-HartmanFrom: Mike Snitzer <[email protected]>
commit 47ace7e012b9f7ad71d43ac9063d335ea3d6820b upstream.
Move blk_queue_make_request() to dm.c:alloc_dev() so that q->make_request_fn is never NULL during the lifetime of a DM device (even one that is created without a DM table).
Otherwise generic_make_request() will crash simply by doing:
dmsetup create -n test
mount /dev/dm-N /mnt
While at it, move ->congested_data initialization out of dm.c:alloc_dev() and into the bio-based specific init method.
Depends-on: c12c9a3c3860c ("dm: various cleanups to md->queue initialization code")
--- drivers/md/dm.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/md/dm.c +7 -2
@@ -1859,6 +1859,7 @@ static void dm_init_normal_md_queue(stru/* * Initialize aspects of queue that aren't relevant for blk-mq */ + md->queue->backing_dev_info->congested_data = md; md->queue->backing_dev_info->congested_fn = dm_any_congested; }
@@ -1949,7 +1950,12 @@ static struct mapped_device *alloc_dev(iif (!md->queue) goto bad; md->queue->queuedata = md; - md->queue->backing_dev_info->congested_data = md; +/* +* default to bio-based required ->make_request_fn until DM +* table is loaded and md->type established. If request-based +* table is loaded: blk-mq will override accordingly. +*/ +blk_queue_make_request (md->queue, dm_make_request); md->disk =alloc_disk_node (1 , md->numa_node_id);if (!md->disk)
@@ -2264,7 +2270,6 @@ int dm_setup_md_queue(struct mapped_devicase DM_TYPE_DAX_BIO_BASED :case DM_TYPE_NVME_BIO_BASED :dm_init_normal_md_queue (md); -blk_queue_make_request (md->queue, dm_make_request);break ;case DM_TYPE_NONE :WARN_ON_ONCE (true);
Greg Kroah-Hartman
maintainerPATCH 148/367 ASoC: SOF: Introduce state machine for FW boot
In reply to Greg Kroah-HartmanFrom: Ranjani Sridharan <[email protected]>
[ Upstream commit 6ca5cecbd1c1758666ab79446f19e0e61ed11444 ]
Add a state machine for FW boot to track the different stages of FW boot and replace the boot_complete field with fw_state field in struct snd_sof_dev. This will be used to determine the actions to be performed during system suspend.
One of the main motivations for adding this change is the fact that errors during the top-level SOF device probe cannot be propagated and therefore suspending the SOF device normally during system suspend could potentially run into errors. For example, with the current flow, if the FW boot failed for some reason and the system suspends, the SOF device suspend could fail because the CTX_SAVE IPC would be attempted even though the FW never really booted successfully causing it to time out. Another scenario that the state machine fixes is when the runtime suspend for the SOF device fails and the DSP is powered down nevertheless, the CTX_SAVE IPC during system suspend would timeout because the DSP is already powered down.
--- sound/soc/sof/core.c | 50 +++++++++++++++++++++++++++++++- sound/soc/sof/intel/hda-loader.c | 1 - sound/soc/sof/intel/hda.c | 4 +-- sound/soc/sof/ipc.c | 17 ++++------- sound/soc/sof/loader.c | 19 ++++++++---- sound/soc/sof/pm.c | 21 +++++++++++++- sound/soc/sof/sof-priv.h | 11 ++++++- 7 files changed, 99 insertions(+), 24 deletions(-)
sound/soc/sof/core.c +49 -1
@@ -306,6 +306,46 @@ static int sof_machine_check(struct snd_sof_dev *sdev)#endif } +/* +* FW Boot State Transition Diagram +* +* +-----------------------------------------------------------------------+ +* | | +* ------------------ ------------------ | +* | | | | | +* | BOOT_FAILED | | READY_FAILED |-------------------------+ | +* | | | | | | +* ------------------ ------------------ | | +* ^ ^ | | +* | | | | +* (FW Boot Timeout) (FW_READY FAIL) | | +* | | | | +* | | | | +* ------------------ | ------------------ | | +* | | | | | | | +* | IN_PROGRESS |---------------+------------->| COMPLETE | | | +* | | (FW Boot OK) (FW_READY OK) | | | | +* ------------------ ------------------ | | +* ^ | | | +* | | | | +* (FW Loading OK) (System Suspend/Runtime Suspend) +* | | | | +* | | | | +* ------------------ ------------------ | | | +* | | | |<-----+ | | +* | PREPARE | | NOT_STARTED |<---------------------+ | +* | | | |<---------------------------+ +* ------------------ ------------------ +* | ^ | ^ +* | | | | +* | +-----------------------+ | +* | (DSP Probe OK) | +* | | +* | | +* +------------------------------------+ +* (System Suspend/Runtime Suspend) +*/ +static int sof_probe_continue (struct snd_sof_dev *sdev) {struct snd_sof_pdata *plat_data = sdev->pdata;
@@ -321,6 +361,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)return ret; } + sdev->fw_state =SOF_FW_BOOT_PREPARE ; +/* check machine info */ ret =sof_machine_check (sdev);if (ret <0 ) {
@@ -360,7 +402,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_load_err; } -/* boot the firmware */ + sdev->fw_state =SOF_FW_BOOT_IN_PROGRESS ; + +/* +* Boot the firmware. The FW boot status will be modified +* in snd_sof_run_firmware() depending on the outcome. +*/ ret =snd_sof_run_firmware (sdev);if (ret <0 ) { dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
@@ -479,6 +526,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; + sdev->fw_state =SOF_FW_BOOT_NOT_STARTED ;dev_set_drvdata (dev, sdev);/* check all mandatory ops */
sound/soc/sof/intel/hda-loader.c +0 -1
@@ -295,7 +295,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)/* init for booting wait */ init_waitqueue_head (&sdev->boot_wait); - sdev->boot_complete = false;/* prepare DMA for code loader stream */ tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size,
sound/soc/sof/intel/hda.c +2 -2
@@ -168,7 +168,7 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags) panic =snd_sof_dsp_read (sdev,HDA_DSP_BAR ,HDA_ADSP_ERROR_CODE_SKL +0x4 ); -if (sdev->boot_complete) { +if (sdev->fw_state ==SOF_FW_BOOT_COMPLETE ) {hda_dsp_get_registers (sdev, &xoops, &panic_info, stack,HDA_DSP_STACK_DUMP_SIZE ); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
@@ -195,7 +195,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) HDA_DSP_SRAM_REG_FW_STATUS); panic =snd_sof_dsp_read (sdev,HDA_DSP_BAR ,HDA_DSP_SRAM_REG_FW_TRACEP ); -if (sdev->boot_complete) { +if (sdev->fw_state ==SOF_FW_BOOT_COMPLETE ) {hda_dsp_get_registers (sdev, &xoops, &panic_info, stack,HDA_DSP_STACK_DUMP_SIZE ); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
sound/soc/sof/ipc.c +5 -12
@@ -346,19 +346,12 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)break ;case SOF_IPC_FW_READY :/* check for FW boot completion */ -if (!sdev->boot_complete) { +if (sdev->fw_state ==SOF_FW_BOOT_IN_PROGRESS ) { err =sof_ops (sdev)->fw_ready (sdev, cmd); -if (err <0 ) { -/* -* this indicates a mismatch in ABI -* between the driver and fw -*/ -dev_err (sdev->dev,"error: ABI mismatch %d\n" , - err); - }else { -/* firmware boot completed OK */ - sdev->boot_complete = true; - } +if (err <0 ) + sdev->fw_state =SOF_FW_BOOT_READY_FAILED ; +else + sdev->fw_state =SOF_FW_BOOT_COMPLETE ;/* wake up firmware loader */ wake_up (&sdev->boot_wait);
sound/soc/sof/loader.c +13 -6
@@ -512,7 +512,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)int init_core_mask;init_waitqueue_head (&sdev->boot_wait); - sdev->boot_complete = false;/* create read-only fw_version debugfs to store boot version info */ if (sdev->first_boot) {
@@ -544,19 +543,27 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) init_core_mask = ret; -/* now wait for the DSP to boot */ - ret =wait_event_timeout (sdev->boot_wait, sdev->boot_complete, +/* +* now wait for the DSP to boot. There are 3 possible outcomes: +* 1. Boot wait times out indicating FW boot failure. +* 2. FW boots successfully and fw_ready op succeeds. +* 3. FW boots but fw_ready op fails. +*/ + ret =wait_event_timeout (sdev->boot_wait, + sdev->fw_state >SOF_FW_BOOT_IN_PROGRESS ,msecs_to_jiffies (sdev->boot_timeout));if (ret ==0 ) {dev_err (sdev->dev,"error: firmware boot failure\n" );snd_sof_dsp_dbg_dump (sdev,SOF_DBG_REGS |SOF_DBG_MBOX |SOF_DBG_TEXT |SOF_DBG_PCI ); -/* after this point FW_READY msg should be ignored */ - sdev->boot_complete = true; + sdev->fw_state =SOF_FW_BOOT_FAILED ;return -EIO ; } -dev_info (sdev->dev,"firmware boot complete\n" ); +if (sdev->fw_state ==SOF_FW_BOOT_COMPLETE ) +dev_info (sdev->dev,"firmware boot complete\n" ); +else +return -EIO ;/* FW boots but fw_ready op failed */ /* perform post fw run operations */ ret =snd_sof_dsp_post_fw_run (sdev);
sound/soc/sof/pm.c +20 -1
@@ -283,6 +283,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)return ret; } + sdev->fw_state =SOF_FW_BOOT_PREPARE ; +/* load the firmware */ ret =snd_sof_load_firmware (sdev);if (ret <0 ) {
@@ -292,7 +294,12 @@ static int sof_resume(struct device *dev, bool runtime_resume)return ret; } -/* boot the firmware */ + sdev->fw_state =SOF_FW_BOOT_IN_PROGRESS ; + +/* +* Boot the firmware. The FW boot status will be modified +* in snd_sof_run_firmware() depending on the outcome. +*/ ret =snd_sof_run_firmware (sdev);if (ret <0 ) { dev_err(sdev->dev,
@@ -341,6 +348,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)if (!sof_ops (sdev)->suspend)return 0 ; +if (sdev->fw_state !=SOF_FW_BOOT_COMPLETE ) + goto power_down; +/* release trace */ snd_sof_release_trace (sdev);
@@ -378,6 +388,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) ret); } +power_down: + +/* return if the DSP was not probed successfully */ +if (sdev->fw_state ==SOF_FW_BOOT_NOT_STARTED ) +return 0 ; +/* power down all DSP cores */ if (runtime_suspend) ret =snd_sof_dsp_runtime_suspend (sdev);
@@ -388,6 +404,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)"error: failed to power down DSP during suspend %d\n" , ret); +/* reset FW state */ + sdev->fw_state =SOF_FW_BOOT_NOT_STARTED ; +return ret; }
sound/soc/sof/sof-priv.h +10 -1
@@ -387,6 +387,15 @@ struct snd_sof_dai {struct list_head list;/* list in sdev dai list */ }; +enum snd_sof_fw_state { +SOF_FW_BOOT_NOT_STARTED =0 , +SOF_FW_BOOT_PREPARE , +SOF_FW_BOOT_IN_PROGRESS , +SOF_FW_BOOT_FAILED , +SOF_FW_BOOT_READY_FAILED ,/* firmware booted but fw_ready op failed */ +SOF_FW_BOOT_COMPLETE , +}; +/* * SOF Device Level. */
@@ -408,7 +417,7 @@ struct snd_sof_dev {/* DSP firmware boot */ wait_queue_head_t boot_wait; -u32 boot_complete; +enum snd_sof_fw_state fw_state;u32 first_boot;/* work queue in case the probe is implemented in two steps */
Signature · 1 line
2.20.1
Greg Kroah-Hartman
maintainerPATCH 149/367 ASoC: SOF: core: release resources on errors in probe_continue
In reply to Greg Kroah-HartmanFrom: Pierre-Louis Bossart <[email protected]>
[ Upstream commit 410e5e55c9c1c9c0d452ac5b9adb37b933a7747e ]
The initial intent of releasing resources in the .remove does not work well with HDaudio codecs. If the probe_continue() fails in a work queue, e.g. due to missing firmware or authentication issues, we don't release any resources, and as a result the kernel oopses during suspend operations.
The suggested fix is to release all resources during errors in probe_continue(), and use fw_state to track resource allocation state, so that .remove does not attempt to release the same hardware resources twice. PM operations are also modified so that no action is done if DSP resources have been freed due to an error at probe.
Bugzilla: http://bugzilla.suse.com/show_bug.cgi?id=1161246
--- sound/soc/sof/core.c | 32 +++++++++++--------------------- sound/soc/sof/pm.c | 4 ++++ 2 files changed, 15 insertions(+), 21 deletions(-)
sound/soc/sof/core.c +11 -21
@@ -466,7 +466,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)return 0 ; -#if !IS_ENABLED (CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE ) fw_trace_err:snd_sof_free_trace (sdev); fw_run_err:
@@ -477,22 +476,10 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)snd_sof_free_debug (sdev); dbg_err:snd_sof_remove (sdev); -#else - -/* -* when the probe_continue is handled in a work queue, the -* probe does not fail so we don't release resources here. -* They will be released with an explicit call to -* snd_sof_device_remove() when the PCI/ACPI device is removed -*/ - -fw_trace_err: -fw_run_err: -fw_load_err: -ipc_err: -dbg_err: -#endif +/* all resources freed, update state to match */ + sdev->fw_state =SOF_FW_BOOT_NOT_STARTED ; + sdev->first_boot = true;return ret; }
@@ -575,10 +562,12 @@ int snd_sof_device_remove(struct device *dev)if (IS_ENABLED (CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE ))cancel_work_sync (&sdev->probe_work); -snd_sof_fw_unload (sdev); -snd_sof_ipc_free (sdev); -snd_sof_free_debug (sdev); -snd_sof_free_trace (sdev); +if (sdev->fw_state >SOF_FW_BOOT_NOT_STARTED ) { +snd_sof_fw_unload (sdev); +snd_sof_ipc_free (sdev); +snd_sof_free_debug (sdev); +snd_sof_free_trace (sdev); + }/* * Unregister machine driver. This will unbind the snd_card which
@@ -594,7 +583,8 @@ int snd_sof_device_remove(struct device *dev)* scheduled on, when they are unloaded. Therefore, the DSP must be * removed only after the topology has been unloaded. */ -snd_sof_remove (sdev); +if (sdev->fw_state >SOF_FW_BOOT_NOT_STARTED ) +snd_sof_remove (sdev);/* release firmware */ release_firmware (pdata->fw);
sound/soc/sof/pm.c +4 -0
@@ -269,6 +269,10 @@ static int sof_resume(struct device *dev, bool runtime_resume)if (!sof_ops (sdev)->resume || !sof_ops (sdev)->runtime_resume)return 0 ; +/* DSP was never successfully started, nothing to resume */ +if (sdev->first_boot) +return 0 ; +/* * if the runtime_resume flag is set, call the runtime_resume routine * or else call the system resume routine
Signature · 1 line
2.20.1
Greg Kroah-Hartman
maintainerPATCH 150/367 tracing: Annotate ftrace_graph_hash pointer with __rcu
In reply to Greg Kroah-HartmanFrom: Amol Grover <[email protected]>
[ Upstream commit 24a9729f831462b1d9d61dc85ecc91c59037243f ]
Fix following instances of sparse error kernel/trace/ftrace.c:5664:29: error: incompatible types in comparison kernel/trace/ftrace.c:5785:21: error: incompatible types in comparison kernel/trace/ftrace.c:5864:36: error: incompatible types in comparison kernel/trace/ftrace.c:5866:25: error: incompatible types in comparison
Use rcu_dereference_protected to access the __rcu annotated pointer.
--- kernel/trace/ftrace.c | 2 +- kernel/trace/trace.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-)
kernel/trace/ftrace.c +1 -1
@@ -5596,7 +5596,7 @@ static const struct file_operations ftrace_notrace_fops = {static DEFINE_MUTEX (graph_lock); -struct ftrace_hash *ftrace_graph_hash =EMPTY_HASH ; +struct ftrace_hash __rcu *ftrace_graph_hash =EMPTY_HASH ;struct ftrace_hash *ftrace_graph_notrace_hash =EMPTY_HASH ;enum graph_filter_type {
kernel/trace/trace.h +6 -3
@@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr, unsigned long flags, int pc);#ifdef CONFIG_DYNAMIC_FTRACE -extern struct ftrace_hash *ftrace_graph_hash; +extern struct ftrace_hash __rcu *ftrace_graph_hash;extern struct ftrace_hash *ftrace_graph_notrace_hash;static inline int ftrace_graph_addr (struct ftrace_graph_ent *trace) {unsigned long addr = trace->func;int ret =0 ; +struct ftrace_hash *hash;preempt_disable_notrace (); -if (ftrace_hash_empty (ftrace_graph_hash)) { + hash =rcu_dereference_protected (ftrace_graph_hash, !preemptible ()); + +if (ftrace_hash_empty (hash)) { ret =1 ; goto out; } -if (ftrace_lookup_ip (ftrace_graph_hash, addr)) { +if (ftrace_lookup_ip (hash, addr)) {/* * This needs to be cleared on the return functions
Signature · 1 line
2.20.1
Re: [PATCH 5.5 150/367] tracing: Annotate ftrace_graph_hash pointer with __rcu
Quoted 4 lines · Greg Kroah-Hartman
From: Amol Grover <[email protected]>
[ Upstream commit
24a9729f831462b1d9d61dc85ecc91c59037243f]
Amol, can you send a follow-up patch to annotateftrace_graph_notrace_hash as well?
- Joel
Quoted 71 lines
Fix following instances of sparse error
kernel/trace/ftrace.c:5664:29: error: incompatible types in comparisonkernel/trace/ftrace.c:5785:21: error: incompatible types in comparisonkernel/trace/ftrace.c:5864:36: error: incompatible types in comparisonkernel/trace/ftrace.c:5866:25: error: incompatible types in comparisonUse
rcu_dereference_protectedto access the__rcuannotated pointer.Reviewed-by Joel Fernandes (Google) <[email protected]>Signed-off-by Amol Grover <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- kernel/trace/ftrace.c | 2 +- kernel/trace/trace.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-)kernel/trace/ftrace.c +1
-1@@ -5596,7 +5596,7 @@ static const struct file_operations ftrace_notrace_fops = {static DEFINE_MUTEX (graph_lock); -struct ftrace_hash *ftrace_graph_hash =EMPTY_HASH ; +struct ftrace_hash __rcu *ftrace_graph_hash =EMPTY_HASH ;struct ftrace_hash *ftrace_graph_notrace_hash =EMPTY_HASH ;enum graph_filter_type {kernel/trace/trace.h +6
-4@@ -950,22 +950,25 @@ extern void __trace_graph_return(struct trace_array *tr, unsigned long flags, int pc);#ifdef CONFIG_DYNAMIC_FTRACE -extern struct ftrace_hash *ftrace_graph_hash; +extern struct ftrace_hash __rcu *ftrace_graph_hash;extern struct ftrace_hash *ftrace_graph_notrace_hash;static inline int ftrace_graph_addr (struct ftrace_graph_ent *trace) {unsigned long addr = trace->func;int ret =0 ; +struct ftrace_hash *hash;preempt_disable_notrace (); -if (ftrace_hash_empty (ftrace_graph_hash)) { + hash =rcu_dereference_protected (ftrace_graph_hash, !preemptible ()); + +if (ftrace_hash_empty (hash)) { ret =1 ; goto out; } -if (ftrace_lookup_ip (ftrace_graph_hash, addr)) { +if (ftrace_lookup_ip (hash, addr)) {/* * This needs to be cleared on the return functions -- 2.20.1
Sasha Levin
maintainerRe: [PATCH 5.5 150/367] tracing: Annotate ftrace_graph_hash pointer with __rcu
Quoted 9 lines · Joel Fernandes
Quoted 4 lines · Greg Kroah-Hartman
From: Amol Grover <[email protected]>
[ Upstream commit
24a9729f831462b1d9d61dc85ecc91c59037243f]Amol, can you send a follow-up patch to annotate
ftrace_graph_notrace_hashas well?
Note that I took this to make later stable tagged patch apply cleanly. I don't expect these annotation changes to be picked up for stable on their own.
Signature · 2 lines
Thanks, Sasha
Re: [PATCH 5.5 150/367] tracing: Annotate ftrace_graph_hash pointer with __rcu
Quoted 6 lines · Sasha Levin
Quoted 5 lines · Joel Fernandes
Quoted 3 lines · Greg Kroah-Hartman
From: Amol Grover <[email protected]>
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
[ Upstream commit
24a9729f831462b1d9d61dc85ecc91c59037243f]Amol, can you send a follow-up patch to annotate
ftrace_graph_notrace_hashas well?Note that I took this to make later stable tagged patch apply cleanly. I don't expect these annotation changes to be picked up for stable on their own.
Yes, that should be fine.
thanks,
- Joel
Greg Kroah-Hartman
maintainerPATCH 151/367 tracing: Annotate ftrace_graph_notrace_hash pointer with __rcu
In reply to Greg Kroah-HartmanFrom: Amol Grover <[email protected]>
[ Upstream commit fd0e6852c407dd9aefc594f54ddcc21d84803d3b ]
Fix following instances of sparse error kernel/trace/ftrace.c:5667:29: error: incompatible types in comparison kernel/trace/ftrace.c:5813:21: error: incompatible types in comparison kernel/trace/ftrace.c:5868:36: error: incompatible types in comparison kernel/trace/ftrace.c:5870:25: error: incompatible types in comparison
Use rcu_dereference_protected to dereference the newly annotated pointer.
--- kernel/trace/ftrace.c | 2 +- kernel/trace/trace.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-)
kernel/trace/ftrace.c +1 -1
@@ -5597,7 +5597,7 @@ static const struct file_operations ftrace_notrace_fops = {static DEFINE_MUTEX (graph_lock);struct ftrace_hash __rcu *ftrace_graph_hash =EMPTY_HASH ; -struct ftrace_hash *ftrace_graph_notrace_hash =EMPTY_HASH ; +struct ftrace_hash __rcu *ftrace_graph_notrace_hash =EMPTY_HASH ;enum graph_filter_type {GRAPH_FILTER_NOTRACE =0 ,
kernel/trace/trace.h +6 -2
@@ -951,7 +951,7 @@ extern void __trace_graph_return(struct trace_array *tr,#ifdef CONFIG_DYNAMIC_FTRACE extern struct ftrace_hash __rcu *ftrace_graph_hash; -extern struct ftrace_hash *ftrace_graph_notrace_hash; +extern struct ftrace_hash __rcu *ftrace_graph_notrace_hash;static inline int ftrace_graph_addr (struct ftrace_graph_ent *trace) {
@@ -1004,10 +1004,14 @@ static inline void ftrace_graph_addr_finish(struct ftrace_graph_ret *trace)static inline int ftrace_graph_notrace_addr (unsigned long addr) {int ret =0 ; +struct ftrace_hash *notrace_hash;preempt_disable_notrace (); -if (ftrace_lookup_ip (ftrace_graph_notrace_hash, addr)) + notrace_hash =rcu_dereference_protected (ftrace_graph_notrace_hash, + !preemptible ()); + +if (ftrace_lookup_ip (notrace_hash, addr)) ret =1 ;preempt_enable_notrace ();
Signature · 1 line
2.20.1
Greg Kroah-Hartman
maintainerPATCH 152/367 ftrace: Add comment to why rcu_dereference_sched() is open coded
In reply to Greg Kroah-HartmanFrom: Steven Rostedt (VMware) <[email protected]>
[ Upstream commit 16052dd5bdfa16dbe18d8c1d4cde2ddab9d23177 ]
Because the function graph tracer can execute in sections where RCU is not "watching", the rcu_dereference_sched() for the has needs to be open coded. This is fine because the RCU "flavor" of the ftrace hash is protected by its own RCU handling (it does its own little synchronization on every CPU and does not rely on RCU sched).
--- kernel/trace/trace.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/trace/trace.h +10 -0
@@ -961,6 +961,11 @@ static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)preempt_disable_notrace (); +/* +* Have to open code "rcu_dereference_sched()" because the +* function graph tracer can be called when RCU is not +* "watching". +*/ hash =rcu_dereference_protected (ftrace_graph_hash, !preemptible ());if (ftrace_hash_empty (hash)) {
@@ -1008,6 +1013,11 @@ static inline int ftrace_graph_notrace_addr(unsigned long addr)preempt_disable_notrace (); +/* +* Have to open code "rcu_dereference_sched()" because the +* function graph tracer can be called when RCU is not +* "watching". +*/ notrace_hash =rcu_dereference_protected (ftrace_graph_notrace_hash, !preemptible ());
Signature · 1 line
2.20.1
Greg Kroah-Hartman
maintainerPATCH 153/367 ftrace: Protect ftrace_graph_hash with ftrace_sync
In reply to Greg Kroah-HartmanFrom: Steven Rostedt (VMware) <[email protected]>
[ Upstream commit 54a16ff6f2e50775145b210bcd94d62c3c2af117 ]
As function_graph tracer can run when RCU is not "watching", it can not be protected by synchronize_rcu() it requires running a task on each CPU before it can be freed. Calling schedule_on_each_cpu(ftrace_sync) needs to be used.
--- kernel/trace/ftrace.c | 11 +++++++++-- kernel/trace/trace.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-)
kernel/trace/ftrace.c +9 -2
@@ -5872,8 +5872,15 @@ ftrace_graph_release(struct inode *inode, struct file *file)mutex_unlock (&graph_lock); -/* Wait till all users are no longer using the old hash */ -synchronize_rcu (); +/* +* We need to do a hard force of sched synchronization. +* This is because we use preempt_disable() to do RCU, but +* the function tracers can be called where RCU is not watching +* (like before user_exit()). We can not rely on the RCU +* infrastructure to do the synchronization, thus we must do it +* ourselves. +*/ +schedule_on_each_cpu (ftrace_sync);free_ftrace_hash (old_hash); }
kernel/trace/trace.h +2 -0
@@ -965,6 +965,7 @@ static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace)* Have to open code "rcu_dereference_sched()" because the * function graph tracer can be called when RCU is not * "watching". +* Protected with schedule_on_each_cpu(ftrace_sync) */ hash =rcu_dereference_protected (ftrace_graph_hash, !preemptible ());
@@ -1017,6 +1018,7 @@ static inline int ftrace_graph_notrace_addr(unsigned long addr)* Have to open code "rcu_dereference_sched()" because the * function graph tracer can be called when RCU is not * "watching". +* Protected with schedule_on_each_cpu(ftrace_sync) */ notrace_hash =rcu_dereference_protected (ftrace_graph_notrace_hash, !preemptible ());
Signature · 1 line
2.20.1
Greg Kroah-Hartman
maintainerPATCH 154/367 crypto: pcrypt - Avoid deadlock by using per-instance padata queues
In reply to Greg Kroah-HartmanFrom: Herbert Xu <[email protected]>
commit bbefa1dd6a6d53537c11624752219e39959d04fb upstream.
If the pcrypt template is used multiple times in an algorithm, then a deadlock occurs because all pcrypt instances share the same padata_instance, which completes requests in the order submitted. That is, the inner pcrypt request waits for the outer pcrypt request while the outer request is already waiting for the inner.
This patch fixes this by allocating a set of queues for each pcrypt instance instead of using two global queues. In order to maintain the existing user-space interface, the pinst structure remains global so any sysfs modifications will apply to every pcrypt instance.
Note that when an update occurs we have to allocate memory for every pcrypt instance. Should one of the allocations fail we will abort the update without rolling back changes already made.
The new per-instance data structure is called padata_shell and is essentially a wrapper around parallel_data.
Reproducer:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
struct sockaddr_alg addr = {
.salg_type = "aead",
.salg_name = "pcrypt(pcrypt(rfc4106-gcm-aesni))"
};
int algfd, reqfd;
char buf[32] = { 0 };
algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(algfd, (void *)&addr, sizeof(addr));
setsockopt(algfd, SOL_ALG, ALG_SET_KEY, buf, 20);
reqfd = accept(algfd, 0, 0);
write(reqfd, buf, 32);
read(reqfd, buf, 16);
}--- crypto/pcrypt.c | 36 ++++++- include/linux/padata.h | 34 ++++++- kernel/padata.c | 236 ++++++++++++++++++++++++++++++++++--------------- 3 files changed, 227 insertions(+), 79 deletions(-)
crypto/pcrypt.c +33 -3
@@ -24,6 +24,8 @@ static struct kset *pcrypt_ksestruct pcrypt_instance_ctx {struct crypto_aead_spawn spawn; +struct padata_shell *psenc; +struct padata_shell *psdec;atomic_t tfm_count; };
@@ -32,6 +34,12 @@ struct pcrypt_aead_ctx {unsigned int cb_cpu; }; +static inline struct pcrypt_instance_ctx *pcrypt_tfm_ictx ( +struct crypto_aead *tfm) +{ +return aead_instance_ctx (aead_alg_instance (tfm)); +} +static int pcrypt_aead_setkey (struct crypto_aead *parent,const u8 *key,unsigned int keylen) {
@@ -90,6 +98,9 @@ static int pcrypt_aead_encrypt(struct aestruct crypto_aead *aead =crypto_aead_reqtfm (req);struct pcrypt_aead_ctx *ctx =crypto_aead_ctx (aead);u32 flags =aead_request_flags (req); +struct pcrypt_instance_ctx *ictx; + + ictx =pcrypt_tfm_ictx (aead);memset (padata,0 ,sizeof (struct padata_priv ));
@@ -103,7 +114,7 @@ static int pcrypt_aead_encrypt(struct ae req->cryptlen, req->iv);aead_request_set_ad (creq, req->assoclen); - err =padata_do_parallel (pencrypt, padata, &ctx->cb_cpu); + err =padata_do_parallel (ictx->psenc, padata, &ctx->cb_cpu);if (!err)return -EINPROGRESS ;
@@ -132,6 +143,9 @@ static int pcrypt_aead_decrypt(struct aestruct crypto_aead *aead =crypto_aead_reqtfm (req);struct pcrypt_aead_ctx *ctx =crypto_aead_ctx (aead);u32 flags =aead_request_flags (req); +struct pcrypt_instance_ctx *ictx; + + ictx =pcrypt_tfm_ictx (aead);memset (padata,0 ,sizeof (struct padata_priv ));
@@ -145,7 +159,7 @@ static int pcrypt_aead_decrypt(struct ae req->cryptlen, req->iv);aead_request_set_ad (creq, req->assoclen); - err =padata_do_parallel (pdecrypt, padata, &ctx->cb_cpu); + err =padata_do_parallel (ictx->psdec, padata, &ctx->cb_cpu);if (!err)return -EINPROGRESS ;
@@ -192,6 +206,8 @@ static void pcrypt_free(struct aead_inststruct pcrypt_instance_ctx *ctx =aead_instance_ctx (inst);crypto_drop_aead (&ctx->spawn); +padata_free_shell (ctx->psdec); +padata_free_shell (ctx->psenc);kfree (inst); }
@@ -233,12 +249,22 @@ static int pcrypt_create_aead(struct cryif (!inst)return -ENOMEM ; + err = -ENOMEM ; + ctx =aead_instance_ctx (inst); + ctx->psenc =padata_alloc_shell (pencrypt); +if (!ctx->psenc) + goto out_free_inst; + + ctx->psdec =padata_alloc_shell (pdecrypt); +if (!ctx->psdec) + goto out_free_psenc; +crypto_set_aead_spawn (&ctx->spawn,aead_crypto_instance (inst)); err =crypto_grab_aead (&ctx->spawn, name,0 ,0 );if (err) - goto out_free_inst; + goto out_free_psdec; alg =crypto_spawn_aead_alg (&ctx->spawn); err =pcrypt_init_instance (aead_crypto_instance (inst), &alg->base);
@@ -271,6 +297,10 @@ out: out_drop_aead:crypto_drop_aead (&ctx->spawn); +out_free_psdec: +padata_free_shell (ctx->psdec); +out_free_psenc: +padata_free_shell (ctx->psenc); out_free_inst:kfree (inst); goto out;
include/linux/padata.h +29 -5
@@ -9,6 +9,7 @@#ifndef PADATA_H #define PADATA_H +#include <linux/compiler_types.h> #include <linux/workqueue.h> #include <linux/spinlock.h> #include <linux/list.h>
@@ -98,7 +99,7 @@ struct padata_cpumask {* struct parallel_data - Internal control structure, covers everything * that depends on the cpumask in use. * -* @pinst: padata instance. +* @sh: padata_shell object. * @pqueue: percpu padata queues used for parallelization. * @squeue: percpu padata queues used for serialuzation. * @reorder_objects: Number of objects waiting in the reorder queues.
@@ -111,7 +112,7 @@ struct padata_cpumask {* @lock: Reorder lock. */ struct parallel_data { -struct padata_instance *pinst; +struct padata_shell *ps;struct padata_parallel_queue __percpu *pqueue;struct padata_serial_queue __percpu *squeue;atomic_t reorder_objects;
@@ -125,13 +126,32 @@ struct parallel_data { };/** +* struct padata_shell - Wrapper around struct parallel_data, its +* purpose is to allow the underlying control structure to be replaced +* on the fly using RCU. +* +* @pinst: padat instance. +* @pd: Actual parallel_data structure which may be substituted on the fly. +* @opd: Pointer to old pd to be freed by padata_replace. +* @list: List entry in padata_instance list. +*/ +struct padata_shell { +struct padata_instance *pinst; +struct parallel_data __rcu *pd; +struct parallel_data *opd; +struct list_head list; +}; + +/** * struct padata_instance - The overall control structure. * * @cpu_notifier: cpu hotplug notifier. * @parallel_wq: The workqueue used for parallel work. * @serial_wq: The workqueue used for serial work. -* @pd: The internal control structure. +* @pslist: List of padata_shell objects attached to this instance. * @cpumask: User supplied cpumasks for parallel and serial works. +* @rcpumask: Actual cpumasks based on user cpumask and cpu_online_mask. +* @omask: Temporary storage used to compute the notification mask. * @cpumask_change_notifier: Notifiers chain for user-defined notify * callbacks that will be called when either @pcpu or @cbcpu * or both cpumasks change.
@@ -143,8 +163,10 @@ struct padata_instance {struct hlist_node node;struct workqueue_struct *parallel_wq;struct workqueue_struct *serial_wq; -struct parallel_data *pd; +struct list_head pslist;struct padata_cpumask cpumask; +struct padata_cpumask rcpumask; +cpumask_var_t omask;struct blocking_notifier_head cpumask_change_notifier;struct kobject kobj;struct mutex lock;
@@ -156,7 +178,9 @@ struct padata_instance {extern struct padata_instance *padata_alloc_possible (const char *name);extern void padata_free (struct padata_instance *pinst); -extern int padata_do_parallel (struct padata_instance *pinst, +extern struct padata_shell *padata_alloc_shell (struct padata_instance *pinst); +extern void padata_free_shell (struct padata_shell *ps); +extern int padata_do_parallel (struct padata_shell *ps,struct padata_priv *padata,int *cb_cpu);extern void padata_do_serial (struct padata_priv *padata); extern int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
kernel/padata.c +165 -71
399 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 155/367 btrfs: fix improper setting of scanned for range cyclic write cache pages
In reply to Greg Kroah-HartmanFrom: Josef Bacik <[email protected]>
commit 556755a8a99be8ca3cd9fbe36aaf9b3b0339a00d upstream.
We noticed that we were having regular CG OOM kills in cases where there was still enough dirty pages to avoid OOM'ing. It turned out there's this corner case in btrfs's handling of range_cyclic where files that were being redirtied were not getting fully written out because of how we do range_cyclic writeback.
We unconditionally were setting scanned = 1; the first time we found any pages in the inode. This isn't actually what we want, we want it to be set if we've scanned the entire file. For range_cyclic we could be starting in the middle or towards the end of the file, so we could write one page and then not write any of the other dirty pages in the file because we set scanned = 1.
Fix this by not setting scanned = 1 if we find pages. The rules for setting scanned should be
1) !range_cyclic. In this case we have a specified range to write out.
2) range_cyclic && index == 0. In this case we've started at the beginning and there is no need to loop around a second time.
3) range_cyclic && we started at index > 0 and we've reached the end of the file without satisfying our nr_to_write.
This patch fixes both of our writepages implementations to make sure these rules hold true. This fixed our over zealous CG OOMs in production.
--- fs/btrfs/extent_io.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
fs/btrfs/extent_io.c +10 -2
@@ -3941,6 +3941,11 @@ int btree_write_cache_pages(struct addreif (wbc->range_cyclic) { index = mapping->writeback_index;/* Start from prev offset */ end =-1 ; +/* +* Start from the beginning does not need to cycle over the +* range, mark it as scanned. +*/ + scanned = (index ==0 ); }else { index = wbc->range_start >>PAGE_SHIFT ; end = wbc->range_end >>PAGE_SHIFT ;
@@ -3958,7 +3963,6 @@ retry: tag))) {unsigned i; - scanned =1 ;for (i =0 ; i < nr_pages; i++) {struct page *page = pvec.pages[i];
@@ -4087,6 +4091,11 @@ static int extent_write_cache_pages(struif (wbc->range_cyclic) { index = mapping->writeback_index;/* Start from prev offset */ end =-1 ; +/* +* Start from the beginning does not need to cycle over the +* range, mark it as scanned. +*/ + scanned = (index ==0 ); }else { index = wbc->range_start >>PAGE_SHIFT ; end = wbc->range_end >>PAGE_SHIFT ;
@@ -4120,7 +4129,6 @@ retry: &index, end, tag))) {unsigned i; - scanned =1 ;for (i =0 ; i < nr_pages; i++) {struct page *page = pvec.pages[i];
Greg Kroah-Hartman
maintainerPATCH 156/367 btrfs: Handle another split brain scenario with metadata uuid feature
In reply to Greg Kroah-HartmanFrom: Nikolay Borisov <[email protected]>
commit 05840710149c7d1a78ea85a2db5723f706e97d8f upstream.
There is one more cases which isn't handled by the original metadata uuid work. Namely, when a filesystem has METADATA_UUID incompat bit and the user decides to change the FSID to the original one e.g. have metadata_uuid and fsid match. In case of power failure while this operation is in progress we could end up in a situation where some of the disks have the incompat bit removed and the other half have both METADATA_UUID_INCOMPAT and FSID_CHANGING_IN_PROGRESS flags.
This patch handles the case where a disk that has successfully changed its FSID such that it equals METADATA_UUID is scanned first. Subsequently when a disk with both METADATA_UUID_INCOMPAT/FSID_CHANGING_IN_PROGRESS flags is scanned find_fsid_changed won't be able to find an appropriate btrfs_fs_devices. This is done by extending find_fsid_changed to correctly find btrfs_fs_devices whose metadata_uuid/fsid are the same and they match the metadata_uuid of the currently scanned device.
--- fs/btrfs/volumes.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
fs/btrfs/volumes.c +14 -3
@@ -697,17 +697,28 @@ static struct btrfs_fs_devices *find_fsi/* * Handles the case where scanned device is part of an fs that had * multiple successful changes of FSID but curently device didn't -* observe it. Meaning our fsid will be different than theirs. +* observe it. Meaning our fsid will be different than theirs. We need +* to handle two subcases : +* 1 - The fs still continues to have different METADATA/FSID uuids. +* 2 - The fs is switched back to its original FSID (METADATA/FSID +* are equal). */ list_for_each_entry (fs_devices, &fs_uuids, fs_list) { +/* Changed UUIDs */ if (memcmp (fs_devices->metadata_uuid, fs_devices->fsid,BTRFS_FSID_SIZE ) !=0 &&memcmp (fs_devices->metadata_uuid, disk_super->metadata_uuid,BTRFS_FSID_SIZE ) ==0 &&memcmp (fs_devices->fsid, disk_super->fsid, -BTRFS_FSID_SIZE ) !=0 ) { +BTRFS_FSID_SIZE ) !=0 ) +return fs_devices; + +/* Unchanged UUIDs */ +if (memcmp (fs_devices->metadata_uuid, fs_devices->fsid, +BTRFS_FSID_SIZE ) ==0 && +memcmp (fs_devices->fsid, disk_super->metadata_uuid, +BTRFS_FSID_SIZE ) ==0 )return fs_devices; - } }return NULL ;
Greg Kroah-Hartman
maintainerPATCH 157/367 riscv, bpf: Fix broken BPF tail calls
In reply to Greg Kroah-HartmanFrom: Björn Töpel <[email protected]>
commit f1003b787c00fbaa4b11619c6b23a885bfce8f07 upstream.
The BPF JIT incorrectly clobbered the a0 register, and did not flag usage of s5 register when BPF stack was being used.
--- arch/riscv/net/bpf_jit_comp.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
arch/riscv/net/bpf_jit_comp.c +11 -2
@@ -120,6 +120,11 @@ static bool seen_reg(int reg, struct rv_return false; } +static void mark_fp (struct rv_jit_context *ctx) +{ +__set_bit (RV_CTX_F_SEEN_S5 , &ctx->flags); +} +static void mark_call (struct rv_jit_context *ctx) {__set_bit (RV_CTX_F_SEEN_CALL , &ctx->flags);
@@ -596,7 +601,8 @@ static void __build_epilogue(u8 reg, stremit (rv_addi (RV_REG_SP ,RV_REG_SP , stack_adjust), ctx);/* Set return value. */ -emit (rv_addi (RV_REG_A0 ,RV_REG_A5 ,0 ), ctx); +if (reg ==RV_REG_RA ) +emit (rv_addi (RV_REG_A0 ,RV_REG_A5 ,0 ), ctx);emit (rv_jalr (RV_REG_ZERO , reg,0 ), ctx); }
@@ -1426,6 +1432,10 @@ static void build_prologue(struct rv_jit {int stack_adjust =0 , store_offset, bpf_stack_adjust; + bpf_stack_adjust =round_up (ctx->prog->aux->stack_depth,16 ); +if (bpf_stack_adjust) +mark_fp (ctx); +if (seen_reg (RV_REG_RA , ctx)) stack_adjust +=8 ; stack_adjust +=8 ;/* RV_REG_FP */
@@ -1443,7 +1453,6 @@ static void build_prologue(struct rv_jit stack_adjust +=8 ; stack_adjust =round_up (stack_adjust,16 ); - bpf_stack_adjust =round_up (ctx->prog->aux->stack_depth,16 ); stack_adjust += bpf_stack_adjust; store_offset = stack_adjust -8 ;
Greg Kroah-Hartman
maintainerPATCH 158/367 libbpf: Fix readelf output parsing for Fedora
In reply to Greg Kroah-HartmanFrom: Thadeu Lima de Souza Cascardo <[email protected]>
commit aa915931ac3e53ccf371308e6750da510e3591dd upstream.
Fedora binutils has been patched to show "other info" for a symbol at the end of the line. This was done in order to support unmaintained scripts that would break with the extra info. [1]
[1] https://src.fedoraproject.org/rpms/binutils/c/b8265c46f7ddae23a792ee8306fbaaeacba83bf8
This in turn has been done to fix the build of ruby, because of checksec.
[2] Thanks Michael Ellerman for the pointer.
[2] https://bugzilla.redhat.com/show_bug.cgi?id=1479302
As libbpf Makefile is not unmaintained, we can simply deal with either output format, by just removing the "other info" field, as it always comes inside brackets.
--- tools/lib/bpf/Makefile | 2 ++ 1 file changed, 2 insertions(+)
tools/lib/bpf/Makefile +2 -0
@@ -148,6 +148,7 @@ TAGS_PROG := $(if $(shell which etags 2> GLOBAL_SYM_COUNT = $(shell readelf -s --wide $(BPF_IN_SHARED) | \ cut -d "@" -f1 | sed 's/_v[0-9]_[0-9]_[0-9].*//' | \ + sed 's/\[.*\]//' | \ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ sort -u | wc -l) VERSIONED_SYM_COUNT = $(shell readelf -s --wide $(OUTPUT)libbpf.so | \
@@ -214,6 +215,7 @@ check_abi: $(OUTPUT)libbpf.so "versioned in $(VERSION_SCRIPT)." >&2; \ readelf -s --wide $(BPF_IN_SHARED) | \ cut -d "@" -f1 | sed 's/_v[0-9]_[0-9]_[0-9].*//' | \ + sed 's/\[.*\]//' | \ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ sort -u > $(OUTPUT)libbpf_global_syms.tmp; \ readelf -s --wide $(OUTPUT)libbpf.so | \
Greg Kroah-Hartman
maintainerPATCH 159/367 libbpf: Fix printf compilation warnings on ppc64le arch
In reply to Greg Kroah-HartmanFrom: Andrii Nakryiko <[email protected]>
commit 679152d3a32e305c213f83160c328c37566ae8bc upstream.
On ppc64le __u64 and __s64 are defined as long int and unsigned long int, respectively. This causes compiler to emit warning when %lld/%llu are used to printf 64-bit numbers. Fix this by casting to size_t/ssize_t with %zu and %zd format specifiers, respectively.
v1->v2:
- use size_t/ssize_t instead of custom typedefs (Martin).
--- tools/lib/bpf/libbpf.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-)
tools/lib/bpf/libbpf.c +19 -18
@@ -1242,15 +1242,15 @@ static int bpf_object__init_user_btf_map } sz =btf__resolve_size (obj->btf, t->type);if (sz <0 ) { -pr_warn ("map '%s': can't determine key size for type [%u]: %lld.\n" , - map_name, t->type, sz); +pr_warn ("map '%s': can't determine key size for type [%u]: %zd.\n" , + map_name, t->type, (ssize_t )sz);return sz; } -pr_debug ("map '%s': found key [%u], sz = %lld.\n" , - map_name, t->type, sz); +pr_debug ("map '%s': found key [%u], sz = %zd.\n" , + map_name, t->type, (ssize_t )sz);if (map->def.key_size && map->def.key_size != sz) { -pr_warn ("map '%s': conflicting key size %u != %lld.\n" , - map_name, map->def.key_size, sz); +pr_warn ("map '%s': conflicting key size %u != %zd.\n" , + map_name, map->def.key_size, (ssize_t )sz);return -EINVAL ; } map->def.key_size = sz;
@@ -1285,15 +1285,15 @@ static int bpf_object__init_user_btf_map } sz =btf__resolve_size (obj->btf, t->type);if (sz <0 ) { -pr_warn ("map '%s': can't determine value size for type [%u]: %lld.\n" , - map_name, t->type, sz); +pr_warn ("map '%s': can't determine value size for type [%u]: %zd.\n" , + map_name, t->type, (ssize_t )sz);return sz; } -pr_debug ("map '%s': found value [%u], sz = %lld.\n" , - map_name, t->type, sz); +pr_debug ("map '%s': found value [%u], sz = %zd.\n" , + map_name, t->type, (ssize_t )sz);if (map->def.value_size && map->def.value_size != sz) { -pr_warn ("map '%s': conflicting value size %u != %lld.\n" , - map_name, map->def.value_size, sz); +pr_warn ("map '%s': conflicting value size %u != %zd.\n" , + map_name, map->def.value_size, (ssize_t )sz);return -EINVAL ; } map->def.value_size = sz;
@@ -1817,7 +1817,8 @@ static int bpf_program__record_reloc(strreturn -LIBBPF_ERRNO__RELOC ; }if (sym->st_value %8 ) { -pr_warn ("bad call relo offset: %llu\n" , (__u64 )sym->st_value); +pr_warn ("bad call relo offset: %zu\n" , + (size_t )sym->st_value);return -LIBBPF_ERRNO__RELOC ; } reloc_desc->type =RELO_CALL ;
@@ -1859,8 +1860,8 @@ static int bpf_program__record_reloc(strbreak ; }if (map_idx >= nr_maps) { -pr_warn ("map relo failed to find map for sec %u, off %llu\n" , - shdr_idx, (__u64 )sym->st_value); +pr_warn ("map relo failed to find map for sec %u, off %zu\n" , + shdr_idx, (size_t )sym->st_value);return -LIBBPF_ERRNO__RELOC ; } reloc_desc->type =RELO_LD64 ;
@@ -1941,9 +1942,9 @@ bpf_program__collect_reloc(struct bpf_pr name =elf_strptr (obj->efile.elf, obj->efile.strtabidx, sym.st_name) ? :"<?>" ; -pr_debug ("relo for shdr %u, symb %llu, value %llu, type %d, bind %d, name %d (\'%s\'), insn %u\n" , - (__u32 )sym.st_shndx, (__u64 )GELF_R_SYM (rel.r_info), - (__u64 )sym.st_value,GELF_ST_TYPE (sym.st_info), +pr_debug ("relo for shdr %u, symb %zu, value %zu, type %d, bind %d, name %d (\'%s\'), insn %u\n" , + (__u32 )sym.st_shndx, (size_t )GELF_R_SYM (rel.r_info), + (size_t )sym.st_value,GELF_ST_TYPE (sym.st_info),GELF_ST_BIND (sym.st_info), sym.st_name, name, insn_idx);
Greg Kroah-Hartman
maintainerPATCH 160/367 libbpf: Dont attach perf_buffer to offline/missing CPUs
In reply to Greg Kroah-HartmanFrom: Andrii Nakryiko <[email protected]>
commit 783b8f01f5942a786998f5577bd9ff3992f22a1a upstream.
It's quite common on some systems to have more CPUs enlisted as "possible", than there are (and could ever be) present/online CPUs. In such cases, perf_buffer creationg will fail due to inability to create perf event on missing CPU with error like this:
libbpf: failed to open perf buffer event on cpu #16: No such device
This patch fixes the logic of perf_buffer__new() to ignore CPUs that are missing or currently offline. In rare cases where user explicitly listed specific CPUs to connect to, behavior is unchanged: libbpf will try to open perf event buffer on specified CPU(s) anyways.
--- tools/lib/bpf/libbpf.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-)
tools/lib/bpf/libbpf.c +25 -7
@@ -5945,7 +5945,7 @@ struct perf_buffer {size_t mmap_size;struct perf_cpu_buf **cpu_bufs;struct epoll_event *events; -int cpu_cnt; +int cpu_cnt;/* number of allocated CPU buffers */ int epoll_fd;/* perf event FD */ int map_fd;/* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */ };
@@ -6079,11 +6079,13 @@ perf_buffer__new_raw(int map_fd, size_tstatic struct perf_buffer *__perf_buffer__new (int map_fd,size_t page_cnt,struct perf_buffer_params *p) { +const char *online_cpus_file ="/sys/devices/system/cpu/online" ;struct bpf_map_info map = {};char msg[STRERR_BUFSIZE ];struct perf_buffer *pb; +bool *online =NULL ;__u32 map_info_len; -int err, i; +int err, i, j, n;if (page_cnt & (page_cnt -1 )) { pr_warn("page count should be power of two, but is %zu\n",
@@ -6152,20 +6154,32 @@ static struct perf_buffer *__perf_buffer goto error; } -for (i =0 ; i < pb->cpu_cnt; i++) { + err =parse_cpu_mask_file (online_cpus_file, &online, &n); +if (err) { +pr_warn ("failed to get online CPU mask: %d\n" , err); + goto error; + } + +for (i =0 , j =0 ; i < pb->cpu_cnt; i++) {struct perf_cpu_buf *cpu_buf;int cpu, map_key; cpu = p->cpu_cnt >0 ? p->cpus[i] : i; map_key = p->cpu_cnt >0 ? p->map_keys[i] : i; +/* in case user didn't explicitly requested particular CPUs to +* be attached to, skip offline/not present CPUs +*/ +if (p->cpu_cnt <=0 && (cpu >= n || !online[cpu])) +continue ; + cpu_buf =perf_buffer__open_cpu_buf (pb, p->attr, cpu, map_key);if (IS_ERR (cpu_buf)) { err =PTR_ERR (cpu_buf); goto error; } - pb->cpu_bufs[i] = cpu_buf; + pb->cpu_bufs[j] = cpu_buf; err =bpf_map_update_elem (pb->map_fd, &map_key, &cpu_buf->fd,0 );
@@ -6177,21 +6191,25 @@ static struct perf_buffer *__perf_buffer goto error; } - pb->events[i].events =EPOLLIN ; - pb->events[i].data.ptr = cpu_buf; + pb->events[j].events =EPOLLIN ; + pb->events[j].data.ptr = cpu_buf;if (epoll_ctl (pb->epoll_fd,EPOLL_CTL_ADD , cpu_buf->fd, - &pb->events[i]) <0 ) { + &pb->events[j]) <0 ) { err = -errno;pr_warn ("failed to epoll_ctl cpu #%d perf FD %d: %s\n" , cpu, cpu_buf->fd,libbpf_strerror_r (err, msg,sizeof (msg))); goto error; } + j++; } + pb->cpu_cnt = j; +free (online);return pb; error: +free (online);if (pb)perf_buffer__free (pb);return ERR_PTR (err);
Greg Kroah-Hartman
maintainerPATCH 161/367 selftests/bpf: Fix perf_buffer test on systems w/ offline CPUs
In reply to Greg Kroah-HartmanFrom: Andrii Nakryiko <[email protected]>
commit 91cbdf740a476cf2c744169bf407de2e3ac1f3cf upstream.
Fix up perf_buffer.c selftest to take into account offline/missing CPUs.
--- tools/testing/selftests/bpf/prog_tests/perf_buffer.c | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-)
tools/testing/selftests/bpf/prog_tests/perf_buffer.c +24 -5
@@ -4,6 +4,7 @@#include <sched.h> #include <sys/socket.h> #include <test_progs.h> +#include "libbpf_internal.h" static void on_sample (void *ctx,int cpu,void *data,__u32 size) {
@@ -19,7 +20,7 @@ static void on_sample(void *ctx, int cpuvoid test_perf_buffer (void ) { -int err, prog_fd, nr_cpus, i, duration =0 ; +int err, prog_fd, on_len, nr_on_cpus =0 , nr_cpus, i, duration =0 ;const char *prog_name ="kprobe/sys_nanosleep" ;const char *file ="./test_perf_buffer.o" ;struct perf_buffer_opts pb_opts = {};
@@ -29,15 +30,27 @@ void test_perf_buffer(void)struct bpf_object *obj;struct perf_buffer *pb;struct bpf_link *link; +bool *online; nr_cpus =libbpf_num_possible_cpus ();if (CHECK (nr_cpus <0 ,"nr_cpus" ,"err %d\n" , nr_cpus))return ; + err =parse_cpu_mask_file ("/sys/devices/system/cpu/online" , + &online, &on_len); +if (CHECK (err,"nr_on_cpus" ,"err %d\n" , err)) +return ; + +for (i =0 ; i < on_len; i++) +if (online[i]) + nr_on_cpus++; +/* load program */ err =bpf_prog_load (file,BPF_PROG_TYPE_KPROBE , &obj, &prog_fd); -if (CHECK (err,"obj_load" ,"err %d errno %d\n" , err, errno)) -return ; +if (CHECK (err,"obj_load" ,"err %d errno %d\n" , err, errno)) { + obj =NULL ; + goto out_close; + } prog =bpf_object__find_program_by_title (obj, prog_name);if (CHECK (!prog,"find_probe" ,"prog '%s' not found\n" , prog_name))
@@ -64,6 +77,11 @@ void test_perf_buffer(void)/* trigger kprobe on every CPU */ CPU_ZERO (&cpu_seen);for (i =0 ; i < nr_cpus; i++) { +if (i >= on_len || !online[i]) { +printf ("skipping offline CPU #%d\n" , i); +continue ; + } +CPU_ZERO (&cpu_set);CPU_SET (i, &cpu_set);
@@ -81,8 +99,8 @@ void test_perf_buffer(void)if (CHECK (err <0 ,"perf_buffer__poll" ,"err %d\n" , err)) goto out_free_pb; -if (CHECK (CPU_COUNT (&cpu_seen) != nr_cpus,"seen_cpu_cnt" , -"expect %d, seen %d\n" , nr_cpus,CPU_COUNT (&cpu_seen))) +if (CHECK (CPU_COUNT (&cpu_seen) != nr_on_cpus,"seen_cpu_cnt" , +"expect %d, seen %d\n" , nr_on_cpus,CPU_COUNT (&cpu_seen))) goto out_free_pb; out_free_pb:
@@ -91,4 +109,5 @@ out_detach:bpf_link__destroy (link); out_close:bpf_object__close (obj); +free (online); }
Greg Kroah-Hartman
maintainerPATCH 162/367 flow_dissector: Fix to use new variables for port ranges in bpf hook
In reply to Greg Kroah-HartmanFrom: Yoshiki Komachi <[email protected]>
commit 59fb9b62fb6c929a756563152a89f39b07cf8893 upstream.
This patch applies new flag (FLOW_DISSECTOR_KEY_PORTS_RANGE) and field (tp_range) to BPF flow dissector to generate appropriate flow keys when classified by specified port ranges.
--- net/core/flow_dissector.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
net/core/flow_dissector.c +9 -2
@@ -834,10 +834,10 @@ static void __skb_flow_bpf_to_target(construct flow_dissector *flow_dissector, void *target_container) { + struct flow_dissector_key_ports *key_ports = NULL;struct flow_dissector_key_control *key_control;struct flow_dissector_key_basic *key_basic;struct flow_dissector_key_addrs *key_addrs; -struct flow_dissector_key_ports *key_ports;struct flow_dissector_key_tags *key_tags; key_control = skb_flow_dissector_target(flow_dissector,
@@ -876,10 +876,17 @@ static void __skb_flow_bpf_to_target(con key_control->addr_type =FLOW_DISSECTOR_KEY_IPV6_ADDRS ; } -if (dissector_uses_key (flow_dissector,FLOW_DISSECTOR_KEY_PORTS )) { +if (dissector_uses_key (flow_dissector,FLOW_DISSECTOR_KEY_PORTS )) key_ports =skb_flow_dissector_target (flow_dissector,FLOW_DISSECTOR_KEY_PORTS , target_container); +else if (dissector_uses_key (flow_dissector, +FLOW_DISSECTOR_KEY_PORTS_RANGE )) + key_ports =skb_flow_dissector_target (flow_dissector, +FLOW_DISSECTOR_KEY_PORTS_RANGE , + target_container); + +if (key_ports) { key_ports->src = flow_keys->sport; key_ports->dst = flow_keys->dport; }
Greg Kroah-Hartman
maintainerPATCH 163/367 bpf, devmap: Pass lockdep expression to RCU lists
In reply to Greg Kroah-HartmanFrom: Amol Grover <[email protected]>
commit 485ec2ea9cf556e9c120e07961b7b459d776a115 upstream.
head is traversed using hlist_for_each_entry_rcu outside an RCU read-side critical section but under the protection of dtab->index_lock.
Hence, add corresponding lockdep expression to silence false-positive lockdep warnings, and harden RCU lists.
--- kernel/bpf/devmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/bpf/devmap.c +2 -1
@@ -293,7 +293,8 @@ struct bpf_dtab_netdev *__dev_map_hash_lstruct hlist_head *head =dev_map_index_hash (dtab, key);struct bpf_dtab_netdev *dev; -hlist_for_each_entry_rcu (dev, head, index_hlist) +hlist_for_each_entry_rcu (dev, head, index_hlist, +lockdep_is_held (&dtab->index_lock))if (dev->idx == key)return dev;
Greg Kroah-Hartman
maintainerPATCH 164/367 libbpf: Add missing newline in opts validation macro
In reply to Greg Kroah-HartmanFrom: Toke Høiland-Jørgensen <[email protected]>
commit 12dd14b230b3c742b80272ecb8a83cdf824625ca upstream.
The error log output in the opts validation macro was missing a newline.
--- tools/lib/bpf/libbpf_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/lib/bpf/libbpf_internal.h +1 -1
@@ -76,7 +76,7 @@ static inline bool libbpf_validate_opts(for (i = opts_sz; i < user_sz; i++) {if (opts[i]) { -pr_warn ("%s has non-zero extra bytes" , +pr_warn ("%s has non-zero extra bytes\n" , type_name);return false; }
Greg Kroah-Hartman
maintainerPATCH 165/367 libbpf: Fix realloc usage in bpf_core_find_cands
In reply to Greg Kroah-HartmanFrom: Andrii Nakryiko <[email protected]>
commit 35b9211c0a2427e8f39e534f442f43804fc8d5ca upstream.
Fix bug requesting invalid size of reallocated array when constructing CO-RE relocation candidate list. This can cause problems if there are many potential candidates and a very fine-grained memory allocator bucket sizes are used.
--- tools/lib/bpf/libbpf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
tools/lib/bpf/libbpf.c +3 -1
@@ -2744,7 +2744,9 @@ static struct ids_vec *bpf_core_find_canif (strncmp (local_name, targ_name, local_essent_len) ==0 ) {pr_debug ("[%d] %s: found candidate [%d] %s\n" , local_type_id, local_name, i, targ_name); - new_ids =realloc (cand_ids->data, cand_ids->len +1 ); + new_ids =reallocarray (cand_ids->data, + cand_ids->len +1 , +sizeof (*cand_ids->data));if (!new_ids) { err = -ENOMEM ; goto err_out;
Greg Kroah-Hartman
maintainerPATCH 166/367 tc-testing: fix eBPF tests failure on linux fresh clones
In reply to Greg Kroah-HartmanFrom: Davide Caratti <[email protected]>
commit 7145fcfffef1fad4266aaf5ca96727696916edb7 upstream.
when the following command is done on a fresh clone of the kernel tree,
[root@f31 tc-testing]# ./tdc.py -c bpf
test cases that need to build the eBPF sample program fail systematically, because 'buildebpfPlugin' is unable to install the kernel headers (i.e, the 'khdr' target fails). Pass the correct environment to 'make', in place of ENVIR, to allow running these tests.
--- tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py +1 -1
@@ -54,7 +54,7 @@ class SubPlugin(TdcPlugin): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=ENVIR) + env=os.environ.copy()) (rawout, serr) = proc.communicate() if proc.returncode != 0 and len(serr) > 0:
Greg Kroah-Hartman
maintainerPATCH 167/367 samples/bpf: Dont try to remove users homedir on clean
In reply to Greg Kroah-HartmanFrom: Toke Høiland-Jørgensen <[email protected]>
commit b2e5e93ae8af6a34bca536cdc4b453ab1e707b8b upstream.
The 'clean' rule in the samples/bpf Makefile tries to remove backup files (ending in ~). However, if no such files exist, it will instead try to remove the user's home directory. While the attempt is mostly harmless, it does lead to a somewhat scary warning like this:
rm: cannot remove '~': Is a directory
Fix this by using find instead of shell expansion to locate any actual backup files that need to be removed.
--- samples/bpf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
samples/bpf/Makefile +1 -1
@@ -251,7 +251,7 @@ all: clean: $(MAKE) -C ../../ M=$(CURDIR) clean - @rm -f *~ + @find $(CURDIR) -type f -name '*~' -delete $(LIBBPF): FORCE # Fix up variables inherited from Kbuild that tools/ build system won't like
Greg Kroah-Hartman
maintainerPATCH 168/367 samples/bpf: Xdp_redirect_cpu fix missing tracepoint attach
In reply to Greg Kroah-HartmanFrom: Jesper Dangaard Brouer <[email protected]>
commit f9e6bfdbaf0cf304d72c70a05d81acac01a04f48 upstream.
When sample xdp_redirect_cpu was converted to use libbpf, the tracepoints used by this sample were not getting attached automatically like with bpf_load.c. The BPF-maps was still getting loaded, thus nobody notice that the tracepoints were not updating these maps.
This fix doesn't use the new skeleton code, as this bug was introduced in v5.1 and stable might want to backport this. E.g. Red Hat QA uses this sample as part of their testing.
--- samples/bpf/xdp_redirect_cpu_user.c | 59 +++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-)
samples/bpf/xdp_redirect_cpu_user.c +55 -4
@@ -16,6 +16,10 @@ static const char *__doc__ =#include <getopt.h> #include <net/if.h> #include <time.h> +#include <linux/limits.h> + +#define __must_check +#include <linux/err.h> #include <arpa/inet.h> #include <linux/if_link.h>
@@ -46,6 +50,10 @@ static int cpus_count_map_fd;static int cpus_iterator_map_fd;static int exception_cnt_map_fd; +#define NUM_TP 5 +struct bpf_link *tp_links[NUM_TP ] = {0 }; +static int tp_cnt =0 ; +/* Exit return codes */ #define EXIT_OK 0#define EXIT_FAIL 1
@@ -88,6 +96,10 @@ static void int_exit(int sig)printf ("program on interface changed, not removing\n" ); } } +/* Detach tracepoints */ +while (tp_cnt) +bpf_link__destroy (tp_links[--tp_cnt]); +exit (EXIT_OK ); }
@@ -588,23 +600,61 @@ static void stats_poll(int interval, boofree_stats_record (prev); } +static struct bpf_link *attach_tp (struct bpf_object *obj, +const char *tp_category, +const char * tp_name) +{ +struct bpf_program *prog; +struct bpf_link *link; +char sec_name[PATH_MAX ]; +int len; + + len =snprintf (sec_name,PATH_MAX ,"tracepoint/%s/%s" , + tp_category, tp_name); +if (len <0 ) +exit (EXIT_FAIL ); + + prog =bpf_object__find_program_by_title (obj, sec_name); +if (!prog) { +fprintf (stderr,"ERR: finding progsec: %s\n" , sec_name); +exit (EXIT_FAIL_BPF ); + } + + link =bpf_program__attach_tracepoint (prog, tp_category, tp_name); +if (IS_ERR (link)) +exit (EXIT_FAIL_BPF ); + +return link; +} + +static void init_tracepoints (struct bpf_object *obj) { + tp_links[tp_cnt++] =attach_tp (obj,"xdp" ,"xdp_redirect_err" ); + tp_links[tp_cnt++] =attach_tp (obj,"xdp" ,"xdp_redirect_map_err" ); + tp_links[tp_cnt++] =attach_tp (obj,"xdp" ,"xdp_exception" ); + tp_links[tp_cnt++] =attach_tp (obj,"xdp" ,"xdp_cpumap_enqueue" ); + tp_links[tp_cnt++] =attach_tp (obj,"xdp" ,"xdp_cpumap_kthread" ); +} +static int init_map_fds (struct bpf_object *obj) { - cpu_map_fd =bpf_object__find_map_fd_by_name (obj,"cpu_map" ); - rx_cnt_map_fd =bpf_object__find_map_fd_by_name (obj,"rx_cnt" ); +/* Maps updated by tracepoints */ redirect_err_cnt_map_fd =bpf_object__find_map_fd_by_name (obj,"redirect_err_cnt" ); + exception_cnt_map_fd = +bpf_object__find_map_fd_by_name (obj,"exception_cnt" ); cpumap_enqueue_cnt_map_fd =bpf_object__find_map_fd_by_name (obj,"cpumap_enqueue_cnt" ); cpumap_kthread_cnt_map_fd =bpf_object__find_map_fd_by_name (obj,"cpumap_kthread_cnt" ); + +/* Maps used by XDP */ + rx_cnt_map_fd =bpf_object__find_map_fd_by_name (obj,"rx_cnt" ); + cpu_map_fd =bpf_object__find_map_fd_by_name (obj,"cpu_map" ); cpus_available_map_fd =bpf_object__find_map_fd_by_name (obj,"cpus_available" ); cpus_count_map_fd =bpf_object__find_map_fd_by_name (obj,"cpus_count" ); cpus_iterator_map_fd =bpf_object__find_map_fd_by_name (obj,"cpus_iterator" ); - exception_cnt_map_fd = -bpf_object__find_map_fd_by_name (obj,"exception_cnt" ); if (cpu_map_fd < 0 || rx_cnt_map_fd < 0 || redirect_err_cnt_map_fd < 0 || cpumap_enqueue_cnt_map_fd < 0 ||
@@ -662,6 +712,7 @@ int main(int argc, char **argv) strerror(errno));return EXIT_FAIL ; } +init_tracepoints (obj);if (init_map_fds (obj) <0 ) {fprintf (stderr,"bpf_object__find_map_fd_by_name failed\n" );return EXIT_FAIL ;
Greg Kroah-Hartman
maintainerPATCH 169/367 samples/bpf: Reintroduce missed build targets
In reply to Greg Kroah-HartmanFrom: Prashant Bhole <[email protected]>
commit 5984dc6cb5aa6cce342a44f01f984fde09ed05b1 upstream.
Add xdp_redirect and per_socket_stats_example in build targets. They got removed from build targets in Makefile reorganization.
--- samples/bpf/Makefile | 2 ++ 1 file changed, 2 insertions(+)
samples/bpf/Makefile +2 -0
@@ -38,6 +38,8 @@ tprogs-y += tc_l2_redirect tprogs-y += lwt_len_hist tprogs-y += xdp_tx_iptunnel tprogs-y += test_map_in_map +tprogs-y += per_socket_stats_example +tprogs-y += xdp_redirect tprogs-y += xdp_redirect_map tprogs-y += xdp_redirect_cpu tprogs-y += xdp_monitor
Greg Kroah-Hartman
maintainerPATCH 170/367 selftests/bpf: Fix test_attach_probe
In reply to Greg Kroah-HartmanFrom: Alexei Starovoitov <[email protected]>
commit 580205dd4fe800b1e95be8b6df9e2991f975a8ad upstream.
Fix two issues in test_attach_probe:
1. it was not able to parse /proc/self/maps beyond the first line, since %s means parse string until white space.
2. offset has to be accounted for otherwise uprobed address is incorrect.
--- tools/testing/selftests/bpf/prog_tests/attach_probe.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
tools/testing/selftests/bpf/prog_tests/attach_probe.c +4 -3
@@ -20,7 +20,7 @@ extern char NAME##_data[]; \ extern int NAME##_size;ssize_t get_base_addr () { -size_t start; +size_t start, offset;char buf[256 ];FILE *f;
@@ -28,10 +28,11 @@ ssize_t get_base_addr() {if (!f)return -errno; -while (fscanf (f,"%zx-%*x %s %*s\n" , &start, buf) ==2 ) { +while (fscanf (f,"%zx-%*x %s %zx %*[^\n]\n" , + &start, buf, &offset) ==3 ) {if (strcmp (buf,"r-xp" ) ==0 ) {fclose (f); -return start; +return start - offset; } }
Greg Kroah-Hartman
maintainerPATCH 171/367 selftests/bpf: Skip perf hw events test if the setup disabled it
In reply to Greg Kroah-HartmanFrom: Hangbin Liu <[email protected]>
commit f1c3656c6d9c147d07d16614455aceb34932bdeb upstream.
The same with commit 4e59afbbed96 ("selftests/bpf: skip nmi test when perf hw events are disabled"), it would make more sense to skip the test_stacktrace_build_id_nmi test if the setup (e.g. virtual machines) has disabled hardware perf events.
--- tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c +6 -2
@@ -49,8 +49,12 @@ retry: pmu_fd =syscall (__NR_perf_event_open, &attr,-1 /* pid */ ,0 /* cpu 0 */ ,-1 /* group id */ ,0 /* flags */ ); -if (CHECK (pmu_fd <0 ,"perf_event_open" , -"err %d errno %d. Does the test host support PERF_COUNT_HW_CPU_CYCLES?\n" , +if (pmu_fd <0 && errno ==ENOENT ) { +printf ("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n" , __func__); +test__skip (); + goto cleanup; + } +if (CHECK (pmu_fd <0 ,"perf_event_open" ,"err %d errno %d\n" , pmu_fd, errno)) goto close_prog;
Greg Kroah-Hartman
maintainerPATCH 172/367 selftests: bpf: Use a temporary file in test_sockmap
In reply to Greg Kroah-HartmanFrom: Lorenz Bauer <[email protected]>
commit c31dbb1e41d1857b403f9bf58c87f5898519a0bc upstream.
Use a proper temporary file for sendpage tests. This means that running the tests doesn't clutter the working directory, and allows running the test on read-only filesystems.
--- tools/testing/selftests/bpf/test_sockmap.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-)
tools/testing/selftests/bpf/test_sockmap.c +5 -10
@@ -331,7 +331,7 @@ static int msg_loop_sendpage(int fd, intFILE *file;int i, fp; - file =fopen (".sendpage_tst.tmp" ,"w+" ); + file =tmpfile ();if (!file) {perror ("create file for sendpage" );return 1 ;
@@ -340,13 +340,8 @@ static int msg_loop_sendpage(int fd, intfwrite (&k,sizeof (char ),1 , file);fflush (file);fseek (file,0 ,SEEK_SET ); -fclose (file); - fp =open (".sendpage_tst.tmp" ,O_RDONLY ); -if (fp <0 ) { -perror ("reopen file for sendpage" ); -return 1 ; - } + fp =fileno (file);clock_gettime (CLOCK_MONOTONIC , &s->start);for (i =0 ; i < cnt; i++) {
@@ -354,11 +349,11 @@ static int msg_loop_sendpage(int fd, intif (!drop && sent <0 ) {perror ("send loop error" ); -close (fp); +fclose (file);return sent; }else if (drop && sent >=0 ) {printf ("sendpage loop error expected: %i\n" , sent); -close (fp); +fclose (file);return -EIO ; }
@@ -366,7 +361,7 @@ static int msg_loop_sendpage(int fd, int s->bytes_sent += sent; }clock_gettime (CLOCK_MONOTONIC , &s->end); -close (fp); +fclose (file);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 173/367 selftests: bpf: Ignore FIN packets for reuseport tests
In reply to Greg Kroah-HartmanFrom: Lorenz Bauer <[email protected]>
commit 8bec4f665e0baecb5f1b683379fc10b3745eb612 upstream.
The reuseport tests currently suffer from a race condition: FIN packets count towards DROP_ERR_SKB_DATA, since they don't contain a valid struct cmd. Tests will spuriously fail depending on whether check_results is called before or after the FIN is processed.
Exit the BPF program early if FIN is set.
--- tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c | 6 ++++++ 1 file changed, 6 insertions(+)
tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c +6 -0
@@ -113,6 +113,12 @@ int _select_by_skb_data(struct sk_reusep data_check.skb_ports[0 ] = th->source; data_check.skb_ports[1 ] = th->dest; +if (th->fin) +/* The connection is being torn down at the end of a +* test. It can't contain a cmd, so return early. +*/ +return SK_PASS ; +if ((th->doff <<2 ) +sizeof (*cmd) > data_check.len)GOTO_DONE (DROP_ERR_SKB_DATA ); if (bpf_skb_load_bytes(reuse_md, th->doff << 2, &cmd_copy,
Greg Kroah-Hartman
maintainerPATCH 174/367 crypto: sun8i-ss - fix removal of module
In reply to Greg Kroah-HartmanFrom: Corentin Labbe <[email protected]>
commit 7b3d853ead8187288bf99df38ed71ee02773a65f upstream.
Removing the driver cause an oops due to the fact we clean an extra channel.
Let's give the right index to the cleaning function.
--- drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c +2 -2
@@ -595,7 +595,7 @@ error_alg: error_irq:sun8i_ss_pm_exit (ss); error_pm: -sun8i_ss_free_flows (ss,MAXFLOW ); +sun8i_ss_free_flows (ss,MAXFLOW -1 );return err; }
@@ -609,7 +609,7 @@ static int sun8i_ss_remove(struct platfodebugfs_remove_recursive (ss->dbgfs_dir);#endif -sun8i_ss_free_flows (ss,MAXFLOW ); +sun8i_ss_free_flows (ss,MAXFLOW -1 );sun8i_ss_pm_exit (ss);
Greg Kroah-Hartman
maintainerPATCH 175/367 crypto: amlogic - fix removal of module
In reply to Greg Kroah-HartmanFrom: Corentin Labbe <[email protected]>
commit 24775ac2fe68132d3e0e7cd3a0521ccb1a5d7243 upstream.
Removing the driver cause an oops due to the fact we clean an extra channel.
Let's give the right index to the cleaning function.
--- drivers/crypto/amlogic/amlogic-gxl-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/crypto/amlogic/amlogic-gxl-core.c +2 -2
@@ -289,7 +289,7 @@ static int meson_crypto_probe(struct pla error_alg:meson_unregister_algs (mc); error_flow: -meson_free_chanlist (mc,MAXFLOW ); +meson_free_chanlist (mc,MAXFLOW -1 );clk_disable_unprepare (mc->busclk);return err; }
@@ -304,7 +304,7 @@ static int meson_crypto_remove(struct plmeson_unregister_algs (mc); -meson_free_chanlist (mc,MAXFLOW ); +meson_free_chanlist (mc,MAXFLOW -1 );clk_disable_unprepare (mc->busclk);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 176/367 crypto: sun8i-ce - fix removal of module
In reply to Greg Kroah-HartmanFrom: Corentin Labbe <[email protected]>
commit 93d24ac4b26770f8e5118a731cd9314f3808bd10 upstream.
Removing the driver cause an oops due to the fact we clean an extra channel.
Let's give the right index to the cleaning function.
--- drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c +2 -2
@@ -624,7 +624,7 @@ error_alg: error_irq:sun8i_ce_pm_exit (ce); error_pm: -sun8i_ce_free_chanlist (ce,MAXFLOW ); +sun8i_ce_free_chanlist (ce,MAXFLOW -1 );return err; }
@@ -638,7 +638,7 @@ static int sun8i_ce_remove(struct platfodebugfs_remove_recursive (ce->dbgfs_dir);#endif -sun8i_ce_free_chanlist (ce,MAXFLOW ); +sun8i_ce_free_chanlist (ce,MAXFLOW -1 );sun8i_ce_pm_exit (ce);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 177/367 crypto: api - fix unexpectedly getting generic implementation
In reply to Greg Kroah-HartmanFrom: Herbert Xu <[email protected]>
commit 2bbb3375d967155bccc86a5887d4a6e29c56b683 upstream.
When CONFIG_CRYPTO_MANAGER_EXTRA_TESTS=y, the first lookup of an algorithm that needs to be instantiated using a template will always get the generic implementation, even when an accelerated one is available.
This happens because the extra self-tests for the accelerated implementation allocate the generic implementation for comparison purposes, and then crypto_alg_tested() for the generic implementation "fulfills" the original request (i.e. sets crypto_larval::adult).
This patch fixes this by only fulfilling the original request if we are currently the best outstanding larval as judged by the priority. If we're not the best then we will ask all waiters on that larval request to retry the lookup.
Note that this patch introduces a behaviour change when the module providing the new algorithm is unregistered during the process. Previously we would have failed with ENOENT, after the patch we will instead redo the lookup.
--- crypto/algapi.c | 24 +++++++++++++++++++++--- crypto/api.c | 4 +++- 2 files changed, 24 insertions(+), 4 deletions(-)
crypto/algapi.c +21 -3
@@ -257,6 +257,7 @@ void crypto_alg_tested(const char *name,struct crypto_alg *alg;struct crypto_alg *q;LIST_HEAD (list); +bool best;down_write (&crypto_alg_sem);list_for_each_entry (q, &crypto_alg_list, cra_list) {
@@ -280,6 +281,21 @@ found: alg->cra_flags |=CRYPTO_ALG_TESTED ; +/* Only satisfy larval waiters if we are the best. */ + best = true; +list_for_each_entry (q, &crypto_alg_list, cra_list) { +if (crypto_is_moribund (q) || !crypto_is_larval (q)) +continue ; + +if (strcmp (alg->cra_name, q->cra_name)) +continue ; + +if (q->cra_priority > alg->cra_priority) { + best = false; +break ; + } + } +list_for_each_entry (q, &crypto_alg_list, cra_list) {if (q == alg)continue ;
@@ -303,10 +319,12 @@ found:continue ;if ((q->cra_flags ^ alg->cra_flags) & larval->mask)continue ; -if (!crypto_mod_get (alg)) -continue ; - larval->adult = alg; +if (best &&crypto_mod_get (alg)) + larval->adult = alg; +else + larval->adult =ERR_PTR (-EAGAIN ); +continue ; }
crypto/api.c +3 -1
@@ -97,7 +97,7 @@ static void crypto_larval_destroy(structstruct crypto_larval *larval = (void *)alg;BUG_ON (!crypto_is_larval (alg)); -if (larval->adult) +if (!IS_ERR_OR_NULL (larval->adult))crypto_mod_put (larval->adult);kfree (larval); }
@@ -178,6 +178,8 @@ static struct crypto_alg *crypto_larval_ alg =ERR_PTR (-ETIMEDOUT ); else if (!alg) alg =ERR_PTR (-ENOENT ); + else if (IS_ERR(alg)) + ; else if (crypto_is_test_larval(larval) && !(alg->cra_flags & CRYPTO_ALG_TESTED)) alg =ERR_PTR (-EAGAIN );
Greg Kroah-Hartman
maintainerPATCH 178/367 crypto: hisilicon - Fix issue with wrong number of sg elements after dma map
In reply to Greg Kroah-HartmanFrom: Jonathan Cameron <[email protected]>
commit 8debacd60c69beab80736d4af4feca47c2e2bd9e upstream.
We fill the hardware scatter gather list assuming it will need the same number of elements at the original scatterlist. If an IOMMU is involved, then it may well need fewer. The return value of dma_map_sg tells us how many.
Probably never caused visible problems as the hardware won't get to the elements that are incorrect before it finds enough space.
--- drivers/crypto/hisilicon/sgl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/crypto/hisilicon/sgl.c +8 -5
@@ -202,18 +202,21 @@ hisi_acc_sg_buf_map_to_hw_sgl(struct devdma_addr_t curr_sgl_dma =0 ;struct acc_hw_sge *curr_hw_sge;struct scatterlist *sg; -int i, ret, sg_n; +int i, sg_n, sg_n_mapped;if (!dev || !sgl || !pool || !hw_sgl_dma)return ERR_PTR (-EINVAL ); sg_n =sg_nents (sgl); -if (sg_n > pool->sge_nr) + + sg_n_mapped =dma_map_sg (dev, sgl, sg_n,DMA_BIDIRECTIONAL ); +if (!sg_n_mapped)return ERR_PTR (-EINVAL ); - ret =dma_map_sg (dev, sgl, sg_n,DMA_BIDIRECTIONAL ); -if (!ret) +if (sg_n_mapped > pool->sge_nr) { +dma_unmap_sg (dev, sgl, sg_n,DMA_BIDIRECTIONAL );return ERR_PTR (-EINVAL ); + } curr_hw_sgl =acc_get_sgl (pool, index, &curr_sgl_dma);if (IS_ERR (curr_hw_sgl)) {
@@ -224,7 +227,7 @@ hisi_acc_sg_buf_map_to_hw_sgl(struct dev curr_hw_sgl->entry_length_in_sgl =cpu_to_le16 (pool->sge_nr); curr_hw_sge = curr_hw_sgl->sge_entries; -for_each_sg (sgl, sg, sg_n, i) { +for_each_sg (sgl, sg, sg_n_mapped, i) {sg_map_to_hw_sg (sg, curr_hw_sge);inc_hw_sgl_sge (curr_hw_sgl); curr_hw_sge++;
Greg Kroah-Hartman
maintainerPATCH 179/367 crypto: hisilicon - Use the offset fields in sqe to avoid need to split scatterlists
In reply to Greg Kroah-HartmanFrom: Jonathan Cameron <[email protected]>
commit 484a897ffa3005f16cd9a31efd747bcf8155826f upstream.
We can configure sgl offset fields in ZIP sqe to let ZIP engine read/write sgl data with skipped data. Hence no need to splite the sgl.
--- drivers/crypto/hisilicon/Kconfig | 1 drivers/crypto/hisilicon/zip/zip.h | 4 + drivers/crypto/hisilicon/zip/zip_crypto.c | 92 +++++++----------------------- 3 files changed, 27 insertions(+), 70 deletions(-)
drivers/crypto/hisilicon/Kconfig +0 -1
@@ -44,7 +44,6 @@ config CRYPTO_DEV_HISI_ZIP depends on ARM64 || (COMPILE_TEST && 64BIT) depends on !CPU_BIG_ENDIAN || COMPILE_TEST select CRYPTO_DEV_HISI_QM - select SG_SPLIT help Support for HiSilicon ZIP Driver
drivers/crypto/hisilicon/zip/zip.h +4 -0
@@ -11,6 +11,10 @@/* hisi_zip_sqe dw3 */ #define HZIP_BD_STATUS_M GENMASK(7, 0) +/* hisi_zip_sqe dw7 */ +#define HZIP_IN_SGE_DATA_OFFSET_M GENMASK(23, 0) +/* hisi_zip_sqe dw8 */ +#define HZIP_OUT_SGE_DATA_OFFSET_M GENMASK(23, 0)/* hisi_zip_sqe dw9 */ #define HZIP_REQ_TYPE_M GENMASK(7, 0)#define HZIP_ALG_TYPE_ZLIB 0x02
drivers/crypto/hisilicon/zip/zip_crypto.c +23 -69
178 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 180/367 crypto: ccp - set max RSA modulus size for v3 platform devices as well
In reply to Greg Kroah-HartmanFrom: Ard Biesheuvel <[email protected]>
commit 11548f5a5747813ff84bed6f2ea01100053b0d8d upstream.
AMD Seattle incorporates a non-PCI version of the v3 CCP crypto accelerator, and this version was left behind when the maximum RSA modulus size was parameterized in order to support v5 hardware which supports larger moduli than v3 hardware does. Due to this oversight, RSA acceleration no longer works at all on these systems.
Fix this by setting the .rsamax property to the appropriate value for v3 platform hardware.
--- drivers/crypto/ccp/ccp-dev-v3.c | 1 + 1 file changed, 1 insertion(+)
drivers/crypto/ccp/ccp-dev-v3.c +1 -0
@@ -586,6 +586,7 @@ const struct ccp_vdata ccpv3_platform = .setup = NULL, .perform = &ccp3_actions, .offset = 0, + .rsamax = CCP_RSA_MAX_WIDTH, };const struct ccp_vdata ccpv3 = {
Greg Kroah-Hartman
maintainerPATCH 181/367 crypto: arm64/ghash-neon - bump priority to 150
In reply to Greg Kroah-HartmanFrom: Ard Biesheuvel <[email protected]>
commit 5441c6507bc84166e9227e9370a56c57ba13794a upstream.
The SIMD based GHASH implementation for arm64 is typically much faster than the generic one, and doesn't use any lookup tables, so it is clearly preferred when available. So bump the priority to reflect that.
--- arch/arm64/crypto/ghash-ce-glue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/crypto/ghash-ce-glue.c +1 -1
@@ -259,7 +259,7 @@ static int ghash_setkey(struct crypto_shstatic struct shash_alg ghash_alg[] = {{ .base.cra_name ="ghash" , .base.cra_driver_name ="ghash-neon" , - .base.cra_priority =100 , + .base.cra_priority =150 , .base.cra_blocksize =GHASH_BLOCK_SIZE , .base.cra_ctxsize =sizeof (struct ghash_key ), .base.cra_module =THIS_MODULE ,
Greg Kroah-Hartman
maintainerPATCH 182/367 crypto: pcrypt - Do not clear MAY_SLEEP flag in original request
In reply to Greg Kroah-HartmanFrom: Herbert Xu <[email protected]>
commit e8d998264bffade3cfe0536559f712ab9058d654 upstream.
We should not be modifying the original request's MAY_SLEEP flag upon completion. It makes no sense to do so anyway.
--- crypto/pcrypt.c | 1 - 1 file changed, 1 deletion(-)
crypto/pcrypt.c +0 -1
@@ -71,7 +71,6 @@ static void pcrypt_aead_done(struct crypstruct padata_priv *padata =pcrypt_request_padata (preq); padata->info = err; - req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP ;padata_do_serial (padata); }
Greg Kroah-Hartman
maintainerPATCH 183/367 crypto: hisilicon - select CRYPTO_SKCIPHER, not CRYPTO_BLKCIPHER
In reply to Greg Kroah-HartmanFrom: Eric Biggers <[email protected]>
commit 8e8c778d9ed4fdc5a9af108c7023bfb640a673f2 upstream.
Another instance of CRYPTO_BLKCIPHER made it in just after it was renamed to CRYPTO_SKCIPHER. Fix it.
--- drivers/crypto/hisilicon/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/crypto/hisilicon/Kconfig +1 -1
@@ -16,7 +16,7 @@ config CRYPTO_DEV_HISI_SEC config CRYPTO_DEV_HISI_SEC2 tristate "Support for HiSilicon SEC2 crypto block cipher accelerator" - select CRYPTO_BLKCIPHER + select CRYPTO_SKCIPHER select CRYPTO_ALGAPI select CRYPTO_LIB_DES select CRYPTO_DEV_HISI_QM
Greg Kroah-Hartman
maintainerPATCH 184/367 crypto: atmel-aes - Fix counter overflow in CTR mode
In reply to Greg Kroah-HartmanFrom: Tudor Ambarus <[email protected]>
commit 781a08d9740afa73357f1a60d45d7c93d7cca2dd upstream.
32 bit counter is not supported by neither of our AES IPs, all implement a 16 bit block counter. Drop the 32 bit block counter logic.
--- drivers/crypto/atmel-aes.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-)
drivers/crypto/atmel-aes.c +12 -25
@@ -89,7 +89,6 @@struct atmel_aes_caps {bool has_dualbuff;bool has_cfb64; -bool has_ctr32;bool has_gcm;bool has_xts;bool has_authenc;
@@ -1015,8 +1014,9 @@ static int atmel_aes_ctr_transfer(structstruct atmel_aes_ctr_ctx *ctx =atmel_aes_ctr_ctx_cast (dd->ctx);struct skcipher_request *req =skcipher_request_cast (dd->areq);struct scatterlist *src, *dst; -u32 ctr, blocks;size_t datalen; +u32 ctr; +u16 blocks, start, end;bool use_dma, fragmented = false;/* Check for transfer completion. */
@@ -1028,27 +1028,17 @@ static int atmel_aes_ctr_transfer(struct datalen = req->cryptlen - ctx->offset; blocks =DIV_ROUND_UP (datalen,AES_BLOCK_SIZE ); ctr =be32_to_cpu (ctx->iv[3 ]); -if (dd->caps.has_ctr32) { -/* Check 32bit counter overflow. */ -u32 start = ctr; -u32 end = start + blocks -1 ; - -if (end < start) { - ctr |=0xffffffff ; - datalen =AES_BLOCK_SIZE * -start; - fragmented = true; - } - }else { -/* Check 16bit counter overflow. */ -u16 start = ctr &0xffff ; -u16 end = start + (u16 )blocks -1 ; - -if (blocks >>16 || end < start) { - ctr |=0xffff ; - datalen =AES_BLOCK_SIZE * (0x10000 -start); - fragmented = true; - } + +/* Check 16bit counter overflow. */ + start = ctr &0xffff ; + end = start + blocks -1 ; + +if (blocks >>16 || end < start) { + ctr |=0xffff ; + datalen =AES_BLOCK_SIZE * (0x10000 - start); + fragmented = true; } + use_dma = (datalen >=ATMEL_AES_DMA_THRESHOLD );/* Jump to offset. */
@@ -2533,7 +2523,6 @@ static void atmel_aes_get_cap(struct atm { dd->caps.has_dualbuff =0 ; dd->caps.has_cfb64 =0 ; - dd->caps.has_ctr32 =0 ; dd->caps.has_gcm =0 ; dd->caps.has_xts =0 ; dd->caps.has_authenc =0 ;
@@ -2544,7 +2533,6 @@ static void atmel_aes_get_cap(struct atmcase 0x500 : dd->caps.has_dualbuff =1 ; dd->caps.has_cfb64 =1 ; - dd->caps.has_ctr32 =1 ; dd->caps.has_gcm =1 ; dd->caps.has_xts =1 ; dd->caps.has_authenc =1 ;
@@ -2553,7 +2541,6 @@ static void atmel_aes_get_cap(struct atmcase 0x200 : dd->caps.has_dualbuff =1 ; dd->caps.has_cfb64 =1 ; - dd->caps.has_ctr32 =1 ; dd->caps.has_gcm =1 ; dd->caps.max_burst_size =4 ;break ;
Greg Kroah-Hartman
maintainerPATCH 185/367 crypto: api - Fix race condition in crypto_spawn_alg
In reply to Greg Kroah-HartmanFrom: Herbert Xu <[email protected]>
commit 73669cc556462f4e50376538d77ee312142e8a8a upstream.
The function crypto_spawn_alg is racy because it drops the lock before shooting the dying algorithm. The algorithm could disappear altogether before we shoot it.
This patch fixes it by moving the shooting into the locked section.
--- crypto/algapi.c | 16 +++++----------- crypto/api.c | 3 +-- crypto/internal.h | 1 - 3 files changed, 6 insertions(+), 14 deletions(-)
crypto/algapi.c +5 -11
@@ -697,22 +697,16 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn);static struct crypto_alg *crypto_spawn_alg (struct crypto_spawn *spawn) {struct crypto_alg *alg; -struct crypto_alg *alg2;down_read (&crypto_alg_sem); alg = spawn->alg; - alg2 = alg; -if (alg2) - alg2 =crypto_mod_get (alg2); -up_read (&crypto_alg_sem); - -if (!alg2) { -if (alg) -crypto_shoot_alg (alg); -return ERR_PTR (-EAGAIN ); +if (alg && !crypto_mod_get (alg)) { + alg->cra_flags |=CRYPTO_ALG_DYING ; + alg =NULL ; } +up_read (&crypto_alg_sem); -return alg; +return alg ?:ERR_PTR (-EAGAIN ); } struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type,
crypto/api.c +1 -2
@@ -346,13 +346,12 @@ static unsigned int crypto_ctxsize(strucreturn len; } -void crypto_shoot_alg (struct crypto_alg *alg) +static void crypto_shoot_alg (struct crypto_alg *alg) {down_write (&crypto_alg_sem); alg->cra_flags |=CRYPTO_ALG_DYING ;up_write (&crypto_alg_sem); } -EXPORT_SYMBOL_GPL (crypto_shoot_alg);struct crypto_tfm *__crypto_alloc_tfm (struct crypto_alg *alg,u32 type,u32 mask)
crypto/internal.h +0 -1
@@ -68,7 +68,6 @@ void crypto_alg_tested(const char *name,void crypto_remove_spawns (struct crypto_alg *alg,struct list_head *list,struct crypto_alg *nalg);void crypto_remove_final (struct list_head *list); -void crypto_shoot_alg (struct crypto_alg *alg);struct crypto_tfm *__crypto_alloc_tfm (struct crypto_alg *alg,u32 type,u32 mask); void *crypto_create_tfm(struct crypto_alg *alg,
Greg Kroah-Hartman
maintainerPATCH 186/367 crypto: picoxcell - adjust the position of tasklet_init and fix missed tasklet_kill
In reply to Greg Kroah-HartmanFrom: Chuhong Yuan <[email protected]>
commit 7f8c36fe9be46862c4f3c5302f769378028a34fa upstream.
Since tasklet is needed to be initialized before registering IRQ handler, adjust the position of tasklet_init to fix the wrong order.
Besides, to fix the missed tasklet_kill, this patch adds a helper function and uses devm_add_action to kill the tasklet automatically.
--- drivers/crypto/picoxcell_crypto.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/crypto/picoxcell_crypto.c +13 -2
@@ -1595,6 +1595,11 @@ static const struct of_device_id spacc_oMODULE_DEVICE_TABLE (of, spacc_of_id_table);#endif /* CONFIG_OF */ +static void spacc_tasklet_kill (void *data) +{ +tasklet_kill (data); +} +static int spacc_probe (struct platform_device *pdev) {int i, err, ret;
@@ -1637,6 +1642,14 @@ static int spacc_probe(struct platform_dreturn -ENXIO ; } +tasklet_init (&engine->complete, spacc_spacc_complete, + (unsigned long )engine); + + ret =devm_add_action (&pdev->dev, spacc_tasklet_kill, + &engine->complete); +if (ret) +return ret; +if (devm_request_irq (&pdev->dev, irq->start, spacc_spacc_irq,0 , engine->name, engine)) {dev_err (engine->dev,"failed to request IRQ\n" );
@@ -1694,8 +1707,6 @@ static int spacc_probe(struct platform_dINIT_LIST_HEAD (&engine->completed);INIT_LIST_HEAD (&engine->in_progress); engine->in_flight =0 ; -tasklet_init (&engine->complete, spacc_spacc_complete, - (unsigned long )engine);platform_set_drvdata (pdev, engine);
Greg Kroah-Hartman
maintainerPATCH 187/367 powerpc/futex: Fix incorrect user access blocking
In reply to Greg Kroah-HartmanFrom: Michael Ellerman <[email protected]>
commit 9dc086f1e9ef39dd823bd27954b884b2062f9e70 upstream.
The early versions of our kernel user access prevention (KUAP) were written by Russell and Christophe, and didn't have separate read/write access.
At some point I picked up the series and added the read/write access, but I failed to update the usages in futex.h to correctly allow read and write.
However we didn't notice because of another bug which was causing the low-level code to always enable read and write. That bug was fixed recently in commit 1d8f739b07bd ("powerpc/kuap: Fix set direction in allow/prevent_user_access()").
futex_atomic_cmpxchg_inatomic() is passed the user address as %3 and does:
1: lwarx %1, 0, %3
cmpw 0, %1, %4
bne- 3f
2: stwcx. %5, 0, %3Which clearly loads and stores from/to %3. The logic in arch_futex_atomic_op_inuser() is similar, so fix both of them to use allow_read_write_user().
Without this fix, and with PPC_KUAP_DEBUG=y, we see eg:
Bug: Read fault blocked by AMR!
WARNING: CPU: 94 PID: 149215 at arch/powerpc/include/asm/book3s/64/kup-radix.h:126 __do_page_fault+0x600/0xf30
CPU: 94 PID: 149215 Comm: futex_requeue_p Tainted: G W 5.5.0-rc7-gcc9x-g4c25df5640ae #1
...
NIP [c000000000070680] __do_page_fault+0x600/0xf30
LR [c00000000007067c] __do_page_fault+0x5fc/0xf30
Call Trace:
[c00020138e5637e0] [c00000000007067c] __do_page_fault+0x5fc/0xf30 (unreliable)
[c00020138e5638c0] [c00000000000ada8] handle_page_fault+0x10/0x30
--- interrupt: 301 at cmpxchg_futex_value_locked+0x68/0xd0
LR = futex_lock_pi_atomic+0xe0/0x1f0
[c00020138e563bc0] [c000000000217b50] futex_lock_pi_atomic+0x80/0x1f0 (unreliable)
[c00020138e563c30] [c00000000021b668] futex_requeue+0x438/0xb60
[c00020138e563d60] [c00000000021c6cc] do_futex+0x1ec/0x2b0
[c00020138e563d90] [c00000000021c8b8] sys_futex+0x128/0x200
[c00020138e563e20] [c00000000000b7ac] system_call+0x5c/0x68--- arch/powerpc/include/asm/futex.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
arch/powerpc/include/asm/futex.h +6 -4
@@ -35,7 +35,7 @@ static inline int arch_futex_atomic_op_i {int oldval =0 , ret; -allow_write_to_user (uaddr,sizeof (*uaddr)); +allow_read_write_user (uaddr, uaddr,sizeof (*uaddr));pagefault_disable ();switch (op) {
@@ -62,7 +62,7 @@ static inline int arch_futex_atomic_op_i *oval = oldval; -prevent_write_to_user (uaddr,sizeof (*uaddr)); +prevent_read_write_user (uaddr, uaddr,sizeof (*uaddr));return ret; }
@@ -76,7 +76,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval,if (!access_ok (uaddr,sizeof (u32)))return -EFAULT ; -allow_write_to_user (uaddr,sizeof (*uaddr)); +allow_read_write_user (uaddr, uaddr,sizeof (*uaddr)); + __asm__ __volatile__ ( PPC_ATOMIC_ENTRY_BARRIER "1: lwarx %1,0,%3 # futex_atomic_cmpxchg_inatomic\n\
@@ -97,7 +98,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, : "cc", "memory"); *uval = prev; -prevent_write_to_user (uaddr,sizeof (*uaddr)); +prevent_read_write_user (uaddr, uaddr,sizeof (*uaddr)); +return ret; }
Greg Kroah-Hartman
maintainerPATCH 188/367 scsi: qla2xxx: Fix unbound NVME response length
In reply to Greg Kroah-HartmanFrom: Arun Easi <[email protected]>
commit 00fe717ee1ea3c2979db4f94b1533c57aed8dea9 upstream.
On certain cases when response length is less than 32, NVME response data is supplied inline in IOCB. This is indicated by some combination of state flags. There was an instance when a high, and incorrect, response length was indicated causing driver to overrun buffers. Fix this by checking and limiting the response payload length.
--- drivers/scsi/qla2xxx/qla_dbg.c | 6 ------ drivers/scsi/qla2xxx/qla_dbg.h | 6 ++++++ drivers/scsi/qla2xxx/qla_isr.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-)
drivers/scsi/qla2xxx/qla_dbg.c +0 -6
@@ -2519,12 +2519,6 @@ qla83xx_fw_dump_failed:/* Driver Debug Functions. */ /****************************************************************************/ -static inline int -ql_mask_match (uint level) -{ -return (level & ql2xextended_error_logging) == level; -} -/* * This function is for formatting and logging debug information. * It is to be used when vha is available. It formats the message
drivers/scsi/qla2xxx/qla_dbg.h +6 -0
@@ -374,3 +374,9 @@ extern int qla24xx_dump_ram(struct qla_hextern void qla24xx_pause_risc (struct device_reg_24xx __iomem *,struct qla_hw_data *);extern int qla24xx_soft_reset (struct qla_hw_data *); + +static inline int +ql_mask_match (uint level) +{ +return (level & ql2xextended_error_logging) == level; +}
drivers/scsi/qla2xxx/qla_isr.c +12 -0
@@ -1918,6 +1918,18 @@ static void qla24xx_nvme_iocb_entry(scsi inbuf = (uint32_t *)&sts->nvme_ersp_data; outbuf = (uint32_t *)fd->rspaddr; iocb->u.nvme.rsp_pyld_len =le16_to_cpu (sts->nvme_rsp_pyld_len); +if (unlikely (iocb->u.nvme.rsp_pyld_len > +sizeof (struct nvme_fc_ersp_iu ))) { +if (ql_mask_match (ql_dbg_io)) { +WARN_ONCE (1 ,"Unexpected response payload length %u.\n" , + iocb->u.nvme.rsp_pyld_len); +ql_log (ql_log_warn, fcport->vha,0x5100 , +"Unexpected response payload length %u.\n" , + iocb->u.nvme.rsp_pyld_len); + } + iocb->u.nvme.rsp_pyld_len = +sizeof (struct nvme_fc_ersp_iu ); + } iter = iocb->u.nvme.rsp_pyld_len >>2 ;for (; iter; iter--) *outbuf++ =swab32 (*inbuf++);
Greg Kroah-Hartman
maintainerPATCH 189/367 NFS: Fix memory leaks and corruption in readdir
In reply to Greg Kroah-HartmanFrom: Trond Myklebust <[email protected]>
commit 4b310319c6a8ce708f1033d57145e2aa027a883c upstream.
nfs_readdir_xdr_to_array() must not exit without having initialised the array, so that the page cache deletion routines can safely call nfs_readdir_clear_array().
Furthermore, we should ensure that if we exit nfs_readdir_filler() with an error, we free up any page contents to prevent a leak if we try to fill the page again.
--- fs/nfs/dir.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
fs/nfs/dir.c +15 -2
@@ -162,6 +162,17 @@ typedef struct {bool eof; } nfs_readdir_descriptor_t; +static +void nfs_readdir_init_array (struct page *page) +{ +struct nfs_cache_array *array; + + array =kmap_atomic (page); +memset (array,0 ,sizeof (struct nfs_cache_array )); + array->eof_index =-1 ; +kunmap_atomic (array); +} +/* * we are freeing strings created by nfs_add_to_readdir_array() */
@@ -174,6 +185,7 @@ void nfs_readdir_clear_array(struct page array =kmap_atomic (page);for (i =0 ; i < array->size; i++)kfree (array->array[i].string.name); + array->size =0 ;kunmap_atomic (array); }
@@ -610,6 +622,8 @@ int nfs_readdir_xdr_to_array(nfs_readdirint status = -ENOMEM ;unsigned int array_size =ARRAY_SIZE (pages); +nfs_readdir_init_array (page); + entry.prev_cookie =0 ; entry.cookie = desc->last_cookie; entry.eof =0 ;
@@ -626,8 +640,6 @@ int nfs_readdir_xdr_to_array(nfs_readdir } array =kmap (page); -memset (array,0 ,sizeof (struct nfs_cache_array )); - array->eof_index =-1 ; status =nfs_readdir_alloc_pages (pages, array_size);if (status <0 )
@@ -682,6 +694,7 @@ int nfs_readdir_filler(void *data, strucunlock_page (page);return 0 ; error: +nfs_readdir_clear_array (page);unlock_page (page);return ret; }
Greg Kroah-Hartman
maintainerPATCH 190/367 NFS: Directory page cache pages need to be locked when read
In reply to Greg Kroah-HartmanFrom: Trond Myklebust <[email protected]>
commit 114de38225d9b300f027e2aec9afbb6e0def154b upstream.
When a NFS directory page cache page is removed from the page cache, its contents are freed through a call to nfs_readdir_clear_array(). To prevent the removal of the page cache entry until after we've finished reading it, we must take the page lock.
--- fs/nfs/dir.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-)
fs/nfs/dir.c +19 -11
@@ -702,8 +702,6 @@ int nfs_readdir_filler(void *data, strucstatic void cache_page_release (nfs_readdir_descriptor_t *desc) { -if (!desc->page->mapping) -nfs_readdir_clear_array (desc->page);put_page (desc->page); desc->page =NULL ; }
@@ -717,19 +715,28 @@ struct page *get_cache_page(nfs_readdir_/* * Returns 0 if desc->dir_cookie was found on page desc->page_index +* and locks the page to prevent removal from the page cache. */ static -int find_cache_page (nfs_readdir_descriptor_t *desc) +int find_and_lock_cache_page (nfs_readdir_descriptor_t *desc) {int res; desc->page =get_cache_page (desc);if (IS_ERR (desc->page))return PTR_ERR (desc->page); - - res =nfs_readdir_search_array (desc); + res =lock_page_killable (desc->page);if (res !=0 ) -cache_page_release (desc); + goto error; + res = -EAGAIN ; +if (desc->page->mapping !=NULL ) { + res =nfs_readdir_search_array (desc); +if (res ==0 ) +return 0 ; + } +unlock_page (desc->page); +error: +cache_page_release (desc);return res; }
@@ -744,7 +751,7 @@ int readdir_search_pagecache(nfs_readdir desc->last_cookie =0 ; }do { - res =find_cache_page (desc); + res =find_and_lock_cache_page (desc); }while (res == -EAGAIN );return res; }
@@ -783,7 +790,6 @@ int nfs_do_filldir(nfs_readdir_descripto desc->eof = true;kunmap (desc->page); -cache_page_release (desc);dfprintk (DIRCACHE ,"NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n" , (unsigned long long )*desc->dir_cookie, res);return res;
@@ -829,13 +835,13 @@ int uncached_readdir(nfs_readdir_descrip status =nfs_do_filldir (desc); + out_release: +nfs_readdir_clear_array (desc->page); +cache_page_release (desc); out:dfprintk (DIRCACHE ,"NFS: %s: returns %d\n" , __func__, status);return status; - out_release: -cache_page_release (desc); - goto out; }/* The file offset position represents the dirent entry number. A
@@ -900,6 +906,8 @@ static int nfs_readdir(struct file *filebreak ; res =nfs_do_filldir (desc); +unlock_page (desc->page); +cache_page_release (desc);if (res <0 )break ; }while (!desc->eof);
Greg Kroah-Hartman
maintainerPATCH 191/367 nfsd: fix filecache lookup
In reply to Greg Kroah-HartmanFrom: Trond Myklebust <[email protected]>
commit 28c7d86bb6172ffbb1a1237c6388e77f9fe5f181 upstream.
If the lookup keeps finding a nfsd_file with an unhashed open file, then retry once only.
--- fs/nfsd/filecache.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/nfsd/filecache.c +6 -0
@@ -789,6 +789,7 @@ nfsd_file_acquire(struct svc_rqst *rqstpstruct nfsd_file *nf, *new;struct inode *inode;unsigned int hashval; +bool retry = true;/* FIXME: skip this if fh_dentry is already set? */ status = fh_verify(rqstp, fhp, S_IFREG,
@@ -824,6 +825,11 @@ wait_for_construction:/* Did construction of this file fail? */ if (!test_bit (NFSD_FILE_HASHED , &nf->nf_flags)) { +if (!retry) { + status = nfserr_jukebox; + goto out; + } + retry = false;nfsd_file_put_noref (nf); goto retry; }
Greg Kroah-Hartman
maintainerPATCH 192/367 jbd2_seq_info_next should increase position index
In reply to Greg Kroah-HartmanFrom: Vasily Averin <[email protected]>
commit 1a8e9cf40c9a6a2e40b1e924b13ed303aeea4418 upstream.
if seq_file .next fuction does not change position index, read after some lseek can generate unexpected output.
Script below generates endless output
$ q=;while read -r r;do echo "$((++q)) $r";done </proc/fs/jbd2/DEV/info
https://bugzilla.kernel.org/show_bug.cgi?id=206283
--- fs/jbd2/journal.c | 1 + 1 file changed, 1 insertion(+)
fs/jbd2/journal.c +1 -0
@@ -982,6 +982,7 @@ static void *jbd2_seq_info_start(structstatic void *jbd2_seq_info_next (struct seq_file *seq,void *v,loff_t *pos) { + (*pos)++;return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 193/367 ext4: fix deadlock allocating crypto bounce page from mempool
In reply to Greg Kroah-HartmanFrom: Eric Biggers <[email protected]>
commit 547c556f4db7c09447ecf5f833ab6aaae0c5ab58 upstream.
ext4_writepages() on an encrypted file has to encrypt the data, but it can't modify the pagecache pages in-place, so it encrypts the data into bounce pages and writes those instead. All bounce pages are allocated from a mempool using GFP_NOFS.
This is not correct use of a mempool, and it can deadlock. This is because GFP_NOFS includes __GFP_DIRECT_RECLAIM, which enables the "never fail" mode for mempool_alloc() where a failed allocation will fall back to waiting for one of the preallocated elements in the pool.
But since this mode is used for all a bio's pages and not just the first, it can deadlock waiting for pages already in the bio to be freed.
This deadlock can be reproduced by patching mempool_alloc() to pretend that pool->alloc() always fails (so that it always falls back to the preallocations), and then creating an encrypted file of size > 128 KiB.
Fix it by only using GFP_NOFS for the first page in the bio. For subsequent pages just use GFP_NOWAIT, and if any of those fail, just submit the bio and start a new one.
This will need to be fixed in f2fs too, but that's less straightforward.
--- fs/ext4/page-io.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
fs/ext4/page-io.c +14 -5
@@ -512,17 +512,26 @@ int ext4_bio_write_page(struct ext4_io_sgfp_t gfp_flags =GFP_NOFS ;unsigned int enc_bytes =round_up (len,i_blocksize (inode)); +/* +* Since bounce page allocation uses a mempool, we can only use +* a waiting mask (i.e. request guaranteed allocation) on the +* first page of the bio. Otherwise it can deadlock. +*/ +if (io->io_bio) + gfp_flags =GFP_NOWAIT | __GFP_NOWARN; retry_encrypt: bounce_page =fscrypt_encrypt_pagecache_blocks (page, enc_bytes,0 , gfp_flags);if (IS_ERR (bounce_page)) { ret =PTR_ERR (bounce_page); -if (ret == -ENOMEM && wbc->sync_mode ==WB_SYNC_ALL ) { -if (io->io_bio) { +if (ret == -ENOMEM && + (io->io_bio || wbc->sync_mode ==WB_SYNC_ALL )) { + gfp_flags =GFP_NOFS ; +if (io->io_bio)ext4_io_submit (io); -congestion_wait (BLK_RW_ASYNC ,HZ /50 ); - } - gfp_flags |= __GFP_NOFAIL; +else + gfp_flags |= __GFP_NOFAIL; +congestion_wait (BLK_RW_ASYNC ,HZ /50 ); goto retry_encrypt; }
Greg Kroah-Hartman
maintainerPATCH 194/367 ext4: fix race conditions in ->d_compare() and ->d_hash()
In reply to Greg Kroah-HartmanFrom: Eric Biggers <[email protected]>
commit ec772f01307a2c06ebf6cdd221e6b518a71ddae7 upstream.
Since ->d_compare() and ->d_hash() can be called in RCU-walk mode, ->d_parent and ->d_inode can be concurrently modified, and in particular, ->d_inode may be changed to NULL. For ext4_d_hash() this resulted in a reproducible NULL dereference if a lookup is done in a directory being deleted, e.g. with:
int main()
{
if (fork()) {
for (;;) {
mkdir("subdir", 0700);
rmdir("subdir");
}
} else {
for (;;)
access("subdir/file", 0);
}
}... or by running the 't_encrypted_d_revalidate' program from xfstests. Both repros work in any directory on a filesystem with the encoding feature, even if the directory doesn't actually have the casefold flag.
I couldn't reproduce a crash in ext4_d_compare(), but it appears that a similar crash is possible there.
Fix these bugs by reading ->d_parent and ->d_inode using READ_ONCE() and falling back to the case sensitive behavior if the inode is NULL.
--- fs/ext4/dir.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
fs/ext4/dir.c +6 -3
@@ -672,9 +672,11 @@ static int ext4_d_compare(const struct d const char *str, const struct qstr *name) { struct qstr qstr = {.name = str, .len = len }; -struct inode *inode = dentry->d_parent->d_inode; +const struct dentry *parent =READ_ONCE (dentry->d_parent); +const struct inode *inode =READ_ONCE (parent->d_inode); -if (!IS_CASEFOLDED (inode) || !EXT4_SB (inode->i_sb)->s_encoding) { +if (!inode || !IS_CASEFOLDED (inode) || + !EXT4_SB (inode->i_sb)->s_encoding) {if (len != name->len)return -1 ;return memcmp (str, name->name, len);
@@ -687,10 +689,11 @@ static int ext4_d_hash(const struct dent {const struct ext4_sb_info *sbi =EXT4_SB (dentry->d_sb);const struct unicode_map *um = sbi->s_encoding; +const struct inode *inode =READ_ONCE (dentry->d_inode);unsigned char *norm;int len, ret =0 ; -if (!IS_CASEFOLDED (dentry->d_inode) || !um) +if (!inode || !IS_CASEFOLDED (inode) || !um)return 0 ; norm =kmalloc (PATH_MAX ,GFP_ATOMIC );
Greg Kroah-Hartman
maintainerPATCH 195/367 Btrfs: fix missing hole after hole punching and fsync when using NO_HOLES
In reply to Greg Kroah-HartmanFrom: Filipe Manana <[email protected]>
commit 0e56315ca147b3e60c7bf240233a301d3c7fb508 upstream.
When using the NO_HOLES feature, if we punch a hole into a file and then fsync it, there are cases where a subsequent fsync will miss the fact that a hole was punched, resulting in the holes not existing after replaying the log tree.
Essentially these cases all imply that, tree-log.c:copy_items(), is not invoked for the leafs that delimit holes, because nothing changed those leafs in the current transaction. And it's precisely copy_items() where we currenly detect and log holes, which works as long as the holes are between file extent items in the input leaf or between the beginning of input leaf and the previous leaf or between the last item in the leaf and the next leaf.
First example where we miss a hole:
*) The extent items of the inode span multiple leafs;
*) The punched hole covers a range that affects only the extent items of
the first leaf;
*) The fsync operation is done in full mode (BTRFS_INODE_NEEDS_FULL_SYNC
is set in the inode's runtime flags).
That results in the hole not existing after replaying the log tree.
For example, if the fs/subvolume tree has the following layout for a particular inode:
Leaf N, generation 10:
[ ... INODE_ITEM INODE_REF EXTENT_ITEM (0 64K) EXTENT_ITEM (64K 128K) ]
Leaf N + 1, generation 10:
[ EXTENT_ITEM (128K 64K) ... ]
If at transaction 11 we punch a hole coverting the range [0, 128K[, we end up dropping the two extent items from leaf N, but we don't touch the other leaf, so we end up in the following state:
Leaf N, generation 11:
[ ... INODE_ITEM INODE_REF ]
Leaf N + 1, generation 10:
[ EXTENT_ITEM (128K 64K) ... ]
A full fsync after punching the hole will only process leaf N because it was modified in the current transaction, but not leaf N + 1, since it was not modified in the current transaction (generation 10 and not 11). As a result the fsync will not log any holes, because it didn't process any leaf with extent items.
Second example where we will miss a hole:
*) An inode as its items spanning 5 (or more) leafs;
*) A hole is punched and it covers only the extents items of the 3rd
leaf. This resulsts in deleting the entire leaf and not touching any of the other leafs.
So the only leaf that is modified in the current transaction, when punching the hole, is the first leaf, which contains the inode item. During the full fsync, the only leaf that is passed to copy_items() is that first leaf, and that's not enough for the hole detection code in copy_items() to determine there's a hole between the last file extent item in the 2nd leaf and the first file extent item in the 3rd leaf (which was the 4th leaf before punching the hole).
Fix this by scanning all leafs and punch holes as necessary when doing a full fsync (less common than a non-full fsync) when the NO_HOLES feature is enabled. The lack of explicit file extent items to mark holes makes it necessary to scan existing extents to determine if holes exist.
A test case for fstests follows soon.
--- fs/btrfs/tree-log.c | 388 +++++++++++++--------------------------------------- 1 file changed, 100 insertions(+), 288 deletions(-)
fs/btrfs/tree-log.c +100 -288
@@ -3935,7 +3935,7 @@ static int log_csums(struct btrfs_trans_ static noinline int copy_items(struct btrfs_trans_handle *trans,struct btrfs_inode *inode,struct btrfs_path *dst_path, -struct btrfs_path *src_path,u64 *last_extent, +struct btrfs_path *src_path,int start_slot,int nr,int inode_only,u64 logged_isize) {
@@ -3946,7 +3946,6 @@ static noinline int copy_items(struct btstruct btrfs_file_extent_item *extent;struct btrfs_inode_item *inode_item;struct extent_buffer *src = src_path->nodes[0 ]; -struct btrfs_key first_key, last_key, key;int ret;struct btrfs_key *ins_keys;u32 *ins_sizes;
@@ -3954,9 +3953,6 @@ static noinline int copy_items(struct btint i;struct list_head ordered_sums;int skip_csum = inode->flags &BTRFS_INODE_NODATASUM ; -bool has_extents = false; -bool need_find_last_extent = true; -bool done = false;INIT_LIST_HEAD (&ordered_sums);
@@ -3965,8 +3961,6 @@ static noinline int copy_items(struct btif (!ins_data)return -ENOMEM ; - first_key.objectid = (u64)-1 ; - ins_sizes = (u32 *)ins_data; ins_keys = (struct btrfs_key *)(ins_data + nr *sizeof (u32));
@@ -3987,9 +3981,6 @@ static noinline int copy_items(struct bt src_offset =btrfs_item_ptr_offset (src, start_slot + i); -if (i == nr -1 ) - last_key = ins_keys[i]; -if (ins_keys[i].type ==BTRFS_INODE_ITEM_KEY ) { inode_item = btrfs_item_ptr(dst_path->nodes[0], dst_path->slots[0],
@@ -4003,20 +3994,6 @@ static noinline int copy_items(struct bt src_offset, ins_sizes[i]); } -/* -* We set need_find_last_extent here in case we know we were -* processing other items and then walk into the first extent in -* the inode. If we don't hit an extent then nothing changes, -* we'll do the last search the next time around. -*/ -if (ins_keys[i].type ==BTRFS_EXTENT_DATA_KEY ) { - has_extents = true; -if (first_key.objectid == (u64)-1 ) - first_key = ins_keys[i]; - }else { - need_find_last_extent = false; - } -/* take a reference on file data extents so that truncates * or deletes of this inode don't have to relog the inode * again
@@ -4082,167 +4059,6 @@ static noinline int copy_items(struct btkfree (sums); } -if (!has_extents) -return ret; - -if (need_find_last_extent && *last_extent == first_key.offset) { -/* -* We don't have any leafs between our current one and the one -* we processed before that can have file extent items for our -* inode (and have a generation number smaller than our current -* transaction id). -*/ - need_find_last_extent = false; - } - -/* -* Because we use btrfs_search_forward we could skip leaves that were -* not modified and then assume *last_extent is valid when it really -* isn't. So back up to the previous leaf and read the end of the last -* extent before we go and fill in holes. -*/ -if (need_find_last_extent) { -u64 len; - - ret =btrfs_prev_leaf (inode->root, src_path); -if (ret <0 ) -return ret; -if (ret) - goto fill_holes; -if (src_path->slots[0 ]) - src_path->slots[0 ]--; - src = src_path->nodes[0 ]; -btrfs_item_key_to_cpu (src, &key, src_path->slots[0 ]); -if (key.objectid !=btrfs_ino (inode) || - key.type !=BTRFS_EXTENT_DATA_KEY ) - goto fill_holes; - extent =btrfs_item_ptr (src, src_path->slots[0 ], - struct btrfs_file_extent_item); -if (btrfs_file_extent_type (src, extent) == -BTRFS_FILE_EXTENT_INLINE ) { - len =btrfs_file_extent_ram_bytes (src, extent); - *last_extent =ALIGN (key.offset + len, - fs_info->sectorsize); - }else { - len =btrfs_file_extent_num_bytes (src, extent); - *last_extent = key.offset + len; - } - } -fill_holes: -/* So we did prev_leaf, now we need to move to the next leaf, but a few -* things could have happened -* -* 1) A merge could have happened, so we could currently be on a leaf -* that holds what we were copying in the first place. -* 2) A split could have happened, and now not all of the items we want -* are on the same leaf. -* -* So we need to adjust how we search for holes, we need to drop the -* path and re-search for the first extent key we found, and then walk -* forward until we hit the last one we copied. -*/ -if (need_find_last_extent) { -/* btrfs_prev_leaf could return 1 without releasing the path */ -btrfs_release_path (src_path); - ret =btrfs_search_slot (NULL , inode->root, &first_key, - src_path,0 ,0 ); -if (ret <0 ) -return ret; -ASSERT (ret ==0 ); - src = src_path->nodes[0 ]; - i = src_path->slots[0 ]; - }else { - i = start_slot; - } - -/* -* Ok so here we need to go through and fill in any holes we may have -* to make sure that holes are punched for those areas in case they had -* extents previously. -*/ -while (!done) { -u64 offset, len; -u64 extent_end; - -if (i >=btrfs_header_nritems (src_path->nodes[0 ])) { - ret =btrfs_next_leaf (inode->root, src_path); -if (ret <0 ) -return ret; -ASSERT (ret ==0 ); - src = src_path->nodes[0 ]; - i =0 ; - need_find_last_extent = true; - } - -btrfs_item_key_to_cpu (src, &key, i); -if (!btrfs_comp_cpu_keys (&key, &last_key)) - done = true; -if (key.objectid !=btrfs_ino (inode) || - key.type !=BTRFS_EXTENT_DATA_KEY ) { - i++; -continue ; - } - extent = btrfs_item_ptr(src, i, struct btrfs_file_extent_item); -if (btrfs_file_extent_type (src, extent) == -BTRFS_FILE_EXTENT_INLINE ) { - len =btrfs_file_extent_ram_bytes (src, extent); - extent_end =ALIGN (key.offset + len, - fs_info->sectorsize); - }else { - len =btrfs_file_extent_num_bytes (src, extent); - extent_end = key.offset + len; - } - i++; - -if (*last_extent == key.offset) { - *last_extent = extent_end; -continue ; - } - offset = *last_extent; - len = key.offset - *last_extent; - ret =btrfs_insert_file_extent (trans, log,btrfs_ino (inode), - offset,0 ,0 , len,0 , len,0 ,0 ,0 ); -if (ret) -break ; - *last_extent = extent_end; - } - -/* -* Check if there is a hole between the last extent found in our leaf -* and the first extent in the next leaf. If there is one, we need to -* log an explicit hole so that at replay time we can punch the hole. -*/ -if (ret ==0 && - key.objectid ==btrfs_ino (inode) && - key.type ==BTRFS_EXTENT_DATA_KEY && - i ==btrfs_header_nritems (src_path->nodes[0 ])) { - ret =btrfs_next_leaf (inode->root, src_path); - need_find_last_extent = true; -if (ret >0 ) { - ret =0 ; - }else if (ret ==0 ) { -btrfs_item_key_to_cpu (src_path->nodes[0 ], &key, - src_path->slots[0 ]); -if (key.objectid ==btrfs_ino (inode) && - key.type ==BTRFS_EXTENT_DATA_KEY && - *last_extent < key.offset) { -const u64 len = key.offset - *last_extent; - - ret =btrfs_insert_file_extent (trans, log, -btrfs_ino (inode), - *last_extent,0 , -0 , len,0 , len, -0 ,0 ,0 ); - *last_extent += len; - } - } - } -/* -* Need to let the callers know we dropped the path so they should -* re-search. -*/ -if (!ret && need_find_last_extent) - ret =1 ;return ret; }
@@ -4407,7 +4223,7 @@ static int btrfs_log_prealloc_extents(stconst u64 i_size =i_size_read (&inode->vfs_inode);const u64 ino =btrfs_ino (inode);struct btrfs_path *dst_path =NULL ; -u64 last_extent = (u64)-1 ; +bool dropped_extents = false;int ins_nr =0 ;int start_slot;int ret;
@@ -4429,8 +4245,7 @@ static int btrfs_log_prealloc_extents(stif (slot >=btrfs_header_nritems (leaf)) {if (ins_nr >0 ) { ret =copy_items (trans, inode, dst_path, path, - &last_extent, start_slot, - ins_nr,1 ,0 ); + start_slot, ins_nr,1 ,0 );if (ret <0 ) goto out; ins_nr =0 ;
@@ -4454,8 +4269,7 @@ static int btrfs_log_prealloc_extents(st path->slots[0 ]++;continue ; } -if (last_extent == (u64)-1 ) { - last_extent = key.offset; +if (!dropped_extents) {/* * Avoid logging extent items logged in past fsync calls * and leading to duplicate keys in the log tree.
@@ -4469,6 +4283,7 @@ static int btrfs_log_prealloc_extents(st }while (ret == -EAGAIN );if (ret) goto out; + dropped_extents = true; }if (ins_nr ==0 ) start_slot = slot;
@@ -4483,7 +4298,7 @@ static int btrfs_log_prealloc_extents(st } }if (ins_nr >0 ) { - ret =copy_items (trans, inode, dst_path, path, &last_extent, + ret =copy_items (trans, inode, dst_path, path, start_slot, ins_nr,1 ,0 );if (ret >0 ) ret =0 ;
@@ -4670,13 +4485,8 @@ static int btrfs_log_all_xattrs(struct bif (slot >= nritems) {if (ins_nr >0 ) { -u64 last_extent =0 ; - ret =copy_items (trans, inode, dst_path, path, - &last_extent, start_slot, - ins_nr,1 ,0 ); -/* can't be 1, extent items aren't processed */ -ASSERT (ret <=0 ); + start_slot, ins_nr,1 ,0 );if (ret <0 )return ret; ins_nr =0 ;
@@ -4700,13 +4510,8 @@ static int btrfs_log_all_xattrs(struct bcond_resched (); }if (ins_nr >0 ) { -u64 last_extent =0 ; - ret =copy_items (trans, inode, dst_path, path, - &last_extent, start_slot, - ins_nr,1 ,0 ); -/* can't be 1, extent items aren't processed */ -ASSERT (ret <=0 ); + start_slot, ins_nr,1 ,0 );if (ret <0 )return ret; }
@@ -4715,100 +4520,119 @@ static int btrfs_log_all_xattrs(struct b }/* -* If the no holes feature is enabled we need to make sure any hole between the -* last extent and the i_size of our inode is explicitly marked in the log. This -* is to make sure that doing something like: -* -* 1) create file with 128Kb of data -* 2) truncate file to 64Kb -* 3) truncate file to 256Kb -* 4) fsync file -* 5) <crash/power failure> -* 6) mount fs and trigger log replay -* -* Will give us a file with a size of 256Kb, the first 64Kb of data match what -* the file had in its first 64Kb of data at step 1 and the last 192Kb of the -* file correspond to a hole. The presence of explicit holes in a log tree is -* what guarantees that log replay will remove/adjust file extent items in the -* fs/subvol tree. -* -* Here we do not need to care about holes between extents, that is already done -* by copy_items(). We also only need to do this in the full sync path, where we -* lookup for extents from the fs/subvol tree only. In the fast path case, we -* lookup the list of modified extent maps and if any represents a hole, we -* insert a corresponding extent representing a hole in the log tree. +* When using the NO_HOLES feature if we punched a hole that causes the +* deletion of entire leafs or all the extent items of the first leaf (the one +* that contains the inode item and references) we may end up not processing +* any extents, because there are no leafs with a generation matching the +* current transaction that have extent items for our inode. So we need to find +* if any holes exist and then log them. We also need to log holes after any +* truncate operation that changes the inode's size. */ -static int btrfs_log_trailing_hole (struct btrfs_trans_handle *trans, -struct btrfs_root *root, -struct btrfs_inode *inode, -struct btrfs_path *path) +static int btrfs_log_holes (struct btrfs_trans_handle *trans, +struct btrfs_root *root, +struct btrfs_inode *inode, +struct btrfs_path *path) {struct btrfs_fs_info *fs_info = root->fs_info; -int ret;struct btrfs_key key; -u64 hole_start; -u64 hole_size; -struct extent_buffer *leaf; -struct btrfs_root *log = root->log_root;const u64 ino =btrfs_ino (inode);const u64 i_size =i_size_read (&inode->vfs_inode); +u64 prev_extent_end =0 ; +int ret; -if (!btrfs_fs_incompat (fs_info,NO_HOLES )) +if (!btrfs_fs_incompat (fs_info,NO_HOLES ) || i_size ==0 )return 0 ; key.objectid = ino; key.type =BTRFS_EXTENT_DATA_KEY ; - key.offset = (u64)-1 ; + key.offset =0 ; ret =btrfs_search_slot (NULL , root, &key, path,0 ,0 ); -ASSERT (ret !=0 );if (ret <0 )return ret; -ASSERT (path->slots[0 ] >0 ); - path->slots[0 ]--; - leaf = path->nodes[0 ]; -btrfs_item_key_to_cpu (leaf, &key, path->slots[0 ]); - -if (key.objectid != ino || key.type !=BTRFS_EXTENT_DATA_KEY ) { -/* inode does not have any extents */ - hole_start =0 ; - hole_size = i_size; - }else { +while (true) {struct btrfs_file_extent_item *extent; +struct extent_buffer *leaf = path->nodes[0 ];u64 len; -/* -* If there's an extent beyond i_size, an explicit hole was -* already inserted by copy_items(). -*/ -if (key.offset >= i_size) -return 0 ; +if (path->slots[0 ] >=btrfs_header_nritems (path->nodes[0 ])) { + ret =btrfs_next_leaf (root, path); +if (ret <0 ) +return ret; +if (ret >0 ) { + ret =0 ; +break ; + } + leaf = path->nodes[0 ]; + } + +btrfs_item_key_to_cpu (leaf, &key, path->slots[0 ]); +if (key.objectid != ino || key.type !=BTRFS_EXTENT_DATA_KEY ) +break ; + +/* We have a hole, log it. */ +if (prev_extent_end < key.offset) { +const u64 hole_len = key.offset - prev_extent_end; + +/* +* Release the path to avoid deadlocks with other code +* paths that search the root while holding locks on +* leafs from the log root. +*/ +btrfs_release_path (path); + ret =btrfs_insert_file_extent (trans, root->log_root, + ino, prev_extent_end,0 , +0 , hole_len,0 , hole_len, +0 ,0 ,0 ); +if (ret <0 ) +return ret; + +/* +* Search for the same key again in the root. Since it's +* an extent item and we are holding the inode lock, the +* key must still exist. If it doesn't just emit warning +* and return an error to fall back to a transaction +* commit. +*/ + ret =btrfs_search_slot (NULL , root, &key, path,0 ,0 ); +if (ret <0 ) +return ret; +if (WARN_ON (ret >0 )) +return -ENOENT ; + leaf = path->nodes[0 ]; + } extent =btrfs_item_ptr (leaf, path->slots[0 ], struct btrfs_file_extent_item); -if (btrfs_file_extent_type (leaf, extent) == -BTRFS_FILE_EXTENT_INLINE ) -return 0 ; +BTRFS_FILE_EXTENT_INLINE ) { + len =btrfs_file_extent_ram_bytes (leaf, extent); + prev_extent_end =ALIGN (key.offset + len, + fs_info->sectorsize); + }else { + len =btrfs_file_extent_num_bytes (leaf, extent); + prev_extent_end = key.offset + len; + } - len =btrfs_file_extent_num_bytes (leaf, extent); -/* Last extent goes beyond i_size, no need to log a hole. */ -if (key.offset + len > i_size) -return 0 ; - hole_start = key.offset + len; - hole_size = i_size - hole_start; + path->slots[0 ]++; +cond_resched (); } -btrfs_release_path (path); -/* Last extent ends at i_size. */ -if (hole_size ==0 ) -return 0 ; +if (prev_extent_end < i_size) { +u64 hole_len; - hole_size =ALIGN (hole_size, fs_info->sectorsize); - ret =btrfs_insert_file_extent (trans, log, ino, hole_start,0 ,0 , - hole_size,0 , hole_size,0 ,0 ,0 ); -return ret; +btrfs_release_path (path); + hole_len =ALIGN (i_size - prev_extent_end, fs_info->sectorsize); + ret =btrfs_insert_file_extent (trans, root->log_root, + ino, prev_extent_end,0 ,0 , + hole_len,0 , hole_len, +0 ,0 ,0 ); +if (ret <0 ) +return ret; + } + +return 0 ; }/*
@@ -5110,7 +4934,6 @@ static int btrfs_log_inode(struct btrfs_struct btrfs_key min_key;struct btrfs_key max_key;struct btrfs_root *log = root->log_root; -u64 last_extent =0 ;int err =0 ;int ret;int nritems;
@@ -5288,7 +5111,7 @@ again: ins_start_slot = path->slots[0 ]; } ret =copy_items (trans, inode, dst_path, path, - &last_extent, ins_start_slot, + ins_start_slot, ins_nr, inode_only, logged_isize);if (ret <0 ) {
@@ -5311,17 +5134,13 @@ again:if (ins_nr ==0 ) goto next_slot; ret =copy_items (trans, inode, dst_path, path, - &last_extent, ins_start_slot, + ins_start_slot, ins_nr, inode_only, logged_isize);if (ret <0 ) { err = ret; goto out_unlock; } ins_nr =0 ; -if (ret) { -btrfs_release_path (path); -continue ; - } goto next_slot; }
@@ -5334,18 +5153,13 @@ again: goto next_slot; } - ret =copy_items (trans, inode, dst_path, path, &last_extent, + ret =copy_items (trans, inode, dst_path, path, ins_start_slot, ins_nr, inode_only, logged_isize);if (ret <0 ) { err = ret; goto out_unlock; } -if (ret) { - ins_nr =0 ; -btrfs_release_path (path); -continue ; - } ins_nr =1 ; ins_start_slot = path->slots[0 ]; next_slot:
@@ -5359,13 +5173,12 @@ next_slot: }if (ins_nr) { ret =copy_items (trans, inode, dst_path, path, - &last_extent, ins_start_slot, + ins_start_slot, ins_nr, inode_only, logged_isize);if (ret <0 ) { err = ret; goto out_unlock; } - ret =0 ; ins_nr =0 ; }btrfs_release_path (path);
@@ -5380,14 +5193,13 @@ next_key: } }if (ins_nr) { - ret =copy_items (trans, inode, dst_path, path, &last_extent, + ret =copy_items (trans, inode, dst_path, path, ins_start_slot, ins_nr, inode_only, logged_isize);if (ret <0 ) { err = ret; goto out_unlock; } - ret =0 ; ins_nr =0 ; }
@@ -5400,7 +5212,7 @@ next_key:if (max_key.type >=BTRFS_EXTENT_DATA_KEY && !fast_search) {btrfs_release_path (path);btrfs_release_path (dst_path); - err =btrfs_log_trailing_hole (trans, root, inode, path); + err =btrfs_log_holes (trans, root, inode, path);if (err) goto out_unlock; }
Greg Kroah-Hartman
maintainerPATCH 196/367 Btrfs: make deduplication with range including the last block work
In reply to Greg Kroah-HartmanFrom: Filipe Manana <[email protected]>
commit 831d2fa25ab8e27592b1b0268dae6f2dfaf7cc43 upstream.
Since btrfs was migrated to use the generic VFS helpers for clone and deduplication, it stopped allowing for the last block of a file to be deduplicated when the source file size is not sector size aligned (when eof is somewhere in the middle of the last block). There are two reasons for that:
1) The generic code always rounds down, to a multiple of the block size, the range's length for deduplications. This means we end up never deduplicating the last block when the eof is not block size aligned, even for the safe case where the destination range's end offset matches the destination file's size. That rounding down operation is done at generic_remap_check_len();
2) Because of that, the btrfs specific code does not expect anymore any non-aligned range length's for deduplication and therefore does not work if such nona-aligned length is given.
This patch addresses that second part, and it depends on a patch that fixes generic_remap_check_len(), in the VFS, which was submitted ealier and has the following subject:
"fs: allow deduplication of eof block into the end of the destination file"
These two patches address reports from users that started seeing lower deduplication rates due to the last block never being deduplicated when the file size is not aligned to the filesystem's block size.
--- fs/btrfs/ioctl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/btrfs/ioctl.c +2 -1
@@ -3243,6 +3243,7 @@ static void btrfs_double_extent_lock(strstatic int btrfs_extent_same_range (struct inode *src,u64 loff,u64 len,struct inode *dst,u64 dst_loff) { +const u64 bs =BTRFS_I (src)->root->fs_info->sb->s_blocksize;int ret;/*
@@ -3250,7 +3251,7 @@ static int btrfs_extent_same_range(struc* source range to serialize with relocation. */ btrfs_double_extent_lock (src, loff, dst, dst_loff, len); - ret =btrfs_clone (src, dst, loff, len, len, dst_loff,1 ); + ret =btrfs_clone (src, dst, loff, len,ALIGN (len, bs), dst_loff,1 );btrfs_double_extent_unlock (src, loff, dst, dst_loff, len);return ret;
Greg Kroah-Hartman
maintainerPATCH 197/367 Btrfs: fix infinite loop during fsync after rename operations
In reply to Greg Kroah-HartmanFrom: Filipe Manana <[email protected]>
commit b5e4ff9d465da1233a2d9a47ebce487c70d8f4ab upstream.
Recently fsstress (from fstests) sporadically started to trigger an infinite loop during fsync operations. This turned out to be because support for the rename exchange and whiteout operations was added to fsstress in fstests. These operations, unlike any others in fsstress, cause file names to be reused, whence triggering this issue. However it's not necessary to use rename exchange and rename whiteout operations trigger this issue, simple rename operations and file creations are enough to trigger the issue.
The issue boils down to when we are logging inodes that conflict (that had the name of any inode we need to log during the fsync operation), we keep logging them even if they were already logged before, and after that we check if there's any other inode that conflicts with them and then add it again to the list of inodes to log. Skipping already logged inodes fixes the issue.
Consider the following example:
$ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt
$ mkdir /mnt/testdir # inode 257
$ touch /mnt/testdir/zz # inode 258 $ ln /mnt/testdir/zz /mnt/testdir/zz_link
$ touch /mnt/testdir/a # inode 259
$ sync
# The following 3 renames achieve the same result as a rename exchange
# operation (<rename_exchange> /mnt/testdir/zz_link to /mnt/testdir/a).
$ mv /mnt/testdir/a /mnt/testdir/a/tmp $ mv /mnt/testdir/zz_link /mnt/testdir/a $ mv /mnt/testdir/a/tmp /mnt/testdir/zz_link
# The following rename and file creation give the same result as a
# rename whiteout operation (<rename_whiteout> zz to a2).
$ mv /mnt/testdir/zz /mnt/testdir/a2 $ touch /mnt/testdir/zz # inode 260
$ xfs_io -c fsync /mnt/testdir/zz
--> results in the infinite loopThe following steps happen:
1) When logging inode 260, we find that its reference named "zz" was used by inode 258 in the previous transaction (through the commit root), so inode 258 is added to the list of conflicting indoes that need to be logged;
2) After logging inode 258, we find that its reference named "a" was used by inode 259 in the previous transaction, and therefore we add inode 259 to the list of conflicting inodes to be logged;
3) After logging inode 259, we find that its reference named "zz_link" was used by inode 258 in the previous transaction - we add inode 258 to the list of conflicting inodes to log, again - we had already logged it before at step 3. After logging it again, we find again that inode 259 conflicts with him, and we add again 259 to the list, etc - we end up repeating all the previous steps.
So fix this by skipping logging of conflicting inodes that were already logged.
--- fs/btrfs/tree-log.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
fs/btrfs/tree-log.c +44 -0
@@ -4836,6 +4836,50 @@ static int log_conflicting_inodes(structcontinue ; }/* +* If the inode was already logged skip it - otherwise we can +* hit an infinite loop. Example: +* +* From the commit root (previous transaction) we have the +* following inodes: +* +* inode 257 a directory +* inode 258 with references "zz" and "zz_link" on inode 257 +* inode 259 with reference "a" on inode 257 +* +* And in the current (uncommitted) transaction we have: +* +* inode 257 a directory, unchanged +* inode 258 with references "a" and "a2" on inode 257 +* inode 259 with reference "zz_link" on inode 257 +* inode 261 with reference "zz" on inode 257 +* +* When logging inode 261 the following infinite loop could +* happen if we don't skip already logged inodes: +* +* - we detect inode 258 as a conflicting inode, with inode 261 +* on reference "zz", and log it; +* +* - we detect inode 259 as a conflicting inode, with inode 258 +* on reference "a", and log it; +* +* - we detect inode 258 as a conflicting inode, with inode 259 +* on reference "zz_link", and log it - again! After this we +* repeat the above steps forever. +*/ +spin_lock (&BTRFS_I (inode)->lock); +/* +* Check the inode's logged_trans only instead of +* btrfs_inode_in_log(). This is because the last_log_commit of +* the inode is not updated when we only log that it exists and +* and it has the full sync bit set (see btrfs_log_inode()). +*/ +if (BTRFS_I (inode)->logged_trans == trans->transid) { +spin_unlock (&BTRFS_I (inode)->lock); +btrfs_add_delayed_iput (inode); +continue ; + } +spin_unlock (&BTRFS_I (inode)->lock); +/* * We are safe logging the other inode without acquiring its * lock as long as we log with the LOG_INODE_EXISTS mode. We * are safe against concurrent renames of the other inode as
Greg Kroah-Hartman
maintainerPATCH 198/367 btrfs: set trans->drity in btrfs_commit_transaction
In reply to Greg Kroah-HartmanFrom: Josef Bacik <[email protected]>
commit d62b23c94952e78211a383b7d90ef0afbd9a3717 upstream.
If we abort a transaction we have the following sequence
if (!trans->dirty && list_empty(&trans->new_bgs))
return;
WRITE_ONCE(trans->transaction->aborted, err);
The idea being if we didn't modify anything with our trans handle then we don't really need to abort the whole transaction, maybe the other trans handles are fine and we can carry on.
However in the case of create_snapshot we add a pending_snapshot object to our transaction and then commit the transaction. We don't actually modify anything. sync() behaves the same way, attach to an existing transaction and commit it. This means that if we have an IO error in the right places we could abort the committing transaction with our trans->dirty being not set and thus not set transaction->aborted.
This is a problem because in the create_snapshot() case we depend on pending->error being set to something, or btrfs_commit_transaction returning an error.
If we are not the trans handle that gets to commit the transaction, and we're waiting on the commit to happen we get our return value from cur_trans->aborted. If this was not set to anything because sync() hit an error in the transaction commit before it could modify anything then cur_trans->aborted would be 0. Thus we'd return 0 from btrfs_commit_transaction() in create_snapshot.
This is a problem because we then try to do things with pending_snapshot->snap, which will be NULL because we didn't create the snapshot, and then we'll get a NULL pointer dereference like the following
"BUG: kernel NULL pointer dereference, address: 00000000000001f0"
RIP: 0010:btrfs_orphan_cleanup+0x2d/0x330 Call Trace: ? btrfs_mksubvol.isra.31+0x3f2/0x510 btrfs_mksubvol.isra.31+0x4bc/0x510 ? __sb_start_write+0xfa/0x200 ? mnt_want_write_file+0x24/0x50 btrfs_ioctl_snap_create_transid+0x16c/0x1a0 btrfs_ioctl_snap_create_v2+0x11e/0x1a0 btrfs_ioctl+0x1534/0x2c10 ? free_debug_processing+0x262/0x2a3 do_vfs_ioctl+0xa6/0x6b0 ? do_sys_open+0x188/0x220 ? syscall_trace_enter+0x1f8/0x330 ksys_ioctl+0x60/0x90 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x4a/0x1b0
In order to fix this we need to make sure anybody who calls commit_transaction has trans->dirty set so that they properly set the trans->transaction->aborted value properly so any waiters know bad things happened.
This was found while I was running generic/475 with my modified fsstress, it reproduced within a few runs. I ran with this patch all night and didn't see the problem again.
--- fs/btrfs/transaction.c | 8 ++++++++ 1 file changed, 8 insertions(+)
fs/btrfs/transaction.c +8 -0
@@ -2013,6 +2013,14 @@ int btrfs_commit_transaction(struct btrfASSERT (refcount_read (&trans->use_count) ==1 ); +/* +* Some places just start a transaction to commit it. We need to make +* sure that if this commit fails that the abort code actually marks the +* transaction as failed, so set trans->dirty to make the abort code do +* the right thing. +*/ + trans->dirty = true; +/* Stop the commit early if ->aborted is set */ if (unlikely (READ_ONCE (cur_trans->aborted))) { ret = cur_trans->aborted;
Greg Kroah-Hartman
maintainerPATCH 199/367 btrfs: drop log root for dropped roots
In reply to Greg Kroah-HartmanFrom: Josef Bacik <[email protected]>
commit 889bfa39086e86b52fcfaa04d72c95eaeb12f9a5 upstream.
If we fsync on a subvolume and create a log root for that volume, and then later delete that subvolume we'll never clean up its log root. Fix this by making switch_commit_roots free the log for any dropped roots we encounter. The extra churn is because we need a btrfs_trans_handle, not the btrfs_transaction.
--- fs/btrfs/transaction.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-)
fs/btrfs/transaction.c +12 -10
@@ -147,13 +147,14 @@ void btrfs_put_transaction(struct btrfs_ } } -static noinline void switch_commit_roots(struct btrfs_transaction *trans) +static noinline void switch_commit_roots(struct btrfs_trans_handle *trans) { +struct btrfs_transaction *cur_trans = trans->transaction;struct btrfs_fs_info *fs_info = trans->fs_info;struct btrfs_root *root, *tmp;down_write (&fs_info->commit_root_sem); -list_for_each_entry_safe (root, tmp, &trans->switch_commits, +list_for_each_entry_safe (root, tmp, &cur_trans->switch_commits, dirty_list) {list_del_init (&root->dirty_list);free_extent_buffer (root->commit_root);
@@ -165,16 +166,17 @@ static noinline void switch_commit_roots }/* We can free old roots now. */ -spin_lock (&trans->dropped_roots_lock); -while (!list_empty (&trans->dropped_roots)) { - root =list_first_entry (&trans->dropped_roots, +spin_lock (&cur_trans->dropped_roots_lock); +while (!list_empty (&cur_trans->dropped_roots)) { + root =list_first_entry (&cur_trans->dropped_roots, struct btrfs_root, root_list);list_del_init (&root->root_list); -spin_unlock (&trans->dropped_roots_lock); +spin_unlock (&cur_trans->dropped_roots_lock); +btrfs_free_log (trans, root);btrfs_drop_and_free_fs_root (fs_info, root); -spin_lock (&trans->dropped_roots_lock); +spin_lock (&cur_trans->dropped_roots_lock); } -spin_unlock (&trans->dropped_roots_lock); +spin_unlock (&cur_trans->dropped_roots_lock);up_write (&fs_info->commit_root_sem); }
@@ -1421,7 +1423,7 @@ static int qgroup_account_snapshot(struc ret =commit_cowonly_roots (trans);if (ret) goto out; -switch_commit_roots (trans->transaction); +switch_commit_roots (trans); ret =btrfs_write_and_wait_transaction (trans);if (ret) btrfs_handle_fs_error(fs_info, ret,
@@ -2309,7 +2311,7 @@ int btrfs_commit_transaction(struct btrflist_add_tail (&fs_info->chunk_root->dirty_list, &cur_trans->switch_commits); -switch_commit_roots (cur_trans); +switch_commit_roots (trans);ASSERT (list_empty (&cur_trans->dirty_bgs));ASSERT (list_empty (&cur_trans->io_bgs));
Greg Kroah-Hartman
maintainerPATCH 200/367 btrfs: free block groups after freeing fs trees
In reply to Greg Kroah-HartmanFrom: Josef Bacik <[email protected]>
commit 4e19443da1941050b346f8fc4c368aa68413bc88 upstream.
Sometimes when running generic/475 we would trip the WARN_ON(cache->reserved) check when free'ing the block groups on umount. This is because sometimes we don't commit the transaction because of IO errors and thus do not cleanup the tree logs until at umount time.
These blocks are still reserved until they are cleaned up, but they aren't cleaned up until _after_ we do the free block groups work. Fix this by moving the free after free'ing the fs roots, that way all of the tree logs are cleaned up and we have a properly cleaned fs. A bunch of loops of generic/475 confirmed this fixes the problem.
--- fs/btrfs/disk-io.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
fs/btrfs/disk-io.c +9 -2
@@ -4026,11 +4026,18 @@ void __cold close_ctree(struct btrfs_fs_invalidate_inode_pages2 (fs_info->btree_inode->i_mapping);btrfs_stop_all_workers (fs_info); -btrfs_free_block_groups (fs_info); -clear_bit (BTRFS_FS_OPEN , &fs_info->flags);free_root_pointers (fs_info, true); +/* +* We must free the block groups after dropping the fs_roots as we could +* have had an IO error and have left over tree log blocks that aren't +* cleaned up until the fs roots are freed. This makes the block group +* accounting appear to be wrong because there's pending reserved bytes, +* so make sure we do the block group cleanup afterwards. +*/ +btrfs_free_block_groups (fs_info); +iput (fs_info->btree_inode);#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
Greg Kroah-Hartman
maintainerPATCH 201/367 Btrfs: fix race between adding and putting tree mod seq elements and nodes
In reply to Greg Kroah-HartmanFrom: Filipe Manana <[email protected]>
commit 7227ff4de55d931bbdc156c8ef0ce4f100c78a5b upstream.
There is a race between adding and removing elements to the tree mod log list and rbtree that can lead to use-after-free problems.
Consider the following example that explains how/why the problems happens:
1) Task A has mod log element with sequence number 200. It currently is the only element in the mod log list;
2) Task A calls btrfs_put_tree_mod_seq() because it no longer needs to access the tree mod log. When it enters the function, it initializes 'min_seq' to (u64)-1. Then it acquires the lock 'tree_mod_seq_lock' before checking if there are other elements in the mod seq list. Since the list it empty, 'min_seq' remains set to (u64)-1. Then it unlocks the lock 'tree_mod_seq_lock';
3) Before task A acquires the lock 'tree_mod_log_lock', task B adds itself to the mod seq list through btrfs_get_tree_mod_seq() and gets a sequence number of 201;
4) Some other task, name it task C, modifies a btree and because there elements in the mod seq list, it adds a tree mod elem to the tree mod log rbtree. That node added to the mod log rbtree is assigned a sequence number of 202;
5) Task B, which is doing fiemap and resolving indirect back references, calls btrfs get_old_root(), with 'time_seq' == 201, which in turn calls tree_mod_log_search() - the search returns the mod log node from the rbtree with sequence number 202, created by task C;
6) Task A now acquires the lock 'tree_mod_log_lock', starts iterating the mod log rbtree and finds the node with sequence number 202. Since 202 is less than the previously computed 'min_seq', (u64)-1, it removes the node and frees it;
7) Task B still has a pointer to the node with sequence number 202, and it dereferences the pointer itself and through the call to __tree_mod_log_rewind(), resulting in a use-after-free problem.
This issue can be triggered sporadically with the test case generic/561 from fstests, and it happens more frequently with a higher number of duperemove processes. When it happens to me, it either freezes the VM or it produces a trace like the following before crashing:
[ 1245.321140] general protection fault: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC PTI [ 1245.321200] CPU: 1 PID: 26997 Comm: pool Not tainted 5.5.0-rc6-btrfs-next-52 #1 [ 1245.321235] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org 04/01/2014 [ 1245.321287] RIP: 0010:rb_next+0x16/0x50 [ 1245.321307] Code: .... [ 1245.321372] RSP: 0018:ffffa151c4d039b0 EFLAGS: 00010202 [ 1245.321388] RAX: 6b6b6b6b6b6b6b6b RBX: ffff8ae221363c80 RCX: 6b6b6b6b6b6b6b6b [ 1245.321409] RDX: 0000000000000001 RSI: 0000000000000000 RDI: ffff8ae221363c80 [ 1245.321439] RBP: ffff8ae20fcc4688 R08: 0000000000000002 R09: 0000000000000000 [ 1245.321475] R10: ffff8ae20b120910 R11: 00000000243f8bb1 R12: 0000000000000038 [ 1245.321506] R13: ffff8ae221363c80 R14: 000000000000075f R15: ffff8ae223f762b8 [ 1245.321539] FS: 00007fdee1ec7700(0000) GS:ffff8ae236c80000(0000) knlGS:0000000000000000 [ 1245.321591] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1245.321614] CR2: 00007fded4030c48 CR3: 000000021da16003 CR4: 00000000003606e0 [ 1245.321642] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 1245.321668] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 1245.321706] Call Trace: [ 1245.321798] __tree_mod_log_rewind+0xbf/0x280 [btrfs] [ 1245.321841] btrfs_search_old_slot+0x105/0xd00 [btrfs] [ 1245.321877] resolve_indirect_refs+0x1eb/0xc60 [btrfs] [ 1245.321912] find_parent_nodes+0x3dc/0x11b0 [btrfs] [ 1245.321947] btrfs_check_shared+0x115/0x1c0 [btrfs] [ 1245.321980] ? extent_fiemap+0x59d/0x6d0 [btrfs] [ 1245.322029] extent_fiemap+0x59d/0x6d0 [btrfs] [ 1245.322066] do_vfs_ioctl+0x45a/0x750 [ 1245.322081] ksys_ioctl+0x70/0x80 [ 1245.322092] ? trace_hardirqs_off_thunk+0x1a/0x1c [ 1245.322113] __x64_sys_ioctl+0x16/0x20 [ 1245.322126] do_syscall_64+0x5c/0x280 [ 1245.322139] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 1245.322155] RIP: 0033:0x7fdee3942dd7 [ 1245.322177] Code: .... [ 1245.322258] RSP: 002b:00007fdee1ec6c88 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 1245.322294] RAX: ffffffffffffffda RBX: 00007fded40210d8 RCX: 00007fdee3942dd7 [ 1245.322314] RDX: 00007fded40210d8 RSI: 00000000c020660b RDI: 0000000000000004 [ 1245.322337] RBP: 0000562aa89e7510 R08: 0000000000000000 R09: 00007fdee1ec6d44 [ 1245.322369] R10: 0000000000000073 R11: 0000000000000246 R12: 00007fdee1ec6d48 [ 1245.322390] R13: 00007fdee1ec6d40 R14: 00007fded40210d0 R15: 00007fdee1ec6d50 [ 1245.322423] Modules linked in: .... [ 1245.323443] ---[ end trace 01de1e9ec5dff3cd ]---
Fix this by ensuring that btrfs_put_tree_mod_seq() computes the minimum sequence number and iterates the rbtree while holding the lock 'tree_mod_log_lock' in write mode. Also get rid of the 'tree_mod_seq_lock' lock, since it is now redundant.
--- fs/btrfs/ctree.c | 8 ++------ fs/btrfs/ctree.h | 6 ++---- fs/btrfs/delayed-ref.c | 8 ++++---- fs/btrfs/disk-io.c | 1 - fs/btrfs/tests/btrfs-tests.c | 1 - 5 files changed, 8 insertions(+), 16 deletions(-)
fs/btrfs/ctree.c +2 -6
@@ -326,12 +326,10 @@ u64 btrfs_get_tree_mod_seq(struct btrfs_ struct seq_list *elem) {write_lock (&fs_info->tree_mod_log_lock); -spin_lock (&fs_info->tree_mod_seq_lock);if (!elem->seq) { elem->seq =btrfs_inc_tree_mod_seq (fs_info);list_add_tail (&elem->list, &fs_info->tree_mod_seq_list); } -spin_unlock (&fs_info->tree_mod_seq_lock);write_unlock (&fs_info->tree_mod_log_lock);return elem->seq;
@@ -351,7 +349,7 @@ void btrfs_put_tree_mod_seq(struct btrfsif (!seq_putting)return ; -spin_lock (&fs_info->tree_mod_seq_lock); +write_lock (&fs_info->tree_mod_log_lock);list_del (&elem->list); elem->seq =0 ;
@@ -362,19 +360,17 @@ void btrfs_put_tree_mod_seq(struct btrfs* blocker with lower sequence number exists, we * cannot remove anything from the log */ -spin_unlock (&fs_info->tree_mod_seq_lock); +write_unlock (&fs_info->tree_mod_log_lock);return ; } min_seq = cur_elem->seq; } } -spin_unlock (&fs_info->tree_mod_seq_lock);/* * anything that's lower than the lowest existing (read: blocked) * sequence number can be removed from the tree. */ -write_lock (&fs_info->tree_mod_log_lock); tm_root = &fs_info->tree_mod_log;for (node =rb_first (tm_root); node; node = next) { next =rb_next (node);
fs/btrfs/ctree.h +2 -4
@@ -673,14 +673,12 @@ struct btrfs_fs_info {atomic_t nr_delayed_iputs;wait_queue_head_t delayed_iputs_wait; -/* this protects tree_mod_seq_list */ -spinlock_t tree_mod_seq_lock;atomic64_t tree_mod_seq; -struct list_head tree_mod_seq_list; -/* this protects tree_mod_log */ +/* this protects tree_mod_log and tree_mod_seq_list */ rwlock_t tree_mod_log_lock;struct rb_root tree_mod_log; +struct list_head tree_mod_seq_list;atomic_t async_delalloc_pages;
fs/btrfs/delayed-ref.c +4 -4
@@ -492,7 +492,7 @@ void btrfs_merge_delayed_refs(struct btrif (head->is_data)return ; -spin_lock (&fs_info->tree_mod_seq_lock); +read_lock (&fs_info->tree_mod_log_lock);if (!list_empty (&fs_info->tree_mod_seq_list)) {struct seq_list *elem;
@@ -500,7 +500,7 @@ void btrfs_merge_delayed_refs(struct btr struct seq_list, list); seq = elem->seq; } -spin_unlock (&fs_info->tree_mod_seq_lock); +read_unlock (&fs_info->tree_mod_log_lock); again: for (node = rb_first_cached(&head->ref_tree); node;
@@ -518,7 +518,7 @@ int btrfs_check_delayed_seq(struct btrfsstruct seq_list *elem;int ret =0 ; -spin_lock (&fs_info->tree_mod_seq_lock); +read_lock (&fs_info->tree_mod_log_lock);if (!list_empty (&fs_info->tree_mod_seq_list)) { elem =list_first_entry (&fs_info->tree_mod_seq_list, struct seq_list, list);
@@ -531,7 +531,7 @@ int btrfs_check_delayed_seq(struct btrfs } } -spin_unlock (&fs_info->tree_mod_seq_lock); +read_unlock (&fs_info->tree_mod_log_lock);return ret; }
fs/btrfs/disk-io.c +0 -1
@@ -2691,7 +2691,6 @@ int __cold open_ctree(struct super_blockspin_lock_init (&fs_info->fs_roots_radix_lock);spin_lock_init (&fs_info->delayed_iput_lock);spin_lock_init (&fs_info->defrag_inodes_lock); -spin_lock_init (&fs_info->tree_mod_seq_lock);spin_lock_init (&fs_info->super_lock);spin_lock_init (&fs_info->buffer_lock);spin_lock_init (&fs_info->unused_bgs_lock);
fs/btrfs/tests/btrfs-tests.c +0 -1
@@ -121,7 +121,6 @@ struct btrfs_fs_info *btrfs_alloc_dummy_spin_lock_init (&fs_info->qgroup_lock);spin_lock_init (&fs_info->super_lock);spin_lock_init (&fs_info->fs_roots_radix_lock); -spin_lock_init (&fs_info->tree_mod_seq_lock);mutex_init (&fs_info->qgroup_ioctl_lock);mutex_init (&fs_info->qgroup_rescan_lock);rwlock_init (&fs_info->tree_mod_log_lock);
Greg Kroah-Hartman
maintainerPATCH 202/367 btrfs: flush write bio if we loop in extent_write_cache_pages
In reply to Greg Kroah-HartmanFrom: Josef Bacik <[email protected]>
commit 42ffb0bf584ae5b6b38f72259af1e0ee417ac77f upstream.
There exists a deadlock with range_cyclic that has existed forever. If we loop around with a bio already built we could deadlock with a writer who has the page locked that we're attempting to write but is waiting on a page in our bio to be written out. The task traces are as follows
PID: 1329874 TASK: ffff889ebcdf3800 CPU: 33 COMMAND: "kworker/u113:5"
#0 [ffffc900297bb658] __schedule at ffffffff81a4c33f
#1 [ffffc900297bb6e0] schedule at ffffffff81a4c6e3
#2 [ffffc900297bb6f8] io_schedule at ffffffff81a4ca42
#3 [ffffc900297bb708] __lock_page at ffffffff811f145b
#4 [ffffc900297bb798] __process_pages_contig at ffffffff814bc502 #5 [ffffc900297bb8c8] lock_delalloc_pages at ffffffff814bc684 #6 [ffffc900297bb900] find_lock_delalloc_range at ffffffff814be9ff #7 [ffffc900297bb9a0] writepage_delalloc at ffffffff814bebd0 #8 [ffffc900297bba18] __extent_writepage at ffffffff814bfbf2 #9 [ffffc900297bba98] extent_write_cache_pages at ffffffff814bffbd
PID: 2167901 TASK: ffff889dc6a59c00 CPU: 14 COMMAND:
"aio-dio-invalid"
#0 [ffffc9003b50bb18] __schedule at ffffffff81a4c33f #1 [ffffc9003b50bba0] schedule at ffffffff81a4c6e3 #2 [ffffc9003b50bbb8] io_schedule at ffffffff81a4ca42 #3 [ffffc9003b50bbc8] wait_on_page_bit at ffffffff811f24d6 #4 [ffffc9003b50bc60] prepare_pages at ffffffff814b05a7 #5 [ffffc9003b50bcd8] btrfs_buffered_write at ffffffff814b1359 #6 [ffffc9003b50bdb0] btrfs_file_write_iter at ffffffff814b5933 #7 [ffffc9003b50be38] new_sync_write at ffffffff8128f6a8 #8 [ffffc9003b50bec8] vfs_write at ffffffff81292b9d #9 [ffffc9003b50bf00] ksys_pwrite64 at ffffffff81293032
I used drgn to find the respective pages we were stuck on
page_entry.page 0xffffea00fbfc7500 index 8148 bit 15 pid 2167901 page_entry.page 0xffffea00f9bb7400 index 7680 bit 0 pid 1329874
As you can see the kworker is waiting for bit 0 (PG_locked) on index 7680, and aio-dio-invalid is waiting for bit 15 (PG_writeback) on index 8148. aio-dio-invalid has 7680, and the kworker epd looks like the following
crash> struct extent_page_data ffffc900297bbbb0
struct extent_page_data {
bio = 0xffff889f747ed830,
tree = 0xffff889eed6ba448,
extent_locked = 0,
sync_io = 0
}Probably worth mentioning as well that it waits for writeback of the page to complete while holding a lock on it (at prepare_pages()).
Using drgn I walked the bio pages looking for page 0xffffea00fbfc7500 which is the one we're waiting for writeback on
bio = Object(prog, 'struct bio', address=0xffff889f747ed830) for i in range(0, bio.bi_vcnt.value_()):
bv = bio.bi_io_vec[i]
if bv.bv_page.value_() == 0xffffea00fbfc7500:
print("FOUND IT")which validated what I suspected.
The fix for this is simple, flush the epd before we loop back around to the beginning of the file during writeout.
--- fs/btrfs/extent_io.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
fs/btrfs/extent_io.c +10 -1
@@ -4188,7 +4188,16 @@ retry:*/ scanned =1 ; index =0 ; - goto retry; + +/* +* If we're looping we could run into a page that is locked by a +* writer and that writer could be waiting on writeback for a +* page in our current bio, and thus deadlock, so flush the +* write bio here. +*/ + ret =flush_write_bio (epd); +if (!ret) + goto retry; }if (wbc->range_cyclic || (wbc->nr_to_write >0 && range_whole))
Greg Kroah-Hartman
maintainerPATCH 203/367 btrfs: Correctly handle empty trees in find_first_clear_extent_bit
In reply to Greg Kroah-HartmanFrom: Nikolay Borisov <[email protected]>
commit 5750c37523a2c8cbb450b9ef31e21c2ba876b05e upstream.
Raviu reported that running his regular fs_trim segfaulted with the following backtrace:
[ 237.525947] assertion failed: prev, in ../fs/btrfs/extent_io.c:1595 [ 237.525984] ------------[ cut here ]------------ [ 237.525985] kernel BUG at ../fs/btrfs/ctree.h:3117! [ 237.525992] invalid opcode: 0000 [#1] SMP PTI [ 237.525998] CPU: 4 PID: 4423 Comm: fstrim Tainted: G U OE 5.4.14-8-vanilla #1 [ 237.526001] Hardware name: ASUSTeK COMPUTER INC. [ 237.526044] RIP: 0010:assfail.constprop.58+0x18/0x1a [btrfs] [ 237.526079] Call Trace: [ 237.526120] find_first_clear_extent_bit+0x13d/0x150 [btrfs] [ 237.526148] btrfs_trim_fs+0x211/0x3f0 [btrfs] [ 237.526184] btrfs_ioctl_fitrim+0x103/0x170 [btrfs] [ 237.526219] btrfs_ioctl+0x129a/0x2ed0 [btrfs] [ 237.526227] ? filemap_map_pages+0x190/0x3d0 [ 237.526232] ? do_filp_open+0xaf/0x110 [ 237.526238] ? _copy_to_user+0x22/0x30 [ 237.526242] ? cp_new_stat+0x150/0x180 [ 237.526247] ? do_vfs_ioctl+0xa4/0x640 [ 237.526278] ? btrfs_ioctl_get_supported_features+0x30/0x30 [btrfs] [ 237.526283] do_vfs_ioctl+0xa4/0x640 [ 237.526288] ? __do_sys_newfstat+0x3c/0x60 [ 237.526292] ksys_ioctl+0x70/0x80 [ 237.526297] __x64_sys_ioctl+0x16/0x20 [ 237.526303] do_syscall_64+0x5a/0x1c0 [ 237.526310] entry_SYSCALL_64_after_hwframe+0x49/0xbe
That was due to btrfs_fs_device::aloc_tree being empty. Initially I thought this wasn't possible and as a percaution have put the assert in find_first_clear_extent_bit. Turns out this is indeed possible and could happen when a file system with SINGLE data/metadata profile has a 2nd device added. Until balance is run or a new chunk is allocated on this device it will be completely empty.
In this case find_first_clear_extent_bit should return the full range [0, -1ULL] and let the caller handle this i.e for trim the end will be capped at the size of actual device.
--- fs/btrfs/extent_io.c | 32 ++++++++++++++++++-------------- fs/btrfs/tests/extent-io-tests.c | 9 +++++++++ 2 files changed, 27 insertions(+), 14 deletions(-)
fs/btrfs/extent_io.c +18 -14
@@ -1593,21 +1593,25 @@ void find_first_clear_extent_bit(struct/* Find first extent with bits cleared */ while (1 ) { node =__etree_search (tree, start, &next, &prev,NULL ,NULL ); -if (!node) { +if (!node && !next && !prev) { +/* +* Tree is completely empty, send full range and let +* caller deal with it +*/ + *start_ret =0 ; + *end_ret =-1 ; + goto out; + }else if (!node && !next) { +/* +* We are past the last allocated chunk, set start at +* the end of the last extent. +*/ + state = rb_entry(prev, struct extent_state, rb_node); + *start_ret = state->end +1 ; + *end_ret =-1 ; + goto out; + }else if (!node) { node = next; -if (!node) { -/* -* We are past the last allocated chunk, -* set start at the end of the last extent. The -* device alloc tree should never be empty so -* prev is always set. -*/ -ASSERT (prev); - state = rb_entry(prev, struct extent_state, rb_node); - *start_ret = state->end +1 ; - *end_ret =-1 ; - goto out; - } }/* * At this point 'node' either contains 'start' or start is
fs/btrfs/tests/extent-io-tests.c +9 -0
@@ -441,8 +441,17 @@ static int test_find_first_clear_extent_int ret = -EINVAL ;test_msg ("running find_first_clear_extent_bit test" ); +extent_io_tree_init (NULL , &tree,IO_TREE_SELFTEST ,NULL ); +/* Test correct handling of empty tree */ +find_first_clear_extent_bit (&tree,0 , &start, &end,CHUNK_TRIMMED ); +if (start !=0 || end !=-1 ) { +test_err ( +"error getting a range from completely empty tree: start %llu end %llu" , + start, end); + goto out; + }/* * Set 1M-4M alloc/discard and 32M-64M thus leaving a hole between * 4M-32M
Greg Kroah-Hartman
maintainerPATCH 204/367 Btrfs: send, fix emission of invalid clone operations within the same file
In reply to Greg Kroah-HartmanFrom: Filipe Manana <[email protected]>
commit 9722b10148504c4153a74a9c89725af271e490fc upstream.
When doing an incremental send and a file has extents shared with itself at different file offsets, it's possible for send to emit clone operations that will fail at the destination because the source range goes beyond the file's current size. This happens when the file size has increased in the send snapshot, there is a hole between the shared extents and both shared extents are at file offsets which are greater the file's size in the parent snapshot.
Example:
$ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt/sdb
$ xfs_io -f -c "pwrite -S 0xf1 0 64K" /mnt/sdb/foobar $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/base $ btrfs send -f /tmp/1.snap /mnt/sdb/base
# Create a 320K extent at file offset 512K.
$ xfs_io -c "pwrite -S 0xab 512K 64K" /mnt/sdb/foobar $ xfs_io -c "pwrite -S 0xcd 576K 64K" /mnt/sdb/foobar $ xfs_io -c "pwrite -S 0xef 640K 64K" /mnt/sdb/foobar $ xfs_io -c "pwrite -S 0x64 704K 64K" /mnt/sdb/foobar $ xfs_io -c "pwrite -S 0x73 768K 64K" /mnt/sdb/foobar
# Clone part of that 320K extent into a lower file offset (192K). # This file offset is greater than the file's size in the parent
# snapshot (64K). Also the clone range is a bit behind the offset of # the 320K extent so that we leave a hole between the shared extents. $ xfs_io -c "reflink /mnt/sdb/foobar 448K 192K 192K" /mnt/sdb/foobar
$ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/incr $ btrfs send -p /mnt/sdb/base -f /tmp/2.snap /mnt/sdb/incr
$ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt/sdc
$ btrfs receive -f /tmp/1.snap /mnt/sdc $ btrfs receive -f /tmp/2.snap /mnt/sdc ERROR: failed to clone extents to foobar: Invalid argument
The problem is that after processing the extent at file offset 256K, which refers to the first 128K of the 320K extent created by the buffered write operations, we have 'cur_inode_next_write_offset' set to 384K, which corresponds to the end offset of the partially shared extent (256K + 128K) and to the current file size in the receiver. Then when we process the extent at offset 512K, we do extent backreference iteration to figure out if we can clone the extent from some other inode or from the same inode, and we consider the extent at offset 256K of the same inode as a valid source for a clone operation, which is not correct because at that point the current file size in the receiver is 384K, which corresponds to the end of last processed extent (at file offset 256K), so using a clone source range from 256K to 256K + 320K is invalid because that goes past the current size of the file (384K) - this makes the receiver get an -EINVAL error when attempting the clone operation.
So fix this by excluding clone sources that have a range that goes beyond the current file size in the receiver when iterating extent backreferences.
A test case for fstests follows soon.
--- fs/btrfs/send.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/btrfs/send.c +2 -1
@@ -1269,7 +1269,8 @@ static int __iterate_backrefs(u64 ino, u* destination of the stream. */ if (ino == bctx->cur_objectid && - offset >= bctx->sctx->cur_inode_next_write_offset) + offset + bctx->extent_len > + bctx->sctx->cur_inode_next_write_offset)return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 205/367 ARM: tegra: Enable PLLP bypass during Tegra124 LP1
In reply to Greg Kroah-HartmanFrom: Stephen Warren <[email protected]>
commit 1a3388d506bf5b45bb283e6a4c4706cfb4897333 upstream.
For a little over a year, U-Boot has configured the flow controller to perform automatic RAM re-repair on off->on power transitions of the CPU rail[1]. This is mandatory for correct operation of Tegra124. However, RAM re-repair relies on certain clocks, which the kernel must enable and leave running. PLLP is one of those clocks. This clock is shut down during LP1 in order to save power. Enable bypass (which I believe routes osc_div_clk, essentially the crystal clock, to the PLL output) so that this clock signal toggles even though the PLL is not active. This is required so that LP1 power mode (system suspend) operates correctly.
The bypass configuration must then be undone when resuming from LP1, so that all peripheral clocks run at the expected rate. Without this, many peripherals won't work correctly; for example, the UART baud rate would be incorrect.
NVIDIA's downstream kernel code only does this if not compiled for Tegra30, so the added code is made conditional upon the chip ID. NVIDIA's downstream code makes this change conditional upon the active CPU cluster. The upstream kernel currently doesn't support cluster switching, so this patch doesn't test the active CPU cluster ID.
[1] 3cc7942a4ae5 ARM: tegra: implement RAM repair
--- arch/arm/mach-tegra/sleep-tegra30.S | 11 +++++++++++ 1 file changed, 11 insertions(+)
arch/arm/mach-tegra/sleep-tegra30.S +11 -0
@@ -370,6 +370,14 @@ _pll_m_c_x_done: pll_locked r1, r0, CLK_RESET_PLLC_BASE pll_locked r1, r0, CLK_RESET_PLLX_BASE + tegra_get_soc_id TEGRA_APB_MISC_BASE, r1 + cmp r1, #TEGRA30 + beq 1f + ldr r1, [r0, #CLK_RESET_PLLP_BASE] + bic r1, r1, #(1<<31) @ disable PllP bypass + str r1, [r0, #CLK_RESET_PLLP_BASE] +1: + mov32 r7, TEGRA_TMRUS_BASE ldr r1, [r7] add r1, r1, #LOCK_DELAY
@@ -630,7 +638,10 @@ tegra30_switch_cpu_to_clk32k: str r0, [r4, #PMC_PLLP_WB0_OVERRIDE] /* disable PLLP, PLLA, PLLC and PLLX */ + tegra_get_soc_id TEGRA_APB_MISC_BASE, r1 + cmp r1, #TEGRA30 ldr r0, [r5, #CLK_RESET_PLLP_BASE] + orrne r0, r0, #(1 << 31) @ enable PllP bypass on fast cluster bic r0, r0, #(1 << 30) str r0, [r5, #CLK_RESET_PLLP_BASE] ldr r0, [r5, #CLK_RESET_PLLA_BASE]
Greg Kroah-Hartman
maintainerPATCH 206/367 iwlwifi: dont throw error when trying to remove IGTK
In reply to Greg Kroah-HartmanFrom: Luca Coelho <[email protected]>
commit 197288d5ba8a5289f22d3aeb4fca3824bfd9b4af upstream.
The IGTK keys are only removed by mac80211 after it has already removed the AP station. This causes the driver to throw an error because mac80211 is trying to remove the IGTK when the station doesn't exist anymore.
The firmware is aware that the station has been removed and can deal with it the next time we try to add an IGTK for a station, so we shouldn't try to remove the key if the station ID is IWL_MVM_INVALID_STA. Do this by removing the check for mvm_sta before calling iwl_mvm_send_sta_igtk() and check return from that function gracefully if the station ID is invalid.
--- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/wireless/intel/iwlwifi/mvm/sta.c +7 -3
@@ -3320,6 +3320,10 @@ static int iwl_mvm_send_sta_igtk(struct igtk_cmd.sta_id =cpu_to_le32 (sta_id);if (remove_key) { +/* This is a valid situation for IGTK */ +if (sta_id ==IWL_MVM_INVALID_STA ) +return 0 ; + igtk_cmd.ctrl_flags |=cpu_to_le32 (STA_KEY_NOT_VALID ); }else {struct ieee80211_key_seq seq;
@@ -3574,9 +3578,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvIWL_DEBUG_WEP (mvm,"mvm remove dynamic key: idx=%d sta=%d\n" , keyconf->keyidx, sta_id); -if (mvm_sta && (keyconf->cipher ==WLAN_CIPHER_SUITE_AES_CMAC || - keyconf->cipher ==WLAN_CIPHER_SUITE_BIP_GMAC_128 || - keyconf->cipher ==WLAN_CIPHER_SUITE_BIP_GMAC_256 )) +if (keyconf->cipher ==WLAN_CIPHER_SUITE_AES_CMAC || + keyconf->cipher ==WLAN_CIPHER_SUITE_BIP_GMAC_128 || + keyconf->cipher ==WLAN_CIPHER_SUITE_BIP_GMAC_256 )return iwl_mvm_send_sta_igtk (mvm, keyconf, sta_id, true);if (!__test_and_clear_bit (keyconf->hw_key_idx, mvm->fw_key_table)) {
Greg Kroah-Hartman
maintainerPATCH 207/367 mwifiex: fix unbalanced locking in mwifiex_process_country_ie()
In reply to Greg Kroah-HartmanFrom: Brian Norris <[email protected]>
commit 65b1aae0d9d5962faccc06bdb8e91a2a0b09451c upstream.
We called rcu_read_lock(), so we need to call rcu_read_unlock() before we return.
--- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +1 -0
@@ -232,6 +232,7 @@ static int mwifiex_process_country_ie(stif (country_ie_len > (IEEE80211_COUNTRY_STRING_LEN +MWIFIEX_MAX_TRIPLET_802_11D )) { +rcu_read_unlock ();mwifiex_dbg (priv->adapter,ERROR ,"11D: country_ie_len overflow!, deauth AP\n" );return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 208/367 sunrpc: expiry_time should be seconds not timeval
In reply to Greg Kroah-HartmanFrom: Roberto Bergantinos Corpas <[email protected]>
commit 3d96208c30f84d6edf9ab4fac813306ac0d20c10 upstream.
When upcalling gssproxy, cache_head.expiry_time is set as a timeval, not seconds since boot. As such, RPC cache expiry logic will not clean expired objects created under auth.rpcsec.context cache.
This has proven to cause kernel memory leaks on field. Using 64 bit variants of getboottime/timespec
Expiration times have worked this way since 2010's c5b29f885afe "sunrpc:
use seconds since boot in expiry cache". The gssproxy code introduced in 2012 added gss_proxy_save_rsc and introduced the bug. That's a while for this to lurk, but it required a bit of an extreme case to make it obvious.
--- net/sunrpc/auth_gss/svcauth_gss.c | 4 ++++ 1 file changed, 4 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +4 -0
@@ -1248,6 +1248,7 @@ static int gss_proxy_save_rsc(struct cacdprintk ("RPC: No creds found!\n" ); goto out; }else { +struct timespec64 boot;/* steal creds */ rsci.cred = ud->creds;
@@ -1268,6 +1269,9 @@ static int gss_proxy_save_rsc(struct cac &expiry, GFP_KERNEL);if (status) goto out; + +getboottime64 (&boot); + expiry -= boot.tv_sec; } rsci.h.expiry_time = expiry;
Greg Kroah-Hartman
maintainerPATCH 209/367 gfs2: fix gfs2_find_jhead that returns uninitialized jhead with seq 0
In reply to Greg Kroah-HartmanFrom: Abhi Das <[email protected]>
commit 7582026f6f3588ecebd281965c8a71aff6fb6158 upstream.
When the first log header in a journal happens to have a sequence number of 0, a bug in gfs2_find_jhead() causes it to prematurely exit, and return an uninitialized jhead with seq 0. This can cause failures in the caller. For instance, a mount fails in one test case.
The correct behavior is for it to continue searching through the journal to find the correct journal head with the highest sequence number.
--- fs/gfs2/lops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/gfs2/lops.c +1 -1
@@ -422,7 +422,7 @@ static bool gfs2_jhead_pg_srch(struct gffor (offset =0 ; offset <PAGE_SIZE ; offset += sdp->sd_sb.sb_bsize) {if (!__get_log_header (sdp, kaddr + offset,0 , &lh)) { -if (lh.lh_sequence > head->lh_sequence) +if (lh.lh_sequence >= head->lh_sequence) *head = lh;else { ret = true;
Greg Kroah-Hartman
maintainerPATCH 210/367 gfs2: move setting current->backing_dev_info
In reply to Greg Kroah-HartmanFrom: Christoph Hellwig <[email protected]>
commit 4c0e8dda608a51855225c611b5c6b442f95fbc56 upstream.
Set current->backing_dev_info just around the buffered write calls to prepare for the next fix.
--- fs/gfs2/file.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
fs/gfs2/file.c +10 -11
@@ -867,18 +867,15 @@ static ssize_t gfs2_file_write_iter(struinode_lock (inode); ret =generic_write_checks (iocb, from);if (ret <=0 ) - goto out; - -/* We can write back this queue in page reclaim */ - current->backing_dev_info =inode_to_bdi (inode); + goto out_unlock; ret =file_remove_privs (file);if (ret) - goto out2; + goto out_unlock; ret =file_update_time (file);if (ret) - goto out2; + goto out_unlock;if (iocb->ki_flags &IOCB_DIRECT ) {struct address_space *mapping = file->f_mapping;
@@ -887,11 +884,13 @@ static ssize_t gfs2_file_write_iter(stru written =gfs2_file_direct_write (iocb, from);if (written <0 || !iov_iter_count (from)) - goto out2; + goto out_unlock; + current->backing_dev_info =inode_to_bdi (inode); ret =iomap_file_buffered_write (iocb, from, &gfs2_iomap_ops); + current->backing_dev_info =NULL ;if (unlikely (ret <0 )) - goto out2; + goto out_unlock; buffered = ret;/*
@@ -915,14 +914,14 @@ static ssize_t gfs2_file_write_iter(stru*/ } }else { + current->backing_dev_info =inode_to_bdi (inode); ret =iomap_file_buffered_write (iocb, from, &gfs2_iomap_ops); + current->backing_dev_info =NULL ;if (likely (ret >0 )) iocb->ki_pos += ret; } -out2: - current->backing_dev_info =NULL ; -out: +out_unlock:inode_unlock (inode);if (likely (ret >0 )) {/* Handle various SYNC-type writes */
Greg Kroah-Hartman
maintainerPATCH 211/367 gfs2: fix O_SYNC write handling
In reply to Greg Kroah-HartmanFrom: Andreas Gruenbacher <[email protected]>
commit 6e5e41e2dc4e4413296d5a4af54ac92d7cd52317 upstream.
In gfs2_file_write_iter, for direct writes, the error checking in the buffered write fallback case is incomplete. This can cause inode write errors to go undetected. Fix and clean up gfs2_file_write_iter along the way.
Based on a proposed fix by Christoph Hellwig <[email protected]>.
--- fs/gfs2/file.c | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-)
fs/gfs2/file.c +21 -30
@@ -847,7 +847,7 @@ static ssize_t gfs2_file_write_iter(strustruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct gfs2_inode *ip =GFS2_I (inode); -ssize_t written =0 , ret; +ssize_t ret; ret =gfs2_rsqa_alloc (ip);if (ret)
@@ -879,55 +879,46 @@ static ssize_t gfs2_file_write_iter(struif (iocb->ki_flags &IOCB_DIRECT ) {struct address_space *mapping = file->f_mapping; -loff_t pos, endbyte; -ssize_t buffered; +ssize_t buffered, ret2; - written =gfs2_file_direct_write (iocb, from); -if (written <0 || !iov_iter_count (from)) + ret =gfs2_file_direct_write (iocb, from); +if (ret <0 || !iov_iter_count (from)) goto out_unlock; + iocb->ki_flags |=IOCB_DSYNC ; current->backing_dev_info =inode_to_bdi (inode); - ret =iomap_file_buffered_write (iocb, from, &gfs2_iomap_ops); + buffered =iomap_file_buffered_write (iocb, from, &gfs2_iomap_ops); current->backing_dev_info =NULL ; -if (unlikely (ret <0 )) +if (unlikely (buffered <=0 )) goto out_unlock; - buffered = ret;/* * We need to ensure that the page cache pages are written to * disk and invalidated to preserve the expected O_DIRECT -* semantics. +* semantics. If the writeback or invalidate fails, only report +* the direct I/O range as we don't know if the buffered pages +* made it to disk. */ - pos = iocb->ki_pos; - endbyte = pos + buffered -1 ; - ret =filemap_write_and_wait_range (mapping, pos, endbyte); -if (!ret) { - iocb->ki_pos += buffered; - written += buffered; -invalidate_mapping_pages (mapping, - pos >>PAGE_SHIFT , - endbyte >>PAGE_SHIFT ); - }else { -/* -* We don't know how much we wrote, so just return -* the number of bytes which were direct-written -*/ - } + iocb->ki_pos += buffered; + ret2 =generic_write_sync (iocb, buffered); +invalidate_mapping_pages (mapping, + (iocb->ki_pos - buffered) >>PAGE_SHIFT , + (iocb->ki_pos -1 ) >>PAGE_SHIFT ); +if (!ret || ret2 >0 ) + ret += ret2; }else { current->backing_dev_info =inode_to_bdi (inode); ret =iomap_file_buffered_write (iocb, from, &gfs2_iomap_ops); current->backing_dev_info =NULL ; -if (likely (ret >0 )) +if (likely (ret >0 )) { iocb->ki_pos += ret; + ret =generic_write_sync (iocb, ret); + } } out_unlock:inode_unlock (inode); -if (likely (ret >0 )) { -/* Handle various SYNC-type writes */ - ret =generic_write_sync (iocb, ret); - } -return written ? written : ret; +return ret; } static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
Greg Kroah-Hartman
maintainerPATCH 212/367 drm: atmel-hlcdc: use double rate for pixel clock only if supported
In reply to Greg Kroah-HartmanFrom: Claudiu Beznea <[email protected]>
commit 07acf4bafe81dd37eff3fbcfbbdbc48084bc202b upstream.
Doubled system clock should be used as pixel cock source only if this is supported. This is emphasized by the value of atmel_hlcdc_crtc::dc::desc::fixed_clksrc.
--- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +3 -3
@@ -95,14 +95,14 @@ static void atmel_hlcdc_crtc_mode_set_no (adj->crtc_hdisplay -1 ) | ((adj->crtc_vdisplay - 1) << 16)); + prate =clk_get_rate (crtc->dc->hlcdc->sys_clk); + mode_rate = adj->crtc_clock *1000 ;if (!crtc->dc->desc->fixed_clksrc) { + prate *=2 ; cfg |=ATMEL_HLCDC_CLKSEL ; mask |=ATMEL_HLCDC_CLKSEL ; } - prate =2 *clk_get_rate (crtc->dc->hlcdc->sys_clk); - mode_rate = adj->crtc_clock *1000 ; - div =DIV_ROUND_UP (prate, mode_rate);if (div <2 ) { div =2 ;
Greg Kroah-Hartman
maintainerPATCH 213/367 drm: atmel-hlcdc: enable clock before configuring timing engine
In reply to Greg Kroah-HartmanFrom: Claudiu Beznea <[email protected]>
commit 2c1fb9d86f6820abbfaa38a6836157c76ccb4e7b upstream.
Changing pixel clock source without having this clock source enabled will block the timing engine and the next operations after (in this case setting ATMEL_HLCDC_CFG(5) settings in atmel_hlcdc_crtc_mode_set_nofb() will fail). It is recomended (although in datasheet this is not present) to actually enabled pixel clock source before doing any changes on timing enginge (only SAM9X60 datasheet specifies that the peripheral clock and pixel clock must be enabled before using LCD controller).
--- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +7 -1
@@ -73,7 +73,11 @@ static void atmel_hlcdc_crtc_mode_set_nounsigned long prate;unsigned int mask =ATMEL_HLCDC_CLKDIV_MASK |ATMEL_HLCDC_CLKPOL ;unsigned int cfg =0 ; -int div; +int div, ret; + + ret =clk_prepare_enable (crtc->dc->hlcdc->sys_clk); +if (ret) +return ; vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
@@ -147,6 +151,8 @@ static void atmel_hlcdc_crtc_mode_set_noATMEL_HLCDC_VSPSU |ATMEL_HLCDC_VSPHO |ATMEL_HLCDC_GUARDTIME_MASK |ATMEL_HLCDC_MODE_MASK , cfg); + +clk_disable_unprepare (crtc->dc->hlcdc->sys_clk); } static enum drm_mode_status
Greg Kroah-Hartman
maintainerPATCH 214/367 drm: atmel-hlcdc: prefer a lower pixel-clock than requested
In reply to Greg Kroah-HartmanFrom: Peter Rosin <[email protected]>
commit 51a19d150b520f6cb42143f3bdffacd3c33d7ac5 upstream.
The intention was to only select a higher pixel-clock rate than the requested, if a slight overclocking would result in a rate significantly closer to the requested rate than if the conservative lower pixel-clock rate is selected. The fixed patch has the logic the other way around and actually prefers the higher frequency. Fix that.
--- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +2 -2
@@ -121,8 +121,8 @@ static void atmel_hlcdc_crtc_mode_set_noint div_low = prate / mode_rate;if (div_low >=2 && - ((prate / div_low - mode_rate) < -10 * (mode_rate - prate / div))) + (10 * (prate / div_low - mode_rate) < + (mode_rate - prate / div)))/* * At least 10 times better when using a higher * frequency than requested, instead of a lower.
Greg Kroah-Hartman
maintainerPATCH 215/367 drm/rect: Avoid division by zero
In reply to Greg Kroah-HartmanFrom: Ville Syrjälä <[email protected]>
commit 433480c1afd44f3e1e664b85063d98cefeefa0ed upstream.
Check for zero width/height destination rectangle in drm_rect_clip_scaled() to avoid a division by zero.
Testcase: igt/kms_selftest/drm_rect_clip_scaled_div_by_zero
--- drivers/gpu/drm/drm_rect.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_rect.c +6 -1
@@ -54,7 +54,12 @@ EXPORT_SYMBOL(drm_rect_intersect);static u32 clip_scaled (u32 src,u32 dst,u32 clip) { -u64 tmp =mul_u32_u32 (src, dst - clip); +u64 tmp; + +if (dst ==0 ) +return 0 ; + + tmp =mul_u32_u32 (src, dst - clip);/* * Round toward 1.0 when clipping so that we don't accidentally
Greg Kroah-Hartman
maintainerPATCH 216/367 media: iguanair: fix endpoint sanity check
In reply to Greg Kroah-HartmanFrom: Johan Hovold <[email protected]>
commit 1b257870a78b0a9ce98fdfb052c58542022ffb5b upstream.
Make sure to use the current alternate setting, which need not be the first one by index, when verifying the endpoint descriptors and initialising the URBs.
Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on.
--- drivers/media/rc/iguanair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/rc/iguanair.c +1 -1
@@ -413,7 +413,7 @@ static int iguanair_probe(struct usb_intint ret, pipein, pipeout;struct usb_host_interface *idesc; - idesc = intf->altsetting; + idesc = intf->cur_altsetting;if (idesc->desc.bNumEndpoints <2 )return -ENODEV ;
Greg Kroah-Hartman
maintainerPATCH 217/367 media: rc: ensure lirc is initialized before registering input device
In reply to Greg Kroah-HartmanFrom: Sean Young <[email protected]>
commit 080d89f522e2baddb4fbbd1af4b67b5f92537ef8 upstream.
Once rc_open is called on the input device, lirc events can be delivered. Ensure lirc is ready to do so else we might get this:
Registered IR keymap rc-hauppauge
rc rc0: Hauppauge WinTV PVR-350 as /devices/pci0000:00/0000:00:1e.0/0000:04:00.0/i2c-0/0-0018/rc/rc0
input: Hauppauge WinTV PVR-350 as /devices/pci0000:00/0000:00:1e.0/0000:04:00.0/i2c-0/0-0018/rc/rc0/input9
BUG: kernel NULL pointer dereference, address: 0000000000000038 PGD 0 P4D 0 Oops: 0000 [#1] SMP PTI CPU: 1 PID: 17 Comm: kworker/1:0 Not tainted 5.3.11-300.fc31.x86_64 #1 Hardware name: /DG43NB, BIOS NBG4310H.86A.0096.2009.0903.1845 09/03/2009 Workqueue: events ir_work [ir_kbd_i2c] RIP: 0010:ir_lirc_scancode_event+0x3d/0xb0 Code: a6 b4 07 00 00 49 81 c6 b8 07 00 00 55 53 e8 ba a7 9d ff 4c 89 e7 49 89 45 00 e8 5e 7a 25 00 49 8b 1e 48 89 c5 4c 39 f3 74 58 <8b> 43 38 8b 53 40 89 c1 2b 4b 3c 39 ca 72 41 21 d0 49 8b 7d 00 49 RSP: 0018:ffffaae2000b3d88 EFLAGS: 00010017 RAX: 0000000000000002 RBX: 0000000000000000 RCX: 0000000000000019 RDX: 0000000000000001 RSI: 006e801b1f26ce6a RDI: ffff9e39797c37b4 RBP: 0000000000000002 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000001 R12: ffff9e39797c37b4 R13: ffffaae2000b3db8 R14: ffff9e39797c37b8 R15: ffff9e39797c33d8 FS: 0000000000000000(0000) GS:ffff9e397b680000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000038 CR3: 0000000035844000 CR4: 00000000000006e0 Call Trace: ir_do_keydown+0x8e/0x2b0 rc_keydown+0x52/0xc0 ir_work+0xb8/0x130 [ir_kbd_i2c] process_one_work+0x19d/0x340 worker_thread+0x50/0x3b0 kthread+0xfb/0x130 ? process_one_work+0x340/0x340 ? kthread_park+0x80/0x80 ret_from_fork+0x35/0x40 Modules linked in: rc_hauppauge tuner msp3400 saa7127 saa7115 ivtv(+)
tveeprom cx2341x v4l2_common videodev mc i2c_algo_bit ir_kbd_i2c ip_tables firewire_ohci e1000e serio_raw firewire_core ata_generic crc_itu_t pata_acpi pata_jmicron fuse
CR2: 0000000000000038
---[ end trace c67c2697a99fa74b ]--- RIP: 0010:ir_lirc_scancode_event+0x3d/0xb0 Code: a6 b4 07 00 00 49 81 c6 b8 07 00 00 55 53 e8 ba a7 9d ff 4c 89 e7 49 89 45 00 e8 5e 7a 25 00 49 8b 1e 48 89 c5 4c 39 f3 74 58 <8b> 43 38 8b 53 40 89 c1 2b 4b 3c 39 ca 72 41 21 d0 49 8b 7d 00 49 RSP: 0018:ffffaae2000b3d88 EFLAGS: 00010017 RAX: 0000000000000002 RBX: 0000000000000000 RCX: 0000000000000019 RDX: 0000000000000001 RSI: 006e801b1f26ce6a RDI: ffff9e39797c37b4 RBP: 0000000000000002 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000001 R12: ffff9e39797c37b4 R13: ffffaae2000b3db8 R14: ffff9e39797c37b8 R15: ffff9e39797c33d8 FS: 0000000000000000(0000) GS:ffff9e397b680000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000038 CR3: 0000000035844000 CR4: 00000000000006e0 rc rc0: lirc_dev: driver ir_kbd_i2c registered at minor = 0, scancode
receiver, no transmitter
tuner-simple 0-0061: creating new instance
tuner-simple 0-0061: type set to 2 (Philips NTSC (FI1236,FM1236 and compatibles))
ivtv0: Registered device video0 for encoder MPG (4096 kB)
ivtv0: Registered device video32 for encoder YUV (2048 kB)
ivtv0: Registered device vbi0 for encoder VBI (1024 kB)
ivtv0: Registered device video24 for encoder PCM (320 kB)
ivtv0: Registered device radio0 for encoder radio
ivtv0: Registered device video16 for decoder MPG (1024 kB)
ivtv0: Registered device vbi8 for decoder VBI (64 kB)
ivtv0: Registered device vbi16 for decoder VOUT
ivtv0: Registered device video48 for decoder YUV (1024 kB)
--- drivers/media/rc/rc-main.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
drivers/media/rc/rc-main.c +16 -11
@@ -1891,23 +1891,28 @@ int rc_register_device(struct rc_dev *de dev->registered = true; -if (dev->driver_type !=RC_DRIVER_IR_RAW_TX ) { - rc =rc_setup_rx_device (dev); -if (rc) - goto out_dev; - } - -/* Ensure that the lirc kfifo is setup before we start the thread */ +/* +* once the the input device is registered in rc_setup_rx_device, +* userspace can open the input device and rc_open() will be called +* as a result. This results in driver code being allowed to submit +* keycodes with rc_keydown, so lirc must be registered first. +*/ if (dev->allowed_protocols !=RC_PROTO_BIT_CEC ) { rc =ir_lirc_register (dev);if (rc <0 ) - goto out_rx; + goto out_dev; + } + +if (dev->driver_type !=RC_DRIVER_IR_RAW_TX ) { + rc =rc_setup_rx_device (dev); +if (rc) + goto out_lirc; }if (dev->driver_type ==RC_DRIVER_IR_RAW ) { rc =ir_raw_event_register (dev);if (rc <0 ) - goto out_lirc; + goto out_rx; } dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor,
@@ -1915,11 +1920,11 @@ int rc_register_device(struct rc_dev *dereturn 0 ; +out_rx: +rc_free_rx_device (dev); out_lirc:if (dev->allowed_protocols !=RC_PROTO_BIT_CEC )ir_lirc_unregister (dev); -out_rx: -rc_free_rx_device (dev); out_dev:device_del (&dev->dev); out_rx_free:
Greg Kroah-Hartman
maintainerPATCH 218/367 tools/kvm_stat: Fix kvm_exit filter name
In reply to Greg Kroah-HartmanFrom: Gavin Shan <[email protected]>
commit 5fcf3a55a62afb0760ccb6f391d62f20bce4a42f upstream.
The filter name is fixed to "exit_reason" for some kvm_exit events, no matter what architect we have. Actually, the filter name ("exit_reason") is only applicable to x86, meaning it's broken on other architects including aarch64.
This fixes the issue by providing various kvm_exit filter names, depending on architect we're on. Afterwards, the variable filter name is picked and applied through ioctl(fd, SET_FILTER).
--- tools/kvm/kvm_stat/kvm_stat | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
tools/kvm/kvm_stat/kvm_stat +6 -2
@@ -270,6 +270,7 @@ class ArchX86(Arch): def __init__(self, exit_reasons): self.sc_perf_evt_open = 298 self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reason_field = 'exit_reason' self.exit_reasons = exit_reasons def debugfs_is_child(self, field):
@@ -289,6 +290,7 @@ class ArchPPC(Arch): # numbers depend on the wordsize. char_ptr_size = ctypes.sizeof(ctypes.c_char_p) self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 + self.exit_reason_field = 'exit_nr' self.exit_reasons = {} def debugfs_is_child(self, field):
@@ -300,6 +302,7 @@ class ArchA64(Arch): def __init__(self): self.sc_perf_evt_open = 241 self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reason_field = 'esr_ec' self.exit_reasons = AARCH64_EXIT_REASONS def debugfs_is_child(self, field):
@@ -311,6 +314,7 @@ class ArchS390(Arch): def __init__(self): self.sc_perf_evt_open = 331 self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reason_field = None self.exit_reasons = None def debugfs_is_child(self, field):
@@ -541,8 +545,8 @@ class TracepointProvider(Provider): """ filters = {} filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) - if ARCH.exit_reasons: - filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) + if ARCH.exit_reason_field and ARCH.exit_reasons: + filters['kvm_exit'] = (ARCH.exit_reason_field, ARCH.exit_reasons) return filters def _get_available_fields(self):
Greg Kroah-Hartman
maintainerPATCH 219/367 xen/balloon: Support xend-based toolstack take two
In reply to Greg Kroah-HartmanFrom: Juergen Gross <[email protected]>
commit eda4eabf86fd6806eaabc23fb90dd056fdac037b upstream.
Commit 3aa6c19d2f38be ("xen/balloon: Support xend-based toolstack") tried to fix a regression with running on rather ancient Xen versions. Unfortunately the fix was based on the assumption that xend would just use another Xenstore node, but in reality only some downstream versions of xend are doing that. The upstream xend does not write that Xenstore node at all, so the problem must be fixed in another way.
The easiest way to achieve that is to fall back to the behavior before commit 96edd61dcf4436 ("xen/balloon: don't online new memory initially") in case the static memory maximum can't be read.
This is achieved by setting static_max to the current number of memory pages known by the system resulting in target_diff becoming zero.
--- drivers/xen/xen-balloon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/xen/xen-balloon.c +1 -1
@@ -94,7 +94,7 @@ static void watch_target(struct xenbus_w "%llu", &static_max) == 1)) static_max >>=PAGE_SHIFT -10 ; else - static_max = new_target; + static_max = balloon_stats.current_pages; target_diff = (xen_pv_domain () ||xen_initial_domain ()) ?0 : static_max - balloon_stats.target_pages;
Greg Kroah-Hartman
maintainerPATCH 220/367 xen/gntdev: Do not use mm notifiers with autotranslating guests
In reply to Greg Kroah-HartmanFrom: Boris Ostrovsky <[email protected]>
commit 9293724192a73f49c722e9685d45649c6df67dfe upstream.
Commit d3eeb1d77c5d ("xen/gntdev: use mmu_interval_notifier_insert") missed a test for use_ptemod when calling mmu_interval_read_begin(). Fix that.
--- drivers/xen/gntdev.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
drivers/xen/gntdev.c +12 -12
@@ -1006,19 +1006,19 @@ static int gntdev_mmap(struct file *flip }mutex_unlock (&priv->lock); -/* -* gntdev takes the address of the PTE in find_grant_ptes() and passes -* it to the hypervisor in gntdev_map_grant_pages(). The purpose of -* the notifier is to prevent the hypervisor pointer to the PTE from -* going stale. -* -* Since this vma's mappings can't be touched without the mmap_sem, -* and we are holding it now, there is no need for the notifier_range -* locking pattern. -*/ -mmu_interval_read_begin (&map->notifier); -if (use_ptemod) { +/* +* gntdev takes the address of the PTE in find_grant_ptes() and +* passes it to the hypervisor in gntdev_map_grant_pages(). The +* purpose of the notifier is to prevent the hypervisor pointer +* to the PTE from going stale. +* +* Since this vma's mappings can't be touched without the +* mmap_sem, and we are holding it now, there is no need for +* the notifier_range locking pattern. +*/ +mmu_interval_read_begin (&map->notifier); + map->pages_vm_start = vma->vm_start; err = apply_to_page_range(vma->vm_mm, vma->vm_start, vma->vm_end - vma->vm_start,
Greg Kroah-Hartman
maintainerPATCH 221/367 watchdog: fix UAF in reboot notifier handling in watchdog core code
In reply to Greg Kroah-HartmanFrom: Vladis Dronov <[email protected]>
commit 69503e585192fdd84b240f18a0873d20e18a2e0a upstream.
After the commit 44ea39420fc9 ("drivers/watchdog: make use of devm_register_reboot_notifier()") the struct notifier_block reboot_nb in the struct watchdog_device is removed from the reboot notifiers chain at the time watchdog's chardev is closed. But at least in i6300esb.c case reboot_nb is embedded in the struct esb_dev which can be freed on its device removal and before the chardev is closed, thus UAF at reboot:
[ 7.728581] esb_probe: esb_dev.watchdog_device ffff91316f91ab28
ts# uname -r note the address ^^^
5.5.0-rc5-ae6088-wdog
ts# ./openwdog0 &
[1] 696
ts# opened /dev/watchdog0, sleeping 10s...
ts# echo 1 > /sys/devices/pci0000\:00/0000\:00\:09.0/remove
[ 178.086079] devres:rel_nodes: dev ffff91317668a0b0 data ffff91316f91ab28
esb_dev.watchdog_device.reboot_nb memory is freed here ^^^
ts# ...woken up
[ 181.459010] devres:rel_nodes: dev ffff913171781000 data ffff913174a1dae8
[ 181.460195] devm_unreg_reboot_notifier: res ffff913174a1dae8 nb ffff91316f91ab78
attempt to use memory already freed ^^^
[ 181.461063] devm_unreg_reboot_notifier: nb->call 6b6b6b6b6b6b6b6b
[ 181.461243] devm_unreg_reboot_notifier: nb->next 6b6b6b6b6b6b6b6b
freed memory is filled with a slub poison ^^^
[1]+ Done ./openwdog0
ts# reboot
[ 229.921862] systemd-shutdown[1]: Rebooting.
[ 229.939265] notifier_call_chain: nb ffffffff9c6c2f20 nb->next ffffffff9c6d50c0
[ 229.943080] notifier_call_chain: nb ffffffff9c6d50c0 nb->next 6b6b6b6b6b6b6b6b
[ 229.946054] notifier_call_chain: nb 6b6b6b6b6b6b6b6b INVAL
[ 229.957584] general protection fault: 0000 [#1] SMP
[ 229.958770] CPU: 0 PID: 1 Comm: systemd-shutdow Not tainted 5.5.0-rc5-ae6088-wdog
[ 229.960224] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), ...
[ 229.963288] RIP: 0010:notifier_call_chain+0x66/0xd0
[ 229.969082] RSP: 0018:ffffb20dc0013d88 EFLAGS: 00010246
[ 229.970812] RAX: 000000000000002e RBX: 6b6b6b6b6b6b6b6b RCX: 00000000000008b3
[ 229.972929] RDX: 0000000000000000 RSI: 0000000000000096 RDI: ffffffff9ccc46ac
[ 229.975028] RBP: 0000000000000001 R08: 0000000000000000 R09: 00000000000008b3
[ 229.977039] R10: 0000000000000001 R11: ffffffff9c26c740 R12: 0000000000000000
[ 229.979155] R13: 6b6b6b6b6b6b6b6b R14: 0000000000000000 R15: 00000000fffffffa
... slub_debug=FZP poison ^^^
[ 229.989089] Call Trace:
[ 229.990157] blocking_notifier_call_chain+0x43/0x59
[ 229.991401] kernel_restart_prepare+0x14/0x30
[ 229.992607] kernel_restart+0x9/0x30
[ 229.993800] __do_sys_reboot+0x1d2/0x210
[ 230.000149] do_syscall_64+0x3d/0x130
[ 230.001277] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 230.002639] RIP: 0033:0x7f5461bdd177
[ 230.016402] Modules linked in: i6300esb
[ 230.050261] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000bFix the crash by reverting 44ea39420fc9 so unregister_reboot_notifier() is called when watchdog device is removed. This also makes handling of the reboot notifier unified with the handling of the restart handler, which is freed with unregister_restart_handler() in the same place.
--- drivers/watchdog/watchdog_core.c | 35 +++++++++++++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.c | 36 +----------------------------------- 2 files changed, 36 insertions(+), 35 deletions(-)
drivers/watchdog/watchdog_core.c +35 -0
@@ -147,6 +147,25 @@ int watchdog_init_timeout(struct watchdo }EXPORT_SYMBOL_GPL (watchdog_init_timeout); +static int watchdog_reboot_notifier (struct notifier_block *nb, +unsigned long code,void *data) +{ +struct watchdog_device *wdd; + + wdd = container_of(nb, struct watchdog_device, reboot_nb); +if (code ==SYS_DOWN || code ==SYS_HALT ) { +if (watchdog_active (wdd)) { +int ret; + + ret = wdd->ops->stop (wdd); +if (ret) +return NOTIFY_BAD ; + } + } + +return NOTIFY_DONE ; +} +static int watchdog_restart_notifier (struct notifier_block *nb,unsigned long action,void *data) {
@@ -235,6 +254,19 @@ static int __watchdog_register_device(st } } +if (test_bit (WDOG_STOP_ON_REBOOT , &wdd->status)) { + wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; + + ret =register_reboot_notifier (&wdd->reboot_nb); +if (ret) { +pr_err ("watchdog%d: Cannot register reboot notifier (%d)\n" , + wdd->id, ret); +watchdog_dev_unregister (wdd); +ida_simple_remove (&watchdog_ida, id); +return ret; + } + } +if (wdd->ops->restart) { wdd->restart_nb.notifier_call = watchdog_restart_notifier;
@@ -289,6 +321,9 @@ static void __watchdog_unregister_deviceif (wdd->ops->restart)unregister_restart_handler (&wdd->restart_nb); +if (test_bit (WDOG_STOP_ON_REBOOT , &wdd->status)) +unregister_reboot_notifier (&wdd->reboot_nb); +watchdog_dev_unregister (wdd);ida_simple_remove (&watchdog_ida, wdd->id); }
drivers/watchdog/watchdog_dev.c +1 -35
@@ -38,7 +38,6 @@#include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/module.h> /* For module stuff/... */ #include <linux/mutex.h> /* For mutexes */ -#include <linux/reboot.h> /* For reboot notifier */ #include <linux/slab.h> /* For memory functions */ #include <linux/types.h> /* For standard types (like size_t) */ #include <linux/watchdog.h> /* For watchdog specific items */
@@ -1097,25 +1096,6 @@ static void watchdog_cdev_unregister(strput_device (&wd_data->dev); } -static int watchdog_reboot_notifier (struct notifier_block *nb, -unsigned long code,void *data) -{ -struct watchdog_device *wdd; - - wdd = container_of(nb, struct watchdog_device, reboot_nb); -if (code ==SYS_DOWN || code ==SYS_HALT ) { -if (watchdog_active (wdd)) { -int ret; - - ret = wdd->ops->stop (wdd); -if (ret) -return NOTIFY_BAD ; - } - } - -return NOTIFY_DONE ; -} -/* * watchdog_dev_register: register a watchdog device * @wdd: watchdog device
@@ -1134,22 +1114,8 @@ int watchdog_dev_register(struct watchdoreturn ret; ret =watchdog_register_pretimeout (wdd); -if (ret) { +if (ret)watchdog_cdev_unregister (wdd); -return ret; - } - -if (test_bit (WDOG_STOP_ON_REBOOT , &wdd->status)) { - wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; - - ret =devm_register_reboot_notifier (&wdd->wd_data->dev, - &wdd->reboot_nb); -if (ret) { -pr_err ("watchdog%d: Cannot register reboot notifier (%d)\n" , - wdd->id, ret); -watchdog_dev_unregister (wdd); - } - }return ret; }
Greg Kroah-Hartman
maintainerPATCH 222/367 bcache: add readahead cache policy options via sysfs interface
In reply to Greg Kroah-HartmanFrom: Coly Li <[email protected]>
commit 038ba8cc1bffc51250add4a9b9249d4331576d8f upstream.
In year 2007 high performance SSD was still expensive, in order to save more space for real workload or meta data, the readahead I/Os for non-meta data was bypassed and not cached on SSD.
In now days, SSD price drops a lot and people can find larger size SSD with more comfortable price. It is unncessary to alway bypass normal readahead I/Os to save SSD space for now.
This patch adds options for readahead data cache policies via sysfs file /sys/block/bcache<N>/readahead_cache_policy, the options are,
- "all": cache all readahead data I/Os.
- "meta-only": only cache meta data, and bypass other regular I/Os.
If users want to make bcache continue to only cache readahead request for metadata and bypass regular data readahead, please set "meta-only" to this sysfs file. By default, bcache will back to cache all read- ahead requests now.
--- drivers/md/bcache/bcache.h | 3 +++ drivers/md/bcache/request.c | 17 ++++++++++++----- drivers/md/bcache/sysfs.c | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-)
drivers/md/bcache/bcache.h +3 -0
@@ -329,6 +329,9 @@ struct cached_dev {*/ atomic_t has_dirty; +#define BCH_CACHE_READA_ALL 0 +#define BCH_CACHE_READA_META_ONLY 1 +unsigned int cache_readahead_policy;struct bch_ratelimit writeback_rate;struct delayed_work writeback_rate_update;
drivers/md/bcache/request.c +12 -5
@@ -379,13 +379,20 @@ static bool check_should_bypass(struct c goto skip;/* -* Flag for bypass if the IO is for read-ahead or background, -* unless the read-ahead request is for metadata +* If the bio is for read-ahead or background IO, bypass it or +* not depends on the following situations, +* - If the IO is for meta data, always cache it and no bypass +* - If the IO is not meta data, check dc->cache_reada_policy, +* BCH_CACHE_READA_ALL: cache it and not bypass +* BCH_CACHE_READA_META_ONLY: not cache it and bypass +* That is, read-ahead request for metadata always get cached * (eg, for gfs2 or xfs). */ -if (bio->bi_opf & (REQ_RAHEAD |REQ_BACKGROUND ) && - !(bio->bi_opf & (REQ_META |REQ_PRIO ))) - goto skip; +if ((bio->bi_opf & (REQ_RAHEAD |REQ_BACKGROUND ))) { +if (!(bio->bi_opf & (REQ_META |REQ_PRIO )) && + (dc->cache_readahead_policy !=BCH_CACHE_READA_ALL )) + goto skip; + }if (bio->bi_iter.bi_sector & (c->sb.block_size -1 ) ||bio_sectors (bio) & (c->sb.block_size -1 )) {
drivers/md/bcache/sysfs.c +22 -0
@@ -27,6 +27,12 @@ static const char * const bch_cache_modeNULL }; +static const char *const bch_reada_cache_policies[] = { +"all" , +"meta-only" , +NULL +}; +/* Default is 0 ("auto") */ static const char *const bch_stop_on_failure_modes[] = {"auto" ,
@@ -100,6 +106,7 @@ rw_attribute(congested_write_threshold_urw_attribute (sequential_cutoff);rw_attribute (data_csum);rw_attribute (cache_mode); +rw_attribute (readahead_cache_policy);rw_attribute (stop_when_cache_set_failed);rw_attribute (writeback_metadata);rw_attribute (writeback_running);
@@ -168,6 +175,11 @@ SHOW(__bch_cached_dev) bch_cache_modes, BDEV_CACHE_MODE(&dc->sb)); +if (attr == &sysfs_readahead_cache_policy) +return bch_snprint_string_list (buf,PAGE_SIZE , + bch_reada_cache_policies, + dc->cache_readahead_policy); +if (attr == &sysfs_stop_when_cache_set_failed) return bch_snprint_string_list(buf, PAGE_SIZE, bch_stop_on_failure_modes,
@@ -353,6 +365,15 @@ STORE(__cached_dev) } } +if (attr == &sysfs_readahead_cache_policy) { + v =__sysfs_match_string (bch_reada_cache_policies,-1 , buf); +if (v <0 ) +return v; + +if ((unsigned int ) v != dc->cache_readahead_policy) + dc->cache_readahead_policy = v; + } +if (attr == &sysfs_stop_when_cache_set_failed) { v =__sysfs_match_string (bch_stop_on_failure_modes,-1 , buf);if (v <0 )
@@ -467,6 +488,7 @@ static struct attribute *bch_cached_dev_ &sysfs_data_csum, #endif &sysfs_cache_mode, + &sysfs_readahead_cache_policy, &sysfs_stop_when_cache_set_failed, &sysfs_writeback_metadata, &sysfs_writeback_running,
Greg Kroah-Hartman
maintainerPATCH 223/367 io_uring: dont map read/write iovec potentially twice
In reply to Greg Kroah-HartmanFrom: Jens Axboe <[email protected]>
commit 5d204bcfa09330972ad3428a8f81c23f371d3e6d upstream.
If we have a read/write that is deferred, we already setup the async IO context for that request, and mapped it. When we later try and execute the request and we get -EAGAIN, we don't want to attempt to re-map it. If we do, we end up with garbage in the iovec, which typically leads to an -EFAULT or -EINVAL completion.
--- fs/io_uring.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
fs/io_uring.c +5 -3
@@ -1789,10 +1789,12 @@ static int io_setup_async_rw(struct io_kif (req->opcode ==IORING_OP_READ_FIXED || req->opcode ==IORING_OP_WRITE_FIXED )return 0 ; -if (!req->io &&io_alloc_async_ctx (req)) -return -ENOMEM ; +if (!req->io) { +if (io_alloc_async_ctx (req)) +return -ENOMEM ; -io_req_map_rw (req, io_size, iovec, fast_iov, iter); +io_req_map_rw (req, io_size, iovec, fast_iov, iter); + } req->work.func = io_rw_async;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 224/367 io_uring: spin for sq thread to idle on shutdown
In reply to Greg Kroah-HartmanFrom: Jens Axboe <[email protected]>
commit df069d80c8e38c19531c392322e9a16617475c44 upstream.
As part of io_uring shutdown, we cancel work that is pending and won't necessarily complete on its own. That includes requests like poll commands and timeouts.
If we're using SQPOLL for kernel side submission and we shutdown the ring immediately after queueing such work, we can race with the sqthread doing the submission. This means we may miss cancelling some work, which results in the io_uring shutdown hanging forever.
--- fs/io_uring.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/io_uring.c +12 -1
@@ -3902,7 +3902,8 @@ static int io_sq_thread(void *data)* reap events and wake us up. */ if (inflight || - (!time_after (jiffies, timeout) && ret != -EBUSY )) { + (!time_after (jiffies, timeout) && ret != -EBUSY && + !percpu_ref_is_dying (&ctx->refs))) {cond_resched ();continue ; }
@@ -4983,6 +4984,16 @@ static void io_ring_ctx_wait_and_kill(stpercpu_ref_kill (&ctx->refs);mutex_unlock (&ctx->uring_lock); +/* +* Wait for sq thread to idle, if we have one. It won't spin on new +* work after we've killed the ctx ref above. This is important to do +* before we cancel existing commands, as the thread could otherwise +* be queueing new work post that. If that's work we need to cancel, +* it could cause shutdown to hang. +*/ +while (ctx->sqo_thread && !wq_has_sleeper (&ctx->sqo_wait)) +cpu_relax (); +io_kill_timeouts (ctx);io_poll_remove_all (ctx);
Greg Kroah-Hartman
maintainerPATCH 225/367 eventfd: track eventfd_signal() recursion depth
In reply to Greg Kroah-HartmanFrom: Jens Axboe <[email protected]>
commit b5e683d5cab8cd433b06ae178621f083cabd4f63 upstream.
eventfd use cases from aio and io_uring can deadlock due to circular or resursive calling, when eventfd_signal() tries to grab the waitqueue lock. On top of that, it's also possible to construct notification chains that are deep enough that we could blow the stack.
Add a percpu counter that tracks the percpu recursion depth, warn if we exceed it. The counter is also exposed so that users of eventfd_signal() can do the right thing if it's non-zero in the context where it is called.
--- fs/eventfd.c | 15 +++++++++++++++ include/linux/eventfd.h | 14 ++++++++++++++ 2 files changed, 29 insertions(+)
fs/eventfd.c +15 -0
@@ -24,6 +24,8 @@#include <linux/seq_file.h> #include <linux/idr.h> +DEFINE_PER_CPU (int, eventfd_wake_count); +static DEFINE_IDA (eventfd_ida);struct eventfd_ctx {
@@ -60,12 +62,25 @@ __u64 eventfd_signal(struct eventfd_ctx {unsigned long flags; +/* +* Deadlock or stack overflow issues can happen if we recurse here +* through waitqueue wakeup handlers. If the caller users potentially +* nested waitqueues with custom wakeup handlers, then it should +* check eventfd_signal_count() before calling this function. If +* it returns true, the eventfd_signal() call should be deferred to a +* safe context. +*/ +if (WARN_ON_ONCE (this_cpu_read (eventfd_wake_count))) +return 0 ; +spin_lock_irqsave (&ctx->wqh.lock, flags); +this_cpu_inc (eventfd_wake_count);if (ULLONG_MAX - ctx->count < n) n =ULLONG_MAX - ctx->count; ctx->count += n;if (waitqueue_active (&ctx->wqh))wake_up_locked_poll (&ctx->wqh,EPOLLIN ); +this_cpu_dec (eventfd_wake_count);spin_unlock_irqrestore (&ctx->wqh.lock, flags);return n;
include/linux/eventfd.h +14 -0
@@ -12,6 +12,8 @@#include <linux/fcntl.h> #include <linux/wait.h> #include <linux/err.h> +#include <linux/percpu-defs.h> +#include <linux/percpu.h> /* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
@@ -40,6 +42,13 @@ __u64 eventfd_signal(struct eventfd_ctxint eventfd_ctx_remove_wait_queue (struct eventfd_ctx *ctx,wait_queue_entry_t *wait,__u64 *cnt); +DECLARE_PER_CPU (int, eventfd_wake_count); + +static inline bool eventfd_signal_count (void ) +{ +return this_cpu_read (eventfd_wake_count); +} +#else /* CONFIG_EVENTFD */ /*
@@ -68,6 +77,11 @@ static inline int eventfd_ctx_remove_waireturn -ENOSYS ; } +static inline bool eventfd_signal_count (void ) +{ +return false; +} +#endif #endif /* _LINUX_EVENTFD_H */
Greg Kroah-Hartman
maintainerPATCH 226/367 aio: prevent potential eventfd recursion on poll
In reply to Greg Kroah-HartmanFrom: Jens Axboe <[email protected]>
commit 01d7a356872eec22ef34a33a5f9cfa917d145468 upstream.
If we have nested or circular eventfd wakeups, then we can deadlock if we run them inline from our poll waitqueue wakeup handler. It's also possible to have very long chains of notifications, to the extent where we could risk blowing the stack.
Check the eventfd recursion count before calling eventfd_signal(). If it's non-zero, then punt the signaling to async context. This is always safe, as it takes us out-of-line in terms of stack and locking context.
--- fs/aio.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
fs/aio.c +18 -2
@@ -1610,6 +1610,14 @@ static int aio_fsync(struct fsync_iocb *return 0 ; } +static void aio_poll_put_work (struct work_struct *work) +{ + struct poll_iocb *req = container_of(work, struct poll_iocb, work); + struct aio_kiocb *iocb = container_of(req, struct aio_kiocb, poll); + +iocb_put (iocb); +} +static void aio_poll_complete_work (struct work_struct *work) { struct poll_iocb *req = container_of(work, struct poll_iocb, work);
@@ -1674,6 +1682,8 @@ static int aio_poll_wake(struct wait_quelist_del_init (&req->wait.entry);if (mask &&spin_trylock_irqsave (&iocb->ki_ctx->ctx_lock, flags)) { +struct kioctx *ctx = iocb->ki_ctx; +/* * Try to complete the iocb inline if we can. Use * irqsave/irqrestore because not all filesystems (e.g. fuse)
@@ -1683,8 +1693,14 @@ static int aio_poll_wake(struct wait_quelist_del (&iocb->ki_list); iocb->ki_res.res =mangle_poll (mask); req->done = true; -spin_unlock_irqrestore (&iocb->ki_ctx->ctx_lock, flags); -iocb_put (iocb); +if (iocb->ki_eventfd &&eventfd_signal_count ()) { + iocb =NULL ; +INIT_WORK (&req->work, aio_poll_put_work); +schedule_work (&req->work); + } +spin_unlock_irqrestore (&ctx->ctx_lock, flags); +if (iocb) +iocb_put (iocb); }else {schedule_work (&req->work); }
Greg Kroah-Hartman
maintainerPATCH 227/367 KVM: x86: Refactor picdev_write() to prevent Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 14e32321f3606e4b0970200b6e5e47ee6f1e6410 upstream.
This fixes a Spectre-v1/L1TF vulnerability in picdev_write(). It replaces index computations based on the (attacked-controlled) port number with constants through a minor refactoring.
--- arch/x86/kvm/i8259.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
arch/x86/kvm/i8259.c +5 -1
@@ -460,10 +460,14 @@ static int picdev_write(struct kvm_pic *switch (addr) {case 0x20 :case 0x21 : +pic_lock (s); +pic_ioport_write (&s->pics[0 ], addr, data); +pic_unlock (s); +break ;case 0xa0 :case 0xa1 :pic_lock (s); -pic_ioport_write (&s->pics[addr >>7 ], addr, data); +pic_ioport_write (&s->pics[1 ], addr, data);pic_unlock (s);break ;case 0x4d0 :
Greg Kroah-Hartman
maintainerPATCH 228/367 KVM: x86: Refactor prefix decoding to prevent Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 125ffc5e0a56a3eded608dc51e09d5ebf72cf652 upstream.
This fixes Spectre-v1/L1TF vulnerabilities in vmx_read_guest_seg_selector(), vmx_read_guest_seg_base(), vmx_read_guest_seg_limit() and vmx_read_guest_seg_ar(). When invoked from emulation, these functions contain index computations based on the (attacker-influenced) segment value. Using constants prevents the attack.
--- arch/x86/kvm/emulate.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
arch/x86/kvm/emulate.c +14 -2
@@ -5210,16 +5210,28 @@ int x86_decode_insn(struct x86_emulate_c ctxt->ad_bytes = def_ad_bytes ^6 ;break ;case 0x26 :/* ES override */ + has_seg_override = true; + ctxt->seg_override =VCPU_SREG_ES ; +break ;case 0x2e :/* CS override */ + has_seg_override = true; + ctxt->seg_override =VCPU_SREG_CS ; +break ;case 0x36 :/* SS override */ + has_seg_override = true; + ctxt->seg_override =VCPU_SREG_SS ; +break ;case 0x3e :/* DS override */ has_seg_override = true; - ctxt->seg_override = (ctxt->b >>3 ) &3 ; + ctxt->seg_override =VCPU_SREG_DS ;break ;case 0x64 :/* FS override */ + has_seg_override = true; + ctxt->seg_override =VCPU_SREG_FS ; +break ;case 0x65 :/* GS override */ has_seg_override = true; - ctxt->seg_override = ctxt->b &7 ; + ctxt->seg_override =VCPU_SREG_GS ;break ; case 0x40 ... 0x4f: /* REX */if (mode !=X86EMUL_MODE_PROT64 )
Greg Kroah-Hartman
maintainerPATCH 229/367 KVM: x86: Protect pmu_intel.c from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 66061740f1a487f4ed54fde75e724709f805da53 upstream.
This fixes Spectre-v1/L1TF vulnerabilities in intel_find_fixed_event() and intel_rdpmc_ecx_to_pmc().kvm_rdpmc() (ancestor of intel_find_fixed_event()) and reprogram_fixed_counter() (ancestor of intel_rdpmc_ecx_to_pmc()) are exported symbols so KVM should treat them conservatively from a security perspective.
--- arch/x86/kvm/vmx/pmu_intel.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
arch/x86/kvm/vmx/pmu_intel.c +16 -8
@@ -86,10 +86,14 @@ static unsigned intel_find_arch_event(ststatic unsigned intel_find_fixed_event (int idx) { -if (idx >=ARRAY_SIZE (fixed_pmc_events)) +u32 event; +size_t size =ARRAY_SIZE (fixed_pmc_events); + +if (idx >= size)return PERF_COUNT_HW_MAX ; -return intel_arch_events[fixed_pmc_events[idx]].event_type; + event = fixed_pmc_events[array_index_nospec (idx, size)]; +return intel_arch_events[event].event_type; }/* check if a PMC is enabled by comparing it with globl_ctrl bits. */
@@ -130,16 +134,20 @@ static struct kvm_pmc *intel_rdpmc_ecx_tstruct kvm_pmu *pmu =vcpu_to_pmu (vcpu);bool fixed = idx & (1u <<30 );struct kvm_pmc *counters; +unsigned int num_counters; idx &= ~(3u <<30 ); -if (!fixed && idx >= pmu->nr_arch_gp_counters) -return NULL ; -if (fixed && idx >= pmu->nr_arch_fixed_counters) +if (fixed) { + counters = pmu->fixed_counters; + num_counters = pmu->nr_arch_fixed_counters; + }else { + counters = pmu->gp_counters; + num_counters = pmu->nr_arch_gp_counters; + } +if (idx >= num_counters)return NULL ; - counters = fixed ? pmu->fixed_counters : pmu->gp_counters; *mask &= pmu->counter_bitmask[fixed ?KVM_PMC_FIXED :KVM_PMC_GP ]; - -return &counters[idx]; +return &counters[array_index_nospec (idx, num_counters)]; }static bool intel_is_valid_msr (struct kvm_vcpu *vcpu,u32 msr)
Greg Kroah-Hartman
maintainerPATCH 230/367 KVM: x86: Protect DR-based index computations from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit ea740059ecb37807ba47b84b33d1447435a8d868 upstream.
This fixes a Spectre-v1/L1TF vulnerability in __kvm_set_dr() and kvm_get_dr().
Both kvm_get_dr() and kvm_set_dr() (a wrapper of __kvm_set_dr()) are exported symbols so KVM should tream them conservatively from a security perspective.
--- arch/x86/kvm/x86.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
arch/x86/kvm/x86.c +6 -2
@@ -1047,9 +1047,11 @@ static u64 kvm_dr6_fixed(struct kvm_vcpustatic int __kvm_set_dr (struct kvm_vcpu *vcpu,int dr,unsigned long val) { +size_t size =ARRAY_SIZE (vcpu->arch.db); +switch (dr) { case 0 ... 3: - vcpu->arch.db[dr] = val; + vcpu->arch.db[array_index_nospec (dr, size)] = val;if (!(vcpu->guest_debug &KVM_GUESTDBG_USE_HW_BP )) vcpu->arch.eff_db[dr] = val;break ;
@@ -1086,9 +1088,11 @@ EXPORT_SYMBOL_GPL(kvm_set_dr);int kvm_get_dr (struct kvm_vcpu *vcpu,int dr,unsigned long *val) { +size_t size =ARRAY_SIZE (vcpu->arch.db); +switch (dr) { case 0 ... 3: - *val = vcpu->arch.db[dr]; + *val = vcpu->arch.db[array_index_nospec (dr, size)];break ;case 4 :/* fall through */
Greg Kroah-Hartman
maintainerPATCH 231/367 KVM: x86: Protect kvm_lapic_reg_write() from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 4bf79cb089f6b1c6c632492c0271054ce52ad766 upstream.
This fixes a Spectre-v1/L1TF vulnerability in kvm_lapic_reg_write(). This function contains index computations based on the (attacker-controlled) MSR number.
--- arch/x86/kvm/lapic.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
arch/x86/kvm/lapic.c +9 -4
@@ -1963,15 +1963,20 @@ int kvm_lapic_reg_write(struct kvm_lapiccase APIC_LVTTHMR :case APIC_LVTPC :case APIC_LVT1 : -case APIC_LVTERR : +case APIC_LVTERR : {/* TODO: Check vector */ +size_t size; +u32 index; +if (!kvm_apic_sw_enabled (apic)) val |=APIC_LVT_MASKED ; - - val &= apic_lvt_mask[(reg -APIC_LVTT ) >>4 ]; + size =ARRAY_SIZE (apic_lvt_mask); + index =array_index_nospec ( + (reg -APIC_LVTT ) >>4 , size); + val &= apic_lvt_mask[index];kvm_lapic_set_reg (apic, reg, val); -break ; + }case APIC_LVTT :if (!kvm_apic_sw_enabled (apic))
Greg Kroah-Hartman
maintainerPATCH 232/367 KVM: x86: Protect kvm_hv_msr_[get|set]_crash_data() from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 8618793750071d66028584a83ed0b4fa7eb4f607 upstream.
This fixes Spectre-v1/L1TF vulnerabilities in kvm_hv_msr_get_crash_data() and kvm_hv_msr_set_crash_data().
These functions contain index computations that use the (attacker-controlled) MSR number.
--- arch/x86/kvm/hyperv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
arch/x86/kvm/hyperv.c +6 -4
@@ -809,11 +809,12 @@ static int kvm_hv_msr_get_crash_data(str u32 index, u64 *pdata) { struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; +size_t size =ARRAY_SIZE (hv->hv_crash_param); -if (WARN_ON_ONCE (index >=ARRAY_SIZE (hv->hv_crash_param))) +if (WARN_ON_ONCE (index >= size))return -EINVAL ; - *pdata = hv->hv_crash_param[index]; + *pdata = hv->hv_crash_param[array_index_nospec (index, size)];return 0 ; }
@@ -852,11 +853,12 @@ static int kvm_hv_msr_set_crash_data(str u32 index, u64 data) {struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; +size_t size =ARRAY_SIZE (hv->hv_crash_param); -if (WARN_ON_ONCE (index >=ARRAY_SIZE (hv->hv_crash_param))) +if (WARN_ON_ONCE (index >= size))return -EINVAL ; - hv->hv_crash_param[index] = data; + hv->hv_crash_param[array_index_nospec (index, size)] = data;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 233/367 KVM: x86: Protect ioapic_write_indirect() from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 670564559ca35b439c8d8861fc399451ddf95137 upstream.
This fixes a Spectre-v1/L1TF vulnerability in ioapic_write_indirect(). This function contains index computations based on the (attacker-controlled) IOREGSEL register.
This patch depends on patch
"KVM: x86: Protect ioapic_read_indirect() from Spectre-v1/L1TF attacks".
--- arch/x86/kvm/ioapic.c | 1 + 1 file changed, 1 insertion(+)
arch/x86/kvm/ioapic.c +1 -0
@@ -292,6 +292,7 @@ static void ioapic_write_indirect(structif (index >=IOAPIC_NUM_PINS )return ; + index =array_index_nospec (index,IOAPIC_NUM_PINS ); e = &ioapic->redirtbl[index]; mask_before = e->fields.mask;/* Preserve read-only fields */
Greg Kroah-Hartman
maintainerPATCH 234/367 KVM: x86: Protect MSR-based index computations in pmu.h from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 13c5183a4e643cc2b03a22d0e582c8e17bb7457d upstream.
This fixes a Spectre-v1/L1TF vulnerability in the get_gp_pmc() and get_fixed_pmc() functions.
They both contain index computations based on the (attacker-controlled) MSR number.
--- arch/x86/kvm/pmu.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
arch/x86/kvm/pmu.h +14 -4
@@ -2,6 +2,8 @@#ifndef __KVM_X86_PMU_H#define __KVM_X86_PMU_H +#include <linux/nospec.h> +#define vcpu_to_pmu (vcpu) (&(vcpu)->arch.pmu)#define pmu_to_vcpu (pmu) (container_of((pmu), struct kvm_vcpu, arch.pmu))#define pmc_to_pmu (pmc) (&(pmc)->vcpu->arch.pmu)
@@ -102,8 +104,12 @@ static inline bool kvm_valid_perf_globalstatic inline struct kvm_pmc *get_gp_pmc (struct kvm_pmu *pmu,u32 msr,u32 base) { -if (msr >= base && msr < base + pmu->nr_arch_gp_counters) -return &pmu->gp_counters[msr - base]; +if (msr >= base && msr < base + pmu->nr_arch_gp_counters) { +u32 index =array_index_nospec (msr - base, + pmu->nr_arch_gp_counters); + +return &pmu->gp_counters[index]; + }return NULL ; }
@@ -113,8 +119,12 @@ static inline struct kvm_pmc *get_fixed_ {int base =MSR_CORE_PERF_FIXED_CTR0 ; -if (msr >= base && msr < base + pmu->nr_arch_fixed_counters) -return &pmu->fixed_counters[msr - base]; +if (msr >= base && msr < base + pmu->nr_arch_fixed_counters) { +u32 index =array_index_nospec (msr - base, + pmu->nr_arch_fixed_counters); + +return &pmu->fixed_counters[index]; + }return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 235/367 KVM: x86: Protect ioapic_read_indirect() from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 8c86405f606ca8508b8d9280680166ca26723695 upstream.
This fixes a Spectre-v1/L1TF vulnerability in ioapic_read_indirect(). This function contains index computations based on the (attacker-controlled) IOREGSEL register.
--- arch/x86/kvm/ioapic.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
arch/x86/kvm/ioapic.c +8 -6
@@ -36,6 +36,7 @@#include <linux/io.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/nospec.h> #include <asm/processor.h> #include <asm/page.h> #include <asm/current.h>
@@ -68,13 +69,14 @@ static unsigned long ioapic_read_indirecdefault : {u32 redir_index = (ioapic->ioregsel -0x10 ) >>1 ; -u64 redir_content; +u64 redir_content = ~0ULL ; -if (redir_index <IOAPIC_NUM_PINS ) - redir_content = - ioapic->redirtbl[redir_index].bits; -else - redir_content = ~0ULL ; +if (redir_index <IOAPIC_NUM_PINS ) { +u32 index =array_index_nospec ( + redir_index,IOAPIC_NUM_PINS ); + + redir_content = ioapic->redirtbl[index].bits; + } result = (ioapic->ioregsel &0x1 ) ? (redir_content >> 32) & 0xffffffff :
Greg Kroah-Hartman
maintainerPATCH 236/367 KVM: x86: Protect MSR-based index computations from Spectre-v1/L1TF attacks in x86.c
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 6ec4c5eee1750d5d17951c4e1960d953376a0dda upstream.
This fixes a Spectre-v1/L1TF vulnerability in set_msr_mce() and get_msr_mce().
Both functions contain index computations based on the (attacker-controlled) MSR number.
--- arch/x86/kvm/x86.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
arch/x86/kvm/x86.c +8 -2
@@ -2489,7 +2489,10 @@ static int set_msr_mce(struct kvm_vcpu *default :if (msr >=MSR_IA32_MC0_CTL && msr <MSR_IA32_MCx_CTL (bank_num)) { -u32 offset = msr -MSR_IA32_MC0_CTL ; +u32 offset =array_index_nospec ( + msr -MSR_IA32_MC0_CTL , +MSR_IA32_MCx_CTL (bank_num) -MSR_IA32_MC0_CTL ); +/* only 0 or all 1s can be written to IA32_MCi_CTL * some Linux kernels though clear bit 10 in bank 4 to * workaround a BIOS/GART TBL issue on AMD K8s, ignore
@@ -2930,7 +2933,10 @@ static int get_msr_mce(struct kvm_vcpu *default :if (msr >=MSR_IA32_MC0_CTL && msr <MSR_IA32_MCx_CTL (bank_num)) { -u32 offset = msr -MSR_IA32_MC0_CTL ; +u32 offset =array_index_nospec ( + msr -MSR_IA32_MC0_CTL , +MSR_IA32_MCx_CTL (bank_num) -MSR_IA32_MC0_CTL ); + data = vcpu->arch.mce_banks[offset];break ; }
Greg Kroah-Hartman
maintainerPATCH 237/367 KVM: x86: Protect x86_decode_insn from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 3c9053a2cae7ba2ba73766a34cea41baa70f57f7 upstream.
This fixes a Spectre-v1/L1TF vulnerability in x86_decode_insn(). kvm_emulate_instruction() (an ancestor of x86_decode_insn()) is an exported symbol, so KVM should treat it conservatively from a security perspective.
--- arch/x86/kvm/emulate.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
arch/x86/kvm/emulate.c +8 -3
@@ -5315,10 +5315,15 @@ done_prefixes: }break ;case Escape: -if (ctxt->modrm >0xbf ) - opcode = opcode.u.esc->high[ctxt->modrm -0xc0 ]; -else +if (ctxt->modrm >0xbf ) { +size_t size =ARRAY_SIZE (opcode.u.esc->high); +u32 index =array_index_nospec ( + ctxt->modrm -0xc0 , size); + + opcode = opcode.u.esc->high[index]; + }else { opcode = opcode.u.esc->op[(ctxt->modrm >>3 ) &7 ]; + }break ;case InstrDual:if ((ctxt->modrm >>6 ) ==3 )
Greg Kroah-Hartman
maintainerPATCH 238/367 KVM: x86: Protect MSR-based index computations in fixed_msr_to_seg_unit() from Spectre-v1/L1TF attacks
In reply to Greg Kroah-HartmanFrom: Marios Pomonis <[email protected]>
commit 25a5edea71b7c154b6a0b8cec14c711cafa31d26 upstream.
This fixes a Spectre-v1/L1TF vulnerability in fixed_msr_to_seg_unit(). This function contains index computations based on the (attacker-controlled) MSR number.
--- arch/x86/kvm/mtrr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
arch/x86/kvm/mtrr.c +6 -2
@@ -192,11 +192,15 @@ static bool fixed_msr_to_seg_unit(u32 msbreak ; case MSR_MTRRfix16K_80000 ... MSR_MTRRfix16K_A0000: *seg =1 ; - *unit = msr - MSR_MTRRfix16K_80000; + *unit =array_index_nospec ( + msr - MSR_MTRRfix16K_80000, + MSR_MTRRfix16K_A0000 - MSR_MTRRfix16K_80000 +1 );break ; case MSR_MTRRfix4K_C0000 ... MSR_MTRRfix4K_F8000: *seg =2 ; - *unit = msr - MSR_MTRRfix4K_C0000; + *unit =array_index_nospec ( + msr - MSR_MTRRfix4K_C0000, + MSR_MTRRfix4K_F8000 - MSR_MTRRfix4K_C0000 +1 );break ;default :return false;
Greg Kroah-Hartman
maintainerPATCH 239/367 KVM: x86: Fix potential put_fpu() w/o load_fpu() on MPX platform
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit f958bd2314d117f8c29f4821401bc1925bc2e5ef upstream.
Unlike most state managed by XSAVE, MPX is initialized to zero on INIT. Because INITs are usually recognized in the context of a VCPU_RUN call, kvm_vcpu_reset() puts the guest's FPU so that the FPU state is resident in memory, zeros the MPX state, and reloads FPU state to hardware. But, in the unlikely event that an INIT is recognized during kvm_arch_vcpu_ioctl_get_mpstate() via kvm_apic_accept_events(), kvm_vcpu_reset() will call kvm_put_guest_fpu() without a preceding kvm_load_guest_fpu() and corrupt the guest's FPU state (and possibly userspace's FPU state as well).
Given that MPX is being removed from the kernel[*], fix the bug with the simple-but-ugly approach of loading the guest's FPU during KVM_GET_MP_STATE.
[*] See commit f240652b6032b ("x86/mpx: Remove MPX APIs").
--- arch/x86/kvm/x86.c | 4 ++++ 1 file changed, 4 insertions(+)
arch/x86/kvm/x86.c +4 -0
@@ -8724,6 +8724,8 @@ int kvm_arch_vcpu_ioctl_get_mpstate(stru struct kvm_mp_state *mp_state) {vcpu_load (vcpu); +if (kvm_mpx_supported ()) +kvm_load_guest_fpu (vcpu);kvm_apic_accept_events (vcpu); if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED &&
@@ -8732,6 +8734,8 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struelse mp_state->mp_state = vcpu->arch.mp_state; +if (kvm_mpx_supported ()) +kvm_put_guest_fpu (vcpu);vcpu_put (vcpu);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 240/367 KVM: PPC: Book3S HV: Uninit vCPU if vcore creation fails
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit 1a978d9d3e72ddfa40ac60d26301b154247ee0bc upstream.
Call kvm_vcpu_uninit() if vcore creation fails to avoid leaking any resources allocated by kvm_vcpu_init(), i.e. the vcpu->run page.
--- arch/powerpc/kvm/book3s_hv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/powerpc/kvm/book3s_hv.c +3 -1
@@ -2368,7 +2368,7 @@ static struct kvm_vcpu *kvmppc_core_vcpumutex_unlock (&kvm->lock);if (!vcore) - goto free_vcpu; + goto uninit_vcpu;spin_lock (&vcore->lock); ++vcore->num_threads;
@@ -2385,6 +2385,8 @@ static struct kvm_vcpu *kvmppc_core_vcpureturn vcpu; +uninit_vcpu: +kvm_vcpu_uninit (vcpu); free_vcpu:kmem_cache_free (kvm_vcpu_cache, vcpu); out:
Greg Kroah-Hartman
maintainerPATCH 241/367 KVM: PPC: Book3S PR: Free shared page if mmu initialization fails
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit cb10bf9194f4d2c5d830eddca861f7ca0fecdbb4 upstream.
Explicitly free the shared page if kvmppc_mmu_init() fails during kvmppc_core_vcpu_create(), as the page is freed only in kvmppc_core_vcpu_free(), which is not reached via kvm_vcpu_uninit().
--- arch/powerpc/kvm/book3s_pr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/powerpc/kvm/book3s_pr.c +3 -1
@@ -1806,10 +1806,12 @@ static struct kvm_vcpu *kvmppc_core_vcpu err =kvmppc_mmu_init (vcpu);if (err <0 ) - goto uninit_vcpu; + goto free_shared_page;return vcpu; +free_shared_page: +free_page ((unsigned long )vcpu->arch.shared); uninit_vcpu:kvm_vcpu_uninit (vcpu); free_shadow_vcpu:
Greg Kroah-Hartman
maintainerPATCH 242/367 kvm/svm: PKU not currently supported
In reply to Greg Kroah-HartmanFrom: John Allen <[email protected]>
commit a47970ed74a535b1accb4bc73643fd5a93993c3e upstream.
Current SVM implementation does not have support for handling PKU. Guests running on a host with future AMD cpus that support the feature will read garbage from the PKRU register and will hit segmentation faults on boot as memory is getting marked as protected that should not be. Ensure that cpuid from SVM does not advertise the feature.
--- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/cpuid.c | 4 +++- arch/x86/kvm/svm.c | 6 ++++++ arch/x86/kvm/vmx/capabilities.h | 5 +++++ arch/x86/kvm/vmx/vmx.c | 1 + 5 files changed, 16 insertions(+), 1 deletion(-)
arch/x86/include/asm/kvm_host.h +1 -0
@@ -1145,6 +1145,7 @@ struct kvm_x86_ops {bool (*xsaves_supported)(void );bool (*umip_emulated)(void );bool (*pt_supported)(void ); +bool (*pku_supported)(void );int (*check_nested_events)(struct kvm_vcpu *vcpu,bool external_intr);void (*request_immediate_exit)(struct kvm_vcpu *vcpu);
arch/x86/kvm/cpuid.c +3 -1
@@ -352,6 +352,7 @@ static inline void do_cpuid_7_mask(strucunsigned f_umip = kvm_x86_ops->umip_emulated () ?F (UMIP ) :0 ;unsigned f_intel_pt = kvm_x86_ops->pt_supported () ?F (INTEL_PT ) :0 ;unsigned f_la57; +unsigned f_pku = kvm_x86_ops->pku_supported () ?F (PKU ) :0 ;/* cpuid 7.0.ebx */ const u32 kvm_cpuid_7_0_ebx_x86_features =
@@ -363,7 +364,7 @@ static inline void do_cpuid_7_mask(struc/* cpuid 7.0.ecx*/ const u32 kvm_cpuid_7_0_ecx_x86_features = -F (AVX512VBMI ) |F (LA57 ) |F (PKU ) |0 /*OSPKE*/ |F (RDPID ) | +F (AVX512VBMI ) |F (LA57 ) |0 /*PKU*/ |0 /*OSPKE*/ |F (RDPID ) |F (AVX512_VPOPCNTDQ ) |F (UMIP ) |F (AVX512_VBMI2 ) |F (GFNI ) |F (VAES ) |F (VPCLMULQDQ ) |F (AVX512_VNNI ) |F (AVX512_BITALG ) |F (CLDEMOTE ) |F (MOVDIRI ) |F (MOVDIR64B ) |0 /*WAITPKG*/ ;
@@ -392,6 +393,7 @@ static inline void do_cpuid_7_mask(struc/* Set LA57 based on hardware capability. */ entry->ecx |= f_la57; entry->ecx |= f_umip; + entry->ecx |= f_pku;/* PKU is not yet implemented for shadow paging. */ if (!tdp_enabled || !boot_cpu_has (X86_FEATURE_OSPKE )) entry->ecx &= ~F (PKU );
arch/x86/kvm/svm.c +6 -0
@@ -6001,6 +6001,11 @@ static bool svm_has_wbinvd_exit(void)return true; } +static bool svm_pku_supported (void ) +{ +return false; +} +#define PRE_EX (exit) { .exit_code = (exit), \ .stage = X86_ICPT_PRE_EXCEPT, }#define POST_EX (exit) { .exit_code = (exit), \
@@ -7341,6 +7346,7 @@ static struct kvm_x86_ops svm_x86_ops __ .xsaves_supported = svm_xsaves_supported, .umip_emulated = svm_umip_emulated, .pt_supported = svm_pt_supported, + .pku_supported = svm_pku_supported, .set_supported_cpuid = svm_set_supported_cpuid,
arch/x86/kvm/vmx/capabilities.h +5 -0
@@ -145,6 +145,11 @@ static inline bool vmx_umip_emulated(voiSECONDARY_EXEC_DESC ; } +static inline bool vmx_pku_supported (void ) +{ +return boot_cpu_has (X86_FEATURE_PKU ); +} +static inline bool cpu_has_vmx_rdtscp (void ) { return vmcs_config.cpu_based_2nd_exec_ctrl &
arch/x86/kvm/vmx/vmx.c +1 -0
@@ -7870,6 +7870,7 @@ static struct kvm_x86_ops vmx_x86_ops __ .xsaves_supported = vmx_xsaves_supported, .umip_emulated = vmx_umip_emulated, .pt_supported = vmx_pt_supported, + .pku_supported = vmx_pku_supported, .request_immediate_exit = vmx_request_immediate_exit,
Greg Kroah-Hartman
maintainerPATCH 243/367 KVM: x86/mmu: Apply max PA check for MMIO sptes to 32-bit KVM
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit e30a7d623dccdb3f880fbcad980b0cb589a1da45 upstream.
Remove the bogus 64-bit only condition from the check that disables MMIO spte optimization when the system supports the max PA, i.e. doesn't have any reserved PA bits. 32-bit KVM always uses PAE paging for the shadow MMU, and per Intel's SDM:
PAE paging translates 32-bit linear addresses to 52-bit physical addresses.
The kernel's restrictions on max physical addresses are limits on how much memory the kernel can reasonably use, not what physical addresses are supported by hardware.
--- arch/x86/kvm/mmu/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kvm/mmu/mmu.c +1 -1
@@ -6249,7 +6249,7 @@ static void kvm_set_mmio_spte_mask(void)* If reserved bit is not supported, clear the present bit to disable * mmio page fault. */ -if (IS_ENABLED (CONFIG_X86_64 ) && shadow_phys_bits ==52 ) +if (shadow_phys_bits ==52 ) mask &= ~1ull ;kvm_mmu_set_mmio_spte_mask (mask, mask,ACC_WRITE_MASK |ACC_USER_MASK );
Greg Kroah-Hartman
maintainerPATCH 244/367 x86/kvm: Be careful not to clear KVM_VCPU_FLUSH_TLB bit
In reply to Greg Kroah-HartmanFrom: Boris Ostrovsky <[email protected]>
commit 8c6de56a42e0c657955e12b882a81ef07d1d073e upstream.
kvm_steal_time_set_preempted() may accidentally clear KVM_VCPU_FLUSH_TLB bit if it is called more than once while VCPU is preempted.
This is part of CVE-2019-3016.
(This bug was also independently discovered by Jim Mattson <[email protected]>)
--- arch/x86/kvm/x86.c | 3 +++ 1 file changed, 3 insertions(+)
arch/x86/kvm/x86.c +3 -0
@@ -3514,6 +3514,9 @@ static void kvm_steal_time_set_preemptedif (!(vcpu->arch.st.msr_val &KVM_MSR_ENABLED ))return ; +if (vcpu->arch.st.steal.preempted) +return ; + vcpu->arch.st.steal.preempted =KVM_VCPU_PREEMPTED ; kvm_write_guest_offset_cached(vcpu->kvm, &vcpu->arch.st.stime,
Greg Kroah-Hartman
maintainerPATCH 245/367 KVM: x86: use CPUID to locate host page table reserved bits
In reply to Greg Kroah-HartmanFrom: Paolo Bonzini <[email protected]>
commit 7adacf5eb2d2048045d9fd8fdab861fd9e7e2e96 upstream.
The comment in kvm_get_shadow_phys_bits refers to MKTME, but the same is actually true of SME and SEV. Just use CPUID[0x8000_0008].EAX[7:0] unconditionally if available, it is simplest and works even if memory is not encrypted.
--- arch/x86/kvm/mmu/mmu.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
arch/x86/kvm/mmu/mmu.c +12 -8
@@ -538,16 +538,20 @@ EXPORT_SYMBOL_GPL(kvm_mmu_set_mask_ptes)static u8 kvm_get_shadow_phys_bits (void ) {/* -* boot_cpu_data.x86_phys_bits is reduced when MKTME is detected -* in CPU detection code, but MKTME treats those reduced bits as -* 'keyID' thus they are not reserved bits. Therefore for MKTME -* we should still return physical address bits reported by CPUID. +* boot_cpu_data.x86_phys_bits is reduced when MKTME or SME are detected +* in CPU detection code, but the processor treats those reduced bits as +* 'keyID' thus they are not reserved bits. Therefore KVM needs to look at +* the physical address bits reported by CPUID. */ -if (!boot_cpu_has (X86_FEATURE_TME ) || -WARN_ON_ONCE (boot_cpu_data.extended_cpuid_level <0x80000008 )) -return boot_cpu_data.x86_phys_bits; +if (likely (boot_cpu_data.extended_cpuid_level >=0x80000008 )) +return cpuid_eax (0x80000008 ) &0xff ; -return cpuid_eax (0x80000008 ) &0xff ; +/* +* Quite weird to have VMX or SVM but not MAXPHYADDR; probably a VM with +* custom CPUID. Proceed with whatever the kernel found since these features +* aren't virtualizable (SME/SEV also require CPUIDs higher than 0x80000008). +*/ +return boot_cpu_data.x86_phys_bits; }static void kvm_mmu_reset_all_pte_masks (void )
Greg Kroah-Hartman
maintainerPATCH 246/367 x86/kvm: Introduce kvm_(un)map_gfn()
In reply to Greg Kroah-HartmanFrom: Boris Ostrovsky <[email protected]>
commit 1eff70a9abd46f175defafd29bc17ad456f398a7 upstream.
kvm_vcpu_(un)map operates on gfns from any current address space. In certain cases we want to make sure we are not mapping SMRAM and for that we can use kvm_(un)map_gfn() that we are introducing in this patch.
This is part of CVE-2019-3016.
--- include/linux/kvm_host.h | 2 ++ virt/kvm/kvm_main.c | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-)
include/linux/kvm_host.h +2 -0
@@ -775,8 +775,10 @@ struct kvm_memory_slot *kvm_vcpu_gfn_to_kvm_pfn_t kvm_vcpu_gfn_to_pfn_atomic (struct kvm_vcpu *vcpu,gfn_t gfn);kvm_pfn_t kvm_vcpu_gfn_to_pfn (struct kvm_vcpu *vcpu,gfn_t gfn);int kvm_vcpu_map (struct kvm_vcpu *vcpu,gpa_t gpa,struct kvm_host_map *map); +int kvm_map_gfn (struct kvm_vcpu *vcpu,gfn_t gfn,struct kvm_host_map *map);struct page *kvm_vcpu_gfn_to_page (struct kvm_vcpu *vcpu,gfn_t gfn);void kvm_vcpu_unmap (struct kvm_vcpu *vcpu,struct kvm_host_map *map,bool dirty); +int kvm_unmap_gfn (struct kvm_vcpu *vcpu,struct kvm_host_map *map,bool dirty);unsigned long kvm_vcpu_gfn_to_hva (struct kvm_vcpu *vcpu,gfn_t gfn);unsigned long kvm_vcpu_gfn_to_hva_prot (struct kvm_vcpu *vcpu,gfn_t gfn,bool *writable); int kvm_vcpu_read_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, void *data, int offset,
virt/kvm/kvm_main.c +24 -5
@@ -1821,12 +1821,13 @@ struct page *gfn_to_page(struct kvm *kvm }EXPORT_SYMBOL_GPL (gfn_to_page); -static int __kvm_map_gfn (struct kvm_memory_slot *slot,gfn_t gfn, +static int __kvm_map_gfn (struct kvm_memslots *slots,gfn_t gfn,struct kvm_host_map *map) {kvm_pfn_t pfn;void *hva =NULL ;struct page *page =KVM_UNMAPPED_PAGE ; +struct kvm_memory_slot *slot =__gfn_to_memslot (slots, gfn);if (!map)return -EINVAL ;
@@ -1855,14 +1856,20 @@ static int __kvm_map_gfn(struct kvm_memoreturn 0 ; } +int kvm_map_gfn (struct kvm_vcpu *vcpu,gfn_t gfn,struct kvm_host_map *map) +{ +return __kvm_map_gfn (kvm_memslots (vcpu->kvm), gfn, map); +} +EXPORT_SYMBOL_GPL (kvm_map_gfn); +int kvm_vcpu_map (struct kvm_vcpu *vcpu,gfn_t gfn,struct kvm_host_map *map) { -return __kvm_map_gfn (kvm_vcpu_gfn_to_memslot (vcpu, gfn), gfn, map); +return __kvm_map_gfn (kvm_vcpu_memslots (vcpu), gfn, map); }EXPORT_SYMBOL_GPL (kvm_vcpu_map); -void kvm_vcpu_unmap (struct kvm_vcpu *vcpu,struct kvm_host_map *map, -bool dirty) +static void __kvm_unmap_gfn (struct kvm_memory_slot *memslot, +struct kvm_host_map *map,bool dirty) {if (!map)return ;
@@ -1878,7 +1885,7 @@ void kvm_vcpu_unmap(struct kvm_vcpu *vcp#endif if (dirty) { -kvm_vcpu_mark_page_dirty (vcpu, map->gfn); +mark_page_dirty_in_slot (memslot, map->gfn);kvm_release_pfn_dirty (map->pfn); }else {kvm_release_pfn_clean (map->pfn);
@@ -1887,6 +1894,18 @@ void kvm_vcpu_unmap(struct kvm_vcpu *vcp map->hva =NULL ; map->page =NULL ; } + +int kvm_unmap_gfn (struct kvm_vcpu *vcpu,struct kvm_host_map *map,bool dirty) +{ +__kvm_unmap_gfn (gfn_to_memslot (vcpu->kvm, map->gfn), map, dirty); +return 0 ; +} +EXPORT_SYMBOL_GPL (kvm_unmap_gfn); + +void kvm_vcpu_unmap (struct kvm_vcpu *vcpu,struct kvm_host_map *map,bool dirty) +{ +__kvm_unmap_gfn (kvm_vcpu_gfn_to_memslot (vcpu, map->gfn), map, dirty); +}EXPORT_SYMBOL_GPL (kvm_vcpu_unmap);struct page *kvm_vcpu_gfn_to_page (struct kvm_vcpu *vcpu,gfn_t gfn)
Greg Kroah-Hartman
maintainerPATCH 247/367 x86/KVM: Make sure KVM_VCPU_FLUSH_TLB flag is not missed
In reply to Greg Kroah-HartmanFrom: Boris Ostrovsky <[email protected]>
commit b043138246a41064527cf019a3d51d9f015e9796 upstream.
There is a potential race in record_steal_time() between setting host-local vcpu->arch.st.steal.preempted to zero (i.e. clearing KVM_VCPU_PREEMPTED) and propagating this value to the guest with kvm_write_guest_cached(). Between those two events the guest may still see KVM_VCPU_PREEMPTED in its copy of kvm_steal_time, set KVM_VCPU_FLUSH_TLB and assume that hypervisor will do the right thing. Which it won't.
Instad of copying, we should map kvm_steal_time and that will guarantee atomicity of accesses to @preempted.
This is part of CVE-2019-3016.
--- arch/x86/kvm/x86.c | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-)
arch/x86/kvm/x86.c +30 -21
@@ -2588,45 +2588,47 @@ static void kvm_vcpu_flush_tlb(struct kvstatic void record_steal_time (struct kvm_vcpu *vcpu) { +struct kvm_host_map map; +struct kvm_steal_time *st; +if (!(vcpu->arch.st.msr_val &KVM_MSR_ENABLED ))return ; -if (unlikely (kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.st.stime, - &vcpu->arch.st.steal,sizeof (struct kvm_steal_time )))) +/* -EAGAIN is returned in atomic context so we can just return. */ +if (kvm_map_gfn (vcpu, vcpu->arch.st.msr_val >>PAGE_SHIFT , + &map, &vcpu->arch.st.cache, false))return ; + st = map.hva + +offset_in_page (vcpu->arch.st.msr_val &KVM_STEAL_VALID_BITS ); +/* * Doing a TLB flush here, on the guest's behalf, can avoid * expensive IPIs. */ trace_kvm_pv_tlb_flush (vcpu->vcpu_id, - vcpu->arch.st.steal.preempted &KVM_VCPU_FLUSH_TLB ); -if (xchg (&vcpu->arch.st.steal.preempted,0 ) &KVM_VCPU_FLUSH_TLB ) + st->preempted &KVM_VCPU_FLUSH_TLB ); +if (xchg (&st->preempted,0 ) &KVM_VCPU_FLUSH_TLB )kvm_vcpu_flush_tlb (vcpu, false); -if (vcpu->arch.st.steal.version &1 ) - vcpu->arch.st.steal.version +=1 ;/* first time write, random junk */ + vcpu->arch.st.steal.preempted =0 ; - vcpu->arch.st.steal.version +=1 ; +if (st->version &1 ) + st->version +=1 ;/* first time write, random junk */ -kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.st.stime, - &vcpu->arch.st.steal,sizeof (struct kvm_steal_time )); + st->version +=1 ;smp_wmb (); - vcpu->arch.st.steal.steal += current->sched_info.run_delay - + st->steal += current->sched_info.run_delay - vcpu->arch.st.last_steal; vcpu->arch.st.last_steal = current->sched_info.run_delay; -kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.st.stime, - &vcpu->arch.st.steal,sizeof (struct kvm_steal_time )); -smp_wmb (); - vcpu->arch.st.steal.version +=1 ; + st->version +=1 ; -kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.st.stime, - &vcpu->arch.st.steal,sizeof (struct kvm_steal_time )); +kvm_unmap_gfn (vcpu, &map, &vcpu->arch.st.cache, true, false); }int kvm_set_msr_common (struct kvm_vcpu *vcpu,struct msr_data *msr_info)
@@ -3511,18 +3513,25 @@ void kvm_arch_vcpu_load(struct kvm_vcpustatic void kvm_steal_time_set_preempted (struct kvm_vcpu *vcpu) { +struct kvm_host_map map; +struct kvm_steal_time *st; +if (!(vcpu->arch.st.msr_val &KVM_MSR_ENABLED ))return ;if (vcpu->arch.st.steal.preempted)return ; - vcpu->arch.st.steal.preempted =KVM_VCPU_PREEMPTED ; +if (kvm_map_gfn (vcpu, vcpu->arch.st.msr_val >>PAGE_SHIFT , &map, + &vcpu->arch.st.cache, true)) +return ; + + st = map.hva + +offset_in_page (vcpu->arch.st.msr_val &KVM_STEAL_VALID_BITS ); + + st->preempted = vcpu->arch.st.steal.preempted =KVM_VCPU_PREEMPTED ; -kvm_write_guest_offset_cached (vcpu->kvm, &vcpu->arch.st.stime, - &vcpu->arch.st.steal.preempted, - offsetof(struct kvm_steal_time , preempted), -sizeof (vcpu->arch.st.steal.preempted)); +kvm_unmap_gfn (vcpu, &map, &vcpu->arch.st.cache, true, true); }void kvm_arch_vcpu_put (struct kvm_vcpu *vcpu)
Greg Kroah-Hartman
maintainerPATCH 248/367 x86/kvm: Cache gfn to pfn translation
In reply to Greg Kroah-HartmanFrom: Boris Ostrovsky <[email protected]>
commit 917248144db5d7320655dbb41d3af0b8a0f3d589 upstream.
__kvm_map_gfn()'s call to gfn_to_pfn_memslot() is
* relatively expensive
* in certain cases (such as when done from atomic context) cannot be called
Stashing gfn-to-pfn mapping should help with both cases.
This is part of CVE-2019-3016.
--- arch/x86/include/asm/kvm_host.h | 1 arch/x86/kvm/x86.c | 10 ++++ include/linux/kvm_host.h | 7 ++ include/linux/kvm_types.h | 9 +++ virt/kvm/kvm_main.c | 98 ++++++++++++++++++++++++++++++++-------- 5 files changed, 103 insertions(+), 22 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -0
@@ -689,6 +689,7 @@ struct kvm_vcpu_arch {u64 last_steal;struct gfn_to_hva_cache stime;struct kvm_steal_time steal; +struct gfn_to_pfn_cache cache; } st;u64 tsc_offset;
arch/x86/kvm/x86.c +10 -0
@@ -9111,6 +9111,9 @@ static void fx_init(struct kvm_vcpu *vcpvoid kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) {void *wbinvd_dirty_mask = vcpu->arch.wbinvd_dirty_mask; +struct gfn_to_pfn_cache *cache = &vcpu->arch.st.cache; + +kvm_release_pfn (cache->pfn, cache->dirty, cache);kvmclock_reset (vcpu);
@@ -9784,11 +9787,18 @@ out_free:void kvm_arch_memslots_updated (struct kvm *kvm,u64 gen) { +struct kvm_vcpu *vcpu; +int i; +/* * memslots->generation has been incremented. * mmio generation may have reached its maximum value. */ kvm_mmu_invalidate_mmio_sptes (kvm, gen); + +/* Force re-initialization of steal_time cache */ +kvm_for_each_vcpu (i, vcpu, kvm) +kvm_vcpu_kick (vcpu); } int kvm_arch_prepare_memory_region(struct kvm *kvm,
include/linux/kvm_host.h +5 -2
@@ -723,6 +723,7 @@ void kvm_set_pfn_dirty(kvm_pfn_t pfn);void kvm_set_pfn_accessed (kvm_pfn_t pfn);void kvm_get_pfn (kvm_pfn_t pfn); +void kvm_release_pfn (kvm_pfn_t pfn,bool dirty,struct gfn_to_pfn_cache *cache);int kvm_read_guest_page (struct kvm *kvm,gfn_t gfn,void *data,int offset,int len); int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data,
@@ -775,10 +776,12 @@ struct kvm_memory_slot *kvm_vcpu_gfn_to_kvm_pfn_t kvm_vcpu_gfn_to_pfn_atomic (struct kvm_vcpu *vcpu,gfn_t gfn);kvm_pfn_t kvm_vcpu_gfn_to_pfn (struct kvm_vcpu *vcpu,gfn_t gfn);int kvm_vcpu_map (struct kvm_vcpu *vcpu,gpa_t gpa,struct kvm_host_map *map); -int kvm_map_gfn (struct kvm_vcpu *vcpu,gfn_t gfn,struct kvm_host_map *map); +int kvm_map_gfn (struct kvm_vcpu *vcpu,gfn_t gfn,struct kvm_host_map *map, +struct gfn_to_pfn_cache *cache,bool atomic);struct page *kvm_vcpu_gfn_to_page (struct kvm_vcpu *vcpu,gfn_t gfn);void kvm_vcpu_unmap (struct kvm_vcpu *vcpu,struct kvm_host_map *map,bool dirty); -int kvm_unmap_gfn (struct kvm_vcpu *vcpu,struct kvm_host_map *map,bool dirty); +int kvm_unmap_gfn (struct kvm_vcpu *vcpu,struct kvm_host_map *map, +struct gfn_to_pfn_cache *cache,bool dirty,bool atomic);unsigned long kvm_vcpu_gfn_to_hva (struct kvm_vcpu *vcpu,gfn_t gfn);unsigned long kvm_vcpu_gfn_to_hva_prot (struct kvm_vcpu *vcpu,gfn_t gfn,bool *writable); int kvm_vcpu_read_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, void *data, int offset,
include/linux/kvm_types.h +8 -1
@@ -18,7 +18,7 @@ struct kvm_memslots;enum kvm_mr_change ; -#include <asm/types.h> +#include <linux/types.h> /* * Address types:
@@ -51,4 +51,11 @@ struct gfn_to_hva_cache { struct kvm_memory_slot *memslot; }; +struct gfn_to_pfn_cache { +u64 generation; +gfn_t gfn; +kvm_pfn_t pfn; +bool dirty; +}; +#endif /* __KVM_TYPES_H__ */
virt/kvm/kvm_main.c +79 -19
160 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 249/367 x86/KVM: Clean up hosts steal time structure
In reply to Greg Kroah-HartmanFrom: Boris Ostrovsky <[email protected]>
commit a6bd811f1209fe1c64c9f6fd578101d6436c6b6e upstream.
Now that we are mapping kvm_steal_time from the guest directly we don't need keep a copy of it in kvm_vcpu_arch.st. The same is true for the stime field.
This is part of CVE-2019-3016.
--- arch/x86/include/asm/kvm_host.h | 3 +-- arch/x86/kvm/x86.c | 11 +++-------- 2 files changed, 4 insertions(+), 10 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -2
@@ -685,10 +685,9 @@ struct kvm_vcpu_arch {bool pvclock_set_guest_stopped_request;struct { +u8 preempted;u64 msr_val;u64 last_steal; -struct gfn_to_hva_cache stime; -struct kvm_steal_time steal;struct gfn_to_pfn_cache cache; } st;
arch/x86/kvm/x86.c +3 -8
@@ -2611,7 +2611,7 @@ static void record_steal_time(struct kvmif (xchg (&st->preempted,0 ) &KVM_VCPU_FLUSH_TLB )kvm_vcpu_flush_tlb (vcpu, false); - vcpu->arch.st.steal.preempted =0 ; + vcpu->arch.st.preempted =0 ;if (st->version &1 ) st->version +=1 ;/* first time write, random junk */
@@ -2795,11 +2795,6 @@ int kvm_set_msr_common(struct kvm_vcpu *if (data &KVM_STEAL_RESERVED_MASK )return 1 ; -if (kvm_gfn_to_hva_cache_init (vcpu->kvm, &vcpu->arch.st.stime, - data &KVM_STEAL_VALID_BITS , -sizeof (struct kvm_steal_time ))) -return 1 ; - vcpu->arch.st.msr_val = data;if (!(data &KVM_MSR_ENABLED ))
@@ -3519,7 +3514,7 @@ static void kvm_steal_time_set_preemptedif (!(vcpu->arch.st.msr_val &KVM_MSR_ENABLED ))return ; -if (vcpu->arch.st.steal.preempted) +if (vcpu->arch.st.preempted)return ; if (kvm_map_gfn(vcpu, vcpu->arch.st.msr_val >> PAGE_SHIFT, &map,
@@ -3529,7 +3524,7 @@ static void kvm_steal_time_set_preempted st = map.hva +offset_in_page (vcpu->arch.st.msr_val &KVM_STEAL_VALID_BITS ); - st->preempted = vcpu->arch.st.steal.preempted =KVM_VCPU_PREEMPTED ; + st->preempted = vcpu->arch.st.preempted =KVM_VCPU_PREEMPTED ;kvm_unmap_gfn (vcpu, &map, &vcpu->arch.st.cache, true, true); }
Greg Kroah-Hartman
maintainerPATCH 250/367 KVM: x86: Use gpa_t for cr2/gpa to fix TDP support on 32-bit KVM
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit 736c291c9f36b07f8889c61764c28edce20e715d upstream.
Convert a plethora of parameters and variables in the MMU and page fault flows from type gva_t to gpa_t to properly handle TDP on 32-bit KVM.
Thanks to PSE and PAE paging, 32-bit kernels can access 64-bit physical addresses. When TDP is enabled, the fault address is a guest physical address and thus can be a 64-bit value, even when both KVM and its guest are using 32-bit virtual addressing, e.g. VMX's VMCS.GUEST_PHYSICAL is a 64-bit field, not a natural width field.
Using a gva_t for the fault address means KVM will incorrectly drop the upper 32-bits of the GPA. Ditto for gva_to_gpa() when it is used to translate L2 GPAs to L1 GPAs.
Opportunistically rename variables and parameters to better reflect the dual address modes, e.g. use "cr2_or_gpa" for fault addresses and plain "addr" instead of "vaddr" when the address may be either a GVA or an L2 GPA. Similarly, use "gpa" in the nonpaging_page_fault() flows to avoid a confusing "gpa_t gva" declaration; this also sets the stage for a future patch to combing nonpaging_page_fault() and tdp_page_fault() with minimal churn.
Sprinkle in a few comments to document flows where an address is known to be a GVA and thus can be safely truncated to a 32-bit value. Add WARNs in kvm_handle_page_fault() and FNAME(gva_to_gpa_nested)() to help document such cases and detect bugs.
--- arch/x86/include/asm/kvm_host.h | 8 ++-- arch/x86/kvm/mmu/mmu.c | 69 +++++++++++++++++++++++----------------- arch/x86/kvm/mmu/paging_tmpl.h | 25 +++++++++----- arch/x86/kvm/mmutrace.h | 12 +++--- arch/x86/kvm/x86.c | 40 +++++++++++------------ arch/x86/kvm/x86.h | 2 - include/linux/kvm_host.h | 6 +-- virt/kvm/async_pf.c | 10 ++--- 8 files changed, 94 insertions(+), 78 deletions(-)
arch/x86/include/asm/kvm_host.h +4 -4
@@ -378,12 +378,12 @@ struct kvm_mmu {void (*set_cr3)(struct kvm_vcpu *vcpu,unsigned long root);unsigned long (*get_cr3)(struct kvm_vcpu *vcpu);u64 (*get_pdptr)(struct kvm_vcpu *vcpu,int index); -int (*page_fault)(struct kvm_vcpu *vcpu,gva_t gva,u32 err, +int (*page_fault)(struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa,u32 err,bool prefault);void (*inject_page_fault)(struct kvm_vcpu *vcpu,struct x86_exception *fault); -gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu,gva_t gva,u32 access, -struct x86_exception *exception); +gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu,gpa_t gva_or_gpa, +u32 access,struct x86_exception *exception);gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu,gpa_t gpa,u32 access,struct x86_exception *exception); int (*sync_page)(struct kvm_vcpu *vcpu,
@@ -1469,7 +1469,7 @@ void kvm_vcpu_deactivate_apicv(struct kvint kvm_emulate_hypercall (struct kvm_vcpu *vcpu); -int kvm_mmu_page_fault (struct kvm_vcpu *vcpu,gva_t gva,u64 error_code, +int kvm_mmu_page_fault (struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa,u64 error_code,void *insn,int insn_len);void kvm_mmu_invlpg (struct kvm_vcpu *vcpu,gva_t gva);void kvm_mmu_invpcid_gva (struct kvm_vcpu *vcpu,gva_t gva,unsigned long pcid);
arch/x86/kvm/mmu/mmu.c +40 -29
214 lines not on this page · raw message
arch/x86/kvm/mmu/paging_tmpl.h +16 -9
@@ -291,11 +291,11 @@ static inline unsigned FNAME(gpte_pkeys) }/* -* Fetch a guest pte for a guest virtual address +* Fetch a guest pte for a guest virtual address, or for an L2's GPA. */ static int FNAME (walk_addr_generic )(struct guest_walker *walker,struct kvm_vcpu *vcpu,struct kvm_mmu *mmu, -gva_t addr,u32 access) +gpa_t addr,u32 access) {int ret;pt_element_t pte;
@@ -496,7 +496,7 @@ error: }static int FNAME (walk_addr )(struct guest_walker *walker, -struct kvm_vcpu *vcpu,gva_t addr,u32 access) +struct kvm_vcpu *vcpu,gpa_t addr,u32 access) {return FNAME (walk_addr_generic)(walker, vcpu, vcpu->arch.mmu, addr, access);
@@ -611,7 +611,7 @@ static void FNAME(pte_prefetch)(struct k* If the guest tries to write a write-protected page, we need to * emulate this operation, return 1 to indicate this case. */ -static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, +static int FNAME(fetch)(struct kvm_vcpu *vcpu, gpa_t addr, struct guest_walker *gw, int write_fault, int hlevel, kvm_pfn_t pfn, bool map_writable, bool prefault,
@@ -765,7 +765,7 @@ FNAME(is_self_change_mapping)(struct kvm* Returns: 1 if we need to emulate the instruction, 0 otherwise, or * a negative value on error. */ -static int FNAME (page_fault )(struct kvm_vcpu *vcpu,gva_t addr,u32 error_code, +static int FNAME (page_fault )(struct kvm_vcpu *vcpu,gpa_t addr,u32 error_code,bool prefault) {int write_fault = error_code &PFERR_WRITE_MASK ;
@@ -945,18 +945,19 @@ static void FNAME(invlpg)(struct kvm_vcpspin_unlock (&vcpu->kvm->mmu_lock); } -static gpa_t FNAME (gva_to_gpa )(struct kvm_vcpu *vcpu,gva_t vaddr,u32 access, +/* Note, @addr is a GPA when gva_to_gpa() translates an L2 GPA to an L1 GPA. */ +static gpa_t FNAME (gva_to_gpa )(struct kvm_vcpu *vcpu,gpa_t addr,u32 access,struct x86_exception *exception) {struct guest_walker walker;gpa_t gpa =UNMAPPED_GVA ;int r; - r =FNAME (walk_addr)(&walker, vcpu, vaddr, access); + r =FNAME (walk_addr)(&walker, vcpu, addr, access);if (r) { gpa =gfn_to_gpa (walker.gfn); - gpa |= vaddr & ~PAGE_MASK ; + gpa |= addr & ~PAGE_MASK ; }else if (exception) *exception = walker.fault;
@@ -964,7 +965,8 @@ static gpa_t FNAME(gva_to_gpa)(struct kv }#if PTTYPE !=PTTYPE_EPT -static gpa_t FNAME (gva_to_gpa_nested )(struct kvm_vcpu *vcpu,gva_t vaddr, +/* Note, gva_to_gpa_nested() is only used to translate L2 GVAs. */ +static gpa_t FNAME (gva_to_gpa_nested )(struct kvm_vcpu *vcpu,gpa_t vaddr,u32 access,struct x86_exception *exception) {
@@ -972,6 +974,11 @@ static gpa_t FNAME(gva_to_gpa_nested)(stgpa_t gpa =UNMAPPED_GVA ;int r; +#ifndef CONFIG_X86_64 +/* A 64-bit GVA should be impossible on 32-bit KVM. */ +WARN_ON_ONCE (vaddr >>32 ); +#endif + r =FNAME (walk_addr_nested)(&walker, vcpu, vaddr, access);if (r) {
arch/x86/kvm/mmutrace.h +6 -6
@@ -249,13 +249,13 @@ TRACE_EVENT( TRACE_EVENT( fast_page_fault, - TP_PROTO(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code, + TP_PROTO(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u32 error_code, u64 *sptep, u64 old_spte, bool retry), - TP_ARGS(vcpu, gva, error_code, sptep, old_spte, retry), + TP_ARGS(vcpu, cr2_or_gpa, error_code, sptep, old_spte, retry), TP_STRUCT__entry( __field(int, vcpu_id) - __field(gva_t, gva) + __field(gpa_t, cr2_or_gpa) __field(u32, error_code) __field(u64 *, sptep) __field(u64, old_spte)
@@ -265,7 +265,7 @@ TRACE_EVENT(TP_fast_assign ( __entry->vcpu_id = vcpu->vcpu_id; - __entry->gva = gva; + __entry->cr2_or_gpa = cr2_or_gpa; __entry->error_code = error_code; __entry->sptep = sptep; __entry->old_spte = old_spte;
@@ -273,9 +273,9 @@ TRACE_EVENT( __entry->retry = retry; ), -TP_printk ("vcpu %d gva %lx error_code %s sptep %p old %#llx" +TP_printk ("vcpu %d gva %llx error_code %s sptep %p old %#llx" " new %llx spurious %d fixed %d" , __entry->vcpu_id, - __entry->gva,__print_flags (__entry->error_code,"|" , + __entry->cr2_or_gpa,__print_flags (__entry->error_code,"|" , kvm_mmu_trace_pferr_flags), __entry->sptep, __entry->old_spte, __entry->new_spte, __spte_satisfied(old_spte), __spte_satisfied(new_spte)
arch/x86/kvm/x86.c +19 -21
@@ -6396,11 +6396,11 @@ static int handle_emulation_failure(strureturn 1 ; } -static bool reexecute_instruction (struct kvm_vcpu *vcpu,gva_t cr2, +static bool reexecute_instruction (struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa,bool write_fault_to_shadow_pgtable,int emulation_type) { -gpa_t gpa = cr2; +gpa_t gpa = cr2_or_gpa;kvm_pfn_t pfn;if (!(emulation_type &EMULTYPE_ALLOW_RETRY ))
@@ -6414,7 +6414,7 @@ static bool reexecute_instruction(struct* Write permission should be allowed since only * write access need to be emulated. */ - gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2,NULL ); + gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2_or_gpa,NULL );/* * If the mapping is invalid in guest, let cpu retry
@@ -6471,10 +6471,10 @@ static bool reexecute_instruction(struct }static bool retry_instruction (struct x86_emulate_ctxt *ctxt, -unsigned long cr2,int emulation_type) +gpa_t cr2_or_gpa,int emulation_type) {struct kvm_vcpu *vcpu =emul_to_vcpu (ctxt); -unsigned long last_retry_eip, last_retry_addr, gpa = cr2; +unsigned long last_retry_eip, last_retry_addr, gpa = cr2_or_gpa; last_retry_eip = vcpu->arch.last_retry_eip; last_retry_addr = vcpu->arch.last_retry_addr;
@@ -6503,14 +6503,14 @@ static bool retry_instruction(struct x86if (x86_page_table_writing_insn (ctxt))return false; -if (ctxt->eip == last_retry_eip && last_retry_addr == cr2) +if (ctxt->eip == last_retry_eip && last_retry_addr == cr2_or_gpa)return false; vcpu->arch.last_retry_eip = ctxt->eip; - vcpu->arch.last_retry_addr = cr2; + vcpu->arch.last_retry_addr = cr2_or_gpa;if (!vcpu->arch.mmu->direct_map) - gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2,NULL ); + gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2_or_gpa,NULL );kvm_mmu_unprotect_page (vcpu->kvm,gpa_to_gfn (gpa));
@@ -6656,11 +6656,8 @@ static bool is_vmware_backdoor_opcode(streturn false; } -int x86_emulate_instruction (struct kvm_vcpu *vcpu, -unsigned long cr2, -int emulation_type, -void *insn, -int insn_len) +int x86_emulate_instruction (struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa, +int emulation_type,void *insn,int insn_len) {int r;struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt;
@@ -6706,8 +6703,9 @@ int x86_emulate_instruction(struct kvm_vkvm_queue_exception (vcpu,UD_VECTOR );return 1 ; } -if (reexecute_instruction (vcpu, cr2, write_fault_to_spt, - emulation_type)) +if (reexecute_instruction (vcpu, cr2_or_gpa, + write_fault_to_spt, + emulation_type))return 1 ;if (ctxt->have_exception) {/*
@@ -6741,7 +6739,7 @@ int x86_emulate_instruction(struct kvm_vreturn 1 ; } -if (retry_instruction (ctxt, cr2, emulation_type)) +if (retry_instruction (ctxt, cr2_or_gpa, emulation_type))return 1 ;/* this is needed for vmware backdoor interface to work since it
@@ -6753,7 +6751,7 @@ int x86_emulate_instruction(struct kvm_v restart:/* Save the faulting GPA (cr2) in the address field */ - ctxt->exception.address = cr2; + ctxt->exception.address = cr2_or_gpa; r =x86_emulate_insn (ctxt);
@@ -6761,7 +6759,7 @@ restart:return 1 ;if (r ==EMULATION_FAILED ) { -if (reexecute_instruction (vcpu, cr2, write_fault_to_spt, +if (reexecute_instruction (vcpu, cr2_or_gpa, write_fault_to_spt, emulation_type))return 1 ;
@@ -10045,7 +10043,7 @@ void kvm_arch_async_page_ready(struct kv work->arch.cr3 != vcpu->arch.mmu->get_cr3(vcpu)) return; - vcpu->arch.mmu->page_fault (vcpu, work->gva,0 , true); + vcpu->arch.mmu->page_fault (vcpu, work->cr2_or_gpa,0 , true); }static inline u32 kvm_async_pf_hash_fn (gfn_t gfn)
@@ -10158,7 +10156,7 @@ void kvm_arch_async_page_not_present(str {struct x86_exception fault; -trace_kvm_async_pf_not_present (work->arch.token, work->gva); +trace_kvm_async_pf_not_present (work->arch.token, work->cr2_or_gpa);kvm_add_async_pf_gfn (vcpu, work->arch.gfn); if (kvm_can_deliver_async_pf(vcpu) &&
@@ -10193,7 +10191,7 @@ void kvm_arch_async_page_present(struct work->arch.token = ~0 ;/* broadcast wakeup */ else kvm_del_async_pf_gfn(vcpu, work->arch.gfn); -trace_kvm_async_pf_ready (work->arch.token, work->gva); +trace_kvm_async_pf_ready (work->arch.token, work->cr2_or_gpa);if (vcpu->arch.apf.msr_val &KVM_ASYNC_PF_ENABLED && !apf_get_user (vcpu, &val)) {
arch/x86/kvm/x86.h +1 -1
@@ -289,7 +289,7 @@ int kvm_mtrr_get_msr(struct kvm_vcpu *vcbool kvm_mtrr_check_gfn_range_consistency (struct kvm_vcpu *vcpu,gfn_t gfn,int page_num);bool kvm_vector_hashing_enabled (void ); -int x86_emulate_instruction (struct kvm_vcpu *vcpu,unsigned long cr2, +int x86_emulate_instruction (struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa,int emulation_type,void *insn,int insn_len);#define KVM_SUPPORTED_XCR0 (XFEATURE_MASK_FP | XFEATURE_MASK_SSE \
include/linux/kvm_host.h +3 -3
@@ -204,7 +204,7 @@ struct kvm_async_pf {struct list_head queue;struct kvm_vcpu *vcpu;struct mm_struct *mm; -gva_t gva; +gpa_t cr2_or_gpa;unsigned long addr;struct kvm_arch_async_pf arch;bool wakeup_all;
@@ -212,8 +212,8 @@ struct kvm_async_pf {void kvm_clear_async_pf_completion_queue (struct kvm_vcpu *vcpu);void kvm_check_async_pf_completion (struct kvm_vcpu *vcpu); -int kvm_setup_async_pf (struct kvm_vcpu *vcpu,gva_t gva,unsigned long hva, -struct kvm_arch_async_pf *arch); +int kvm_setup_async_pf (struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa, +unsigned long hva,struct kvm_arch_async_pf *arch);int kvm_async_pf_wakeup_all (struct kvm_vcpu *vcpu);#endif
virt/kvm/async_pf.c +5 -5
@@ -64,7 +64,7 @@ static void async_pf_execute(struct workstruct mm_struct *mm = apf->mm;struct kvm_vcpu *vcpu = apf->vcpu;unsigned long addr = apf->addr; -gva_t gva = apf->gva; +gpa_t cr2_or_gpa = apf->cr2_or_gpa;int locked =1 ;might_sleep ();
@@ -92,7 +92,7 @@ static void async_pf_execute(struct work* this point */ -trace_kvm_async_pf_completed (addr, gva); +trace_kvm_async_pf_completed (addr, cr2_or_gpa);if (swq_has_sleeper (&vcpu->wq))swake_up_one (&vcpu->wq);
@@ -165,8 +165,8 @@ void kvm_check_async_pf_completion(struc } } -int kvm_setup_async_pf (struct kvm_vcpu *vcpu,gva_t gva,unsigned long hva, -struct kvm_arch_async_pf *arch) +int kvm_setup_async_pf (struct kvm_vcpu *vcpu,gpa_t cr2_or_gpa, +unsigned long hva,struct kvm_arch_async_pf *arch) {struct kvm_async_pf *work;
@@ -185,7 +185,7 @@ int kvm_setup_async_pf(struct kvm_vcpu * work->wakeup_all = false; work->vcpu = vcpu; - work->gva = gva; + work->cr2_or_gpa = cr2_or_gpa; work->addr = hva; work->arch = *arch; work->mm = current->mm;
Greg Kroah-Hartman
maintainerPATCH 251/367 KVM: VMX: Add non-canonical check on writes to RTIT address MSRs
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit fe6ed369fca98e99df55c932b85782a5687526b5 upstream.
Reject writes to RTIT address MSRs if the data being written is a non-canonical address as the MSRs are subject to canonical checks, e.g. KVM will trigger an unchecked #GP when loading the values to hardware during pt_guest_enter().
--- arch/x86/kvm/vmx/vmx.c | 2 ++ 1 file changed, 2 insertions(+)
arch/x86/kvm/vmx/vmx.c +2 -0
@@ -2146,6 +2146,8 @@ static int vmx_set_msr(struct kvm_vcpu * (index >= 2 * intel_pt_validate_cap(vmx->pt_desc.caps, PT_CAP_num_address_ranges)))return 1 ; +if (is_noncanonical_address (data, vcpu)) +return 1 ;if (index %2 ) vmx->pt_desc.guest.addr_b[index /2 ] = data;else
Greg Kroah-Hartman
maintainerPATCH 252/367 KVM: x86: Dont let userspace set host-reserved cr4 bits
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit b11306b53b2540c6ba068c4deddb6a17d9f8d95b upstream.
Calculate the host-reserved cr4 bits at runtime based on the system's capabilities (using logic similar to __do_cpuid_func()), and use the dynamically generated mask for the reserved bit check in kvm_set_cr4() instead using of the static CR4_RESERVED_BITS define. This prevents userspace from "enabling" features in cr4 that are not supported by the system, e.g. by ignoring KVM_GET_SUPPORTED_CPUID and specifying a bogus CPUID for the vCPU.
Allowing userspace to set unsupported bits in cr4 can lead to a variety of undesirable behavior, e.g. failed VM-Enter, and in general increases KVM's attack surface. A crafty userspace can even abuse CR4.LA57 to induce an unchecked #GP on a WRMSR.
On a platform without LA57 support:
KVM_SET_CPUID2 // CPUID_7_0_ECX.LA57 = 1
KVM_SET_SREGS // CR4.LA57 = 1
KVM_SET_MSRS // KERNEL_GS_BASE = 0x0004000000000000
KVM_RUN
leads to a #GP when writing KERNEL_GS_BASE into hardware:
unchecked MSR access error: WRMSR to 0xc0000102 (tried to write 0x0004000000000000) at rIP: 0xffffffffa00f239a (vmx_prepare_switch_to_guest+0x10a/0x1d0 [kvm_intel])
Call Trace: kvm_arch_vcpu_ioctl_run+0x671/0x1c70 [kvm] kvm_vcpu_ioctl+0x36b/0x5d0 [kvm] do_vfs_ioctl+0xa1/0x620 ksys_ioctl+0x66/0x70 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x4c/0x170 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x7fc08133bf47
Note, the above sequence fails VM-Enter due to invalid guest state. Userspace can allow VM-Enter to succeed (after the WRMSR #GP) by adding a KVM_SET_SREGS w/ CR4.LA57=0 after KVM_SET_MSRS, in which case KVM will technically leak the host's KERNEL_GS_BASE into the guest. But, as KERNEL_GS_BASE is a userspace-defined value/address, the leak is largely benign as a malicious userspace would simply be exposing its own data to the guest, and attacking a benevolent userspace would require multiple bugs in the userspace VMM.
--- arch/x86/kvm/x86.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-)
arch/x86/kvm/x86.c +34 -1
@@ -93,6 +93,8 @@ u64 __read_mostly efer_reserved_bits = ~static u64 __read_mostly efer_reserved_bits = ~((u64 )EFER_SCE );#endif +static u64 __read_mostly cr4_reserved_bits =CR4_RESERVED_BITS ; +#define VM_STAT (x, ...) offsetof(struct kvm, stat.x), KVM_STAT_VM, ## __VA_ARGS__#define VCPU_STAT (x, ...) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU, ## __VA_ARGS__
@@ -879,9 +881,38 @@ int kvm_set_xcr(struct kvm_vcpu *vcpu, u }EXPORT_SYMBOL_GPL (kvm_set_xcr); +static u64 kvm_host_cr4_reserved_bits (struct cpuinfo_x86 *c) +{ +u64 reserved_bits =CR4_RESERVED_BITS ; + +if (!cpu_has (c,X86_FEATURE_XSAVE )) + reserved_bits |=X86_CR4_OSXSAVE ; + +if (!cpu_has (c,X86_FEATURE_SMEP )) + reserved_bits |=X86_CR4_SMEP ; + +if (!cpu_has (c,X86_FEATURE_SMAP )) + reserved_bits |=X86_CR4_SMAP ; + +if (!cpu_has (c,X86_FEATURE_FSGSBASE )) + reserved_bits |=X86_CR4_FSGSBASE ; + +if (!cpu_has (c,X86_FEATURE_PKU )) + reserved_bits |=X86_CR4_PKE ; + +if (!cpu_has (c,X86_FEATURE_LA57 ) && + !(cpuid_ecx (0x7 ) &bit (X86_FEATURE_LA57 ))) + reserved_bits |=X86_CR4_LA57 ; + +if (!cpu_has (c,X86_FEATURE_UMIP ) && !kvm_x86_ops->umip_emulated ()) + reserved_bits |=X86_CR4_UMIP ; + +return reserved_bits; +} +static int kvm_valid_cr4 (struct kvm_vcpu *vcpu,unsigned long cr4) { -if (cr4 &CR4_RESERVED_BITS ) +if (cr4 & cr4_reserved_bits)return -EINVAL ;if (!guest_cpuid_has (vcpu,X86_FEATURE_XSAVE ) && (cr4 &X86_CR4_OSXSAVE ))
@@ -9369,6 +9400,8 @@ int kvm_arch_hardware_setup(void)if (r !=0 )return r; + cr4_reserved_bits =kvm_host_cr4_reserved_bits (&boot_cpu_data); +if (kvm_has_tsc_control) {/* * Make sure the user can only configure tsc_khz values that
Greg Kroah-Hartman
maintainerPATCH 253/367 KVM: x86: Free wbinvd_dirty_mask if vCPU creation fails
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit 16be9ddea268ad841457a59109963fff8c9de38d upstream.
Free the vCPU's wbinvd_dirty_mask if vCPU creation fails after kvm_arch_vcpu_init(), e.g. when installing the vCPU's file descriptor. Do the freeing by calling kvm_arch_vcpu_free() instead of open coding the freeing. This adds a likely superfluous, but ultimately harmless, call to kvmclock_reset(), which only clears vcpu->arch.pv_time_enabled. Using kvm_arch_vcpu_free() allows for additional cleanup in the future.
--- arch/x86/kvm/x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kvm/x86.c +1 -1
@@ -9208,7 +9208,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vckvm_mmu_unload (vcpu);vcpu_put (vcpu); - kvm_x86_ops->vcpu_free (vcpu); +kvm_arch_vcpu_free (vcpu); }void kvm_vcpu_reset (struct kvm_vcpu *vcpu,bool init_event)
Greg Kroah-Hartman
maintainerPATCH 254/367 KVM: x86: fix overlap between SPTE_MMIO_MASK and generation
In reply to Greg Kroah-HartmanFrom: Paolo Bonzini <[email protected]>
commit 56871d444bc4d7ea66708775e62e2e0926384dbc upstream.
The SPTE_MMIO_MASK overlaps with the bits used to track MMIO generation number. A high enough generation number would overwrite the SPTE_SPECIAL_MASK region and cause the MMIO SPTE to be misinterpreted.
Likewise, setting bits 52 and 53 would also cause an incorrect generation number to be read from the PTE, though this was partially mitigated by the (useless if it weren't for the bug) removal of SPTE_SPECIAL_MASK from the spte in get_mmio_spte_generation. Drop that removal, and replace it with a compile-time assertion.
--- arch/x86/kvm/mmu/mmu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
arch/x86/kvm/mmu/mmu.c +5 -5
@@ -418,22 +418,24 @@ static inline bool is_access_track_spte(* requires a full MMU zap). The flag is instead explicitly queried when * checking for MMIO spte cache hits. */ -#define MMIO_SPTE_GEN_MASK GENMASK_ULL(18, 0) +#define MMIO_SPTE_GEN_MASK GENMASK_ULL(17, 0)#define MMIO_SPTE_GEN_LOW_START 3#define MMIO_SPTE_GEN_LOW_END 11#define MMIO_SPTE_GEN_LOW_MASK GENMASK_ULL(MMIO_SPTE_GEN_LOW_END, \ MMIO_SPTE_GEN_LOW_START) -#define MMIO_SPTE_GEN_HIGH_START 52 -#define MMIO_SPTE_GEN_HIGH_END 61 +#define MMIO_SPTE_GEN_HIGH_START PT64_SECOND_AVAIL_BITS_SHIFT +#define MMIO_SPTE_GEN_HIGH_END 62#define MMIO_SPTE_GEN_HIGH_MASK GENMASK_ULL(MMIO_SPTE_GEN_HIGH_END, \ MMIO_SPTE_GEN_HIGH_START) +static u64 generation_mmio_spte_mask (u64 gen) {u64 mask;WARN_ON (gen & ~MMIO_SPTE_GEN_MASK ); +BUILD_BUG_ON ((MMIO_SPTE_GEN_HIGH_MASK |MMIO_SPTE_GEN_LOW_MASK ) &SPTE_SPECIAL_MASK ); mask = (gen <<MMIO_SPTE_GEN_LOW_START ) &MMIO_SPTE_GEN_LOW_MASK ; mask |= (gen <<MMIO_SPTE_GEN_HIGH_START ) &MMIO_SPTE_GEN_HIGH_MASK ;
@@ -444,8 +446,6 @@ static u64 get_mmio_spte_generation(u64 {u64 gen; - spte &= ~shadow_mmio_mask; - gen = (spte &MMIO_SPTE_GEN_LOW_MASK ) >>MMIO_SPTE_GEN_LOW_START ; gen |= (spte &MMIO_SPTE_GEN_HIGH_MASK ) >>MMIO_SPTE_GEN_HIGH_START ;return gen;
Greg Kroah-Hartman
maintainerPATCH 255/367 KVM: x86: Handle TIF_NEED_FPU_LOAD in kvm_{load,put}_guest_fpu()
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit c9aef3b85f425d1f6635382ec210ee5a7ef55d7d upstream.
Handle TIF_NEED_FPU_LOAD similar to how fpu__copy() handles the flag when duplicating FPU state to a new task struct. TIF_NEED_FPU_LOAD can be set any time control is transferred out of KVM, be it voluntarily, e.g. if I/O is triggered during a KVM call to get_user_pages, or involuntarily, e.g. if softirq runs after an IRQ occurs. Therefore, KVM must account for TIF_NEED_FPU_LOAD whenever it is (potentially) accessing CPU FPU state.
--- arch/x86/kvm/x86.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
arch/x86/kvm/x86.c +17 -2
@@ -8517,12 +8517,26 @@ static int complete_emulated_mmio(structreturn 0 ; } +static void kvm_save_current_fpu (struct fpu *fpu) +{ +/* +* If the target FPU state is not resident in the CPU registers, just +* memcpy() from current, else save CPU state directly to the target. +*/ +if (test_thread_flag (TIF_NEED_FPU_LOAD )) +memcpy (&fpu->state, ¤t->thread.fpu.state, + fpu_kernel_xstate_size); +else +copy_fpregs_to_fpstate (fpu); +} +/* Swap (qemu) user FPU context for the guest FPU context. */ static void kvm_load_guest_fpu (struct kvm_vcpu *vcpu) {fpregs_lock (); -copy_fpregs_to_fpstate (vcpu->arch.user_fpu); +kvm_save_current_fpu (vcpu->arch.user_fpu); +/* PKRU is separately restored in kvm_x86_ops->run. */ __copy_kernel_to_fpregs (&vcpu->arch.guest_fpu->state, ~XFEATURE_MASK_PKRU );
@@ -8538,7 +8552,8 @@ static void kvm_put_guest_fpu(struct kvm {fpregs_lock (); -copy_fpregs_to_fpstate (vcpu->arch.guest_fpu); +kvm_save_current_fpu (vcpu->arch.guest_fpu); +copy_kernel_to_fpregs (&vcpu->arch.user_fpu->state);fpregs_mark_activate ();
Greg Kroah-Hartman
maintainerPATCH 256/367 KVM: x86: Ensure guests FPU state is loaded when accessing for emulation
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit a7baead7e312f5a05381d68585fb6dc68e19e90f upstream.
Lock the FPU regs and reload the current thread's FPU state, which holds the guest's FPU state, to the CPU registers if necessary prior to accessing guest FPU state as part of emulation. kernel_fpu_begin() can be called from softirq context, therefore KVM must ensure softirqs are disabled (locking the FPU regs disables softirqs) when touching CPU FPU state.
Note, for all intents and purposes this reverts commit 6ab0b9feb82a7 ("x86,kvm: remove KVM emulator get_fpu / put_fpu"), but at the time it was applied, removing get/put_fpu() was correct. The re-introduction of {get,put}_fpu() is necessitated by the deferring of FPU state load.
--- arch/x86/kvm/emulate.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
arch/x86/kvm/emulate.c +39 -0
@@ -22,6 +22,7 @@#include "kvm_cache_regs.h" #include <asm/kvm_emulate.h> #include <linux/stringify.h> +#include <asm/fpu/api.h> #include <asm/debugreg.h> #include <asm/nospec-branch.h>
@@ -1075,8 +1076,23 @@ static void fetch_register_operand(struc } } +static void emulator_get_fpu (void ) +{ +fpregs_lock (); + +fpregs_assert_state_consistent (); +if (test_thread_flag (TIF_NEED_FPU_LOAD )) +switch_fpu_return (); +} + +static void emulator_put_fpu (void ) +{ +fpregs_unlock (); +} +static void read_sse_reg (struct x86_emulate_ctxt *ctxt,sse128_t *data,int reg) { +emulator_get_fpu ();switch (reg) {case 0 : asm("movdqa %%xmm0, %0" :"=m" (*data));break ;case 1 : asm("movdqa %%xmm1, %0" :"=m" (*data));break ;
@@ -1098,11 +1114,13 @@ static void read_sse_reg(struct x86_emul#endif default :BUG (); } +emulator_put_fpu (); }static void write_sse_reg (struct x86_emulate_ctxt *ctxt,sse128_t *data,int reg) { +emulator_get_fpu ();switch (reg) {case 0 : asm("movdqa %0, %%xmm0" : :"m" (*data));break ;case 1 : asm("movdqa %0, %%xmm1" : :"m" (*data));break ;
@@ -1124,10 +1142,12 @@ static void write_sse_reg(struct x86_emu#endif default :BUG (); } +emulator_put_fpu (); }static void read_mmx_reg (struct x86_emulate_ctxt *ctxt,u64 *data,int reg) { +emulator_get_fpu ();switch (reg) {case 0 : asm("movq %%mm0, %0" :"=m" (*data));break ;case 1 : asm("movq %%mm1, %0" :"=m" (*data));break ;
@@ -1139,10 +1159,12 @@ static void read_mmx_reg(struct x86_emulcase 7 : asm("movq %%mm7, %0" :"=m" (*data));break ;default :BUG (); } +emulator_put_fpu (); }static void write_mmx_reg (struct x86_emulate_ctxt *ctxt,u64 *data,int reg) { +emulator_get_fpu ();switch (reg) {case 0 : asm("movq %0, %%mm0" : :"m" (*data));break ;case 1 : asm("movq %0, %%mm1" : :"m" (*data));break ;
@@ -1154,6 +1176,7 @@ static void write_mmx_reg(struct x86_emucase 7 : asm("movq %0, %%mm7" : :"m" (*data));break ;default :BUG (); } +emulator_put_fpu (); }static int em_fninit (struct x86_emulate_ctxt *ctxt)
@@ -1161,7 +1184,9 @@ static int em_fninit(struct x86_emulate_if (ctxt->ops->get_cr (ctxt,0 ) & (X86_CR0_TS |X86_CR0_EM ))return emulate_nm (ctxt); +emulator_get_fpu (); asmvolatile ("fninit" ); +emulator_put_fpu ();return X86EMUL_CONTINUE ; }
@@ -1172,7 +1197,9 @@ static int em_fnstcw(struct x86_emulate_if (ctxt->ops->get_cr (ctxt,0 ) & (X86_CR0_TS |X86_CR0_EM ))return emulate_nm (ctxt); +emulator_get_fpu (); asmvolatile ("fnstcw %0" :"+m" (fcw)); +emulator_put_fpu (); ctxt->dst.val = fcw;
@@ -1186,7 +1213,9 @@ static int em_fnstsw(struct x86_emulate_if (ctxt->ops->get_cr (ctxt,0 ) & (X86_CR0_TS |X86_CR0_EM ))return emulate_nm (ctxt); +emulator_get_fpu (); asmvolatile ("fnstsw %0" :"+m" (fsw)); +emulator_put_fpu (); ctxt->dst.val = fsw;
@@ -4092,8 +4121,12 @@ static int em_fxsave(struct x86_emulate_if (rc !=X86EMUL_CONTINUE )return rc; +emulator_get_fpu (); + rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state)); +emulator_put_fpu (); +if (rc !=X86EMUL_CONTINUE )return rc;
@@ -4136,6 +4169,8 @@ static int em_fxrstor(struct x86_emulateif (rc !=X86EMUL_CONTINUE )return rc; +emulator_get_fpu (); +if (size <__fxstate_size (16 )) { rc =fxregs_fixup (&fx_state, size);if (rc !=X86EMUL_CONTINUE )
@@ -4151,6 +4186,8 @@ static int em_fxrstor(struct x86_emulate rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state)); out: +emulator_put_fpu (); +return rc; }
@@ -5465,7 +5502,9 @@ static int flush_pending_x87_faults(stru {int rc; +emulator_get_fpu (); rc =asm_safe ("fwait" ); +emulator_put_fpu ();if (unlikely (rc !=X86EMUL_CONTINUE ))return emulate_exception (ctxt,MF_VECTOR ,0 , false);
Greg Kroah-Hartman
maintainerPATCH 257/367 KVM: x86: Revert "KVM: X86: Fix fpu state crash in kvm guest"
In reply to Greg Kroah-HartmanFrom: Sean Christopherson <[email protected]>
commit 2620fe268e80d667a94553cd37a94ccaa2cb8c83 upstream.
Reload the current thread's FPU state, which contains the guest's FPU state, to the CPU registers if necessary during vcpu_enter_guest(). TIF_NEED_FPU_LOAD can be set any time control is transferred out of KVM, e.g. if I/O is triggered during a KVM call to get_user_pages() or if a softirq occurs while KVM is scheduled in.
Moving the handling of TIF_NEED_FPU_LOAD from vcpu_enter_guest() to kvm_arch_vcpu_load(), effectively kvm_sched_in(), papered over a bug where kvm_put_guest_fpu() failed to account for TIF_NEED_FPU_LOAD. The easiest way to the kvm_put_guest_fpu() bug was to run with involuntary preemption enable, thus handling TIF_NEED_FPU_LOAD during kvm_sched_in() made the bug go away. But, removing the handling in vcpu_enter_guest() exposed KVM to the rare case of a softirq triggering kernel_fpu_begin() between vcpu_load() and vcpu_enter_guest().
Now that kvm_{load,put}_guest_fpu() correctly handle TIF_NEED_FPU_LOAD, revert the commit to both restore the vcpu_enter_guest() behavior and eliminate the superfluous switch_fpu_return() in kvm_arch_vcpu_load().
Note, leaving the handling in kvm_arch_vcpu_load() isn't wrong per se, but it is unnecessary, and most critically, makes it extremely difficult to find bugs such as the kvm_put_guest_fpu() issue due to shrinking the window where a softirq can corrupt state.
A sample trace triggered by warning if TIF_NEED_FPU_LOAD is set while vcpu state is loaded:
<IRQ> gcmaes_crypt_by_sg.constprop.12+0x26e/0x660 ? 0xffffffffc024547d ? __qdisc_run+0x83/0x510 ? __dev_queue_xmit+0x45e/0x990 ? ip_finish_output2+0x1a8/0x570 ? fib4_rule_action+0x61/0x70 ? fib4_rule_action+0x70/0x70 ? fib_rules_lookup+0x13f/0x1c0 ? helper_rfc4106_decrypt+0x82/0xa0 ? crypto_aead_decrypt+0x40/0x70 ? crypto_aead_decrypt+0x40/0x70 ? crypto_aead_decrypt+0x40/0x70 ? esp_output_tail+0x8f4/0xa5a [esp4] ? skb_ext_add+0xd3/0x170 ? xfrm_input+0x7a6/0x12c0 ? xfrm4_rcv_encap+0xae/0xd0 ? xfrm4_transport_finish+0x200/0x200 ? udp_queue_rcv_one_skb+0x1ba/0x460 ? udp_unicast_rcv_skb.isra.63+0x72/0x90 ? __udp4_lib_rcv+0x51b/0xb00 ? ip_protocol_deliver_rcu+0xd2/0x1c0 ? ip_local_deliver_finish+0x44/0x50 ? ip_local_deliver+0xe0/0xf0 ? ip_protocol_deliver_rcu+0x1c0/0x1c0 ? ip_rcv+0xbc/0xd0 ? ip_rcv_finish_core.isra.19+0x380/0x380 ? __netif_receive_skb_one_core+0x7e/0x90 ? netif_receive_skb_internal+0x3d/0xb0 ? napi_gro_receive+0xed/0x150 ? 0xffffffffc0243c77 ? net_rx_action+0x149/0x3b0 ? __do_softirq+0xe4/0x2f8 ? handle_irq_event_percpu+0x6a/0x80 ? irq_exit+0xe6/0xf0 ? do_IRQ+0x7f/0xd0 ? common_interrupt+0xf/0xf </IRQ> ? irq_entries_start+0x20/0x660 ? vmx_get_interrupt_shadow+0x2f0/0x710 [kvm_intel] ? kvm_set_msr_common+0xfc7/0x2380 [kvm] ? recalibrate_cpu_khz+0x10/0x10 ? ktime_get+0x3a/0xa0 ? kvm_arch_vcpu_ioctl_run+0x107/0x560 [kvm] ? kvm_init+0x6bf/0xd00 [kvm] ? __seccomp_filter+0x7a/0x680 ? do_vfs_ioctl+0xa4/0x630 ? security_file_ioctl+0x32/0x50 ? ksys_ioctl+0x60/0x90 ? __x64_sys_ioctl+0x16/0x20 ? do_syscall_64+0x5f/0x1a0 ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 ---[ end trace 9564a1ccad733a90 ]---
This reverts commit e751732486eb3f159089a64d1901992b1357e7cc.
--- arch/x86/kvm/x86.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
arch/x86/kvm/x86.c +3 -6
@@ -3496,10 +3496,6 @@ void kvm_arch_vcpu_load(struct kvm_vcpu kvm_x86_ops->vcpu_load (vcpu, cpu); -fpregs_assert_state_consistent (); -if (test_thread_flag (TIF_NEED_FPU_LOAD )) -switch_fpu_return (); -/* Apply any externally detected TSC adjustments (due to suspend) */ if (unlikely (vcpu->arch.tsc_offset_adjustment)) {adjust_tsc_offset_host (vcpu, vcpu->arch.tsc_offset_adjustment);
@@ -8244,8 +8240,9 @@ static int vcpu_enter_guest(struct kvm_vtrace_kvm_entry (vcpu->vcpu_id);guest_enter_irqoff (); -/* The preempt notifier should have taken care of the FPU already. */ -WARN_ON_ONCE (test_thread_flag (TIF_NEED_FPU_LOAD )); +fpregs_assert_state_consistent (); +if (test_thread_flag (TIF_NEED_FPU_LOAD )) +switch_fpu_return ();if (unlikely (vcpu->arch.switch_db_regs)) {set_debugreg (0 ,7 );
Greg Kroah-Hartman
maintainerPATCH 258/367 KVM: s390: do not clobber registers during guest reset/store status
In reply to Greg Kroah-HartmanFrom: Christian Borntraeger <[email protected]>
commit 55680890ea78be0df5e1384989f1be835043c084 upstream.
The initial CPU reset clobbers the userspace fpc and the store status ioctl clobbers the guest acrs + fpr. As these calls are only done via ioctl (and not via vcpu_run), no CPU context is loaded, so we can (and must) act directly on the sync regs, not on the thread context.
--- arch/s390/kvm/kvm-s390.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
arch/s390/kvm/kvm-s390.c +2 -4
@@ -2860,9 +2860,7 @@ static void kvm_s390_vcpu_initial_reset( vcpu->arch.sie_block->gcr[14 ] =CR14_UNUSED_32 |CR14_UNUSED_33 |CR14_EXTERNAL_DAMAGE_SUBMASK ; -/* make sure the new fpc will be lazily loaded */ -save_fpu_regs (); - current->thread.fpu.fpc =0 ; + vcpu->run->s.regs.fpc =0 ; vcpu->arch.sie_block->gbea =1 ; vcpu->arch.sie_block->pp =0 ; vcpu->arch.sie_block->fpf &= ~FPF_BPBC ;
@@ -4351,7 +4349,7 @@ long kvm_arch_vcpu_ioctl(struct file *fiswitch (ioctl) {case KVM_S390_STORE_STATUS : idx =srcu_read_lock (&vcpu->kvm->srcu); - r =kvm_s390_vcpu_store_status (vcpu, arg); + r =kvm_s390_store_status_unloaded (vcpu, arg);srcu_read_unlock (&vcpu->kvm->srcu, idx);break ;case KVM_S390_SET_INITIAL_PSW : {
Greg Kroah-Hartman
maintainerPATCH 259/367 KVM: x86: reorganize pvclock_gtod_data members
In reply to Greg Kroah-HartmanFrom: Paolo Bonzini <[email protected]>
commit 917f9475c0a8ab8958db7f22a5d495b9a1d51be6 upstream.
We will need a copy of tk->offs_boot in the next patch. Store it and cleanup the struct: instead of storing tk->tkr_xxx.base with the tk->offs_boot included, store the raw value in struct pvclock_clock and sum it indo_monotonic_raw and do_realtime. tk->tkr_xxx.xtime_nsec also moves
to struct pvclock_clock.
While at it, fix a (usually harmless) typo in do_monotonic_raw, which was using gtod->clock.shift instead of gtod->raw_clock.shift.
--- arch/x86/kvm/x86.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-)
arch/x86/kvm/x86.c +12 -17
@@ -1580,6 +1580,8 @@ struct pvclock_clock {u64 mask;u32 mult;u32 shift; +u64 base_cycles; +u64 offset; };struct pvclock_gtod_data {
@@ -1588,11 +1590,8 @@ struct pvclock_gtod_data {struct pvclock_clock clock;/* extract of a clocksource struct */ struct pvclock_clock raw_clock;/* extract of a clocksource struct */ -u64 boot_ns_raw; -u64 boot_ns; -u64 nsec_base; +ktime_t offs_boot;u64 wall_time_sec; -u64 monotonic_raw_nsec; };static struct pvclock_gtod_data pvclock_gtod_data;
@@ -1600,10 +1599,6 @@ static struct pvclock_gtod_data pvclock_static void update_pvclock_gtod (struct timekeeper *tk) {struct pvclock_gtod_data *vdata = &pvclock_gtod_data; -u64 boot_ns, boot_ns_raw; - - boot_ns =ktime_to_ns (ktime_add (tk->tkr_mono.base, tk->offs_boot)); - boot_ns_raw =ktime_to_ns (ktime_add (tk->tkr_raw.base, tk->offs_boot));write_seqcount_begin (&vdata->seq);
@@ -1613,20 +1608,20 @@ static void update_pvclock_gtod(struct t vdata->clock.mask = tk->tkr_mono.mask; vdata->clock.mult = tk->tkr_mono.mult; vdata->clock.shift = tk->tkr_mono.shift; + vdata->clock.base_cycles = tk->tkr_mono.xtime_nsec; + vdata->clock.offset = tk->tkr_mono.base; vdata->raw_clock.vclock_mode = tk->tkr_raw.clock->archdata.vclock_mode; vdata->raw_clock.cycle_last = tk->tkr_raw.cycle_last; vdata->raw_clock.mask = tk->tkr_raw.mask; vdata->raw_clock.mult = tk->tkr_raw.mult; vdata->raw_clock.shift = tk->tkr_raw.shift; - - vdata->boot_ns = boot_ns; - vdata->nsec_base = tk->tkr_mono.xtime_nsec; + vdata->raw_clock.base_cycles = tk->tkr_raw.xtime_nsec; + vdata->raw_clock.offset = tk->tkr_raw.base; vdata->wall_time_sec = tk->xtime_sec; - vdata->boot_ns_raw = boot_ns_raw; - vdata->monotonic_raw_nsec = tk->tkr_raw.xtime_nsec; + vdata->offs_boot = tk->offs_boot;write_seqcount_end (&vdata->seq); }
@@ -2096,10 +2091,10 @@ static int do_monotonic_raw(s64 *t, u64do { seq =read_seqcount_begin (>od->seq); - ns = gtod->monotonic_raw_nsec; + ns = gtod->raw_clock.base_cycles; ns +=vgettsc (>od->raw_clock, tsc_timestamp, &mode); - ns >>= gtod->clock.shift; - ns += gtod->boot_ns_raw; + ns >>= gtod->raw_clock.shift; + ns +=ktime_to_ns (ktime_add (gtod->raw_clock.offset, gtod->offs_boot)); }while (unlikely (read_seqcount_retry (>od->seq, seq))); *t = ns;
@@ -2116,7 +2111,7 @@ static int do_realtime(struct timespec64do { seq =read_seqcount_begin (>od->seq); ts->tv_sec = gtod->wall_time_sec; - ns = gtod->nsec_base; + ns = gtod->clock.base_cycles; ns +=vgettsc (>od->clock, tsc_timestamp, &mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));
Greg Kroah-Hartman
maintainerPATCH 260/367 KVM: x86: use raw clock values consistently
In reply to Greg Kroah-HartmanFrom: Paolo Bonzini <[email protected]>
commit 8171cd68806bd2fc28ef688e32fb2a3b3deb04e5 upstream.
Commit 53fafdbb8b21f ("KVM: x86: switch KVMCLOCK base to monotonic raw clock") changed kvmclock to use tkr_raw instead of tkr_mono. However, the default kvmclock_offset for the VM was still based on the monotonic clock and, if the raw clock drifted enough from the monotonic clock, this could cause a negative system_time to be written to the guest's struct pvclock. RHEL5 does not like it and (if it boots fast enough to observe a negative time value) it hangs.
There is another thing to be careful about: getboottime64 returns the host boot time with tkr_mono frequency, and subtracting the tkr_raw-based kvmclock value will cause the wallclock to be off if tkr_raw drifts from tkr_mono. To avoid this, compute the wallclock delta from the current time instead of being clever and using getboottime64.
--- arch/x86/kvm/x86.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-)
arch/x86/kvm/x86.c +23 -15
@@ -1625,6 +1625,18 @@ static void update_pvclock_gtod(struct t write_seqcount_end(&vdata->seq); } + +static s64 get_kvmclock_base_ns (void ) +{ +/* Count up from boot time, but with the frequency of the raw clock. */ +return ktime_to_ns (ktime_add (ktime_get_raw (), pvclock_gtod_data.offs_boot)); +} +#else +static s64 get_kvmclock_base_ns (void ) +{ +/* Master clock not used, so we can just use CLOCK_BOOTTIME. */ +return ktime_get_boottime_ns (); +}#endif void kvm_set_pending_timer (struct kvm_vcpu *vcpu)
@@ -1638,7 +1650,7 @@ static void kvm_write_wall_clock(structint version;int r;struct pvclock_wall_clock wc; -struct timespec64 boot; +u64 wall_nsec;if (!wall_clock)return ;
@@ -1658,17 +1670,12 @@ static void kvm_write_wall_clock(struct/* * The guest calculates current wall clock time by adding * system time (updated by kvm_guest_time_update below) to the -* wall clock specified here. guest system time equals host -* system time for us, thus we must fill in host boot time here. +* wall clock specified here. We do the reverse here. */ -getboottime64 (&boot); + wall_nsec =ktime_get_real_ns () -get_kvmclock_ns (kvm); -if (kvm->arch.kvmclock_offset) { -struct timespec64 ts =ns_to_timespec64 (kvm->arch.kvmclock_offset); - boot =timespec64_sub (boot, ts); - } - wc.sec = (u32 )boot.tv_sec;/* overflow in 2106 guest time */ - wc.nsec = boot.tv_nsec; + wc.nsec =do_div (wall_nsec,1000000000 ); + wc.sec = (u32 )wall_nsec;/* overflow in 2106 guest time */ wc.version = version;kvm_write_guest (kvm, wall_clock, &wc,sizeof (wc));
@@ -1916,7 +1923,7 @@ void kvm_write_tsc(struct kvm_vcpu *vcpuraw_spin_lock_irqsave (&kvm->arch.tsc_write_lock, flags); offset =kvm_compute_tsc_offset (vcpu, data); - ns =ktime_get_boottime_ns (); + ns =get_kvmclock_base_ns (); elapsed = ns - kvm->arch.last_tsc_nsec;if (vcpu->arch.virtual_tsc_khz) {
@@ -2254,7 +2261,7 @@ u64 get_kvmclock_ns(struct kvm *kvm)spin_lock (&ka->pvclock_gtod_sync_lock);if (!ka->use_master_clock) {spin_unlock (&ka->pvclock_gtod_sync_lock); -return ktime_get_boottime_ns () + ka->kvmclock_offset; +return get_kvmclock_base_ns () + ka->kvmclock_offset; } hv_clock.tsc_timestamp = ka->master_cycle_now;
@@ -2270,7 +2277,7 @@ u64 get_kvmclock_ns(struct kvm *kvm) &hv_clock.tsc_to_system_mul); ret =__pvclock_read_cycles (&hv_clock,rdtsc ()); }else - ret =ktime_get_boottime_ns () + ka->kvmclock_offset; + ret =get_kvmclock_base_ns () + ka->kvmclock_offset;put_cpu ();
@@ -2369,7 +2376,7 @@ static int kvm_guest_time_update(struct }if (!use_master_clock) { host_tsc =rdtsc (); - kernel_ns =ktime_get_boottime_ns (); + kernel_ns =get_kvmclock_base_ns (); } tsc_timestamp =kvm_read_l1_tsc (v, host_tsc);
@@ -2409,6 +2416,7 @@ static int kvm_guest_time_update(struct vcpu->hv_clock.tsc_timestamp = tsc_timestamp; vcpu->hv_clock.system_time = kernel_ns + v->kvm->arch.kvmclock_offset; vcpu->last_guest_tsc = tsc_timestamp; +WARN_ON (vcpu->hv_clock.system_time <0 );/* If the host uses TSC clocksource, then it is stable */ pvclock_flags =0 ;
@@ -9580,7 +9588,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unmutex_init (&kvm->arch.apic_map_lock);spin_lock_init (&kvm->arch.pvclock_gtod_sync_lock); - kvm->arch.kvmclock_offset = -ktime_get_boottime_ns (); + kvm->arch.kvmclock_offset = -get_kvmclock_base_ns ();pvclock_update_vm_gtod_copy (kvm); kvm->arch.guest_can_read_msr_platform_info = true;
Greg Kroah-Hartman
maintainerPATCH 261/367 ocfs2: fix oops when writing cloned file
In reply to Greg Kroah-HartmanFrom: Gang He <[email protected]>
commit 2d797e9ff95ecbcf0a83d657928ed20579444857 upstream.
Writing a cloned file triggers a kernel oops and the user-space command process is also killed by the system. The bug can be reproduced stably via:
1) create a file under ocfs2 file system directory.
journalctl -b > aa.txt
2) create a cloned file for this file.
reflink aa.txt bb.txt
3) write the cloned file with dd command.
dd if=/dev/zero of=bb.txt bs=512 count=1 conv=notrunc
The dd command is killed by the kernel, then you can see the oops message via dmesg command.
[ 463.875404] BUG: kernel NULL pointer dereference, address: 0000000000000028 [ 463.875413] #PF: supervisor read access in kernel mode [ 463.875416] #PF: error_code(0x0000) - not-present page [ 463.875418] PGD 0 P4D 0 [ 463.875425] Oops: 0000 [#1] SMP PTI [ 463.875431] CPU: 1 PID: 2291 Comm: dd Tainted: G OE 5.3.16-2-default [ 463.875433] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 [ 463.875500] RIP: 0010:ocfs2_refcount_cow+0xa4/0x5d0 [ocfs2] [ 463.875505] Code: 06 89 6c 24 38 89 eb f6 44 24 3c 02 74 be 49 8b 47 28 [ 463.875508] RSP: 0018:ffffa2cb409dfce8 EFLAGS: 00010202 [ 463.875512] RAX: ffff8b1ebdca8000 RBX: 0000000000000001 RCX: ffff8b1eb73a9df0 [ 463.875515] RDX: 0000000000056a01 RSI: 0000000000000000 RDI: 0000000000000000 [ 463.875517] RBP: 0000000000000001 R08: ffff8b1eb73a9de0 R09: 0000000000000000 [ 463.875520] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000 [ 463.875522] R13: ffff8b1eb922f048 R14: 0000000000000000 R15: ffff8b1eb922f048 [ 463.875526] FS: 00007f8f44d15540(0000) GS:ffff8b1ebeb00000(0000) knlGS:0000000000000000 [ 463.875529] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 463.875532] CR2: 0000000000000028 CR3: 000000003c17a000 CR4: 00000000000006e0 [ 463.875546] Call Trace: [ 463.875596] ? ocfs2_inode_lock_full_nested+0x18b/0x960 [ocfs2] [ 463.875648] ocfs2_file_write_iter+0xaf8/0xc70 [ocfs2] [ 463.875672] new_sync_write+0x12d/0x1d0 [ 463.875688] vfs_write+0xad/0x1a0 [ 463.875697] ksys_write+0xa1/0xe0 [ 463.875710] do_syscall_64+0x60/0x1f0 [ 463.875743] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 463.875758] RIP: 0033:0x7f8f4482ed44 [ 463.875762] Code: 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 80 00 00 00 [ 463.875765] RSP: 002b:00007fff300a79d8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 463.875769] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f8f4482ed44 [ 463.875771] RDX: 0000000000000200 RSI: 000055f771b5c000 RDI: 0000000000000001 [ 463.875774] RBP: 0000000000000200 R08: 00007f8f44af9c78 R09: 0000000000000003 [ 463.875776] R10: 000000000000089f R11: 0000000000000246 R12: 000055f771b5c000 [ 463.875779] R13: 0000000000000200 R14: 0000000000000000 R15: 000055f771b5c000
This regression problem was introduced by commit e74540b28556 ("ocfs2:
protect extent tree in ocfs2_prepare_inode_for_write()").
--- fs/ocfs2/file.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
fs/ocfs2/file.c +6 -8
@@ -2101,17 +2101,15 @@ static int ocfs2_is_io_unaligned(structstatic int ocfs2_inode_lock_for_extent_tree (struct inode *inode,struct buffer_head **di_bh,int meta_level, -int overwrite_io,int write_sem,int wait) {int ret =0 ;if (wait) - ret =ocfs2_inode_lock (inode,NULL , meta_level); + ret =ocfs2_inode_lock (inode, di_bh, meta_level);else - ret =ocfs2_try_inode_lock (inode, - overwrite_io ?NULL : di_bh, meta_level); + ret =ocfs2_try_inode_lock (inode, di_bh, meta_level);if (ret <0 ) goto out;
@@ -2136,6 +2134,7 @@ static int ocfs2_inode_lock_for_extent_t out_unlock:brelse (*di_bh); + *di_bh =NULL ;ocfs2_inode_unlock (inode, meta_level); out:return ret;
@@ -2177,7 +2176,6 @@ static int ocfs2_prepare_inode_for_write ret =ocfs2_inode_lock_for_extent_tree (inode, &di_bh, meta_level, - overwrite_io, write_sem, wait);if (ret <0 ) {
@@ -2233,13 +2231,13 @@ static int ocfs2_prepare_inode_for_write &di_bh, meta_level, write_sem); + meta_level =1 ; + write_sem =1 ; ret =ocfs2_inode_lock_for_extent_tree (inode, &di_bh, meta_level, - overwrite_io, -1 , + write_sem, wait); - write_sem =1 ;if (ret <0 ) {if (ret != -EAGAIN )mlog_errno (ret);
Greg Kroah-Hartman
maintainerPATCH 262/367 mm/page_alloc.c: fix uninitialized memmaps on a partially populated last section
In reply to Greg Kroah-HartmanFrom: David Hildenbrand <[email protected]>
commit e822969cab48b786b64246aad1a3ba2a774f5d23 upstream.
Patch series "mm: fix max_pfn not falling on section boundary", v2.
Playing with different memory sizes for a x86-64 guest, I discovered that some memmaps (highest section if max_mem does not fall on the section boundary) are marked as being valid and online, but contain garbage. We have to properly initialize these memmaps.
Looking at /proc/kpageflags and friends, I found some more issues, partially related to this.
This patch (of 3):
If max_pfn is not aligned to a section boundary, we can easily run into BUGs. This can e.g., be triggered on x86-64 under QEMU by specifying a memory size that is not a multiple of 128MB (e.g., 4097MB, but also 4160MB). I was told that on real HW, we can easily have this scenario (esp., one of the main reasons sub-section hotadd of devmem was added).
The issue is, that we have a valid memmap (pfn_valid()) for the whole section, and the whole section will be marked "online". pfn_to_online_page() will succeed, but the memmap contains garbage.
E.g., doing a "./page-types -r -a 0x144001" when QEMU was started with "-m 4160M" - (see tools/vm/page-types.c):
[ 200.476376] BUG: unable to handle page fault for address: fffffffffffffffe [ 200.477500] #PF: supervisor read access in kernel mode [ 200.478334] #PF: error_code(0x0000) - not-present page [ 200.479076] PGD 59614067 P4D 59614067 PUD 59616067 PMD 0 [ 200.479557] Oops: 0000 [#4] SMP NOPTI [ 200.479875] CPU: 0 PID: 603 Comm: page-types Tainted: G D W 5.5.0-rc1-next-20191209 #93 [ 200.480646] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu4 [ 200.481648] RIP: 0010:stable_page_flags+0x4d/0x410 [ 200.482061] Code: f3 ff 41 89 c0 48 b8 00 00 00 00 01 00 00 00 45 84 c0 0f 85 cd 02 00 00 48 8b 53 08 48 8b 2b 48f [ 200.483644] RSP: 0018:ffffb139401cbe60 EFLAGS: 00010202 [ 200.484091] RAX: fffffffffffffffe RBX: fffffbeec5100040 RCX: 0000000000000000 [ 200.484697] RDX: 0000000000000001 RSI: ffffffff9535c7cd RDI: 0000000000000246 [ 200.485313] RBP: ffffffffffffffff R08: 0000000000000000 R09: 0000000000000000 [ 200.485917] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000144001 [ 200.486523] R13: 00007ffd6ba55f48 R14: 00007ffd6ba55f40 R15: ffffb139401cbf08 [ 200.487130] FS: 00007f68df717580(0000) GS:ffff9ec77fa00000(0000) knlGS:0000000000000000 [ 200.487804] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 200.488295] CR2: fffffffffffffffe CR3: 0000000135d48000 CR4: 00000000000006f0 [ 200.488897] Call Trace: [ 200.489115] kpageflags_read+0xe9/0x140 [ 200.489447] proc_reg_read+0x3c/0x60 [ 200.489755] vfs_read+0xc2/0x170 [ 200.490037] ksys_pread64+0x65/0xa0 [ 200.490352] do_syscall_64+0x5c/0xa0 [ 200.490665] entry_SYSCALL_64_after_hwframe+0x49/0xbe
But it can be triggered much easier via "cat /proc/kpageflags > /dev/null" after cold/hot plugging a DIMM to such a system:
[root@localhost ~]# cat /proc/kpageflags > /dev/null
[ 111.517275] BUG: unable to handle page fault for address: fffffffffffffffe [ 111.517907] #PF: supervisor read access in kernel mode [ 111.518333] #PF: error_code(0x0000) - not-present page [ 111.518771] PGD a240e067 P4D a240e067 PUD a2410067 PMD 0
This patch fixes that by at least zero-ing out that memmap (so e.g., page_to_pfn() will not crash). Commit 907ec5fca3dc ("mm: zero remaining unavailable struct pages") tried to fix a similar issue, but forgot to consider this special case.
After this patch, there are still problems to solve. E.g., not all of these pages falling into a memory hole will actually get initialized later and set PageReserved - they are only zeroed out - but at least the immediate crashes are gone. A follow-up patch will take care of this.
--- mm/page_alloc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
mm/page_alloc.c +12 -2
@@ -6921,7 +6921,8 @@ static u64 zero_pfn_range(unsigned long* This function also addresses a similar issue where struct pages are left * uninitialized because the physical address range is not covered by * memblock.memory or memblock.reserved. That could happen when memblock -* layout is manually configured via memmap=. +* layout is manually configured via memmap=, or when the highest physical +* address (max_pfn) does not end on a section boundary. */ void __init zero_resv_unavail (void ) {
@@ -6939,7 +6940,16 @@ void __init zero_resv_unavail(void) pgcnt +=zero_pfn_range (PFN_DOWN (next),PFN_UP (start)); next = end; } - pgcnt +=zero_pfn_range (PFN_DOWN (next), max_pfn); + +/* +* Early sections always have a fully populated memmap for the whole +* section - see pfn_valid(). If the last section has holes at the +* end and that section is marked "online", the memmap will be +* considered initialized. Make sure that memmap has a well defined +* state. +*/ + pgcnt +=zero_pfn_range (PFN_DOWN (next), +round_up (max_pfn,PAGES_PER_SECTION ));/* * Struct pages that do not have backing memory. This could be because
Greg Kroah-Hartman
maintainerPATCH 263/367 arm64: dts: qcom: qcs404-evb: Set vdd_apc regulator in high power mode
In reply to Greg Kroah-HartmanFrom: Niklas Cassel <[email protected]>
commit eac8ce86cb90ba96cb4bcbf2549d7a8b6938aa30 upstream.
vdd_apc is the regulator that supplies the main CPU cluster.
At sudden CPU load changes, we have noticed invalid page faults on addresses with all bits shifted, as well as on addresses with individual bits flipped.
By putting the vdd_apc regulator in high power mode, the voltage drops during sudden load changes will be less severe, and we have not been able to reproduce the invalid page faults with the regulator in this mode.
--- arch/arm64/boot/dts/qcom/qcs404-evb.dtsi | 1 + 1 file changed, 1 insertion(+)
arch/arm64/boot/dts/qcom/qcs404-evb.dtsi +1 -0
@@ -73,6 +73,7 @@ regulator-always-on; regulator-boot-on; regulator-name = "vdd_apc"; + regulator-initial-mode = <1>; regulator-min-microvolt = <1048000>; regulator-max-microvolt = <1384000>; };
Greg Kroah-Hartman
maintainerPATCH 264/367 mm/mmu_gather: invalidate TLB correctly on batch allocation failure and flush
In reply to Greg Kroah-HartmanFrom: Peter Zijlstra <[email protected]>
commit 0ed1325967ab5f7a4549a2641c6ebe115f76e228 upstream.
Architectures for which we have hardware walkers of Linux page table should flush TLB on mmu gather batch allocation failures and batch flush. Some architectures like POWER supports multiple translation modes (hash and radix) and in the case of POWER only radix translation mode needs the above TLBI. This is because for hash translation mode kernel wants to avoid this extra flush since there are no hardware walkers of linux page table. With radix translation, the hardware also walks linux page table and with that, kernel needs to make sure to TLB invalidate page walk cache before page table pages are freed.
More details in commit d86564a2f085 ("mm/tlb, x86/mm: Support invalidating TLB caches for RCU_TABLE_FREE")
The changes to sparc are to make sure we keep the old behavior since we are now removing HAVE_RCU_TABLE_NO_INVALIDATE. The default value for tlb_needs_table_invalidate is to always force an invalidate and sparc can avoid the table invalidate. Hence we define tlb_needs_table_invalidate to false for sparc architecture.
--- arch/Kconfig | 3 --- arch/powerpc/Kconfig | 1 - arch/powerpc/include/asm/tlb.h | 11 +++++++++++ arch/sparc/Kconfig | 1 - arch/sparc/include/asm/tlb_64.h | 9 +++++++++ include/asm-generic/tlb.h | 22 +++++++++++++++------- mm/mmu_gather.c | 16 ++++++++-------- 7 files changed, 43 insertions(+), 20 deletions(-)
arch/Kconfig +0 -3
@@ -396,9 +396,6 @@ config HAVE_ARCH_JUMP_LABEL_RELATIVE config HAVE_RCU_TABLE_FREE bool -config HAVE_RCU_TABLE_NO_INVALIDATE - bool - config HAVE_MMU_GATHER_PAGE_SIZE bool
arch/powerpc/Kconfig +0 -1
@@ -223,7 +223,6 @@ config PPC select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP select HAVE_RCU_TABLE_FREE - select HAVE_RCU_TABLE_NO_INVALIDATE if HAVE_RCU_TABLE_FREE select HAVE_MMU_GATHER_PAGE_SIZE select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RELIABLE_STACKTRACE if PPC_BOOK3S_64 && CPU_LITTLE_ENDIAN
arch/powerpc/include/asm/tlb.h +11 -0
@@ -26,6 +26,17 @@#define tlb_flush tlb_flushextern void tlb_flush (struct mmu_gather *tlb); +/* +* book3s: +* Hash does not use the linux page-tables, so we can avoid +* the TLB invalidate for page-table freeing, Radix otoh does use the +* page-tables and needs the TLBI. +* +* nohash: +* We still do TLB invalidate in the __pte_free_tlb routine before we +* add the page table pages to mmu gather table batch. +*/ +#define tlb_needs_table_invalidate () radix_enabled()/* Get the generic bits... */ #include <asm-generic/tlb.h>
arch/sparc/Kconfig +0 -1
@@ -65,7 +65,6 @@ config SPARC64 select HAVE_KRETPROBES select HAVE_KPROBES select HAVE_RCU_TABLE_FREE if SMP - select HAVE_RCU_TABLE_NO_INVALIDATE if HAVE_RCU_TABLE_FREE select HAVE_MEMBLOCK_NODE_MAP select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_DYNAMIC_FTRACE
arch/sparc/include/asm/tlb_64.h +9 -0
@@ -28,6 +28,15 @@ void flush_tlb_pending(void);#define __tlb_remove_tlb_entry (tlb, ptep, address) do { } while (0)#define tlb_flush (tlb) flush_tlb_pending() +/* +* SPARC64's hardware TLB fill does not use the Linux page-tables +* and therefore we don't need a TLBI when freeing page-table pages. +*/ + +#ifdef CONFIG_HAVE_RCU_TABLE_FREE +#define tlb_needs_table_invalidate () (false) +#endif +#include <asm-generic/tlb.h> #endif /* _SPARC64_TLB_H */
include/asm-generic/tlb.h +15 -7
@@ -137,13 +137,6 @@* When used, an architecture is expected to provide __tlb_remove_table() * which does the actual freeing of these pages. * -* HAVE_RCU_TABLE_NO_INVALIDATE -* -* This makes HAVE_RCU_TABLE_FREE avoid calling tlb_flush_mmu_tlbonly() before -* freeing the page-table pages. This can be avoided if you use -* HAVE_RCU_TABLE_FREE and your architecture does _NOT_ use the Linux -* page-tables natively. -* * MMU_GATHER_NO_RANGE * * Use this if your architecture lacks an efficient flush_tlb_range().
@@ -189,8 +182,23 @@ struct mmu_table_batch {extern void tlb_remove_table (struct mmu_gather *tlb,void *table); +/* +* This allows an architecture that does not use the linux page-tables for +* hardware to skip the TLBI when freeing page tables. +*/ +#ifndef tlb_needs_table_invalidate +#define tlb_needs_table_invalidate () (true)#endif +#else + +#ifdef tlb_needs_table_invalidate +#error tlb_needs_table_invalidate() requires HAVE_RCU_TABLE_FREE +#endif + +#endif /* CONFIG_HAVE_RCU_TABLE_FREE */ + +#ifndef CONFIG_HAVE_MMU_GATHER_NO_GATHER /* * If we can't allocate a page to make a big batch of page pointers
mm/mmu_gather.c +8 -8
@@ -102,14 +102,14 @@ bool __tlb_remove_page_size(struct mmu_g*/ static inline void tlb_table_invalidate (struct mmu_gather *tlb) { -#ifndef CONFIG_HAVE_RCU_TABLE_NO_INVALIDATE -/* -* Invalidate page-table caches used by hardware walkers. Then we still -* need to RCU-sched wait while freeing the pages because software -* walkers can still be in-flight. -*/ -tlb_flush_mmu_tlbonly (tlb); -#endif +if (tlb_needs_table_invalidate ()) { +/* +* Invalidate page-table caches used by hardware walkers. Then +* we still need to RCU-sched wait while freeing the pages +* because software walkers can still be in-flight. +*/ +tlb_flush_mmu_tlbonly (tlb); + } }static void tlb_remove_table_smp_sync (void *arg)
Greg Kroah-Hartman
maintainerPATCH 265/367 clk: tegra: Mark fuse clock as critical
In reply to Greg Kroah-HartmanFrom: Stephen Warren <[email protected]>
commit bf83b96f87ae2abb1e535306ea53608e8de5dfbb upstream.
For a little over a year, U-Boot on Tegra124 has configured the flow controller to perform automatic RAM re-repair on off->on power transitions of the CPU rail[1]. This is mandatory for correct operation of Tegra124. However, RAM re-repair relies on certain clocks, which the kernel must enable and leave running. The fuse clock is one of those clocks. Mark this clock as critical so that LP1 power mode (system suspend) operates correctly.
[1] 3cc7942a4ae5 ARM: tegra: implement RAM repair
--- drivers/clk/tegra/clk-tegra-periph.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/clk/tegra/clk-tegra-periph.c +5 -1
@@ -777,7 +777,11 @@ static struct tegra_periph_init_data gatGATE ("ahbdma" ,"hclk" ,33 ,0 , tegra_clk_ahbdma,0 ),GATE ("apbdma" ,"pclk" ,34 ,0 , tegra_clk_apbdma,0 ),GATE ("kbc" ,"clk_32k" ,36 ,TEGRA_PERIPH_ON_APB |TEGRA_PERIPH_NO_RESET , tegra_clk_kbc,0 ), -GATE ("fuse" ,"clk_m" ,39 ,TEGRA_PERIPH_ON_APB , tegra_clk_fuse,0 ), +/* +* Critical for RAM re-repair operation, which must occur on resume +* from LP1 system suspend and as part of CCPLEX cluster switching. +*/ +GATE ("fuse" ,"clk_m" ,39 ,TEGRA_PERIPH_ON_APB , tegra_clk_fuse,CLK_IS_CRITICAL ),GATE ("fuse_burn" ,"clk_m" ,39 ,TEGRA_PERIPH_ON_APB , tegra_clk_fuse_burn,0 ),GATE ("kfuse" ,"clk_m" ,40 ,TEGRA_PERIPH_ON_APB , tegra_clk_kfuse,0 ), GATE("apbif", "clk_m", 107, TEGRA_PERIPH_ON_APB, tegra_clk_apbif, 0),
Greg Kroah-Hartman
maintainerPATCH 266/367 drm/amdgpu/navi: fix index for OD MCLK
In reply to Greg Kroah-HartmanFrom: Alex Deucher <[email protected]>
commit 45826e9c4e9e952db43053f4fbed58ec602a410f upstream.
You can only adjust the max mclk, not the min.
--- drivers/gpu/drm/amd/powerplay/navi10_ppt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/powerplay/navi10_ppt.c +1 -1
@@ -812,7 +812,7 @@ static int navi10_print_clk_levels(strucif (!navi10_od_feature_is_supported (od_settings,SMU_11_0_ODFEATURE_UCLK_MAX ))break ; size +=sprintf (buf + size,"OD_MCLK:\n" ); - size +=sprintf (buf + size,"0: %uMHz\n" , od_table->UclkFmax); + size +=sprintf (buf + size,"1: %uMHz\n" , od_table->UclkFmax);break ;case SMU_OD_VDDC_CURVE :if (!smu->od_enabled || !od_table || !od_settings)
Greg Kroah-Hartman
maintainerPATCH 267/367 drm/tegra: Relax IOMMU usage criteria on old Tegra
In reply to Greg Kroah-HartmanFrom: Thierry Reding <[email protected]>
commit 2d9384ff91770a71bd1ff24c25952ef1187a0e9c upstream.
Older Tegra devices only allow addressing 32 bits of memory, so whether or not the host1x is attached to an IOMMU doesn't matter. host1x IOMMU attachment is only needed on devices that can address memory beyond the 32-bit boundary and where the host1x doesn't support the wide GATHER opcode that allows it to access buffers at higher addresses.
--- drivers/gpu/drm/tegra/drm.c | 49 ++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-)
drivers/gpu/drm/tegra/drm.c +32 -17
@@ -1037,23 +1037,9 @@ void tegra_drm_free(struct tegra_drm *tefree_pages ((unsigned long )virt,get_order (size)); } -static int host1x_drm_probe (struct host1x_device *dev) +static bool host1x_drm_wants_iommu (struct host1x_device *dev) { -struct drm_driver *driver = &tegra_drm_driver;struct iommu_domain *domain; -struct tegra_drm *tegra; -struct drm_device *drm; -int err; - - drm =drm_dev_alloc (driver, &dev->dev); -if (IS_ERR (drm)) -return PTR_ERR (drm); - - tegra =kzalloc (sizeof (*tegra),GFP_KERNEL ); -if (!tegra) { - err = -ENOMEM ; - goto put; - }/* * If the Tegra DRM clients are backed by an IOMMU, push buffers are
@@ -1082,9 +1068,38 @@ static int host1x_drm_probe(struct host1* up the device tree appropriately. This is considered an problem * of integration, so care must be taken for the DT to be consistent. */ - domain =iommu_get_domain_for_dev (drm->dev->parent); + domain =iommu_get_domain_for_dev (dev->dev.parent); + +/* +* Tegra20 and Tegra30 don't support addressing memory beyond the +* 32-bit boundary, so the regular GATHER opcodes will always be +* sufficient and whether or not the host1x is attached to an IOMMU +* doesn't matter. +*/ +if (!domain &&dma_get_mask (dev->dev.parent) <=DMA_BIT_MASK (32 )) +return true; + +return domain !=NULL ; +} + +static int host1x_drm_probe (struct host1x_device *dev) +{ +struct drm_driver *driver = &tegra_drm_driver; +struct tegra_drm *tegra; +struct drm_device *drm; +int err; + + drm =drm_dev_alloc (driver, &dev->dev); +if (IS_ERR (drm)) +return PTR_ERR (drm); + + tegra =kzalloc (sizeof (*tegra),GFP_KERNEL ); +if (!tegra) { + err = -ENOMEM ; + goto put; + } -if (domain &&iommu_present (&platform_bus_type)) { +if (host1x_drm_wants_iommu (dev) &&iommu_present (&platform_bus_type)) { tegra->domain =iommu_domain_alloc (&platform_bus_type);if (!tegra->domain) { err = -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 268/367 drm/tegra: Reuse IOVA mapping where possible
In reply to Greg Kroah-HartmanFrom: Thierry Reding <[email protected]>
commit 273da5a046965ccf0ec79eb63f2d5173467e20fa upstream.
This partially reverts the DMA API support that was recently merged because it was causing performance regressions on older Tegra devices. Unfortunately, the cache maintenance performed by dma_map_sg() and dma_unmap_sg() causes performance to drop by a factor of 10.
The right solution for this would be to cache mappings for buffers per consumer device, but that's a bit involved. Instead, we simply revert to the old behaviour of sharing IOVA mappings when we know that devices can do so (i.e. they share the same IOMMU domain).
--- drivers/gpu/drm/tegra/gem.c | 10 ++++++++- drivers/gpu/drm/tegra/plane.c | 46 +++++++++++++++++++++++------------------- drivers/gpu/host1x/job.c | 32 ++++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 24 deletions(-)
drivers/gpu/drm/tegra/gem.c +9 -1
@@ -60,8 +60,16 @@ static struct sg_table *tegra_bo_pin(str/* * If we've manually mapped the buffer object through the IOMMU, make * sure to return the IOVA address of our mapping. +* +* Similarly, for buffers that have been allocated by the DMA API the +* physical address can be used for devices that are not attached to +* an IOMMU. For these devices, callers must pass a valid pointer via +* the @phys argument. +* +* Imported buffers were also already mapped at import time, so the +* existing mapping can be reused. */ -if (phys && obj->mm) { +if (phys) { *phys = obj->iova;return NULL ; }
drivers/gpu/drm/tegra/plane.c +26 -20
86 lines not on this page · raw message
drivers/gpu/host1x/job.c +29 -3
@@ -8,6 +8,7 @@#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/host1x.h> +#include <linux/iommu.h> #include <linux/kref.h> #include <linux/module.h> #include <linux/scatterlist.h>
@@ -101,9 +102,11 @@ static unsigned int pin_job(struct host1 {struct host1x_client *client = job->client;struct device *dev = client->dev; +struct iommu_domain *domain;unsigned int i;int err; + domain =iommu_get_domain_for_dev (dev); job->num_unpins =0 ;for (i =0 ; i < job->num_relocs; i++) {
@@ -117,7 +120,19 @@ static unsigned int pin_job(struct host1 goto unpin; } -if (client->group) +/* +* If the client device is not attached to an IOMMU, the +* physical address of the buffer object can be used. +* +* Similarly, when an IOMMU domain is shared between all +* host1x clients, the IOVA is already available, so no +* need to map the buffer object again. +* +* XXX Note that this isn't always safe to do because it +* relies on an assumption that no cache maintenance is +* needed on the buffer objects. +*/ +if (!domain || client->group) phys = &phys_addr;else phys =NULL ;
@@ -176,6 +191,7 @@ static unsigned int pin_job(struct host1dma_addr_t phys_addr;unsigned long shift;struct iova *alloc; +dma_addr_t *phys;unsigned int j; g->bo =host1x_bo_get (g->bo);
@@ -184,7 +200,17 @@ static unsigned int pin_job(struct host1 goto unpin; } - sgt =host1x_bo_pin (host->dev, g->bo,NULL ); +/** +* If the host1x is not attached to an IOMMU, there is no need +* to map the buffer object for the host1x, since the physical +* address can simply be used. +*/ +if (!iommu_get_domain_for_dev (host->dev)) + phys = &phys_addr; +else + phys =NULL ; + + sgt =host1x_bo_pin (host->dev, g->bo, phys);if (IS_ERR (sgt)) { err =PTR_ERR (sgt); goto unpin;
@@ -214,7 +240,7 @@ static unsigned int pin_job(struct host1 job->unpins[job->num_unpins].size = gather_size; phys_addr =iova_dma_addr (&host->iova, alloc); - }else { + }else if (sgt) { err =dma_map_sg (host->dev, sgt->sgl, sgt->nents,DMA_TO_DEVICE );if (!err) {
Greg Kroah-Hartman
maintainerPATCH 269/367 drm/amd/powerplay: fix navi10 system intermittent reboot issue V2
In reply to Greg Kroah-HartmanFrom: Evan Quan <[email protected]>
commit 1cf8c930b378016846c88ef0f1444248033326ec upstream.
This workaround is needed only for Navi10 12 Gbps SKUs.
V2: added SMU firmware version guard
--- drivers/gpu/drm/amd/powerplay/amdgpu_smu.c | 18 ++++++ drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h | 1 drivers/gpu/drm/amd/powerplay/inc/smu_types.h | 2 drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_ppsmc.h | 5 + drivers/gpu/drm/amd/powerplay/navi10_ppt.c | 58 ++++++++++++++++++++ drivers/gpu/drm/amd/powerplay/smu_internal.h | 3 + 6 files changed, 86 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/powerplay/amdgpu_smu.c +18 -0
@@ -21,6 +21,7 @@*/ #include <linux/firmware.h> +#include <linux/pci.h> #include "pp_debug.h" #include "amdgpu.h"
@@ -1125,6 +1126,23 @@ static int smu_smc_table_hw_init(struct ret =smu_get_power_limit (smu, &smu->default_power_limit, false, false);if (ret)return ret; + +if (adev->asic_type ==CHIP_NAVI10 ) { +if ((adev->pdev->device ==0x731f && (adev->pdev->revision ==0xc2 || + adev->pdev->revision ==0xc3 || + adev->pdev->revision ==0xca || + adev->pdev->revision ==0xcb )) || + (adev->pdev->device ==0x66af && (adev->pdev->revision ==0xf3 || + adev->pdev->revision ==0xf4 || + adev->pdev->revision ==0xf5 || + adev->pdev->revision ==0xf6 ))) { + ret =smu_disable_umc_cdr_12gbps_workaround (smu); +if (ret) { +pr_err ("Workaround failed to disable UMC CDR feature on 12Gbps SKU!\n" ); +return ret; + } + } + } }/*
drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h +1 -0
@@ -550,6 +550,7 @@ struct pptable_funcs {int (*set_soft_freq_limited_range)(struct smu_context *smu,enum smu_clk_type clk_type,uint32_t min,uint32_t max);int (*override_pcie_parameters)(struct smu_context *smu);uint32_t (*get_pptable_power_limit)(struct smu_context *smu); +int (*disable_umc_cdr_12gbps_workaround)(struct smu_context *smu); };int smu_load_microcode (struct smu_context *smu);
drivers/gpu/drm/amd/powerplay/inc/smu_types.h +2 -0
@@ -170,6 +170,8 @@__SMU_DUMMY_MAP (SetSoftMinJpeg), \__SMU_DUMMY_MAP (SetHardMinFclkByFreq), \__SMU_DUMMY_MAP (DFCstateControl), \ +__SMU_DUMMY_MAP (DAL_DISABLE_DUMMY_PSTATE_CHANGE ), \ +__SMU_DUMMY_MAP (DAL_ENABLE_DUMMY_PSTATE_CHANGE ), \ #undef __SMU_DUMMY_MAP#define __SMU_DUMMY_MAP (type) SMU_MSG_##type
drivers/gpu/drm/amd/powerplay/inc/smu_v11_0_ppsmc.h +4 -1
@@ -120,7 +120,10 @@#define PPSMC_MSG_GetVoltageByDpmOverdrive 0x45#define PPSMC_MSG_BacoAudioD3PME 0x48 -#define PPSMC_Message_Count 0x49 +#define PPSMC_MSG_DALDisableDummyPstateChange 0x49 +#define PPSMC_MSG_DALEnableDummyPstateChange 0x4A + +#define PPSMC_Message_Count 0x4Btypedef uint32_t PPSMC_Result ;typedef uint32_t PPSMC_Msg ;
drivers/gpu/drm/amd/powerplay/navi10_ppt.c +58 -0
76 lines not on this page · raw message
drivers/gpu/drm/amd/powerplay/smu_internal.h +3 -0
@@ -201,4 +201,7 @@ int smu_send_smc_msg(struct smu_context#define smu_update_pcie_parameters (smu, pcie_gen_cap, pcie_width_cap) \ ((smu)->ppt_funcs->update_pcie_parameters ? (smu)->ppt_funcs->update_pcie_parameters((smu), (pcie_gen_cap), (pcie_width_cap)) : 0) +#define smu_disable_umc_cdr_12gbps_workaround (smu) \ + ((smu)->ppt_funcs->disable_umc_cdr_12gbps_workaround ? (smu)->ppt_funcs->disable_umc_cdr_12gbps_workaround((smu)) : 0) +#endif
Greg Kroah-Hartman
maintainerPATCH 270/367 drm/amd/dm/mst: Ignore payload update failures
In reply to Greg Kroah-HartmanFrom: Lyude Paul <[email protected]>
commit 58fe03d6dec908a1bec07eea7e94907af5c07eec upstream.
Disabling a display on MST can potentially happen after the entire MST topology has been removed, which means that we can't communicate with the topology at all in this scenario. Likewise, this also means that we can't properly update payloads on the topology and as such, it's a good idea to ignore payload update failures when disabling displays. Currently, amdgpu makes the mistake of halting the payload update process when any payload update failures occur, resulting in leaving DC's local copies of the payload tables out of date.
This ends up causing problems with hotplugging MST topologies, and causes modesets on the second hotplug to fail like so:
[drm] Failed to updateMST allocation table forpipe idx:1
------------[ cut here ]------------ WARNING: CPU: 5 PID: 1511 at drivers/gpu/drm/amd/amdgpu/../display/dc/core/dc_link.c:2677 update_mst_stream_alloc_table+0x11e/0x130 [amdgpu] Modules linked in: cdc_ether usbnet fuse xt_conntrack nf_conntrack
nf_defrag_ipv6 libcrc32c nf_defrag_ipv4 ipt_REJECT nf_reject_ipv4 nft_counter nft_compat nf_tables nfnetlink tun bridge stp llc sunrpc vfat fat wmi_bmof uvcvideo snd_hda_codec_realtek snd_hda_codec_generic snd_hda_codec_hdmi videobuf2_vmalloc snd_hda_intel videobuf2_memops videobuf2_v4l2 snd_intel_dspcfg videobuf2_common crct10dif_pclmul snd_hda_codec videodev crc32_pclmul snd_hwdep snd_hda_core ghash_clmulni_intel snd_seq mc joydev pcspkr snd_seq_device snd_pcm sp5100_tco k10temp i2c_piix4 snd_timer thinkpad_acpi ledtrig_audio snd wmi soundcore video i2c_scmi acpi_cpufreq ip_tables amdgpu(O) rtsx_pci_sdmmc amd_iommu_v2 gpu_sched mmc_core i2c_algo_bit ttm drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops cec drm crc32c_intel serio_raw hid_multitouch r8152 mii nvme r8169 nvme_core rtsx_pci pinctrl_amd
CPU: 5 PID: 1511 Comm: gnome-shell Tainted: G O 5.5.0-rc7Lyude-Test+ #4 Hardware name: LENOVO FA495SIT26/FA495SIT26, BIOS R12ET22W(0.22 ) 01/31/2019 RIP: 0010:update_mst_stream_alloc_table+0x11e/0x130 [amdgpu] Code: 28 00 00 00 75 2b 48 8d 65 e0 5b 41 5c 41 5d 41 5e 5d c3 0f b6 06 49 89 1c 24 41 88 44 24 08 0f b6 46 01 41 88 44 24 09 eb 93 <0f> 0b e9 2f ff ff ff e8 a6 82 a3 c2 66 0f 1f 44 00 00 0f 1f 44 00 RSP: 0018:ffffac428127f5b0 EFLAGS: 00010202 RAX: 0000000000000002 RBX: ffff8d1e166eee80 RCX: 0000000000000000 RDX: ffffac428127f668 RSI: ffff8d1e166eee80 RDI: ffffac428127f610 RBP: ffffac428127f640 R08: ffffffffc03d94a8 R09: 0000000000000000 R10: ffff8d1e24b02000 R11: ffffac428127f5b0 R12: ffff8d1e1b83d000 R13: ffff8d1e1bea0b08 R14: 0000000000000002 R15: 0000000000000002 FS: 00007fab23ffcd80(0000) GS:ffff8d1e28b40000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f151f1711e8 CR3: 00000005997c0000 CR4: 00000000003406e0 Call Trace: ? mutex_lock+0xe/0x30 dc_link_allocate_mst_payload+0x9a/0x210 [amdgpu] ? dm_read_reg_func+0x39/0xb0 [amdgpu] ? core_link_enable_stream+0x656/0x730 [amdgpu] core_link_enable_stream+0x656/0x730 [amdgpu] dce110_apply_ctx_to_hw+0x58e/0x5d0 [amdgpu] ? dcn10_verify_allow_pstate_change_high+0x1d/0x280 [amdgpu] ? dcn10_wait_for_mpcc_disconnect+0x3c/0x130 [amdgpu] dc_commit_state+0x292/0x770 [amdgpu] ? add_timer+0x101/0x1f0 ? ttm_bo_put+0x1a1/0x2f0 [ttm] amdgpu_dm_atomic_commit_tail+0xb59/0x1ff0 [amdgpu] ? amdgpu_move_blit.constprop.0+0xb8/0x1f0 [amdgpu] ? amdgpu_bo_move+0x16d/0x2b0 [amdgpu] ? ttm_bo_handle_move_mem+0x118/0x570 [ttm] ? ttm_bo_validate+0x134/0x150 [ttm] ? dm_plane_helper_prepare_fb+0x1b9/0x2a0 [amdgpu] ? _cond_resched+0x15/0x30 ? wait_for_completion_timeout+0x38/0x160 ? _cond_resched+0x15/0x30 ? wait_for_completion_interruptible+0x33/0x190 commit_tail+0x94/0x130 [drm_kms_helper] drm_atomic_helper_commit+0x113/0x140 [drm_kms_helper] drm_atomic_helper_set_config+0x70/0xb0 [drm_kms_helper] drm_mode_setcrtc+0x194/0x6a0 [drm] ? _cond_resched+0x15/0x30 ? mutex_lock+0xe/0x30 ? drm_mode_getcrtc+0x180/0x180 [drm] drm_ioctl_kernel+0xaa/0xf0 [drm] drm_ioctl+0x208/0x390 [drm] ? drm_mode_getcrtc+0x180/0x180 [drm] amdgpu_drm_ioctl+0x49/0x80 [amdgpu] do_vfs_ioctl+0x458/0x6d0 ksys_ioctl+0x5e/0x90 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x55/0x1b0 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x7fab2121f87b Code: 0f 1e fa 48 8b 05 0d 96 2c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff
ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d dd 95 2c 00 f7 d8 64 89 01 48
RSP: 002b:00007ffd045f9068 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007ffd045f90a0 RCX: 00007fab2121f87b
RDX: 00007ffd045f90a0 RSI: 00000000c06864a2 RDI: 000000000000000b
RBP: 00007ffd045f90a0 R08: 0000000000000000 R09: 000055dbd2985d10
R10: 000055dbd2196280 R11: 0000000000000246 R12: 00000000c06864a2
R13: 000000000000000b R14: 0000000000000000 R15: 000055dbd2196280
---[ end trace 6ea888c24d2059cd ]---
Note as well, I have only been able to reproduce this on setups with 2 MST displays.
Changes since v1:
* Don't return false when part 1 or part 2 of updating the payloads fails, we don't want to abort at any step of the process even if things fail
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +4 -9
@@ -246,7 +246,8 @@ bool dm_helpers_dp_mst_write_payload_alldrm_dp_mst_reset_vcpi_slots (mst_mgr, mst_port); } - ret =drm_dp_update_payload_part1 (mst_mgr); +/* It's OK for this to fail */ +drm_dp_update_payload_part1 (mst_mgr);/* mst_mgr->->payloads are VC payload notify MST branch using DPCD or * AUX message. The sequence is slot 1-63 allocated sequence for each
@@ -255,9 +256,6 @@ bool dm_helpers_dp_mst_write_payload_allget_payload_table (aconnector, proposed_table); -if (ret) -return false; -return true; }
@@ -315,7 +313,6 @@ bool dm_helpers_dp_mst_send_payload_allostruct amdgpu_dm_connector *aconnector;struct drm_dp_mst_topology_mgr *mst_mgr;struct drm_dp_mst_port *mst_port; -int ret; aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;
@@ -329,10 +326,8 @@ bool dm_helpers_dp_mst_send_payload_alloif (!mst_mgr->mst_state)return false; - ret =drm_dp_update_payload_part2 (mst_mgr); - -if (ret) -return false; +/* It's OK for this to fail */ +drm_dp_update_payload_part2 (mst_mgr);if (!enable)drm_dp_mst_deallocate_vcpi (mst_mgr, mst_port);
Greg Kroah-Hartman
maintainerPATCH 271/367 drm/amdgpu: fetch default VDDC curve voltages (v2)
In reply to Greg Kroah-HartmanFrom: Alex Deucher <[email protected]>
commit 0531aa6eb38bfa9514609e2727558a051da7365f upstream.
Ask the SMU for the default VDDC curve voltage values. This properly reports the VDDC values in the OD interface.
v2: only update if the original values are 0
--- drivers/gpu/drm/amd/powerplay/navi10_ppt.c | 50 ++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/powerplay/navi10_ppt.c +49 -1
@@ -121,6 +121,8 @@ static struct smu_11_0_cmn2aisc_mappingMSG_MAP (ArmD3, PPSMC_MSG_ArmD3),MSG_MAP (DAL_DISABLE_DUMMY_PSTATE_CHANGE ,PPSMC_MSG_DALDisableDummyPstateChange),MSG_MAP (DAL_ENABLE_DUMMY_PSTATE_CHANGE , PPSMC_MSG_DALEnableDummyPstateChange), +MSG_MAP (GetVoltageByDpm, PPSMC_MSG_GetVoltageByDpm), + MSG_MAP(GetVoltageByDpmOverdrive, PPSMC_MSG_GetVoltageByDpmOverdrive), }; static struct smu_11_0_cmn2aisc_mapping navi10_clk_map[SMU_CLK_COUNT] = {
@@ -1782,6 +1784,28 @@ static int navi10_od_setting_check_rangereturn 0 ; } +static int navi10_overdrive_get_gfx_clk_base_voltage (struct smu_context *smu, +uint16_t *voltage, +uint32_t freq) +{ +uint32_t param = (freq &0xFFFF ) | (PPCLK_GFXCLK <<16 ); +uint32_t value =0 ; +int ret; + + ret =smu_send_smc_msg_with_param (smu, + SMU_MSG_GetVoltageByDpm, + param); +if (ret) { +pr_err ("[GetBaseVoltage] failed to get GFXCLK AVFS voltage from SMU!" ); +return ret; + } + +smu_read_smc_arg (smu, &value); + *voltage = (uint16_t )value; + +return 0 ; +} +static int navi10_setup_od_limits (struct smu_context *smu) {struct smu_11_0_overdrive_table *overdrive_table =NULL ;struct smu_11_0_powerplay_table *powerplay_table =NULL ;
@@ -1808,16 +1832,40 @@ static int navi10_set_default_od_settingif (ret)return ret; + od_table = (OverDriveTable_t *)smu->smu_table.overdrive_table;if (initialize) { ret =navi10_setup_od_limits (smu);if (ret) {pr_err ("Failed to retrieve board OD limits\n" );return ret; } +if (od_table) { +if (!od_table->GfxclkVolt1) { + ret =navi10_overdrive_get_gfx_clk_base_voltage (smu, + &od_table->GfxclkVolt1, + od_table->GfxclkFreq1); +if (ret) + od_table->GfxclkVolt1 =0 ; + } + +if (!od_table->GfxclkVolt2) { + ret =navi10_overdrive_get_gfx_clk_base_voltage (smu, + &od_table->GfxclkVolt2, + od_table->GfxclkFreq2); +if (ret) + od_table->GfxclkVolt2 =0 ; + } +if (!od_table->GfxclkVolt3) { + ret =navi10_overdrive_get_gfx_clk_base_voltage (smu, + &od_table->GfxclkVolt3, + od_table->GfxclkFreq3); +if (ret) + od_table->GfxclkVolt3 =0 ; + } + } } - od_table = (OverDriveTable_t *)smu->smu_table.overdrive_table;if (od_table) {navi10_dump_od_table (od_table); }