/* SPDX-License-Identifier: GPL-2.0-only */1/*2* Copyright 2024 Google LLC3*4* dbitmap - dynamically sized bitmap library.5*6* Used by the binder driver to optimize the allocation of the smallest7* available descriptor ID. Each bit in the bitmap represents the state8* of an ID.9*10* A dbitmap can grow or shrink as needed. This part has been designed11* considering that users might need to briefly release their locks in12* order to allocate memory for the new bitmap. These operations then,13* are verified to determine if the grow or shrink is sill valid.14*15* This library does not provide protection against concurrent access16* by itself. Binder uses the proc->outer_lock for this purpose.17*/1819#ifndef _LINUX_DBITMAP_H20#define _LINUX_DBITMAP_H21#include <linux/bitmap.h>2223#define NBITS_MIN BITS_PER_TYPE(unsigned long)2425struct dbitmap {26unsigned int nbits;27unsigned long *map;28};2930static inline int dbitmap_enabled(struct dbitmap *dmap)31{32return !!dmap->nbits;33}3435static inline void dbitmap_free(struct dbitmap *dmap)36{37dmap->nbits = 0;38kfree(dmap->map);39dmap->map = NULL;40}4142/* Returns the nbits that a dbitmap can shrink to, 0 if not possible. */43static inline unsigned int dbitmap_shrink_nbits(struct dbitmap *dmap)44{45unsigned int bit;4647if (dmap->nbits <= NBITS_MIN)48return 0;4950/*51* Determine if the bitmap can shrink based on the position of52* its last set bit. If the bit is within the first quarter of53* the bitmap then shrinking is possible. In this case, the54* bitmap should shrink to half its current size.55*/56bit = find_last_bit(dmap->map, dmap->nbits);57if (bit < (dmap->nbits >> 2))58return dmap->nbits >> 1;5960/* find_last_bit() returns dmap->nbits when no bits are set. */61if (bit == dmap->nbits)62return NBITS_MIN;6364return 0;65}6667/* Replace the internal bitmap with a new one of different size */68static inline void69dbitmap_replace(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)70{71bitmap_copy(new, dmap->map, min(dmap->nbits, nbits));72kfree(dmap->map);73dmap->map = new;74dmap->nbits = nbits;75}7677static inline void78dbitmap_shrink(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)79{80if (!new)81return;8283/*84* Verify that shrinking to @nbits is still possible. The @new85* bitmap might have been allocated without locks, so this call86* could now be outdated. In this case, free @new and move on.87*/88if (!dbitmap_enabled(dmap) || dbitmap_shrink_nbits(dmap) != nbits) {89kfree(new);90return;91}9293dbitmap_replace(dmap, new, nbits);94}9596/* Returns the nbits that a dbitmap can grow to. */97static inline unsigned int dbitmap_grow_nbits(struct dbitmap *dmap)98{99return dmap->nbits << 1;100}101102static inline void103dbitmap_grow(struct dbitmap *dmap, unsigned long *new, unsigned int nbits)104{105/*106* Verify that growing to @nbits is still possible. The @new107* bitmap might have been allocated without locks, so this call108* could now be outdated. In this case, free @new and move on.109*/110if (!dbitmap_enabled(dmap) || nbits <= dmap->nbits) {111kfree(new);112return;113}114115/*116* Check for ENOMEM after confirming the grow operation is still117* required. This ensures we only disable the dbitmap when it's118* necessary. Once the dbitmap is disabled, binder will fallback119* to slow_desc_lookup_olocked().120*/121if (!new) {122dbitmap_free(dmap);123return;124}125126dbitmap_replace(dmap, new, nbits);127}128129/*130* Finds and sets the next zero bit in the bitmap. Upon success @bit131* is populated with the index and 0 is returned. Otherwise, -ENOSPC132* is returned to indicate that a dbitmap_grow() is needed.133*/134static inline int135dbitmap_acquire_next_zero_bit(struct dbitmap *dmap, unsigned long offset,136unsigned long *bit)137{138unsigned long n;139140n = find_next_zero_bit(dmap->map, dmap->nbits, offset);141if (n == dmap->nbits)142return -ENOSPC;143144*bit = n;145set_bit(n, dmap->map);146147return 0;148}149150static inline void151dbitmap_clear_bit(struct dbitmap *dmap, unsigned long bit)152{153clear_bit(bit, dmap->map);154}155156static inline int dbitmap_init(struct dbitmap *dmap)157{158dmap->map = bitmap_zalloc(NBITS_MIN, GFP_KERNEL);159if (!dmap->map) {160dmap->nbits = 0;161return -ENOMEM;162}163164dmap->nbits = NBITS_MIN;165166return 0;167}168#endif169170171