Merge tag 'for-next-6.9' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/krisma...
[linux-2.6-microblaze.git] / fs / libfs.c
index c297953..78c71a9 100644 (file)
@@ -240,17 +240,22 @@ const struct inode_operations simple_dir_inode_operations = {
 };
 EXPORT_SYMBOL(simple_dir_inode_operations);
 
-static void offset_set(struct dentry *dentry, u32 offset)
+/* 0 is '.', 1 is '..', so always start with offset 2 or more */
+enum {
+       DIR_OFFSET_MIN  = 2,
+};
+
+static void offset_set(struct dentry *dentry, long offset)
 {
-       dentry->d_fsdata = (void *)((uintptr_t)(offset));
+       dentry->d_fsdata = (void *)offset;
 }
 
-static u32 dentry2offset(struct dentry *dentry)
+static long dentry2offset(struct dentry *dentry)
 {
-       return (u32)((uintptr_t)(dentry->d_fsdata));
+       return (long)dentry->d_fsdata;
 }
 
-static struct lock_class_key simple_offset_xa_lock;
+static struct lock_class_key simple_offset_lock_class;
 
 /**
  * simple_offset_init - initialize an offset_ctx
@@ -259,11 +264,9 @@ static struct lock_class_key simple_offset_xa_lock;
  */
 void simple_offset_init(struct offset_ctx *octx)
 {
-       xa_init_flags(&octx->xa, XA_FLAGS_ALLOC1);
-       lockdep_set_class(&octx->xa.xa_lock, &simple_offset_xa_lock);
-
-       /* 0 is '.', 1 is '..', so always start with offset 2 */
-       octx->next_offset = 2;
+       mt_init_flags(&octx->mt, MT_FLAGS_ALLOC_RANGE);
+       lockdep_set_class(&octx->mt.ma_lock, &simple_offset_lock_class);
+       octx->next_offset = DIR_OFFSET_MIN;
 }
 
 /**
@@ -271,20 +274,19 @@ void simple_offset_init(struct offset_ctx *octx)
  * @octx: directory offset ctx to be updated
  * @dentry: new dentry being added
  *
- * Returns zero on success. @so_ctx and the dentry offset are updated.
+ * Returns zero on success. @octx and the dentry's offset are updated.
  * Otherwise, a negative errno value is returned.
  */
 int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
 {
-       static const struct xa_limit limit = XA_LIMIT(2, U32_MAX);
-       u32 offset;
+       unsigned long offset;
        int ret;
 
        if (dentry2offset(dentry) != 0)
                return -EBUSY;
 
-       ret = xa_alloc_cyclic(&octx->xa, &offset, dentry, limit,
-                             &octx->next_offset, GFP_KERNEL);
+       ret = mtree_alloc_cyclic(&octx->mt, &offset, dentry, DIR_OFFSET_MIN,
+                                LONG_MAX, &octx->next_offset, GFP_KERNEL);
        if (ret < 0)
                return ret;
 
@@ -300,16 +302,48 @@ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
  */
 void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry)
 {
-       u32 offset;
+       long offset;
 
        offset = dentry2offset(dentry);
        if (offset == 0)
                return;
 
-       xa_erase(&octx->xa, offset);
+       mtree_erase(&octx->mt, offset);
        offset_set(dentry, 0);
 }
 
