]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - fs/fuse/dev.c
Merge tag 'rproc-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson...
[mirror_ubuntu-jammy-kernel.git] / fs / fuse / dev.c
index 02b3c36b36766adc2c5c6e3d69363b192a3edbd8..588f8d1240aab377c94fc1e7aa619a1b89b181e3 100644 (file)
@@ -40,20 +40,21 @@ static struct fuse_dev *fuse_get_dev(struct file *file)
        return READ_ONCE(file->private_data);
 }
 
-static void fuse_request_init(struct fuse_req *req)
+static void fuse_request_init(struct fuse_mount *fm, struct fuse_req *req)
 {
        INIT_LIST_HEAD(&req->list);
        INIT_LIST_HEAD(&req->intr_entry);
        init_waitqueue_head(&req->waitq);
        refcount_set(&req->count, 1);
        __set_bit(FR_PENDING, &req->flags);
+       req->fm = fm;
 }
 
-static struct fuse_req *fuse_request_alloc(gfp_t flags)
+static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags)
 {
        struct fuse_req *req = kmem_cache_zalloc(fuse_req_cachep, flags);
        if (req)
-               fuse_request_init(req);
+               fuse_request_init(fm, req);
 
        return req;
 }
@@ -100,10 +101,11 @@ static void fuse_drop_waiting(struct fuse_conn *fc)
        }
 }
 
-static void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
+static void fuse_put_request(struct fuse_req *req);
 
-static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
+static struct fuse_req *fuse_get_req(struct fuse_mount *fm, bool for_background)
 {
+       struct fuse_conn *fc = fm->fc;
        struct fuse_req *req;
        int err;
        atomic_inc(&fc->num_waiting);
@@ -125,7 +127,7 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
        if (fc->conn_error)
                goto out;
 
-       req = fuse_request_alloc(GFP_KERNEL);
+       req = fuse_request_alloc(fm, GFP_KERNEL);
        err = -ENOMEM;
        if (!req) {
                if (for_background)
@@ -143,7 +145,7 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
 
        if (unlikely(req->in.h.uid == ((uid_t)-1) ||
                     req->in.h.gid == ((gid_t)-1))) {
-               fuse_put_request(fc, req);
+               fuse_put_request(req);
                return ERR_PTR(-EOVERFLOW);
        }
        return req;
@@ -153,8 +155,10 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
        return ERR_PTR(err);
 }
 
-static void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
+static void fuse_put_request(struct fuse_req *req)
 {
+       struct fuse_conn *fc = req->fm->fc;
+
        if (refcount_dec_and_test(&req->count)) {
                if (test_bit(FR_BACKGROUND, &req->flags)) {
                        /*
@@ -273,8 +277,10 @@ static void flush_bg_queue(struct fuse_conn *fc)
  * the 'end' callback is called if given, else the reference to the
  * request is released
  */
-void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_request_end(struct fuse_req *req)
 {
+       struct fuse_mount *fm = req->fm;
+       struct fuse_conn *fc = fm->fc;
        struct fuse_iqueue *fiq = &fc->iq;
 
        if (test_and_set_bit(FR_FINISHED, &req->flags))
@@ -309,9 +315,9 @@ void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
                                wake_up(&fc->blocked_waitq);
                }
 
-               if (fc->num_background == fc->congestion_threshold && fc->sb) {
-                       clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
-                       clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
+               if (fc->num_background == fc->congestion_threshold && fm->sb) {
+                       clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
+                       clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
                }
                fc->num_background--;
                fc->active_background--;
@@ -323,14 +329,16 @@ void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
        }
 
        if (test_bit(FR_ASYNC, &req->flags))
-               req->args->end(fc, req->args, req->out.h.error);
+               req->args->end(fm, req->args, req->out.h.error);
 put_request:
-       fuse_put_request(fc, req);
+       fuse_put_request(req);
 }
 EXPORT_SYMBOL_GPL(fuse_request_end);
 
-static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
+static int queue_interrupt(struct fuse_req *req)
 {
+       struct fuse_iqueue *fiq = &req->fm->fc->iq;
+
        spin_lock(&fiq->lock);
        /* Check for we've sent request to interrupt this req */
        if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags))) {
@@ -357,8 +365,9 @@ static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
        return 0;
 }
 
-static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
+static void request_wait_answer(struct fuse_req *req)
 {
+       struct fuse_conn *fc = req->fm->fc;
        struct fuse_iqueue *fiq = &fc->iq;
        int err;
 
@@ -373,7 +382,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
                /* matches barrier in fuse_dev_do_read() */
                smp_mb__after_atomic();
                if (test_bit(FR_SENT, &req->flags))
-                       queue_interrupt(fiq, req);
+                       queue_interrupt(req);
        }
 
        if (!test_bit(FR_FORCE, &req->flags)) {
@@ -402,9 +411,9 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
        wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags));
 }
 
