diff options
Diffstat (limited to 'lib/sbitmap.c')
| -rw-r--r-- | lib/sbitmap.c | 36 | 
1 files changed, 29 insertions, 7 deletions
| diff --git a/lib/sbitmap.c b/lib/sbitmap.c index 1e453f825c05..5e2e93307f0d 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -60,12 +60,30 @@ static inline void update_alloc_hint_after_get(struct sbitmap *sb,  /*   * See if we have deferred clears that we can batch move   */ -static inline bool sbitmap_deferred_clear(struct sbitmap_word *map) +static inline bool sbitmap_deferred_clear(struct sbitmap_word *map, +		unsigned int depth, unsigned int alloc_hint, bool wrap)  { -	unsigned long mask; +	unsigned long mask, word_mask; -	if (!READ_ONCE(map->cleared)) -		return false; +	guard(spinlock_irqsave)(&map->swap_lock); + +	if (!map->cleared) { +		if (depth == 0) +			return false; + +		word_mask = (~0UL) >> (BITS_PER_LONG - depth); +		/* +		 * The current behavior is to always retry after moving +		 * ->cleared to word, and we change it to retry in case +		 * of any free bits. To avoid an infinite loop, we need +		 * to take wrap & alloc_hint into account, otherwise a +		 * soft lockup may occur. +		 */ +		if (!wrap && alloc_hint) +			word_mask &= ~((1UL << alloc_hint) - 1); + +		return (READ_ONCE(map->word) & word_mask) != word_mask; +	}  	/*  	 * First get a stable cleared mask, setting the old mask to 0. @@ -85,6 +103,7 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,  		      bool alloc_hint)  {  	unsigned int bits_per_word; +	int i;  	if (shift < 0)  		shift = sbitmap_calculate_shift(depth); @@ -116,6 +135,9 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,  		return -ENOMEM;  	} +	for (i = 0; i < sb->map_nr; i++) +		spin_lock_init(&sb->map[i].swap_lock); +  	return 0;  }  EXPORT_SYMBOL_GPL(sbitmap_init_node); @@ -126,7 +148,7 @@ void sbitmap_resize(struct sbitmap *sb, unsigned int depth)  	unsigned int i;  	for (i = 0; i < sb->map_nr; i++) -		sbitmap_deferred_clear(&sb->map[i]); +		sbitmap_deferred_clear(&sb->map[i], 0, 0, 0);  	sb->depth = depth;  	sb->map_nr = DIV_ROUND_UP(sb->depth, bits_per_word); @@ -179,7 +201,7 @@ static int sbitmap_find_bit_in_word(struct sbitmap_word *map,  					alloc_hint, wrap);  		if (nr != -1)  			break; -		if (!sbitmap_deferred_clear(map)) +		if (!sbitmap_deferred_clear(map, depth, alloc_hint, wrap))  			break;  	} while (1); @@ -496,7 +518,7 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags,  		unsigned int map_depth = __map_depth(sb, index);  		unsigned long val; -		sbitmap_deferred_clear(map); +		sbitmap_deferred_clear(map, 0, 0, 0);  		val = READ_ONCE(map->word);  		if (val == (1UL << (map_depth - 1)) - 1)  			goto next; |