Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754840AbdLOCdt (ORCPT ); Thu, 14 Dec 2017 21:33:49 -0500 Received: from zeniv.linux.org.uk ([195.92.253.2]:55472 "EHLO ZenIV.linux.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754594AbdLOCds (ORCPT ); Thu, 14 Dec 2017 21:33:48 -0500 Date: Fri, 15 Dec 2017 02:33:43 +0000 From: Al Viro To: Jakub Kicinski Cc: Linus Torvalds , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC][PATCH] Add primitives for manipulating bitfields both in host- and fixed-endian. Message-ID: <20171215023343.GG21978@ZenIV.linux.org.uk> References: <20171212234856.GZ21978@ZenIV.linux.org.uk> <20171212155933.03c88eab@cakuba.netronome.com> <20171213003659.GA21978@ZenIV.linux.org.uk> <20171212170437.4b129e50@cakuba.netronome.com> <20171213013056.GB21978@ZenIV.linux.org.uk> <20171212173528.340cd002@cakuba.netronome.com> <20171213015125.GC21978@ZenIV.linux.org.uk> <20171212184400.13b27cf8@cakuba.netronome.com> <20171213142212.GD21978@ZenIV.linux.org.uk> <20171213174554.GE21978@ZenIV.linux.org.uk> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20171213174554.GE21978@ZenIV.linux.org.uk> User-Agent: Mutt/1.9.0 (2017-09-02) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 3761 Lines: 101 The following primitives are defined in linux/bitfield.h: * u32 le32_get_bits(__le32 val, u32 field) extracts the contents of the bitfield specified by @field in little-endian 32bit value @val and converts it to host-endian. * void le32p_replace_bits(__le32 *p, u32 v, u32 field) replaces the contents of the bitfield specified by @field in little-endian 32bit object pointet to by *p with the value of @v. New value is given in host-endian and stored as little-endian. * __le32 le32_replace_bits(__le32 old, u32 v, u32 field) is equivalent to ({__le32 tmp = old; le32p_replace_bits(&old, v, field); tmp;}) In other words, instead of modifying an object in memory, it takes the initial value and returns the modified one. Such set of helpers is defined for each of little-, big- and host-endian types; e.g. u64_get_bits(val, field) will return the contents of the bitfield specified by @field in host-endian 64bit value @val, etc. Of course, for host-endian no conversion is involved. Fields to access are specified as GENMASK() values - an N-bit field starting at bit #M is encoded as GENMASK(M + N - 1, N). Note that bit numbers refer to endianness of the object we are working with - e.g. GENMASK(11, 0) in __be16 refers to the second byte and the lower 4 bits of the first byte. In __le16 it would refer to the first byte and the lower 4 bits of the second byte, etc. Field specification must be a constant; __builtin_constant_p() doesn't have to be true for it, but compiler must be able to evaluate it at build time. If it cannot or if the value does not encode any bitfield, the build will fail. If the value being stored in ..._replace_bits() is a constant that does not fit into bitfield, a warning will be generated at compile time. Signed-off-by: Al Viro diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h index 1030651f8309..4b4f0531a79c 100644 --- a/include/linux/bitfield.h +++ b/include/linux/bitfield.h @@ -16,6 +16,7 @@ #define _LINUX_BITFIELD_H #include +#include /* * Bitfield access macros @@ -103,4 +104,50 @@ (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ }) +extern void __compiletime_warning("value doesn't fit into mask") +__field_overflow(void); +extern void __compiletime_error("bad bitfield mask") +__bad_mask(void); +static __always_inline u64 mask_to_multiplier(u64 mask) +{ + if ((mask | (mask - 1)) & ((mask | (mask - 1)) + 1)) + __bad_mask(); + return mask & -mask; +} + +#define ____MAKE_OP(type,base,to,from) \ +static __always_inline __##type type##_replace_bits(__##type old, \ + base val, base field) \ +{ \ + __##type m = to(field); \ + if (__builtin_constant_p(val) && \ + (val & ~(field/mask_to_multiplier(field)))) \ + __field_overflow(); \ + return (old & ~m) | \ + (to(val * mask_to_multiplier(field)) & m); \ +} \ +static __always_inline void type##p_replace_bits(__##type *p, \ + base val, base field) \ +{ \ + __##type m = to(field); \ + if (__builtin_constant_p(val) && \ + (val & ~(field/mask_to_multiplier(field)))) \ + __field_overflow(); \ + *p = (*p & ~m) | \ + (to(val * mask_to_multiplier(field)) & m); \ +} \ +static __always_inline base type##_get_bits(__##type v, base field) \ +{ \ + return (from(v) & field)/mask_to_multiplier(field); \ +} +#define __MAKE_OP(size) \ + ____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \ + ____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \ + ____MAKE_OP(u##size,u##size,,) +__MAKE_OP(16) +__MAKE_OP(32) +__MAKE_OP(64) +#undef __MAKE_OP +#undef ____MAKE_OP + #endif