-static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
+static void __fuse_request_send(struct fuse_req *req)
 {
-       struct fuse_iqueue *fiq = &fc->iq;
+       struct fuse_iqueue *fiq = &req->fm->fc->iq;
 
        BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
        spin_lock(&fiq->lock);
@@ -418,7 +427,7 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
                __fuse_get_request(req);
                queue_request_and_unlock(fiq, req);
 
-               request_wait_answer(fc, req);
+               request_wait_answer(req);
                /* Pairs with smp_wmb() in fuse_request_end() */
                smp_rmb();
        }
@@ -457,8 +466,10 @@ static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args)
        }
 }
 
-static void fuse_force_creds(struct fuse_conn *fc, struct fuse_req *req)
+static void fuse_force_creds(struct fuse_req *req)
 {
+       struct fuse_conn *fc = req->fm->fc;
+
        req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid());
        req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid());
        req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns);
@@ -473,23 +484,24 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
                __set_bit(FR_ASYNC, &req->flags);
 }
 
-ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
+ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
 {
+       struct fuse_conn *fc = fm->fc;
        struct fuse_req *req;
        ssize_t ret;
 
        if (args->force) {
                atomic_inc(&fc->num_waiting);
-               req = fuse_request_alloc(GFP_KERNEL | __GFP_NOFAIL);
+               req = fuse_request_alloc(fm, GFP_KERNEL | __GFP_NOFAIL);
 
                if (!args->nocreds)
-                       fuse_force_creds(fc, req);
+                       fuse_force_creds(req);
 
                __set_bit(FR_WAITING, &req->flags);
                __set_bit(FR_FORCE, &req->flags);
        } else {
                WARN_ON(args->nocreds);
-               req = fuse_get_req(fc, false);
+               req = fuse_get_req(fm, false);
                if (IS_ERR(req))
                        return PTR_ERR(req);
        }
@@ -500,20 +512,21 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
 
        if (!args->noreply)
                __set_bit(FR_ISREPLY, &req->flags);
-       __fuse_request_send(fc, req);
+       __fuse_request_send(req);
        ret = req->out.h.error;
        if (!ret && args->out_argvar) {
                BUG_ON(args->out_numargs == 0);
                ret = args->out_args[args->out_numargs - 1].size;
        }
-       fuse_put_request(fc, req);
+       fuse_put_request(req);
 
        return ret;
 }
 
