introduce a parallel variant of ->iterate()
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 21 Apr 2016 03:08:32 +0000 (23:08 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 2 May 2016 23:49:29 +0000 (19:49 -0400)
New method: ->iterate_shared().  Same arguments as in ->iterate(),
called with the directory locked only shared.  Once all filesystems
switch, the old one will be gone.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Documentation/filesystems/porting
fs/coda/dir.c
fs/exportfs/expfs.c
fs/readdir.c
include/linux/fs.h

index 1567a53857bdee03898fed5d47eec7a41f0ed0b3..12c57abdaac934d9c7a37c2ef42d2b44de4d19d4 100644 (file)
@@ -557,3 +557,21 @@ in your dentry operations instead.
        will not happen in parallel ("same" in the sense of your ->d_compare()).
        Lookups on different names in the same directory can and do happen in
        parallel now.
+--
+[recommended]
+       ->iterate_shared() is added; it's a parallel variant of ->iterate().
+       Exclusion on struct file level is still provided (as well as that
+       between it and lseek on the same struct file), but if your directory
+       has been opened several times, you can get these called in parallel.
+       Exclusion between that method and all directory-modifying ones is
+       still provided, of course.
+
+       Often enough ->iterate() can serve as ->iterate_shared() without any
+       changes - it is a read-only operation, after all.  If you have any
+       per-inode or per-dentry in-core data structures modified by ->iterate(),
+       you might need something to serialize the access to them.  If you
+       do dcache pre-seeding, you'll need to switch to d_alloc_parallel() for
+       that; look for in-tree examples.
+
+       Old method is only used if the new one is absent; eventually it will
+       be removed.  Switch while you still can; the old one won't stay.
index 42e731b8c80a680602d401b6b28239acf74291e0..6fb8672c0892c831482736d1f0af9560fe1542bc 100644 (file)
@@ -424,16 +424,22 @@ static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
        BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
        host_file = cfi->cfi_container;
 
-       if (host_file->f_op->iterate) {
+       if (host_file->f_op->iterate || host_file->f_op->iterate_shared) {
                struct inode *host_inode = file_inode(host_file);
-
-               inode_lock(host_inode);
                ret = -ENOENT;
                if (!IS_DEADDIR(host_inode)) {
-                       ret = host_file->f_op->iterate(host_file, ctx);
-                       file_accessed(host_file);
+                       if (host_file->f_op->iterate_shared) {
+                               inode_lock_shared(host_inode);
+                               ret = host_file->f_op->iterate_shared(host_file, ctx);
+                               file_accessed(host_file);
+                               inode_unlock_shared(host_inode);
+                       } else {
+                               inode_lock(host_inode);
+                               ret = host_file->f_op->iterate(host_file, ctx);
+                               file_accessed(host_file);
+                               inode_unlock(host_inode);
+                       }
                }
-               inode_unlock(host_inode);
                return ret;
        }
        /* Venus: we must read Venus dirents from a file */
index 402c5caab5ca8723ff344fa9bdeed17718ae5fff..207ba8d627ca74feca42b4196df15ca0a9a621e6 100644 (file)
@@ -312,7 +312,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
                goto out;
 
        error = -EINVAL;
-       if (!file->f_op->iterate)
+       if (!file->f_op->iterate && !file->f_op->iterate_shared)
                goto out_close;
 
        buffer.sequence = 0;
index d7308b8f6cf7625fde9dba6ee6df22f7b2c33682..a86c6c04b9bcf06797e27e17075b4cc6112d0e62 100644 (file)
 int iterate_dir(struct file *file, struct dir_context *ctx)
 {
        struct inode *inode = file_inode(file);
+       bool shared = false;
        int res = -ENOTDIR;
-       if (!file->f_op->iterate)
+       if (file->f_op->iterate_shared)
+               shared = true;
+       else if (!file->f_op->iterate)
                goto out;
 
        res = security_file_permission(file, MAY_READ);
        if (res)
                goto out;
 
-       inode_lock(inode);
+       if (shared)
+               inode_lock_shared(inode);
+       else
+               inode_lock(inode);
        // res = mutex_lock_killable(&inode->i_mutex);
        // if (res)
        //      goto out;
@@ -40,12 +46,18 @@ int iterate_dir(struct file *file, struct dir_context *ctx)
        res = -ENOENT;
        if (!IS_DEADDIR(inode)) {
                ctx->pos = file->f_pos;
-               res = file->f_op->iterate(file, ctx);
+               if (shared)
+                       res = file->f_op->iterate_shared(file, ctx);
+               else
+                       res = file->f_op->iterate(file, ctx);
                file->f_pos = ctx->pos;
                fsnotify_access(file);
                file_accessed(file);
        }
-       inode_unlock(inode);
+       if (shared)
+               inode_unlock_shared(inode);
+       else
+               inode_unlock(inode);
 out:
        return res;
 }
index 3018f31f7aa0f839a484b86841c9adccc2570a71..3dc0258a2b640c525d7d95588c29ccd452809a10 100644 (file)
@@ -1674,6 +1674,7 @@ struct file_operations {
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
+       int (*iterate_shared) (struct file *, struct dir_context *);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);