summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2016-01-05 08:49:21 -0900
committerKent Overstreet <kent.overstreet@gmail.com>2016-10-07 12:35:16 -0800
commit5dd51f84fe815cb30620322d758650a6fb973eab (patch)
tree4ae31cbaae6b159756c38c77f505297ae6aedb13
parenta7970d6feb8d1ee7d016dea43a7280db3754bae6 (diff)
bcache: Make auxiliary search trees work on big endian
-rw-r--r--drivers/md/bcache/bkey.c12
-rw-r--r--drivers/md/bcache/bset.c77
2 files changed, 59 insertions, 30 deletions
diff --git a/drivers/md/bcache/bkey.c b/drivers/md/bcache/bkey.c
index 2f512fa5155c..9a3a008efb74 100644
--- a/drivers/md/bcache/bkey.c
+++ b/drivers/md/bcache/bkey.c
@@ -633,7 +633,10 @@ const char *bch_bkey_format_validate(struct bkey_format *f)
return NULL;
}
-/* Most significant differing bit */
+/*
+ * Most significant differing bit
+ * Bits are indexed from 0 - return is [0, nr_key_bits)
+ */
unsigned bkey_greatest_differing_bit(const struct bkey_format *format,
const struct bkey_packed *l_k,
const struct bkey_packed *r_k)
@@ -644,7 +647,6 @@ unsigned bkey_greatest_differing_bit(const struct bkey_format *format,
u64 l_v, r_v;
/* for big endian, skip past header */
- nr_key_bits += high_bit_offset;
l_v = *l & (~0ULL >> high_bit_offset);
r_v = *r & (~0ULL >> high_bit_offset);
@@ -658,7 +660,7 @@ unsigned bkey_greatest_differing_bit(const struct bkey_format *format,
}
if (l_v != r_v)
- return fls64(l_v ^ r_v) + nr_key_bits;
+ return fls64(l_v ^ r_v) - 1 + nr_key_bits;
if (!nr_key_bits)
return 0;
@@ -671,6 +673,10 @@ unsigned bkey_greatest_differing_bit(const struct bkey_format *format,
}
}
+/*
+ * First set bit
+ * Bits are indexed from 0 - return is [0, nr_key_bits)
+ */
unsigned bkey_ffs(const struct bkey_format *format,
const struct bkey_packed *k)
{
diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c
index bbf78cfad73e..646f2347ae07 100644
--- a/drivers/md/bcache/bset.c
+++ b/drivers/md/bcache/bset.c
@@ -449,14 +449,24 @@ static struct bkey_packed *table_to_bkey(struct bset_tree *t,
static inline unsigned bfloat_mantissa(const struct bkey_packed *k,
const struct bkey_float *f)
{
- u64 *ptr;
+ u64 v;
EBUG_ON(!bkey_packed(k));
- ptr = (u64 *) (((u32 *) k->_data) + (f->exponent >> 5));
+ v = get_unaligned((u64 *) (((u32 *) k->_data) + (f->exponent >> 5)));
- return (get_unaligned(ptr) >> (f->exponent & 31)) &
- BKEY_MANTISSA_MASK;
+ /*
+ * In little endian, we're shifting off low bits (and then the bits we
+ * want are at the low end), in big endian we're shifting off high bits
+ * (and then the bits we want are at the high end, so we shift them
+ * back down):
+ */
+#ifdef __LITTLE_ENDIAN
+ v >>= f->exponent & 31;
+#else
+ v >>= 64 - BKEY_MANTISSA_BITS - (f->exponent & 31);
+#endif
+ return v & BKEY_MANTISSA_MASK;
}
static void make_bfloat(struct bkey_format *format,
@@ -473,8 +483,7 @@ static void make_bfloat(struct bkey_format *format,
struct bkey_packed *r = is_power_of_2(j + 1)
? bset_bkey_idx(t->data, t->data->u64s - t->end.u64s)
: tree_to_bkey(t, j >> (ffz(j) + 1));
- unsigned exponent, shift, extra = 0, key_bits_start =
- format->key_u64s * 64 - bkey_format_key_bits(format);
+ int shift, exponent;
EBUG_ON(m < l || m > r);
EBUG_ON(bkey_next(p) != m);
@@ -490,33 +499,48 @@ static void make_bfloat(struct bkey_format *format,
return;
}
- exponent = max_t(int, bkey_greatest_differing_bit(format, l, r) -
- BKEY_MANTISSA_BITS + 1, 0);
+ /*
+ * The greatest differing bit of l and r is the first bit we must
+ * include in the bfloat mantissa we're creating in order to do
+ * comparisons - that bit always becomes the high bit of
+ * bfloat->mantissa, and thus the exponent we're calculating here is
+ * the position of what will become the low bit in bfloat->mantissa:
+ *
+ * Note that this may be negative - we may be running off the low end
+ * of the key: we handle this later:
+ */
+ exponent = (int) bkey_greatest_differing_bit(format, l, r) -
+ (BKEY_MANTISSA_BITS - 1);
+ /*
+ * Then we calculate the actual shift value, from the start of the key
+ * (k->_data), to get the key bits starting at exponent:
+ */
#ifdef __LITTLE_ENDIAN
- shift = key_bits_start + exponent;
+ shift = (int) (format->key_u64s * 64 -
+ bkey_format_key_bits(format)) +
+ exponent;
+
+ EBUG_ON(shift + BKEY_MANTISSA_BITS > format->key_u64s * 64);
+#else
+ shift = high_bit_offset +
+ bkey_format_key_bits(format) -
+ exponent -
+ BKEY_MANTISSA_BITS;
+
+ EBUG_ON(shift < KEY_PACKED_BITS_START);
#endif
- EBUG_ON(shift >= BFLOAT_FAILED);
+ EBUG_ON(shift < 0 || shift >= BFLOAT_FAILED);
- /*
- * There might be fewer key bits than BKEY_MANTISSA_BITS:
- * bfloat_mantissa() is in the fast path so it doesn't check for this -
- * it's going to return some garbage bits we don't want.
- *
- * So firstly, ensure that the garbage bits are the least significant
- * bits:
- */
- if (shift > format->key_u64s * 64 - BKEY_MANTISSA_BITS) {
- shift = format->key_u64s * 64 - BKEY_MANTISSA_BITS;
- extra = key_bits_start - shift;
- }
+ f->exponent = shift;
+ f->mantissa = bfloat_mantissa(m, f);
/*
* If we've got garbage bits, set them to all 1s - it's legal for the
* bfloat to compare larger than the original key, but not smaller:
*/
- f->exponent = shift;
- f->mantissa = bfloat_mantissa(m, f) | ~(~0U << extra);
+ if (exponent < 0)
+ f->mantissa |= ~(~0U << -exponent);
/*
* The bfloat must be able to tell its key apart from the previous key -
@@ -524,7 +548,7 @@ static void make_bfloat(struct bkey_format *format,
* flag as failed - unless the keys are actually equal, in which case
* we aren't required to return a specific one:
*/
- if (shift > key_bits_start &&
+ if (exponent > 0 &&
f->mantissa == bfloat_mantissa(p, f) &&
bkey_cmp_packed(format, p, m)) {
f->exponent = BFLOAT_FAILED_PREV;
@@ -536,8 +560,7 @@ static void make_bfloat(struct bkey_format *format,
* the comparison in bset_search_tree. If we're dropping set bits,
* increment it:
*/
- if (shift > key_bits_start &&
- shift > key_bits_start + bkey_ffs(format, m)) {
+ if (exponent > (int) bkey_ffs(format, m)) {
if (f->mantissa == BKEY_MANTISSA_MASK)
f->exponent = BFLOAT_FAILED_OVERFLOW;