115d9da3fSCarlos Llamas /* SPDX-License-Identifier: GPL-2.0-only */
215d9da3fSCarlos Llamas /*
315d9da3fSCarlos Llamas * Copyright 2024 Google LLC
415d9da3fSCarlos Llamas *
515d9da3fSCarlos Llamas * dbitmap - dynamically sized bitmap library.
615d9da3fSCarlos Llamas *
715d9da3fSCarlos Llamas * Used by the binder driver to optimize the allocation of the smallest
815d9da3fSCarlos Llamas * available descriptor ID. Each bit in the bitmap represents the state
9*11512c19SCarlos Llamas * of an ID.
1015d9da3fSCarlos Llamas *
1115d9da3fSCarlos Llamas * A dbitmap can grow or shrink as needed. This part has been designed
1215d9da3fSCarlos Llamas * considering that users might need to briefly release their locks in
1315d9da3fSCarlos Llamas * order to allocate memory for the new bitmap. These operations then,
1415d9da3fSCarlos Llamas * are verified to determine if the grow or shrink is sill valid.
1515d9da3fSCarlos Llamas *
1615d9da3fSCarlos Llamas * This library does not provide protection against concurrent access
1715d9da3fSCarlos Llamas * by itself. Binder uses the proc->outer_lock for this purpose.
1815d9da3fSCarlos Llamas */
1915d9da3fSCarlos Llamas
2015d9da3fSCarlos Llamas #ifndef _LINUX_DBITMAP_H
2115d9da3fSCarlos Llamas #define _LINUX_DBITMAP_H
2215d9da3fSCarlos Llamas #include <linux/bitmap.h>
2315d9da3fSCarlos Llamas
2415d9da3fSCarlos Llamas #define NBITS_MIN BITS_PER_TYPE(unsigned long)
2515d9da3fSCarlos Llamas
2615d9da3fSCarlos Llamas struct dbitmap {
2715d9da3fSCarlos Llamas unsigned int nbits;
2815d9da3fSCarlos Llamas unsigned long *map;
2915d9da3fSCarlos Llamas };
3015d9da3fSCarlos Llamas
dbitmap_enabled(struct dbitmap * dmap)3115d9da3fSCarlos Llamas static inline int dbitmap_enabled(struct dbitmap *dmap)
3215d9da3fSCarlos Llamas {
3315d9da3fSCarlos Llamas return !!dmap->nbits;
3415d9da3fSCarlos Llamas }
3515d9da3fSCarlos Llamas
dbitmap_free(struct dbitmap * dmap)3615d9da3fSCarlos Llamas static inline void dbitmap_free(struct dbitmap *dmap)
3715d9da3fSCarlos Llamas {
3815d9da3fSCarlos Llamas dmap->nbits = 0;
3915d9da3fSCarlos Llamas kfree(dmap->map);
4015d9da3fSCarlos Llamas }
4115d9da3fSCarlos Llamas
4215d9da3fSCarlos Llamas /* Returns the nbits that a dbitmap can shrink to, 0 if not possible. */
dbitmap_shrink_nbits(struct dbitmap * dmap)4315d9da3fSCarlos Llamas static inline unsigned int dbitmap_shrink_nbits(struct dbitmap *dmap)
4415d9da3fSCarlos Llamas {
4515d9da3fSCarlos Llamas unsigned int bit;
4615d9da3fSCarlos Llamas
4715d9da3fSCarlos Llamas if (dmap->nbits <= NBITS_MIN)
4815d9da3fSCarlos Llamas return 0;
4915d9da3fSCarlos Llamas
5015d9da3fSCarlos Llamas /*
5115d9da3fSCarlos Llamas * Determine if the bitmap can shrink based on the position of
5215d9da3fSCarlos Llamas * its last set bit. If the bit is within the first quarter of
5315d9da3fSCarlos Llamas * the bitmap then shrinking is possible. In this case, the
5415d9da3fSCarlos Llamas * bitmap should shrink to half its current size.
5515d9da3fSCarlos Llamas */
5615d9da3fSCarlos Llamas bit = find_last_bit(dmap->map, dmap->nbits);
5715d9da3fSCarlos Llamas if (bit < (dmap->nbits >> 2))
5815d9da3fSCarlos Llamas return dmap->nbits >> 1;
5915d9da3fSCarlos Llamas
60*11512c19SCarlos Llamas /* find_last_bit() returns dmap->nbits when no bits are set. */
6115d9da3fSCarlos Llamas if (bit == dmap->nbits)
6215d9da3fSCarlos Llamas return NBITS_MIN;
6315d9da3fSCarlos Llamas
6415d9da3fSCarlos Llamas return 0;
6515d9da3fSCarlos Llamas }
6615d9da3fSCarlos Llamas
6715d9da3fSCarlos Llamas /* Replace the internal bitmap with a new one of different size */
6815d9da3fSCarlos Llamas static inline void
dbitmap_replace(struct dbitmap * dmap,unsigned long * new,unsigned int nbits)6915d9da3fSCarlos Llamas dbitmap_replace(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)
7015d9da3fSCarlos Llamas {
7115d9da3fSCarlos Llamas bitmap_copy(new, dmap->map, min(dmap->nbits, nbits));
7215d9da3fSCarlos Llamas kfree(dmap->map);
7315d9da3fSCarlos Llamas dmap->map = new;
7415d9da3fSCarlos Llamas dmap->nbits = nbits;
7515d9da3fSCarlos Llamas }
7615d9da3fSCarlos Llamas
7715d9da3fSCarlos Llamas static inline void
dbitmap_shrink(struct dbitmap * dmap,unsigned long * new,unsigned int nbits)7815d9da3fSCarlos Llamas dbitmap_shrink(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)
7915d9da3fSCarlos Llamas {
8015d9da3fSCarlos Llamas if (!new)
8115d9da3fSCarlos Llamas return;
8215d9da3fSCarlos Llamas
8315d9da3fSCarlos Llamas /*
8415d9da3fSCarlos Llamas * Verify that shrinking to @nbits is still possible. The @new
8515d9da3fSCarlos Llamas * bitmap might have been allocated without locks, so this call
8615d9da3fSCarlos Llamas * could now be outdated. In this case, free @new and move on.
8715d9da3fSCarlos Llamas */
8815d9da3fSCarlos Llamas if (!dbitmap_enabled(dmap) || dbitmap_shrink_nbits(dmap) != nbits) {
8915d9da3fSCarlos Llamas kfree(new);
9015d9da3fSCarlos Llamas return;
9115d9da3fSCarlos Llamas }
9215d9da3fSCarlos Llamas
9315d9da3fSCarlos Llamas dbitmap_replace(dmap, new, nbits);
9415d9da3fSCarlos Llamas }
9515d9da3fSCarlos Llamas
9615d9da3fSCarlos Llamas /* Returns the nbits that a dbitmap can grow to. */
dbitmap_grow_nbits(struct dbitmap * dmap)9715d9da3fSCarlos Llamas static inline unsigned int dbitmap_grow_nbits(struct dbitmap *dmap)
9815d9da3fSCarlos Llamas {
9915d9da3fSCarlos Llamas return dmap->nbits << 1;
10015d9da3fSCarlos Llamas }
10115d9da3fSCarlos Llamas
10215d9da3fSCarlos Llamas static inline void
dbitmap_grow(struct dbitmap * dmap,unsigned long * new,unsigned int nbits)10315d9da3fSCarlos Llamas dbitmap_grow(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)
10415d9da3fSCarlos Llamas {
10515d9da3fSCarlos Llamas /*
10615d9da3fSCarlos Llamas * Verify that growing to @nbits is still possible. The @new
10715d9da3fSCarlos Llamas * bitmap might have been allocated without locks, so this call
10815d9da3fSCarlos Llamas * could now be outdated. In this case, free @new and move on.
10915d9da3fSCarlos Llamas */
11015d9da3fSCarlos Llamas if (!dbitmap_enabled(dmap) || nbits <= dmap->nbits) {
11115d9da3fSCarlos Llamas kfree(new);
11215d9da3fSCarlos Llamas return;
11315d9da3fSCarlos Llamas }
11415d9da3fSCarlos Llamas
11515d9da3fSCarlos Llamas /*
11615d9da3fSCarlos Llamas * Check for ENOMEM after confirming the grow operation is still
11715d9da3fSCarlos Llamas * required. This ensures we only disable the dbitmap when it's
11815d9da3fSCarlos Llamas * necessary. Once the dbitmap is disabled, binder will fallback
11915d9da3fSCarlos Llamas * to slow_desc_lookup_olocked().
12015d9da3fSCarlos Llamas */
12115d9da3fSCarlos Llamas if (!new) {
12215d9da3fSCarlos Llamas dbitmap_free(dmap);
12315d9da3fSCarlos Llamas return;
12415d9da3fSCarlos Llamas }
12515d9da3fSCarlos Llamas
12615d9da3fSCarlos Llamas dbitmap_replace(dmap, new, nbits);
12715d9da3fSCarlos Llamas }
12815d9da3fSCarlos Llamas
12915d9da3fSCarlos Llamas /*
130*11512c19SCarlos Llamas * Finds and sets the next zero bit in the bitmap. Upon success @bit
13115d9da3fSCarlos Llamas * is populated with the index and 0 is returned. Otherwise, -ENOSPC
13215d9da3fSCarlos Llamas * is returned to indicate that a dbitmap_grow() is needed.
13315d9da3fSCarlos Llamas */
13415d9da3fSCarlos Llamas static inline int
dbitmap_acquire_next_zero_bit(struct dbitmap * dmap,unsigned long offset,unsigned long * bit)135*11512c19SCarlos Llamas dbitmap_acquire_next_zero_bit(struct dbitmap *dmap, unsigned long offset,
136*11512c19SCarlos Llamas unsigned long *bit)
13715d9da3fSCarlos Llamas {
13815d9da3fSCarlos Llamas unsigned long n;
13915d9da3fSCarlos Llamas
140*11512c19SCarlos Llamas n = find_next_zero_bit(dmap->map, dmap->nbits, offset);
14115d9da3fSCarlos Llamas if (n == dmap->nbits)
14215d9da3fSCarlos Llamas return -ENOSPC;
14315d9da3fSCarlos Llamas
14415d9da3fSCarlos Llamas *bit = n;
14515d9da3fSCarlos Llamas set_bit(n, dmap->map);
14615d9da3fSCarlos Llamas
14715d9da3fSCarlos Llamas return 0;
14815d9da3fSCarlos Llamas }
14915d9da3fSCarlos Llamas
15015d9da3fSCarlos Llamas static inline void
dbitmap_clear_bit(struct dbitmap * dmap,unsigned long bit)15115d9da3fSCarlos Llamas dbitmap_clear_bit(struct dbitmap *dmap, unsigned long bit)
15215d9da3fSCarlos Llamas {
15315d9da3fSCarlos Llamas clear_bit(bit, dmap->map);
15415d9da3fSCarlos Llamas }
15515d9da3fSCarlos Llamas
dbitmap_init(struct dbitmap * dmap)15615d9da3fSCarlos Llamas static inline int dbitmap_init(struct dbitmap *dmap)
15715d9da3fSCarlos Llamas {
15815d9da3fSCarlos Llamas dmap->map = bitmap_zalloc(NBITS_MIN, GFP_KERNEL);
15915d9da3fSCarlos Llamas if (!dmap->map) {
16015d9da3fSCarlos Llamas dmap->nbits = 0;
16115d9da3fSCarlos Llamas return -ENOMEM;
16215d9da3fSCarlos Llamas }
16315d9da3fSCarlos Llamas
16415d9da3fSCarlos Llamas dmap->nbits = NBITS_MIN;
16515d9da3fSCarlos Llamas
16615d9da3fSCarlos Llamas return 0;
16715d9da3fSCarlos Llamas }
16815d9da3fSCarlos Llamas #endif
169