From patchwork Sat Nov 18 15:50:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yury Norov X-Patchwork-Id: 13460121 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UZjC7tBN" Received: from mail-yw1-x1135.google.com (mail-yw1-x1135.google.com [IPv6:2607:f8b0:4864:20::1135]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8B479D5D; Sat, 18 Nov 2023 07:51:11 -0800 (PST) Received: by mail-yw1-x1135.google.com with SMTP id 00721157ae682-5a7b3d33663so33593727b3.3; Sat, 18 Nov 2023 07:51:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700322670; x=1700927470; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=FtFgcsJzlik9FHkciB+wVhA9q1gRoy67qNZRN3dQ8lw=; b=UZjC7tBNqvQ3LIO2JEbZpu4Q0crbsl24RDzQZGegNn0OPUZtZkCs/29OiPNhN9M6hV LGQfkl6+A4mr1ghE7QlKE5WVMvjzh97Noc6mKXMXAtrCrDeH6mN8x1jMHu+o2H8nuIOU fP7UySeohS+rHUNG1bsyWAblwvz4bsNDaC1OXXy9FuUbcxUUA6Ux7wJZWe9MAV4/jedp xZCqNXd1CqM3KVqezWw7KzonotD6xlPCv49WV9PmhnPNew3jHAaJxyGyXSjqFMUF2Ens /EwmD11tsZ4einav7TJeHBAfpDkqAct0wMmE2O8TT/eSczS7RddMqXulsP/Y25Bxw7/N MgAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700322670; x=1700927470; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FtFgcsJzlik9FHkciB+wVhA9q1gRoy67qNZRN3dQ8lw=; b=AYNyWKbpsOhVqyVRgfSGLZYBkgi4+qonyBdzpiqA6V+xDCQAAcpuvE4idFytExulVA dAcFCkKwu1k+3ekNbdAMhfjq/3PVJj8XXOLc11wHTLH4iRJFv4I+WzHGgWo4m9nZ3cah pHM3Q5mh59Mt2ttza1dyvw4V/eNzXs8h/MsIVbsvRrad0aUg71vehFBWgzmDzF7rKbc+ uqBr6f02n0jKguWhofpONka0WbDNJ22aPceP8LK9hkfkx3QIO77Xk78fGjKj39SqOjyY vRMUQhWQUXG7aUZrTbmuoX/nypF0QGVf9UYj/LFirtMTIPhaBNQh6QCxylFlcYvJ/a6J CReQ== X-Gm-Message-State: AOJu0Yysly0aPPgP+HrBgWiIDtZ6UuxLtRUlY5It1T8pzJQGL/yWfVFo XsgfyxzHEA3WHK9NIwZhDeZi63ihhZcdE+4D X-Google-Smtp-Source: AGHT+IHUmWgte7gPRldUGsddVvZ0jBBaJ21NuiOWnYAoUu1ELu9emBQdDvxqbio5n2yoz/YHpW+BMw== X-Received: by 2002:a0d:cbd7:0:b0:5a7:ba17:15ac with SMTP id n206-20020a0dcbd7000000b005a7ba1715acmr3133986ywd.41.1700322669620; Sat, 18 Nov 2023 07:51:09 -0800 (PST) Received: from localhost ([2601:344:8301:57f0:48a9:bd4c:868d:dc97]) by smtp.gmail.com with ESMTPSA id p127-20020a0dcd85000000b005a7bbd713ddsm1151825ywd.108.2023.11.18.07.51.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Nov 2023 07:51:08 -0800 (PST) From: Yury Norov To: linux-kernel@vger.kernel.org, "David S. Miller" , "H. Peter Anvin" , "James E.J. Bottomley" , "K. Y. Srinivasan" , "Md. Haris Iqbal" , Akinobu Mita , Andrew Morton , Bjorn Andersson , Borislav Petkov , Chaitanya Kulkarni , Christian Brauner , Damien Le Moal , Dave Hansen , David Disseldorp , Edward Cree , Eric Dumazet , Fenghua Yu , Geert Uytterhoeven , Greg Kroah-Hartman , Gregory Greenman , Hans Verkuil , Hans de Goede , Hugh Dickins , Ingo Molnar , Jakub Kicinski , Jaroslav Kysela , Jason Gunthorpe , Jens Axboe , Jiri Pirko , Jiri Slaby , Kalle Valo , Karsten Graul , Karsten Keil , Kees Cook , Leon Romanovsky , Mark Rutland , Martin Habets , Mauro Carvalho Chehab , Michael Ellerman , Michal Simek , Nicholas Piggin , Oliver Neukum , Paolo Abeni , Paolo Bonzini , Peter Zijlstra , Ping-Ke Shih , Rich Felker , Rob Herring , Robin Murphy , Sathya Prakash Veerichetty , Sean Christopherson , Shuai Xue , Stanislaw Gruszka , Steven Rostedt , Thomas Bogendoerfer , Thomas Gleixner , Valentin Schneider , Vitaly Kuznetsov , Wenjia Zhang , Will Deacon , Yoshinori Sato , GR-QLogic-Storage-Upstream@marvell.com, alsa-devel@alsa-project.org, ath10k@lists.infradead.org, dmaengine@vger.kernel.org, iommu@lists.linux.dev, kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, linux-block@vger.kernel.org, linux-bluetooth@vger.kernel.org, linux-hyperv@vger.kernel.org, linux-m68k@lists.linux-m68k.org, linux-media@vger.kernel.org, linux-mips@vger.kernel.org, linux-net-drivers@amd.com, linux-pci@vger.kernel.org, linux-rdma@vger.kernel.org, linux-s390@vger.kernel.org, linux-scsi@vger.kernel.org, linux-serial@vger.kernel.org, linux-sh@vger.kernel.org, linux-sound@vger.kernel.org, linux-usb@vger.kernel.org, linux-wireless@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, mpi3mr-linuxdrv.pdl@broadcom.com, netdev@vger.kernel.org, sparclinux@vger.kernel.org, x86@kernel.org Cc: Yury Norov , Jan Kara , Mirsad Todorovac , Matthew Wilcox , Rasmus Villemoes , Andy Shevchenko , Maxim Kuvyrkov , Alexey Klimov Subject: [PATCH 01/34] lib/find: add atomic find_bit() primitives Date: Sat, 18 Nov 2023 07:50:32 -0800 Message-Id: <20231118155105.25678-2-yury.norov@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231118155105.25678-1-yury.norov@gmail.com> References: <20231118155105.25678-1-yury.norov@gmail.com> Precedence: bulk X-Mailing-List: linux-block@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add helpers around test_and_{set,clear}_bit() that allow to search for clear or set bits and flip them atomically. The target patterns may look like this: for (idx = 0; idx < nbits; idx++) if (test_and_clear_bit(idx, bitmap)) do_something(idx); Or like this: do { bit = find_first_bit(bitmap, nbits); if (bit >= nbits) return nbits; } while (!test_and_clear_bit(bit, bitmap)); return bit; In both cases, the opencoded loop may be converted to a single function or iterator call. Correspondingly: for_each_test_and_clear_bit(idx, bitmap, nbits) do_something(idx); Or: return find_and_clear_bit(bitmap, nbits); Obviously, the less routine code people have write themself, the less probability to make a mistake. Those are not only handy helpers but also resolve a non-trivial issue of using non-atomic find_bit() together with atomic test_and_{set,clear)_bit(). The trick is that find_bit() implies that the bitmap is a regular non-volatile piece of memory, and compiler is allowed to use such optimization techniques like re-fetching memory instead of caching it. For example, find_first_bit() is implemented like this: for (idx = 0; idx * BITS_PER_LONG < sz; idx++) { val = addr[idx]; if (val) { sz = min(idx * BITS_PER_LONG + __ffs(val), sz); break; } } On register-memory architectures, like x86, compiler may decide to access memory twice - first time to compare against 0, and second time to fetch its value to pass it to __ffs(). When running find_first_bit() on volatile memory, the memory may get changed in-between, and for instance, it may lead to passing 0 to __ffs(), which is undefined. This is a potentially dangerous call. find_and_clear_bit() as a wrapper around test_and_clear_bit() naturally treats underlying bitmap as a volatile memory and prevents compiler from such optimizations. Now that KCSAN is catching exactly this type of situations and warns on undercover memory modifications. We can use it to reveal improper usage of find_bit(), and convert it to atomic find_and_*_bit() as appropriate. The 1st patch of the series adds the following atomic primitives: find_and_set_bit(addr, nbits); find_and_set_next_bit(addr, nbits, start); ... Here find_and_{set,clear} part refers to the corresponding test_and_{set,clear}_bit function, and suffixes like _wrap or _lock derive semantics from corresponding find() or test() functions. For brevity, the naming omits the fact that we search for zero bit in find_and_set, and correspondingly, search for set bit in find_and_clear functions. The patch also adds iterators with atomic semantics, like for_each_test_and_set_bit(). Here, the naming rule is to simply prefix corresponding atomic operation with 'for_each'. All users of find_bit() API, where heavy concurrency is expected, are encouraged to switch to atomic find_and_bit() as appropriate. Signed-off-by: Yury Norov --- include/linux/find.h | 289 +++++++++++++++++++++++++++++++++++++++++++ lib/find_bit.c | 85 +++++++++++++ 2 files changed, 374 insertions(+) diff --git a/include/linux/find.h b/include/linux/find.h index 5e4f39ef2e72..e8567f336f42 100644 --- a/include/linux/find.h +++ b/include/linux/find.h @@ -32,6 +32,16 @@ extern unsigned long _find_first_and_bit(const unsigned long *addr1, extern unsigned long _find_first_zero_bit(const unsigned long *addr, unsigned long size); extern unsigned long _find_last_bit(const unsigned long *addr, unsigned long size); +unsigned long _find_and_set_bit(volatile unsigned long *addr, unsigned long nbits); +unsigned long _find_and_set_next_bit(volatile unsigned long *addr, unsigned long nbits, + unsigned long start); +unsigned long _find_and_set_bit_lock(volatile unsigned long *addr, unsigned long nbits); +unsigned long _find_and_set_next_bit_lock(volatile unsigned long *addr, unsigned long nbits, + unsigned long start); +unsigned long _find_and_clear_bit(volatile unsigned long *addr, unsigned long nbits); +unsigned long _find_and_clear_next_bit(volatile unsigned long *addr, unsigned long nbits, + unsigned long start); + #ifdef __BIG_ENDIAN unsigned long _find_first_zero_bit_le(const unsigned long *addr, unsigned long size); unsigned long _find_next_zero_bit_le(const unsigned long *addr, unsigned @@ -460,6 +470,267 @@ unsigned long __for_each_wrap(const unsigned long *bitmap, unsigned long size, return bit < start ? bit : size; } +/** + * find_and_set_bit - Find a zero bit and set it atomically + * @addr: The address to base the search on + * @nbits: The bitmap size in bits + * + * This function is designed to operate in concurrent access environment. + * + * Because of concurrency and volatile nature of underlying bitmap, it's not + * guaranteed that the bit found is the 1st bit in the bitmap. It's also not + * guaranteed that if @nbits is returned, the bitmap is empty. + * + * The function does guarantee that if returned value is in range [0 .. @nbits), + * the acquired bit belongs to the caller exclusively. + * + * Returns: found and set bit, or @nbits if no bits found + */ +static inline +unsigned long find_and_set_bit(volatile unsigned long *addr, unsigned long nbits) +{ + if (small_const_nbits(nbits)) { + unsigned long val, ret; + + do { + val = *addr | ~GENMASK(nbits - 1, 0); + if (val == ~0UL) + return nbits; + ret = ffz(val); + } while (test_and_set_bit(ret, addr)); + + return ret; + } + + return _find_and_set_bit(addr, nbits); +} + + +/** + * find_and_set_next_bit - Find a zero bit and set it, starting from @offset + * @addr: The address to base the search on + * @nbits: The bitmap nbits in bits + * @offset: The bitnumber to start searching at + * + * This function is designed to operate in concurrent access environment. + * + * Because of concurrency and volatile nature of underlying bitmap, it's not + * guaranteed that the bit found is the 1st bit in the bitmap, starting from @offset. + * It's also not guaranteed that if @nbits is returned, the bitmap is empty. + * + * The function does guarantee that if returned value is in range [@offset .. @nbits), + * the acquired bit belongs to the caller exclusively. + * + * Returns: found and set bit, or @nbits if no bits found + */ +static inline +unsigned long find_and_set_next_bit(volatile unsigned long *addr, + unsigned long nbits, unsigned long offset) +{ + if (small_const_nbits(nbits)) { + unsigned long val, ret; + + do { + val = *addr | ~GENMASK(nbits - 1, offset); + if (val == ~0UL) + return nbits; + ret = ffz(val); + } while (test_and_set_bit(ret, addr)); + + return ret; + } + + return _find_and_set_next_bit(addr, nbits, offset); +} + +/** + * find_and_set_bit_wrap - find and set bit starting at @offset, wrapping around zero + * @addr: The first address to base the search on + * @nbits: The bitmap size in bits + * @offset: The bitnumber to start searching at + * + * Returns: the bit number for the next clear bit, or first clear bit up to @offset, + * while atomically setting it. If no bits are found, returns @nbits. + */ +static inline +unsigned long find_and_set_bit_wrap(volatile unsigned long *addr, + unsigned long nbits, unsigned long offset) +{ + unsigned long bit = find_and_set_next_bit(addr, nbits, offset); + + if (bit < nbits || offset == 0) + return bit; + + bit = find_and_set_bit(addr, offset); + return bit < offset ? bit : nbits; +} + +/** + * find_and_set_bit_lock - find a zero bit, then set it atomically with lock + * @addr: The address to base the search on + * @nbits: The bitmap nbits in bits + * + * This function is designed to operate in concurrent access environment. + * + * Because of concurrency and volatile nature of underlying bitmap, it's not + * guaranteed that the bit found is the 1st bit in the bitmap. It's also not + * guaranteed that if @nbits is returned, the bitmap is empty. + * + * The function does guarantee that if returned value is in range [0 .. @nbits), + * the acquired bit belongs to the caller exclusively. + * + * Returns: found and set bit, or @nbits if no bits found + */ +static inline +unsigned long find_and_set_bit_lock(volatile unsigned long *addr, unsigned long nbits) +{ + if (small_const_nbits(nbits)) { + unsigned long val, ret; + + do { + val = *addr | ~GENMASK(nbits - 1, 0); + if (val == ~0UL) + return nbits; + ret = ffz(val); + } while (test_and_set_bit_lock(ret, addr)); + + return ret; + } + + return _find_and_set_bit_lock(addr, nbits); +} + +/** + * find_and_set_next_bit_lock - find a zero bit and set it atomically with lock + * @addr: The address to base the search on + * @nbits: The bitmap size in bits + * @offset: The bitnumber to start searching at + * + * This function is designed to operate in concurrent access environment. + * + * Because of concurrency and volatile nature of underlying bitmap, it's not + * guaranteed that the bit found is the 1st bit in the range. It's also not + * guaranteed that if @nbits is returned, the bitmap is empty. + * + * The function does guarantee that if returned value is in range [@offset .. @nbits), + * the acquired bit belongs to the caller exclusively. + * + * Returns: found and set bit, or @nbits if no bits found + */ +static inline +unsigned long find_and_set_next_bit_lock(volatile unsigned long *addr, + unsigned long nbits, unsigned long offset) +{ + if (small_const_nbits(nbits)) { + unsigned long val, ret; + + do { + val = *addr | ~GENMASK(nbits - 1, offset); + if (val == ~0UL) + return nbits; + ret = ffz(val); + } while (test_and_set_bit_lock(ret, addr)); + + return ret; + } + + return _find_and_set_next_bit_lock(addr, nbits, offset); +} + +/** + * find_and_set_bit_wrap_lock - find zero bit starting at @ofset and set it + * with lock, and wrap around zero if nothing found + * @addr: The first address to base the search on + * @nbits: The bitmap size in bits + * @offset: The bitnumber to start searching at + * + * Returns: the bit number for the next set bit, or first set bit up to @offset + * If no bits are set, returns @nbits. + */ +static inline +unsigned long find_and_set_bit_wrap_lock(volatile unsigned long *addr, + unsigned long nbits, unsigned long offset) +{ + unsigned long bit = find_and_set_next_bit_lock(addr, nbits, offset); + + if (bit < nbits || offset == 0) + return bit; + + bit = find_and_set_bit_lock(addr, offset); + return bit < offset ? bit : nbits; +} + +/** + * find_and_clear_bit - Find a set bit and clear it atomically + * @addr: The address to base the search on + * @nbits: The bitmap nbits in bits + * + * This function is designed to operate in concurrent access environment. + * + * Because of concurrency and volatile nature of underlying bitmap, it's not + * guaranteed that the found bit is the 1st bit in the bitmap. It's also not + * guaranteed that if @nbits is returned, the bitmap is empty. + * + * The function does guarantee that if returned value is in range [0 .. @nbits), + * the acquired bit belongs to the caller exclusively. + * + * Returns: found and cleared bit, or @nbits if no bits found + */ +static inline unsigned long find_and_clear_bit(volatile unsigned long *addr, unsigned long nbits) +{ + if (small_const_nbits(nbits)) { + unsigned long val, ret; + + do { + val = *addr & GENMASK(nbits - 1, 0); + if (val == 0) + return nbits; + ret = __ffs(val); + } while (!test_and_clear_bit(ret, addr)); + + return ret; + } + + return _find_and_clear_bit(addr, nbits); +} + +/** + * find_and_clear_next_bit - Find a set bit next after @offset, and clear it atomically + * @addr: The address to base the search on + * @nbits: The bitmap nbits in bits + * @offset: bit offset at which to start searching + * + * This function is designed to operate in concurrent access environment. + * + * Because of concurrency and volatile nature of underlying bitmap, it's not + * guaranteed that the bit found is the 1st bit in the range It's also not + * guaranteed that if @nbits is returned, there's no set bits after @offset. + * + * The function does guarantee that if returned value is in range [@offset .. @nbits), + * the acquired bit belongs to the caller exclusively. + * + * Returns: found and cleared bit, or @nbits if no bits found + */ +static inline +unsigned long find_and_clear_next_bit(volatile unsigned long *addr, + unsigned long nbits, unsigned long offset) +{ + if (small_const_nbits(nbits)) { + unsigned long val, ret; + + do { + val = *addr & GENMASK(nbits - 1, offset); + if (val == 0) + return nbits; + ret = __ffs(val); + } while (!test_and_clear_bit(ret, addr)); + + return ret; + } + + return _find_and_clear_next_bit(addr, nbits, offset); +} + /** * find_next_clump8 - find next 8-bit clump with set bits in a memory region * @clump: location to store copy of found clump @@ -577,6 +848,24 @@ unsigned long find_next_bit_le(const void *addr, unsigned #define for_each_set_bit_from(bit, addr, size) \ for (; (bit) = find_next_bit((addr), (size), (bit)), (bit) < (size); (bit)++) +/* same as for_each_set_bit() but atomically clears each found bit */ +#define for_each_test_and_clear_bit(bit, addr, size) \ + for ((bit) = 0; \ + (bit) = find_and_clear_next_bit((addr), (size), (bit)), (bit) < (size); \ + (bit)++) + +/* same as for_each_clear_bit() but atomically sets each found bit */ +#define for_each_test_and_set_bit(bit, addr, size) \ + for ((bit) = 0; \ + (bit) = find_and_clear_next_bit((addr), (size), (bit)), (bit) < (size); \ + (bit)++) + +/* same as for_each_clear_bit_from() but atomically clears each found bit */ +#define for_each_test_and_set_bit_from(bit, addr, size) \ + for (; \ + (bit) = find_and_set_next_bit((addr), (size), (bit)), (bit) < (size); \ + (bit)++) + #define for_each_clear_bit(bit, addr, size) \ for ((bit) = 0; \ (bit) = find_next_zero_bit((addr), (size), (bit)), (bit) < (size); \ diff --git a/lib/find_bit.c b/lib/find_bit.c index 32f99e9a670e..c9b6b9f96610 100644 --- a/lib/find_bit.c +++ b/lib/find_bit.c @@ -116,6 +116,91 @@ unsigned long _find_first_and_bit(const unsigned long *addr1, EXPORT_SYMBOL(_find_first_and_bit); #endif +unsigned long _find_and_set_bit(volatile unsigned long *addr, unsigned long nbits) +{ + unsigned long bit; + + do { + bit = FIND_FIRST_BIT(~addr[idx], /* nop */, nbits); + if (bit >= nbits) + return nbits; + } while (test_and_set_bit(bit, addr)); + + return bit; +} +EXPORT_SYMBOL(_find_and_set_bit); + +unsigned long _find_and_set_next_bit(volatile unsigned long *addr, + unsigned long nbits, unsigned long start) +{ + unsigned long bit; + + do { + bit = FIND_NEXT_BIT(~addr[idx], /* nop */, nbits, start); + if (bit >= nbits) + return nbits; + } while (test_and_set_bit(bit, addr)); + + return bit; +} +EXPORT_SYMBOL(_find_and_set_next_bit); + +unsigned long _find_and_set_bit_lock(volatile unsigned long *addr, unsigned long nbits) +{ + unsigned long bit; + + do { + bit = FIND_FIRST_BIT(~addr[idx], /* nop */, nbits); + if (bit >= nbits) + return nbits; + } while (test_and_set_bit_lock(bit, addr)); + + return bit; +} +EXPORT_SYMBOL(_find_and_set_bit_lock); + +unsigned long _find_and_set_next_bit_lock(volatile unsigned long *addr, + unsigned long nbits, unsigned long start) +{ + unsigned long bit; + + do { + bit = FIND_NEXT_BIT(~addr[idx], /* nop */, nbits, start); + if (bit >= nbits) + return nbits; + } while (test_and_set_bit_lock(bit, addr)); + + return bit; +} +EXPORT_SYMBOL(_find_and_set_next_bit_lock); + +unsigned long _find_and_clear_bit(volatile unsigned long *addr, unsigned long nbits) +{ + unsigned long bit; + + do { + bit = FIND_FIRST_BIT(addr[idx], /* nop */, nbits); + if (bit >= nbits) + return nbits; + } while (!test_and_clear_bit(bit, addr)); + + return bit; +} +EXPORT_SYMBOL(_find_and_clear_bit); + +unsigned long _find_and_clear_next_bit(volatile unsigned long *addr, + unsigned long nbits, unsigned long start) +{ + do { + start = FIND_NEXT_BIT(addr[idx], /* nop */, nbits, start); + if (start >= nbits) + return nbits; + } while (!test_and_clear_bit(start, addr)); + + return start; +} +EXPORT_SYMBOL(_find_and_clear_next_bit); + #ifndef find_first_zero_bit /* * Find the first cleared bit in a memory region. From patchwork Sat Nov 18 15:50:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yury Norov X-Patchwork-Id: 13460122 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LezXZ6w9" Received: from mail-yw1-x1136.google.com (mail-yw1-x1136.google.com [IPv6:2607:f8b0:4864:20::1136]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 15B5C1A1; Sat, 18 Nov 2023 07:51:14 -0800 (PST) Received: by mail-yw1-x1136.google.com with SMTP id 00721157ae682-5c99226c1e6so1048087b3.1; Sat, 18 Nov 2023 07:51:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700322672; x=1700927472; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=wja3OVPJTOloxhD0fIIOnOKhGR1o2s7RteZ+eX82u+o=; b=LezXZ6w9oRAfsFpEnEKgrHaoIbxiWXXQoO30aBaUNEVY0CLBzI+FewEhmYuXPZgFKO Jlbvc5gUCIIwxhD9C0Bxp70TB5JCyXzNNQCD/G0DpriBVPAjnGN0KHTnVGH0iQun5keL jyuXirnQpGF06pyHvQQLpuvEX+IvtzLyWur7RQ4ym3KhFoLs6n0B/liW7BzI1uCAIQbT Kd9BuNbqP972eUXr4T39BiQssIVmO6b50pP2kH9YNiLulUmAydIfSGoPl+NXoVX7b801 5aPMBuiMvu1zn17OSGlXCEsrbk0gw/lrzL9XTa4UnyrK7dy2+3SEOhtcYRyZrZCbU3Ar TiMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700322672; x=1700927472; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wja3OVPJTOloxhD0fIIOnOKhGR1o2s7RteZ+eX82u+o=; b=fPg9Wk8B7a9XsZV2klt/QL9asY6omZUYzjsXUcoHmLgRPPOtBv0W+d5zj18BdlvGiw 8qbU9JiywXNbxyr0izJ9gdoIBFYqEJvDQJcVUMvtuvHQ/EnTa4VOzWh3/C/Qo/K3D6Ka R3ulKPKJ74X2LSWaDfCsNOzqJU3Jturs0oea+1kSbUM8+UChXGyhwltJrf7/s77E1hZF of9hymMHOK8094oHdnWUaLwo/k8nUeePiWL1ACTMIe8BdTfcGJ+njCyPcoadVdkpf63r lq/B6D5OzEKV1l0z+3S/S0dT7BoJHrEkoshoPkd7Uzm0UD1T7e5ie4sUVSHz9k6D8wjL XF8Q== X-Gm-Message-State: AOJu0YyXNKgAz1Fmg6sH1SglDhhnOHZ61P8qy8Zm6BuFu1RCZVkU9zMK +us+yntGlycaWft6zUeswbeHVhS+wQgazg3e X-Google-Smtp-Source: AGHT+IEFudjl0N99Cl4PwLQEleOne3mxPbcgnDGVV5racEbAxc64DY18CSZOjNB06KLe6F00y6ofIA== X-Received: by 2002:a81:528d:0:b0:5b0:36f5:81d2 with SMTP id g135-20020a81528d000000b005b036f581d2mr1329059ywb.26.1700322671999; Sat, 18 Nov 2023 07:51:11 -0800 (PST) Received: from localhost ([2601:344:8301:57f0:48a9:bd4c:868d:dc97]) by smtp.gmail.com with ESMTPSA id s189-20020a0de9c6000000b005c5c3bae1dfsm1178248ywe.54.2023.11.18.07.51.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Nov 2023 07:51:10 -0800 (PST) From: Yury Norov To: linux-kernel@vger.kernel.org, Jens Axboe , linux-block@vger.kernel.org Cc: Yury Norov , Jan Kara , Mirsad Todorovac , Matthew Wilcox , Rasmus Villemoes , Andy Shevchenko , Maxim Kuvyrkov , Alexey Klimov Subject: [PATCH 02/34] lib/sbitmap; make __sbitmap_get_word() using find_and_set_bit() Date: Sat, 18 Nov 2023 07:50:33 -0800 Message-Id: <20231118155105.25678-3-yury.norov@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231118155105.25678-1-yury.norov@gmail.com> References: <20231118155105.25678-1-yury.norov@gmail.com> Precedence: bulk X-Mailing-List: linux-block@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 __sbitmap_get_word() opencodes either find_and_set_bit_wrap(), or find_and_set_next_bit() depending on hint and wrap parameters. Switch it to use the atomic find_bit() API. While here, simplify sbitmap_find_bit_in_word(), which calls it. Signed-off-by: Yury Norov --- lib/sbitmap.c | 46 ++++++++-------------------------------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/lib/sbitmap.c b/lib/sbitmap.c index d0a5081dfd12..b21aebd07fd6 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -133,38 +133,11 @@ void sbitmap_resize(struct sbitmap *sb, unsigned int depth) } EXPORT_SYMBOL_GPL(sbitmap_resize); -static int __sbitmap_get_word(unsigned long *word, unsigned long depth, +static inline int __sbitmap_get_word(unsigned long *word, unsigned long depth, unsigned int hint, bool wrap) { - int nr; - - /* don't wrap if starting from 0 */ - wrap = wrap && hint; - - while (1) { - nr = find_next_zero_bit(word, depth, hint); - if (unlikely(nr >= depth)) { - /* - * We started with an offset, and we didn't reset the - * offset to 0 in a failure case, so start from 0 to - * exhaust the map. - */ - if (hint && wrap) { - hint = 0; - continue; - } - return -1; - } - - if (!test_and_set_bit_lock(nr, word)) - break; - - hint = nr + 1; - if (hint >= depth - 1) - hint = 0; - } - - return nr; + return wrap ? find_and_set_bit_wrap_lock(word, depth, hint) : + find_and_set_next_bit_lock(word, depth, hint); } static int sbitmap_find_bit_in_word(struct sbitmap_word *map, @@ -175,15 +148,12 @@ static int sbitmap_find_bit_in_word(struct sbitmap_word *map, int nr; do { - nr = __sbitmap_get_word(&map->word, depth, - alloc_hint, wrap); - if (nr != -1) - break; - if (!sbitmap_deferred_clear(map)) - break; - } while (1); + nr = __sbitmap_get_word(&map->word, depth, alloc_hint, wrap); + if (nr < depth) + return nr; + } while (sbitmap_deferred_clear(map)); - return nr; + return -1; } static int sbitmap_find_bit(struct sbitmap *sb, From patchwork Sat Nov 18 15:50:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yury Norov X-Patchwork-Id: 13460123 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LX0Ty0bL" Received: from mail-yb1-xb30.google.com (mail-yb1-xb30.google.com [IPv6:2607:f8b0:4864:20::b30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ECD151BF9; Sat, 18 Nov 2023 07:51:43 -0800 (PST) Received: by mail-yb1-xb30.google.com with SMTP id 3f1490d57ef6-d9ac3b4f42cso3362107276.0; Sat, 18 Nov 2023 07:51:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700322702; x=1700927502; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=tg2dWT1RhFKme6yZPEIj44E7/nsRkUJhN3arViZI26M=; b=LX0Ty0bL4pMGI7gQ5d7p8EDFQ9rDmYzupxxul3h1v0ChIU2ggm3JZfJqnfaxHnlMH7 dMKNUQ+FdJFnmF5e8H4/8nAqVTSbVQOlRuUDrIwHiqXd4sg2OPukdZDT7WeNCa3sOyqI /fo3kQhSmqBcZmgZufy3bEzS8R/LGyjCWczqu6KUYr8Nj3/W9jEjt2Gdx+krsfF0SdUI rXzQKd9bZKzK1aQtcKdJQFLzr2h8crIscLZ+g+H2BZO0netk4WdH/N20fl9aXKZyyd50 TbVdX15R2F919pwplLPX9mQUAxaGk9qHABAdAlJw5fZYAXPjqWxVYZXlFUA7WUHP8b6C g9oA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700322702; x=1700927502; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=tg2dWT1RhFKme6yZPEIj44E7/nsRkUJhN3arViZI26M=; b=IPF8W04ewcwO6sh9e9zownO9utMV4G+4Rp+V8FnsWAjJjgOkX/MduzvDBqT8VsypF1 LwQK+k9i9kQ4WqTC/b4JA4vPSqYnWFOUNZchwrcjug5OGpZ/v4sgAb+z5oV/ovuyj5l6 PFULSZ6ZAcUWw70Xt9c0DWPDM2OyCrQ1IxhBOYgXPfN8svR51mZ3Un5YaXD9oQNrzKAV YJV3j5rRmlhW3mkqj2p6TIeIU7K4k6USgM7VORprI9nczBaJXVht7YT/lt52XBNVt8FA NuETR2X3WuXonV5MocpaK9n5Arw42p4jZPPDYnetHniHOR9TkVZLo0vGZ1DFkyQGi073 3ajA== X-Gm-Message-State: AOJu0Yz2C07TxMPEY/hOyRwClLk8XJH2DDPdQzIhGUiRGtcuOwNNFi3I IpVgxBjnchWtS60v3ruwZH2BA1uz1zw+cBmi X-Google-Smtp-Source: AGHT+IGLuAbyQuCwpl0+dEJc+LhpBrFdlR1P42AFwq9mnuU8O7NnAWExHW0iaDQvbV6DL8BBzMuPMg== X-Received: by 2002:a25:ad4f:0:b0:daf:7702:fd60 with SMTP id l15-20020a25ad4f000000b00daf7702fd60mr1666669ybe.1.1700322702147; Sat, 18 Nov 2023 07:51:42 -0800 (PST) Received: from localhost ([2601:344:8301:57f0:48a9:bd4c:868d:dc97]) by smtp.gmail.com with ESMTPSA id i10-20020a05690200ca00b00daf198ef6fbsm964572ybs.21.2023.11.18.07.51.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Nov 2023 07:51:41 -0800 (PST) From: Yury Norov To: linux-kernel@vger.kernel.org, Jens Axboe , Damien Le Moal , Chaitanya Kulkarni , Ming Lei , Johannes Thumshirn , Chengming Zhou , Nitesh Shetty , Akinobu Mita , "Shin'ichiro Kawasaki" , Yury Norov , linux-block@vger.kernel.org Cc: Jan Kara , Mirsad Todorovac , Matthew Wilcox , Rasmus Villemoes , Andy Shevchenko , Maxim Kuvyrkov , Alexey Klimov Subject: [PATCH 22/34] block: null_blk: fix opencoded find_and_set_bit() in get_tag() Date: Sat, 18 Nov 2023 07:50:53 -0800 Message-Id: <20231118155105.25678-23-yury.norov@gmail.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20231118155105.25678-1-yury.norov@gmail.com> References: <20231118155105.25678-1-yury.norov@gmail.com> Precedence: bulk X-Mailing-List: linux-block@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 get_tag() opencodes find_and_set_bit(). Switch the code to use the dedicated function, and get rid of get_tag entirely. Signed-off-by: Yury Norov --- drivers/block/null_blk/main.c | 41 +++++++++++------------------------ 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 22a3cf7f32e2..a41d146663e1 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -760,19 +760,6 @@ static void put_tag(struct nullb_queue *nq, unsigned int tag) wake_up(&nq->wait); } -static unsigned int get_tag(struct nullb_queue *nq) -{ - unsigned int tag; - - do { - tag = find_first_zero_bit(nq->tag_map, nq->queue_depth); - if (tag >= nq->queue_depth) - return -1U; - } while (test_and_set_bit_lock(tag, nq->tag_map)); - - return tag; -} - static void free_cmd(struct nullb_cmd *cmd) { put_tag(cmd->nq, cmd->tag); @@ -782,24 +769,22 @@ static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer); static struct nullb_cmd *__alloc_cmd(struct nullb_queue *nq) { + unsigned int tag = find_and_set_bit_lock(nq->tag_map, nq->queue_depth); struct nullb_cmd *cmd; - unsigned int tag; - - tag = get_tag(nq); - if (tag != -1U) { - cmd = &nq->cmds[tag]; - cmd->tag = tag; - cmd->error = BLK_STS_OK; - cmd->nq = nq; - if (nq->dev->irqmode == NULL_IRQ_TIMER) { - hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - cmd->timer.function = null_cmd_timer_expired; - } - return cmd; + + if (tag >= nq->queue_depth) + return NULL; + + cmd = &nq->cmds[tag]; + cmd->tag = tag; + cmd->error = BLK_STS_OK; + cmd->nq = nq; + if (nq->dev->irqmode == NULL_IRQ_TIMER) { + hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cmd->timer.function = null_cmd_timer_expired; } - return NULL; + return cmd; } static struct nullb_cmd *alloc_cmd(struct nullb_queue *nq, struct bio *bio)