Merge branch 'for-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
[sfrench/cifs-2.6.git] / fs / kernfs / file.c
index 4f0535890b30c7c8844b7998852449898db3463e..35043a8c452905173487e10dc37f9d749cd1e9ab 100644 (file)
@@ -515,7 +515,7 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
                goto out_put;
 
        rc = 0;
-       of->mmapped = 1;
+       of->mmapped = true;
        of->vm_ops = vma->vm_ops;
        vma->vm_ops = &kernfs_vm_ops;
 out_put:
@@ -707,7 +707,8 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
        if (error)
                goto err_free;
 
-       ((struct seq_file *)file->private_data)->private = of;
+       of->seq_file = file->private_data;
+       of->seq_file->private = of;
 
        /* seq_file clears PWRITE unconditionally, restore it if WRITE */
        if (file->f_mode & FMODE_WRITE)
@@ -716,13 +717,22 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
        /* make sure we have open node struct */
        error = kernfs_get_open_node(kn, of);
        if (error)
-               goto err_close;
+               goto err_seq_release;
+
+       if (ops->open) {
+               /* nobody has access to @of yet, skip @of->mutex */
+               error = ops->open(of);
+               if (error)
+                       goto err_put_node;
+       }
 
        /* open succeeded, put active references */
        kernfs_put_active(kn);
        return 0;
 
-err_close:
+err_put_node:
+       kernfs_put_open_node(kn, of);
+err_seq_release:
        seq_release(inode, file);
 err_free:
        kfree(of->prealloc_buf);
@@ -732,11 +742,41 @@ err_out:
        return error;
 }
 
+/* used from release/drain to ensure that ->release() is called exactly once */
+static void kernfs_release_file(struct kernfs_node *kn,
+                               struct kernfs_open_file *of)
+{
+       /*
+        * @of is guaranteed to have no other file operations in flight and
+        * we just want to synchronize release and drain paths.
+        * @kernfs_open_file_mutex is enough.  @of->mutex can't be used
+        * here because drain path may be called from places which can
+        * cause circular dependency.
+        */
+       lockdep_assert_held(&kernfs_open_file_mutex);
+
+       if (!of->released) {
+               /*
+                * A file is never detached without being released and we
+                * need to be able to release files which are deactivated
+                * and being drained.  Don't use kernfs_ops().
+                */
+               kn->attr.ops->release(of);
+               of->released = true;
+       }
+}
+
 static int kernfs_fop_release(struct inode *inode, struct file *filp)
 {
        struct kernfs_node *kn = filp->f_path.dentry->d_fsdata;
        struct kernfs_open_file *of = kernfs_of(filp);
 
+       if (kn->flags & KERNFS_HAS_RELEASE) {
+               mutex_lock(&kernfs_open_file_mutex);
+               kernfs_release_file(kn, of);
+               mutex_unlock(&kernfs_open_file_mutex);
+       }
+
        kernfs_put_open_node(kn, of);
        seq_release(inode, filp);
        kfree(of->prealloc_buf);
@@ -745,12 +785,12 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
-void kernfs_unmap_bin_file(struct kernfs_node *kn)
+void kernfs_drain_open_files(struct kernfs_node *kn)
 {
        struct kernfs_open_node *on;
        struct kernfs_open_file *of;
 
-       if (!(kn->flags & KERNFS_HAS_MMAP))
+       if (!(kn->flags & (KERNFS_HAS_MMAP | KERNFS_HAS_RELEASE)))
                return;
 
        spin_lock_irq(&kernfs_open_node_lock);
@@ -762,10 +802,16 @@ void kernfs_unmap_bin_file(struct kernfs_node *kn)
                return;
 
        mutex_lock(&kernfs_open_file_mutex);
+
        list_for_each_entry(of, &on->files, list) {
                struct inode *inode = file_inode(of->file);
-               unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+
+               if (kn->flags & KERNFS_HAS_MMAP)
+                       unmap_mapping_range(inode->i_mapping, 0, 0, 1);
+
+               kernfs_release_file(kn, of);
        }
+
        mutex_unlock(&kernfs_open_file_mutex);
 
        kernfs_put_open_node(kn, NULL);
@@ -964,6 +1010,8 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
                kn->flags |= KERNFS_HAS_SEQ_SHOW;
        if (ops->mmap)
                kn->flags |= KERNFS_HAS_MMAP;
+       if (ops->release)
+               kn->flags |= KERNFS_HAS_RELEASE;
 
        rc = kernfs_add_one(kn);
        if (rc) {