-static bool fuse_request_queue_background(struct fuse_conn *fc,
-                                         struct fuse_req *req)
+static bool fuse_request_queue_background(struct fuse_req *req)
 {
+       struct fuse_mount *fm = req->fm;
+       struct fuse_conn *fc = fm->fc;
        bool queued = false;
 
        WARN_ON(!test_bit(FR_BACKGROUND, &req->flags));
@@ -527,9 +540,9 @@ static bool fuse_request_queue_background(struct fuse_conn *fc,
                fc->num_background++;
                if (fc->num_background == fc->max_background)
                        fc->blocked = 1;
-               if (fc->num_background == fc->congestion_threshold && fc->sb) {
-                       set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
-                       set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
+               if (fc->num_background == fc->congestion_threshold && fm->sb) {
+                       set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
+                       set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
                }
                list_add_tail(&req->list, &fc->bg_queue);
                flush_bg_queue(fc);
@@ -540,28 +553,28 @@ static bool fuse_request_queue_background(struct fuse_conn *fc,
        return queued;
 }
 
-int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
+int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args,
                            gfp_t gfp_flags)
 {
        struct fuse_req *req;
 
        if (args->force) {
                WARN_ON(!args->nocreds);
-               req = fuse_request_alloc(gfp_flags);
+               req = fuse_request_alloc(fm, gfp_flags);
                if (!req)
                        return -ENOMEM;
                __set_bit(FR_BACKGROUND, &req->flags);
        } else {
                WARN_ON(args->nocreds);
-               req = fuse_get_req(fc, true);
+               req = fuse_get_req(fm, true);
                if (IS_ERR(req))
                        return PTR_ERR(req);
        }
 
        fuse_args_to_req(req, args);
 
-       if (!fuse_request_queue_background(fc, req)) {
-               fuse_put_request(fc, req);
+       if (!fuse_request_queue_background(req)) {
+               fuse_put_request(req);
                return -ENOTCONN;
        }
 
@@ -569,14 +582,14 @@ int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
 }
 EXPORT_SYMBOL_GPL(fuse_simple_background);
 
-static int fuse_simple_notify_reply(struct fuse_conn *fc,
+static int fuse_simple_notify_reply(struct fuse_mount *fm,
                                    struct fuse_args *args, u64 unique)
 {
        struct fuse_req *req;
-       struct fuse_iqueue *fiq = &fc->iq;
+       struct fuse_iqueue *fiq = &fm->fc->iq;
        int err = 0;
 
-       req = fuse_get_req(fc, false);
+       req = fuse_get_req(fm, false);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
@@ -591,7 +604,7 @@ static int fuse_simple_notify_reply(struct fuse_conn *fc,
        } else {
                err = -ENODEV;
                spin_unlock(&fiq->lock);
-               fuse_put_request(fc, req);
+               fuse_put_request(req);
        }
 
        return err;
@@ -785,15 +798,16 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
        struct page *newpage;
        struct pipe_buffer *buf = cs->pipebufs;
 
+       get_page(oldpage);
        err = unlock_request(cs->req);
        if (err)
-               return err;
+               goto out_put_old;
 
        fuse_copy_finish(cs);
 
        err = pipe_buf_confirm(cs->pipe, buf);
        if (err)
-               return err;
+               goto out_put_old;
 
        BUG_ON(!cs->nr_segs);
        cs->currbuf = buf;
@@ -833,7 +847,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
        err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
        if (err) {
                unlock_page(newpage);
-               return err;
+               goto out_put_old;
        }
 
        get_page(newpage);
@@ -852,14 +866,19 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
        if (err) {
                unlock_page(newpage);
                put_page(newpage);
-               return err;
+               goto out_put_old;
        }
 
        unlock_page(oldpage);
+       /* Drop ref for ap->pages[] array */
        put_page(oldpage);
        cs->len = 0;
 
-       return 0;
+       err = 0;
+out_put_old:
+       /* Drop ref obtained in this function */
+       put_page(oldpage);
+       return err;
 
 out_fallback_unlock:
        unlock_page(newpage);
@@ -868,10 +887,10 @@ out_fallback:
        cs->offset = buf->offset;
 
        err = lock_request(cs->req);
-       if (err)
-               return err;
+       if (!err)
+               err = 1;
 
-       return 1;
+       goto out_put_old;
 }
 
 static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
@@ -883,14 +902,16 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
        if (cs->nr_segs >= cs->pipe->max_usage)
                return -EIO;
 
+       get_page(page);
        err = unlock_request(cs->req);
-       if (err)
+       if (err) {
+               put_page(page);
                return err;
+       }
 
        fuse_copy_finish(cs);
 
        buf = cs->pipebufs;
-       get_page(page);
        buf->page = page;
        buf->offset = offset;
        buf->len = count;
@@ -1250,7 +1271,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
                /* SETXATTR is special, since it may contain too large data */
                if (args->opcode == FUSE_SETXATTR)
                        req->out.h.error = -E2BIG;
-               fuse_request_end(fc, req);
+               fuse_request_end(req);
                goto restart;
        }
        spin_lock(&fpq->lock);
@@ -1284,8 +1305,8 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
        /* matches barrier in request_wait_answer() */
        smp_mb__after_atomic();
        if (test_bit(FR_INTERRUPTED, &req->flags))
-               queue_interrupt(fiq, req);
-       fuse_put_request(fc, req);
+               queue_interrupt(req);
+       fuse_put_request(req);
 
        return reqsize;
 
@@ -1293,7 +1314,7 @@ out_end:
        if (!test_bit(FR_PRIVATE, &req->flags))
                list_del_init(&req->list);
        spin_unlock(&fpq->lock);
-       fuse_request_end(fc, req);
+       fuse_request_end(req);
        return err;
 
  err_unlock:
@@ -1416,11 +1437,8 @@ static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size,
        fuse_copy_finish(cs);
 
        down_read(&fc->killsb);
-       err = -ENOENT;
-       if (fc->sb) {
-               err = fuse_reverse_inval_inode(fc->sb, outarg.ino,
-                                              outarg.off, outarg.len);
-       }
+       err = fuse_reverse_inval_inode(fc, outarg.ino,
+                                      outarg.off, outarg.len);
        up_read(&fc->killsb);
        return err;
 
@@ -1466,9 +1484,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
        buf[outarg.namelen] = 0;
 
        down_read(&fc->killsb);
-       err = -ENOENT;
-       if (fc->sb)
-               err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name);
+       err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name);
        up_read(&fc->killsb);
        kfree(buf);
        return err;
@@ -1516,10 +1532,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
        buf[outarg.namelen] = 0;
 
        down_read(&fc->killsb);
-       err = -ENOENT;
-       if (fc->sb)
-               err = fuse_reverse_inval_entry(fc->sb, outarg.parent,
-                                              outarg.child, &name);
+       err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name);
        up_read(&fc->killsb);
        kfree(buf);
        return err;
