summaryrefslogtreecommitdiff
path: root/c_src
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2025-08-28 13:39:31 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2025-08-28 13:40:49 -0400
commit77ca58e3a7c4e60bc0713be2e048e8ca04a36ed1 (patch)
tree4c7a84ea579d0587488f171c290fc8cf50d0465d /c_src
parent34f9eeba1df78d94f862f268e9d05101ad1dea17 (diff)
posix_to_bcachefs.c: Use rhashtable for tracking hardlinks
bcachefs uses 64 bit inode numbers by default, which overflow genradix keys when we multiply the inode number by the object size for the actual genradix lookup. Switch to a rhashtable, and only index files that actually have hardlinks. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'c_src')
-rw-r--r--c_src/posix_to_bcachefs.c44
-rw-r--r--c_src/posix_to_bcachefs.h2
2 files changed, 37 insertions, 9 deletions
diff --git a/c_src/posix_to_bcachefs.c b/c_src/posix_to_bcachefs.c
index ca44db32..e80a3564 100644
--- a/c_src/posix_to_bcachefs.c
+++ b/c_src/posix_to_bcachefs.c
@@ -14,6 +14,18 @@
#include "libbcachefs/str_hash.h"
#include "libbcachefs/xattr.h"
+struct hardlink {
+ struct rhash_head hash;
+ u64 src, dst;
+};
+
+static const struct rhashtable_params hardlink_params = {
+ .head_offset = offsetof(struct hardlink, hash),
+ .key_offset = offsetof(struct hardlink, src),
+ .key_len = sizeof(u64),
+ .automatic_shrinking = true,
+};
+
static int unlink_and_rm(struct bch_fs *c,
subvol_inum dir_inum,
struct bch_inode_unpacked *dir,
@@ -747,15 +759,24 @@ static int copy_dir(struct bch_fs *c,
if (s->type == BCH_MIGRATE_migrate && stat.st_dev != s->dev)
die("%s does not have correct st_dev!", child_path);
- u64 *dst_inum_p = S_ISREG(stat.st_mode)
- ? genradix_ptr_alloc(&s->hardlinks, stat.st_ino, GFP_KERNEL)
- : NULL;
+ struct hardlink *h = NULL;
+ if (S_ISREG(stat.st_mode) && stat.st_nlink > 1) {
+ u64 ino = stat.st_ino;
+ h = rhashtable_lookup_fast(&s->hardlinks, &ino, hardlink_params);
+ if (!h) {
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h->src = ino;
+ int ret = rhashtable_lookup_insert_fast(&s->hardlinks, &h->hash,
+ hardlink_params);
+ BUG_ON(ret);
+ }
+ }
subvol_inum dst_dir_inum = { 1, dst->bi_inum };
- if (dst_inum_p && *dst_inum_p) {
+ if (h && h->dst) {
ret = create_or_update_link(c, dst_dir_inum, dst, d->d_name,
- (subvol_inum) { 1, *dst_inum_p }, S_IFREG);
+ (subvol_inum) { 1, h->dst}, S_IFREG);
if (ret)
goto err;
goto next;
@@ -767,8 +788,8 @@ static int copy_dir(struct bch_fs *c,
subvol_inum dst_child_inum = { 1, inode.bi_inum };
- if (dst_inum_p)
- *dst_inum_p = inode.bi_inum;
+ if (h)
+ h->dst = inode.bi_inum;
copy_xattrs(c, &inode, d->d_name);
@@ -857,6 +878,8 @@ int copy_fs(struct bch_fs *c, struct copy_fs_state *s,
if (s->type == BCH_MIGRATE_migrate)
syncfs(src_fd);
+ BUG_ON(rhashtable_init(&s->hardlinks, &hardlink_params));
+
struct bch_inode_unpacked root_inode;
int ret = bch2_inode_find_by_inum(c, (subvol_inum) { 1, BCACHEFS_ROOT_INO },
&root_inode);
@@ -883,7 +906,12 @@ int copy_fs(struct bch_fs *c, struct copy_fs_state *s,
update_inode(c, &root_inode);
darray_exit(&s->extents);
- genradix_free(&s->hardlinks);
+ /*
+ * We're currently leaking s->hardlinks: we want to convert this back to
+ * a radix tree, when we have a radix tree that supports real 64 bit
+ * integer keys
+ */
+ //genradix_free(&s->hardlinks);
CLASS(printbuf, buf)();
printbuf_tabstop_push(&buf, 24);
diff --git a/c_src/posix_to_bcachefs.h b/c_src/posix_to_bcachefs.h
index 3fc586d8..65e5de1f 100644
--- a/c_src/posix_to_bcachefs.h
+++ b/c_src/posix_to_bcachefs.h
@@ -33,7 +33,7 @@ struct copy_fs_state {
u64 bcachefs_inum;
dev_t dev;
- GENRADIX(u64) hardlinks;
+ struct rhashtable hardlinks;
ranges extents;
enum bch_migrate_type type;
unsigned verbosity;