summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Ashton <joshua@froggi.es>2023-08-12 22:26:30 +0100
committerKent Overstreet <kent.overstreet@linux.dev>2023-08-14 12:28:45 -0400
commitc36a89a86cfc78de91394fe019ea8218c0bdbcf0 (patch)
treea6c796962a2d7fa583140af11188cce0c5798f68
parent1a9a7ac80d2af2fd44b52a7e4236b31eba3e3e28 (diff)
bcachefs: Optimize bch2_dirent_name_bytes
Avoids doing a full strnlen for getting the length of the name of a dirent entry. Given the fact that the name of dirents is stored at the end of the bkey's value, and we know the length of that in u64s, we can find the last u64 and figure out how many NUL bytes are at the end of the string. On little endian systems this ends up being the leading zeros of the last u64, whereas on big endian systems this ends up being the trailing zeros of the last u64. We can take that value in bits and divide it by 8 to get the number of NUL bytes at the end. There is no endian-fixup or other compatibility here as this is string data interpreted as a u64. Signed-off-by: Joshua Ashton <joshua@froggi.es>
-rw-r--r--fs/bcachefs/dirent.c21
1 files changed, 17 insertions, 4 deletions
diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c
index a87c4e5f089d..6f9eb88c7dba 100644
--- a/fs/bcachefs/dirent.c
+++ b/fs/bcachefs/dirent.c
@@ -15,10 +15,18 @@
static unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d)
{
- unsigned len = bkey_val_bytes(d.k) -
- offsetof(struct bch_dirent, d_name);
-
- return strnlen(d.v->d_name, len);
+ unsigned bkey_u64s = bkey_val_u64s(d.k);
+ unsigned bkey_bytes = bkey_u64s * sizeof(u64);
+ u64 last_u64 = ((u64*)d.v)[bkey_u64s - 1];
+#if CPU_BIG_ENDIAN
+ unsigned trailing_nuls = last_u64 ? __builtin_ctzll(last_u64) / 8 : 64 / 8;
+#else
+ unsigned trailing_nuls = last_u64 ? __builtin_clzll(last_u64) / 8 : 64 / 8;
+#endif
+
+ return bkey_bytes -
+ offsetof(struct bch_dirent, d_name) -
+ trailing_nuls;
}
struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent d)
@@ -113,6 +121,11 @@ int bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k,
return -BCH_ERR_invalid_bkey;
}
+ if (d_name.len != strnlen(d_name.name, d_name.len)) {
+ prt_printf(err, "dirent has stray data after name's NUL");
+ return -BCH_ERR_invalid_bkey;
+ }
+
if (d_name.len == 1 && !memcmp(d_name.name, ".", 1)) {
prt_printf(err, "invalid name");
return -BCH_ERR_invalid_bkey;