@@ -1561,10 +1574,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
        down_read(&fc->killsb);
 
        err = -ENOENT;
-       if (!fc->sb)
-               goto out_up_killsb;
-
-       inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
+       inode = fuse_ilookup(fc, nodeid,  NULL);
        if (!inode)
                goto out_up_killsb;
 
@@ -1621,7 +1631,7 @@ struct fuse_retrieve_args {
        struct fuse_notify_retrieve_in inarg;
 };
 
-static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_args *args,
+static void fuse_retrieve_end(struct fuse_mount *fm, struct fuse_args *args,
                              int error)
 {
        struct fuse_retrieve_args *ra =
@@ -1631,7 +1641,7 @@ static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_args *args,
        kfree(ra);
 }
 
-static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
+static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
                         struct fuse_notify_retrieve_out *outarg)
 {
        int err;
@@ -1642,6 +1652,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
        unsigned int offset;
        size_t total_len = 0;
        unsigned int num_pages;
+       struct fuse_conn *fc = fm->fc;
        struct fuse_retrieve_args *ra;
        size_t args_size = sizeof(*ra);
        struct fuse_args_pages *ap;
@@ -1703,9 +1714,9 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
        args->in_args[0].value = &ra->inarg;
        args->in_args[1].size = total_len;
 
-       err = fuse_simple_notify_reply(fc, args, outarg->notify_unique);
+       err = fuse_simple_notify_reply(fm, args, outarg->notify_unique);
        if (err)
-               fuse_retrieve_end(fc, args, err);
+               fuse_retrieve_end(fm, args, err);
 
        return err;
 }
@@ -1714,7 +1725,9 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
                                struct fuse_copy_state *cs)
 {
        struct fuse_notify_retrieve_out outarg;
+       struct fuse_mount *fm;
        struct inode *inode;
+       u64 nodeid;
        int err;
 
        err = -EINVAL;
@@ -1729,14 +1742,12 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
 
        down_read(&fc->killsb);
        err = -ENOENT;
-       if (fc->sb) {
-               u64 nodeid = outarg.nodeid;
+       nodeid = outarg.nodeid;
 
-               inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
-               if (inode) {
-                       err = fuse_retrieve(fc, inode, &outarg);
-                       iput(inode);
-               }
+       inode = fuse_ilookup(fc, nodeid, &fm);
+       if (inode) {
+               err = fuse_retrieve(fm, inode, &outarg);
+               iput(inode);
        }
        up_read(&fc->killsb);
 
@@ -1875,9 +1886,9 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
                else if (oh.error == -ENOSYS)
                        fc->no_interrupt = 1;
                else if (oh.error == -EAGAIN)
-                       err = queue_interrupt(&fc->iq, req);
+                       err = queue_interrupt(req);
 
-               fuse_put_request(fc, req);
+               fuse_put_request(req);
 
                goto copy_finish;
        }
@@ -1907,7 +1918,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
                list_del_init(&req->list);
        spin_unlock(&fpq->lock);
 
-       fuse_request_end(fc, req);
+       fuse_request_end(req);
 out:
        return err ? err : nbytes;
 
@@ -2045,7 +2056,7 @@ static __poll_t fuse_dev_poll(struct file *file, poll_table *wait)
 }
 
 /* Abort all requests on the given list (pending or processing) */
-static void end_requests(struct fuse_conn *fc, struct list_head *head)
+static void end_requests(struct list_head *head)
 {
        while (!list_empty(head)) {
                struct fuse_req *req;
@@ -2053,7 +2064,7 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
                req->out.h.error = -ECONNABORTED;
                clear_bit(FR_SENT, &req->flags);
                list_del_init(&req->list);
-               fuse_request_end(fc, req);
+               fuse_request_end(req);
        }
 }
 
@@ -2148,7 +2159,7 @@ void fuse_abort_conn(struct fuse_conn *fc)
                wake_up_all(&fc->blocked_waitq);
                spin_unlock(&fc->lock);
 
-               end_requests(fc, &to_end);
+               end_requests(&to_end);
        } else {
                spin_unlock(&fc->lock);
        }
@@ -2178,7 +2189,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
                        list_splice_init(&fpq->processing[i], &to_end);
                spin_unlock(&fpq->lock);
 
-               end_requests(fc, &to_end);
+               end_requests(&to_end);
 
                /* Are we the last open device? */
                if (atomic_dec_and_test(&fc->dev_count)) {