+/**
+ * simple_offset_empty - Check if a dentry can be unlinked
+ * @dentry: dentry to be tested
+ *
+ * Returns 0 if @dentry is a non-empty directory; otherwise returns 1.
+ */
+int simple_offset_empty(struct dentry *dentry)
+{
+       struct inode *inode = d_inode(dentry);
+       struct offset_ctx *octx;
+       struct dentry *child;
+       unsigned long index;
+       int ret = 1;
+
+       if (!inode || !S_ISDIR(inode->i_mode))
+               return ret;
+
+       index = DIR_OFFSET_MIN;
+       octx = inode->i_op->get_offset_ctx(inode);
+       mt_for_each(&octx->mt, child, index, LONG_MAX) {
+               spin_lock(&child->d_lock);
+               if (simple_positive(child)) {
+                       spin_unlock(&child->d_lock);
+                       ret = 0;
+                       break;
+               }
+               spin_unlock(&child->d_lock);
+       }
+
+       return ret;
+}
+
 /**
  * simple_offset_rename_exchange - exchange rename with directory offsets
  * @old_dir: parent of dentry being moved
@@ -327,8 +361,8 @@ int simple_offset_rename_exchange(struct inode *old_dir,
 {
        struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
        struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
-       u32 old_index = dentry2offset(old_dentry);
-       u32 new_index = dentry2offset(new_dentry);
+       long old_index = dentry2offset(old_dentry);
+       long new_index = dentry2offset(new_dentry);
        int ret;
 
        simple_offset_remove(old_ctx, old_dentry);
@@ -354,9 +388,9 @@ int simple_offset_rename_exchange(struct inode *old_dir,
 
 out_restore:
        offset_set(old_dentry, old_index);
-       xa_store(&old_ctx->xa, old_index, old_dentry, GFP_KERNEL);
+       mtree_store(&old_ctx->mt, old_index, old_dentry, GFP_KERNEL);
        offset_set(new_dentry, new_index);
-       xa_store(&new_ctx->xa, new_index, new_dentry, GFP_KERNEL);
+       mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);
        return ret;
 }
 
@@ -369,7 +403,7 @@ out_restore:
  */
 void simple_offset_destroy(struct offset_ctx *octx)
 {
-       xa_destroy(&octx->xa);
+       mtree_destroy(&octx->mt);
 }
 
 /**
@@ -399,15 +433,16 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence)
 
        /* In this case, ->private_data is protected by f_pos_lock */
        file->private_data = NULL;
-       return vfs_setpos(file, offset, U32_MAX);
+       return vfs_setpos(file, offset, LONG_MAX);
 }
 
-static struct dentry *offset_find_next(struct xa_state *xas)
+static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
 {
+       MA_STATE(mas, &octx->mt, offset, offset);
        struct dentry *child, *found = NULL;
 
        rcu_read_lock();
-       child = xas_next_entry(xas, U32_MAX);
+       child = mas_find(&mas, LONG_MAX);
        if (!child)
                goto out;
        spin_lock(&child->d_lock);
@@ -421,8 +456,8 @@ out:
 
 static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
 {
-       u32 offset = dentry2offset(dentry);
        struct inode *inode = d_inode(dentry);
+       long offset = dentry2offset(dentry);
 
        return ctx->actor(ctx, dentry->d_name.name, dentry->d_name.len, offset,
                          inode->i_ino, fs_umode_to_dtype(inode->i_mode));
@@ -430,12 +465,11 @@ static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
 
 static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
 {
-       struct offset_ctx *so_ctx = inode->i_op->get_offset_ctx(inode);
-       XA_STATE(xas, &so_ctx->xa, ctx->pos);
+       struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
        struct dentry *dentry;
 
        while (true) {
-               dentry = offset_find_next(&xas);
+               dentry = offset_find_next(octx, ctx->pos);
                if (!dentry)
                        return ERR_PTR(-ENOENT);
 
@@ -444,8 +478,8 @@ static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
                        break;
                }
 
+               ctx->pos = dentry2offset(dentry) + 1;
                dput(dentry);
-               ctx->pos = xas.xa_index + 1;
        }
        return NULL;
 }
@@ -481,7 +515,7 @@ static int offset_readdir(struct file *file, struct dir_context *ctx)
                return 0;
 
        /* In this case, ->private_data is protected by f_pos_lock */
-       if (ctx->pos == 2)
+       if (ctx->pos == DIR_OFFSET_MIN)
                file->private_data = NULL;
        else if (file->private_data == ERR_PTR(-ENOENT))
                return 0;
@@ -1758,7 +1792,7 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
        const struct inode *dir = READ_ONCE(dentry->d_inode);
        struct super_block *sb = dentry->d_sb;
        const struct unicode_map *um = sb->s_encoding;
-       int ret = 0;
+       int ret;
 
        if (!dir || !IS_CASEFOLDED(dir))
                return 0;