Received: by 2002:a25:c205:0:0:0:0:0 with SMTP id s5csp7283380ybf; Fri, 6 Mar 2020 14:15:36 -0800 (PST) X-Google-Smtp-Source: ADFU+vs/nbizAuk0X3+YxJfl3026rS9fIo0x0mv88+8kv/+088IcLxi9D3eSgr4bAkEV2dht6R1z X-Received: by 2002:aca:ded4:: with SMTP id v203mr2018172oig.19.1583532936778; Fri, 06 Mar 2020 14:15:36 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1583532936; cv=none; d=google.com; s=arc-20160816; b=ovZlOSi4EZtXXm/wJ9U9r0YnpAKrlJ3IzHRDw7taJq/UQrkc2805YYqCW0aS4E9xuW rhtXFi5cApYGfs5D6z0gcITpE4XjL3NXgLPz23Ano69W1dfkta5maGN5Nwu4D87Cj388 3aoqRNGlcMwYpG2hNniDCeNrH2U0qTZV9Af5VkEdNF/XDCNeHMGQEOLrVXIPxtCS5k7o v3dqOXH7xv31DYzqpZOVWZQL2cgvVhrsjSn05x/cWK90Dd2uaf18xvpz1ciWy2LI74R4 kWqsuOXaGIPiOjgj894ljUDb0fkKtugkuaWrVvFLC7jlKkAs2sN3GbEiW3m3xnaFHpXQ 6ERg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=g9A8bb6Dd4q1KloLSMciak2cHd7VLsJhpyC5tpVoZ/I=; b=MPQYKv1BNDDC69gRSdGryg8z3oGFOB8kXJKViqR8SjFGO+rTVvVYz8AELZShuH0PSa XlEraVDQMMqOuO7FNC/MchSRnB5eUe+6PnrpLUNlnQCHyj9MnO/Haxxpz2xPQtUfRzAM bwv7wFO7PsBwcDGMzUNp830uqVi0TRhlBCCWAAtwIpP4vQ3DjJKQINNN7C3/LPZjNNBA VXxXttebe20nGf69bmBKBvgR+WolUQFM0vJS9prx9HvBR6dJPoJtWOSK2UAN8nSXHF6E 9oKdOUCZElu0iaHaADNW4Cv69lIlQASJAjtsHAHWDjjC8+qSpm4GTJAjslQX7nMaPSnY tQKw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=DsNGbxp3; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id h10si375648oie.63.2020.03.06.14.15.25; Fri, 06 Mar 2020 14:15:36 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=DsNGbxp3; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726859AbgCFWOz (ORCPT + 99 others); Fri, 6 Mar 2020 17:14:55 -0500 Received: from mail-lj1-f193.google.com ([209.85.208.193]:38908 "EHLO mail-lj1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726090AbgCFWOy (ORCPT ); Fri, 6 Mar 2020 17:14:54 -0500 Received: by mail-lj1-f193.google.com with SMTP id w1so3824887ljh.5 for ; Fri, 06 Mar 2020 14:14:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=g9A8bb6Dd4q1KloLSMciak2cHd7VLsJhpyC5tpVoZ/I=; b=DsNGbxp3EvIRQ1+Cjbx86npj3Tmdldis3gBYdtgBzEthjzfwXJ8cXLU4vnaCPeUUJN amzAcFKVSnzPE6U5TrEDnZ9EQv56URYXoRHqwPOcqtB/GhdAFWQhNsrnHbnM4NRaORHs nK4RU1o5Rn9HJGjekObGoF4MwRGe5LJ2+fRonC+J4kATT3CvGXO8NaLssYZGWfBAExIl P1Gm3eR8utAnnxpSJiGOtLct3fxb0LxuMAL3XKaot4rEFOhg737r394IFhtJzRevnafs vUWHE0sPeAd1IHj7n00pW/Gnb2jGf0DCq+pVNcHgdl+1Km+NBmukuOiHWvKpcVUbxmjo f79A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=g9A8bb6Dd4q1KloLSMciak2cHd7VLsJhpyC5tpVoZ/I=; b=Vo7i1UAH2VkKJH9C7Jgz3PBiUsZAisS7wvVZ+MTu762dgUtMhcO6I3nAhLO+n96Jta 7kvGVESACzEccDylZR4x36a5s3YJ6RLvQ4JSjMfWoW+9JY82s/FqIbyh0+OHgV8BFm2u q97dARLvntgow7jFtF0wTaZCeltSX9oQ8G7gg752uRcRBTw98NYui1rJ6Vqj2jnq7xlH 1DnVSrXPvtr4dChyxWb3Zf/xwErOa9/+yPS2CJXhQs57Fw/EvvkfIauwrcW8w7wMbXxR fj8hJPTKY7+hMexq5K9z0ejnKSh6gHAht0UI1VgJ7Y0vaiENOxgRYEABR8kP/iZY9Ymb nYkA== X-Gm-Message-State: ANhLgQ3tB7ydA5MzXo89ykr9xJK8jAFGNC59REQPVdORFwFmDYMAwdQG +e/C5Iz3TcvVmLt0FI3BnRlQM9K8 X-Received: by 2002:a2e:b246:: with SMTP id n6mr3295601ljm.70.1583532891947; Fri, 06 Mar 2020 14:14:51 -0800 (PST) Received: from localhost.localdomain ([46.159.134.13]) by smtp.gmail.com with ESMTPSA id s17sm14333158lfs.6.2020.03.06.14.14.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Mar 2020 14:14:51 -0800 (PST) From: Yury Norov To: Stefano Brivio Cc: Yury Norov , Pablo Neira Ayuso , Andrew Morton , Andy Shevchenko , Rasmus Villemoes , linux-kernel@vger.kernel.org Subject: [PATCH] lib/bitmap: rework bitmap_cut() Date: Fri, 6 Mar 2020 14:14:23 -0800 Message-Id: <20200306221423.18631-1-yury.norov@gmail.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org bitmap_cut() refers src after memmove(). If dst and src overlap, it may cause buggy behaviour because src may become inconsistent. The function complexity is of O(nbits * cut_bits), which can be improved to O(nbits). We can also rely on bitmap_shift_right() to do most of the work. I don't like interface of bitmap_cut(). The idea of copying of a whole bitmap inside the function from src to dst doesn't look useful in practice. The function is introduced a few weeks ago and was most probably inspired by bitmap_shift_*. Looking at the code, it's easy to see that bitmap_shift_* is usually passed with dst == src. bitmap_cut() has a single user so far, and it also calls it with dst == src. This patch removes dst so the whole work is made in-place. It also switches the function to bitmap_shift_right() internally in sake of performance and simplicity. I've added a very basic test too. Signed-off-by: Yury Norov --- include/linux/bitmap.h | 7 ++--- lib/bitmap.c | 51 +++++++++------------------------- lib/test_bitmap.c | 32 +++++++++++++++++++++ net/netfilter/nft_set_pipapo.c | 3 +- 4 files changed, 49 insertions(+), 44 deletions(-) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 99058eb81042..ed60b7272437 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -59,7 +59,7 @@ * Iterate over all set regions * bitmap_shift_right(dst, src, n, nbits) *dst = *src >> n * bitmap_shift_left(dst, src, n, nbits) *dst = *src << n - * bitmap_cut(dst, src, first, n, nbits) Cut n bits from first, copy rest + * bitmap_cut(bmap, first, n, nbits) Cut n bits from first, copy rest * bitmap_replace(dst, old, new, mask, nbits) *dst = (*old & ~(*mask)) | (*new & *mask) * bitmap_remap(dst, src, old, new, nbits) *dst = map(old, new)(src) * bitmap_bitremap(oldbit, old, new, nbits) newbit = map(old, new)(oldbit) @@ -140,9 +140,8 @@ extern void __bitmap_shift_right(unsigned long *dst, const unsigned long *src, unsigned int shift, unsigned int nbits); extern void __bitmap_shift_left(unsigned long *dst, const unsigned long *src, unsigned int shift, unsigned int nbits); -extern void bitmap_cut(unsigned long *dst, const unsigned long *src, - unsigned int first, unsigned int cut, - unsigned int nbits); +extern void bitmap_cut(unsigned long *bmap, unsigned int first, + unsigned int cut, unsigned int nbits); extern int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, const unsigned long *bitmap2, unsigned int nbits); extern void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, diff --git a/lib/bitmap.c b/lib/bitmap.c index 89260aa342d6..06e06e0c3096 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -170,67 +170,42 @@ EXPORT_SYMBOL(__bitmap_shift_left); /** * bitmap_cut() - remove bit region from bitmap and right shift remaining bits - * @dst: destination bitmap, might overlap with src - * @src: source bitmap + * @bmap: bitmap to cut * @first: start bit of region to be removed * @cut: number of bits to remove * @nbits: bitmap size, in bits * - * Set the n-th bit of @dst iff the n-th bit of @src is set and - * n is less than @first, or the m-th bit of @src is set for any - * m such that @first <= n < nbits, and m = n + @cut. - * * In pictures, example for a big-endian 32-bit architecture: * - * @src: + * @bmap: * 31 63 * | | * 10000000 11000001 11110010 00010101 10000000 11000001 01110010 00010101 * | | | | * 16 14 0 32 * - * if @cut is 3, and @first is 14, bits 14-16 in @src are cut and @dst is: + * if @cut is 3, and @first is 14, bits 14-16 in @bmap are cut and @dst is: * * 31 63 * | | * 10110000 00011000 00110010 00010101 00010000 00011000 00101110 01000010 * | | | * 14 (bit 17 0 32 - * from @src) - * - * Note that @dst and @src might overlap partially or entirely. - * - * This is implemented in the obvious way, with a shift and carry - * step for each moved bit. Optimisation is left as an exercise - * for the compiler. + * from @bmap) */ -void bitmap_cut(unsigned long *dst, const unsigned long *src, - unsigned int first, unsigned int cut, unsigned int nbits) +void bitmap_cut(unsigned long *bmap, unsigned int first, + unsigned int cut, unsigned int nbits) { - unsigned int len = BITS_TO_LONGS(nbits); - unsigned long keep = 0, carry; - int i; - - memmove(dst, src, len * sizeof(*dst)); - - if (first % BITS_PER_LONG) { - keep = src[first / BITS_PER_LONG] & - (~0UL >> (BITS_PER_LONG - first % BITS_PER_LONG)); - } + unsigned long tmp; + unsigned long *b = bmap + first / BITS_PER_LONG; - while (cut--) { - for (i = first / BITS_PER_LONG; i < len; i++) { - if (i < len - 1) - carry = dst[i + 1] & 1UL; - else - carry = 0; + if (first % BITS_PER_LONG) + tmp = b[0] & BITMAP_LAST_WORD_MASK(first); - dst[i] = (dst[i] >> 1) | (carry << (BITS_PER_LONG - 1)); - } - } + bitmap_shift_right(b, b, cut - first, nbits - first); - dst[first / BITS_PER_LONG] &= ~0UL << (first % BITS_PER_LONG); - dst[first / BITS_PER_LONG] |= keep; + if (first % BITS_PER_LONG) + b[0] = tmp | (b[0] & BITMAP_FIRST_WORD_MASK(first)); } EXPORT_SYMBOL(bitmap_cut); diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index 6b13150667f5..4b2fef13003d 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -540,6 +540,37 @@ static void __init test_bitmap_arr32(void) } } +struct test_bitmap_cut { + unsigned int first; + unsigned int last; + unsigned int nbits; + unsigned long in; + unsigned long out; +}; + +static struct test_bitmap_cut test_cut[] = { + { 0, 0, BITS_PER_LONG, 0xdeadbeefUL, 0xdeadbeefUL }, + { 0, 8, BITS_PER_LONG, 0xdeadbeefUL, 0xdeadbeUL }, + { 4, 8, BITS_PER_LONG, 0xdeadbeefUL, 0xdeadbefUL }, + { 8, 24, BITS_PER_LONG, 0xdeadbeefUL, 0xdeefUL }, + { 16, 32, BITS_PER_LONG, 0xdeadbeefUL, 0xbeefUL }, +}; + +static void __init test_bitmap_cut(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_cut); i++) { + struct test_bitmap_cut *t = &test_cut[i]; + + bitmap_cut(&t->in, t->first, t->last, t->nbits); + if (!bitmap_equal(&t->in, &t->out, t->nbits)) { + pr_err("bitmap_cut #%d failed:\n%*pb.\nExpected:\n%*pb\n", + i, t->nbits, &t->in, t->nbits, &t->out); + } + } +} + static void noinline __init test_mem_optimisations(void) { DECLARE_BITMAP(bmap1, 1024); @@ -617,6 +648,7 @@ static void __init selftest(void) test_copy(); test_replace(); test_bitmap_arr32(); + test_bitmap_cut(); test_bitmap_parse(); test_bitmap_parse_user(); test_bitmap_parselist(); diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 26395c8188b1..68faaf9a4f66 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -1397,8 +1397,7 @@ static void pipapo_drop(struct nft_pipapo_match *m, pos = f->lt + g * NFT_PIPAPO_BUCKETS * f->bsize; for (b = 0; b < NFT_PIPAPO_BUCKETS; b++) { - bitmap_cut(pos, pos, rulemap[i].to, - rulemap[i].n, + bitmap_cut(pos, rulemap[i].to, rulemap[i].n, f->bsize * BITS_PER_LONG); pos += f->bsize; -- 2.20.1