]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Jan 2009 01:01:20 +0000 (17:01 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Jan 2009 01:01:20 +0000 (17:01 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: clean up annotations of fc->lock
  fuse: fix sparse warning in ioctl
  fuse: update interface version
  fuse: add fuse_conn->release()
  fuse: separate out fuse_conn_init() from new_conn()
  fuse: add fuse_ prefix to several functions
  fuse: implement poll support
  fuse: implement unsolicited notification
  fuse: add file kernel handle
  fuse: implement ioctl support
  fuse: don't let fuse_req->end() put the base reference
  fuse: move FUSE_MINOR to miscdevice.h
  fuse: style fixes

1  2 
fs/fuse/dev.c
fs/fuse/dir.c
fs/fuse/file.c

diff --combined fs/fuse/dev.c
index fba571648a8e43efdbbb092a5f20a03a7e84f1e5,69ebf2ecb08969a59dbfcee13961e502b2621678..e0c7ada08a1fe6f3caed4b31dc81fbb4ae9e1984
@@@ -1,6 -1,6 +1,6 @@@
  /*
    FUSE: Filesystem in Userspace
-   Copyright (C) 2001-2006  Miklos Szeredi <miklos@szeredi.hu>
+   Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
  
    This program can be distributed under the terms of the GNU GPL.
    See the file COPYING.
@@@ -87,8 -87,8 +87,8 @@@ static void __fuse_put_request(struct f
  
  static void fuse_req_init_context(struct fuse_req *req)
  {
 -      req->in.h.uid = current->fsuid;
 -      req->in.h.gid = current->fsgid;
 +      req->in.h.uid = current_fsuid();
 +      req->in.h.gid = current_fsgid();
        req->in.h.pid = current->pid;
  }
  
@@@ -269,7 -269,7 +269,7 @@@ static void flush_bg_queue(struct fuse_
   * Called with fc->lock, unlocks it
   */
  static void request_end(struct fuse_conn *fc, struct fuse_req *req)
      __releases(fc->lock)
__releases(&fc->lock)
  {
        void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
        req->end = NULL;
        wake_up(&req->waitq);
        if (end)
                end(fc, req);
-       else
-               fuse_put_request(fc, req);
+       fuse_put_request(fc, req);
  }
  
  static void wait_answer_interruptible(struct fuse_conn *fc,
                                      struct fuse_req *req)
-       __releases(fc->lock) __acquires(fc->lock)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        if (signal_pending(current))
                return;
@@@ -317,7 -317,8 +317,8 @@@ static void queue_interrupt(struct fuse
  }
  
  static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
-       __releases(fc->lock) __acquires(fc->lock)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        if (!fc->no_interrupt) {
                /* Any signal may interrupt this */
        }
  }
  
- void request_send(struct fuse_conn *fc, struct fuse_req *req)
+ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
  {
        req->isreply = 1;
        spin_lock(&fc->lock);
        spin_unlock(&fc->lock);
  }
  
- static void request_send_nowait_locked(struct fuse_conn *fc,
-                                      struct fuse_req *req)
+ static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
+                                           struct fuse_req *req)
  {
        req->background = 1;
        fc->num_background++;
        flush_bg_queue(fc);
  }
  
- static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
+ static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
  {
        spin_lock(&fc->lock);
        if (fc->connected) {
-               request_send_nowait_locked(fc, req);
+               fuse_request_send_nowait_locked(fc, req);
                spin_unlock(&fc->lock);
        } else {
                req->out.h.error = -ENOTCONN;
        }
  }
  
- void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+ void fuse_request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
  {
        req->isreply = 0;
-       request_send_nowait(fc, req);
+       fuse_request_send_nowait(fc, req);
  }
  
- void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
+ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
  {
        req->isreply = 1;
-       request_send_nowait(fc, req);
+       fuse_request_send_nowait(fc, req);
  }
  
  /*
   *
   * fc->connected must have been checked previously
   */
- void request_send_background_locked(struct fuse_conn *fc, struct fuse_req *req)
+ void fuse_request_send_background_locked(struct fuse_conn *fc,
+                                        struct fuse_req *req)
  {
        req->isreply = 1;
-       request_send_nowait_locked(fc, req);
+       fuse_request_send_nowait_locked(fc, req);
  }
  
  /*
@@@ -539,8 -541,8 +541,8 @@@ static int fuse_copy_fill(struct fuse_c
                BUG_ON(!cs->nr_segs);
                cs->seglen = cs->iov[0].iov_len;
                cs->addr = (unsigned long) cs->iov[0].iov_base;
-               cs->iov ++;
-               cs->nr_segs --;
+               cs->iov++;
+               cs->nr_segs--;
        }
        down_read(&current->mm->mmap_sem);
        err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0,
@@@ -589,9 -591,11 +591,11 @@@ static int fuse_copy_page(struct fuse_c
                kunmap_atomic(mapaddr, KM_USER1);
        }
        while (count) {
-               int err;
-               if (!cs->len && (err = fuse_copy_fill(cs)))
-                       return err;
+               if (!cs->len) {
+                       int err = fuse_copy_fill(cs);
+                       if (err)
+                               return err;
+               }
                if (page) {
                        void *mapaddr = kmap_atomic(page, KM_USER1);
                        void *buf = mapaddr + offset;
@@@ -631,9 -635,11 +635,11 @@@ static int fuse_copy_pages(struct fuse_
  static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size)
  {
        while (size) {
-               int err;
-               if (!cs->len && (err = fuse_copy_fill(cs)))
-                       return err;
+               if (!cs->len) {
+                       int err = fuse_copy_fill(cs);
+                       if (err)
+                               return err;
+               }
                fuse_copy_do(cs, &val, &size);
        }
        return 0;
@@@ -664,6 -670,8 +670,8 @@@ static int request_pending(struct fuse_
  
  /* Wait until a request is available on the pending list */
  static void request_wait(struct fuse_conn *fc)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        DECLARE_WAITQUEUE(wait, current);
  
   */
  static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_req *req,
                               const struct iovec *iov, unsigned long nr_segs)
      __releases(fc->lock)
__releases(&fc->lock)
  {
        struct fuse_copy_state cs;
        struct fuse_in_header ih;
@@@ -813,6 -821,34 +821,34 @@@ static ssize_t fuse_dev_read(struct kio
        return err;
  }
  
+ static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size,
+                           struct fuse_copy_state *cs)
+ {
+       struct fuse_notify_poll_wakeup_out outarg;
+       int err;
+       if (size != sizeof(outarg))
+               return -EINVAL;
+       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
+       if (err)
+               return err;
+       return fuse_notify_poll_wakeup(fc, &outarg);
+ }
+ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
+                      unsigned int size, struct fuse_copy_state *cs)
+ {
+       switch (code) {
+       case FUSE_NOTIFY_POLL:
+               return fuse_notify_poll(fc, size, cs);
+       default:
+               return -EINVAL;
+       }
+ }
  /* Look up request on processing list by unique ID */
  static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique)
  {
@@@ -876,9 -912,23 +912,23 @@@ static ssize_t fuse_dev_write(struct ki
        err = fuse_copy_one(&cs, &oh, sizeof(oh));
        if (err)
                goto err_finish;
+       err = -EINVAL;
+       if (oh.len != nbytes)
+               goto err_finish;
+       /*
+        * Zero oh.unique indicates unsolicited notification message
+        * and error contains notification code.
+        */
+       if (!oh.unique) {
+               err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), &cs);
+               fuse_copy_finish(&cs);
+               return err ? err : nbytes;
+       }
        err = -EINVAL;
-       if (!oh.unique || oh.error <= -1000 || oh.error > 0 ||
-           oh.len != nbytes)
+       if (oh.error <= -1000 || oh.error > 0)
                goto err_finish;
  
        spin_lock(&fc->lock);
@@@ -966,6 -1016,8 +1016,8 @@@ static unsigned fuse_dev_poll(struct fi
   * This function releases and reacquires fc->lock
   */
  static void end_requests(struct fuse_conn *fc, struct list_head *head)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        while (!list_empty(head)) {
                struct fuse_req *req;
   * locked).
   */
  static void end_io_requests(struct fuse_conn *fc)
-       __releases(fc->lock) __acquires(fc->lock)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        while (!list_empty(&fc->io)) {
                struct fuse_req *req =
                wake_up(&req->waitq);
                if (end) {
                        req->end = NULL;
-                       /* The end function will consume this reference */
                        __fuse_get_request(req);
                        spin_unlock(&fc->lock);
                        wait_event(req->waitq, !req->locked);
                        end(fc, req);
+                       fuse_put_request(fc, req);
                        spin_lock(&fc->lock);
                }
        }
diff --combined fs/fuse/dir.c
index 95bc22bdd0604e4562ec35b2ae286225530ad0bf,f310768a02fe550b532ad537ffe3c11ea68e6d1d..fdff346e96fdc9dead4f9a00a081f5488fc4a9e3
@@@ -1,6 -1,6 +1,6 @@@
  /*
    FUSE: Filesystem in Userspace
-   Copyright (C) 2001-2006  Miklos Szeredi <miklos@szeredi.hu>
+   Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
  
    This program can be distributed under the terms of the GNU GPL.
    See the file COPYING.
@@@ -189,7 -189,7 +189,7 @@@ static int fuse_dentry_revalidate(struc
                parent = dget_parent(entry);
                fuse_lookup_init(fc, req, get_node_id(parent->d_inode),
                                 &entry->d_name, &outarg);
-               request_send(fc, req);
+               fuse_request_send(fc, req);
                dput(parent);
                err = req->out.h.error;
                fuse_put_request(fc, req);
                                return 0;
                        }
                        spin_lock(&fc->lock);
-                       fi->nlookup ++;
+                       fi->nlookup++;
                        spin_unlock(&fc->lock);
                }
                fuse_put_request(fc, forget_req);
@@@ -283,7 -283,7 +283,7 @@@ int fuse_lookup_name(struct super_bloc
        attr_version = fuse_get_attr_version(fc);
  
        fuse_lookup_init(fc, req, nodeid, name, outarg);
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        /* Zero nodeid is same as -ENOENT, but with valid timeout */
@@@ -369,7 -369,7 +369,7 @@@ static void fuse_sync_release(struct fu
  {
        fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE);
        ff->reserved_req->force = 1;
-       request_send(fc, ff->reserved_req);
+       fuse_request_send(fc, ff->reserved_req);
        fuse_put_request(fc, ff->reserved_req);
        kfree(ff);
  }
@@@ -408,7 -408,7 +408,7 @@@ static int fuse_create_open(struct inod
                goto out_put_forget_req;
  
        err = -ENOMEM;
-       ff = fuse_file_alloc();
+       ff = fuse_file_alloc(fc);
        if (!ff)
                goto out_put_request;
  
        req->out.args[0].value = &outentry;
        req->out.args[1].size = sizeof(outopen);
        req->out.args[1].value = &outopen;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        if (err) {
                if (err == -ENOSYS)
@@@ -502,7 -502,7 +502,7 @@@ static int create_new_entry(struct fuse
        else
                req->out.args[0].size = sizeof(outarg);
        req->out.args[0].value = &outarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err)
@@@ -631,15 -631,17 +631,17 @@@ static int fuse_unlink(struct inode *di
        req->in.numargs = 1;
        req->in.args[0].size = entry->d_name.len + 1;
        req->in.args[0].value = entry->d_name.name;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
                struct inode *inode = entry->d_inode;
  
-               /* Set nlink to zero so the inode can be cleared, if
-                    the inode does have more links this will be
-                    discovered at the next lookup/getattr */
+               /*
+                * Set nlink to zero so the inode can be cleared, if the inode
+                * does have more links this will be discovered at the next
+                * lookup/getattr.
+                */
                clear_nlink(inode);
                fuse_invalidate_attr(inode);
                fuse_invalidate_attr(dir);
@@@ -662,7 -664,7 +664,7 @@@ static int fuse_rmdir(struct inode *dir
        req->in.numargs = 1;
        req->in.args[0].size = entry->d_name.len + 1;
        req->in.args[0].value = entry->d_name.name;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
@@@ -695,7 -697,7 +697,7 @@@ static int fuse_rename(struct inode *ol
        req->in.args[1].value = oldent->d_name.name;
        req->in.args[2].size = newent->d_name.len + 1;
        req->in.args[2].value = newent->d_name.name;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
@@@ -811,7 -813,7 +813,7 @@@ static int fuse_do_getattr(struct inod
        else
                req->out.args[0].size = sizeof(outarg);
        req->out.args[0].value = &outarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err) {
@@@ -869,25 -871,18 +871,25 @@@ int fuse_update_attributes(struct inod
   */
  int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
  {
 -      if (fc->flags & FUSE_ALLOW_OTHER)
 -              return 1;
 +      const struct cred *cred;
 +      int ret;
  
 -      if (task->euid == fc->user_id &&
 -          task->suid == fc->user_id &&
 -          task->uid == fc->user_id &&
 -          task->egid == fc->group_id &&
 -          task->sgid == fc->group_id &&
 -          task->gid == fc->group_id)
 +      if (fc->flags & FUSE_ALLOW_OTHER)
                return 1;
  
 -      return 0;
 +      rcu_read_lock();
 +      ret = 0;
 +      cred = __task_cred(task);
 +      if (cred->euid == fc->user_id &&
 +          cred->suid == fc->user_id &&
 +          cred->uid  == fc->user_id &&
 +          cred->egid == fc->group_id &&
 +          cred->sgid == fc->group_id &&
 +          cred->gid  == fc->group_id)
 +              ret = 1;
 +      rcu_read_unlock();
 +
 +      return ret;
  }
  
  static int fuse_access(struct inode *inode, int mask)
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS) {
@@@ -1033,7 -1028,7 +1035,7 @@@ static int fuse_readdir(struct file *fi
        req->num_pages = 1;
        req->pages[0] = page;
        fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR);
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        nbytes = req->out.args[0].size;
        err = req->out.h.error;
        fuse_put_request(fc, req);
@@@ -1067,7 -1062,7 +1069,7 @@@ static char *read_link(struct dentry *d
        req->out.numargs = 1;
        req->out.args[0].size = PAGE_SIZE - 1;
        req->out.args[0].value = link;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        if (req->out.h.error) {
                free_page((unsigned long) link);
                link = ERR_PTR(req->out.h.error);
@@@ -1273,7 -1268,7 +1275,7 @@@ static int fuse_do_setattr(struct dentr
        else
                req->out.args[0].size = sizeof(outarg);
        req->out.args[0].value = &outarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err) {
@@@ -1367,7 -1362,7 +1369,7 @@@ static int fuse_setxattr(struct dentry 
        req->in.args[1].value = name;
        req->in.args[2].size = size;
        req->in.args[2].value = value;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS) {
@@@ -1413,7 -1408,7 +1415,7 @@@ static ssize_t fuse_getxattr(struct den
                req->out.args[0].size = sizeof(outarg);
                req->out.args[0].value = &outarg;
        }
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        ret = req->out.h.error;
        if (!ret)
                ret = size ? req->out.args[0].size : outarg.size;
@@@ -1463,7 -1458,7 +1465,7 @@@ static ssize_t fuse_listxattr(struct de
                req->out.args[0].size = sizeof(outarg);
                req->out.args[0].value = &outarg;
        }
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        ret = req->out.h.error;
        if (!ret)
                ret = size ? req->out.args[0].size : outarg.size;
@@@ -1496,7 -1491,7 +1498,7 @@@ static int fuse_removexattr(struct dent
        req->in.numargs = 1;
        req->in.args[0].size = strlen(name) + 1;
        req->in.args[0].value = name;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS) {
diff --combined fs/fuse/file.c
index 4c9ee7011265a69faded2eee357fb23a37168bb1,1a057f02e7da459e132122f38189c628e4fd9d75..e8162646a9b5840766636477d1ae14f32ddc569f
@@@ -1,6 -1,6 +1,6 @@@
  /*
    FUSE: Filesystem in Userspace
-   Copyright (C) 2001-2006  Miklos Szeredi <miklos@szeredi.hu>
+   Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
  
    This program can be distributed under the terms of the GNU GPL.
    See the file COPYING.
@@@ -39,14 -39,14 +39,14 @@@ static int fuse_send_open(struct inode 
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(*outargp);
        req->out.args[0].value = outargp;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
  
        return err;
  }
  
- struct fuse_file *fuse_file_alloc(void)
+ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
  {
        struct fuse_file *ff;
        ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
                } else {
                        INIT_LIST_HEAD(&ff->write_entry);
                        atomic_set(&ff->count, 0);
+                       spin_lock(&fc->lock);
+                       ff->kh = ++fc->khctr;
+                       spin_unlock(&fc->lock);
                }
+               RB_CLEAR_NODE(&ff->polled_node);
+               init_waitqueue_head(&ff->poll_wait);
        }
        return ff;
  }
@@@ -79,7 -84,6 +84,6 @@@ static void fuse_release_end(struct fus
  {
        dput(req->misc.release.dentry);
        mntput(req->misc.release.vfsmount);
-       fuse_put_request(fc, req);
  }
  
  static void fuse_file_put(struct fuse_file *ff)
@@@ -89,7 -93,7 +93,7 @@@
                struct inode *inode = req->misc.release.dentry->d_inode;
                struct fuse_conn *fc = get_fuse_conn(inode);
                req->end = fuse_release_end;
-               request_send_background(fc, req);
+               fuse_request_send_background(fc, req);
                kfree(ff);
        }
  }
@@@ -109,6 -113,7 +113,7 @@@ void fuse_finish_open(struct inode *ino
  
  int fuse_open_common(struct inode *inode, struct file *file, int isdir)
  {
+       struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_open_out outarg;
        struct fuse_file *ff;
        int err;
        if (err)
                return err;
  
-       ff = fuse_file_alloc();
+       ff = fuse_file_alloc(fc);
        if (!ff)
                return -ENOMEM;
  
@@@ -167,7 -172,11 +172,11 @@@ int fuse_release_common(struct inode *i
  
                spin_lock(&fc->lock);
                list_del(&ff->write_entry);
+               if (!RB_EMPTY_NODE(&ff->polled_node))
+                       rb_erase(&ff->polled_node, &fc->polled_files);
                spin_unlock(&fc->lock);
+               wake_up_interruptible_sync(&ff->poll_wait);
                /*
                 * Normally this will send the RELEASE request,
                 * however if some asynchronous READ or WRITE requests
@@@ -280,7 -289,7 +289,7 @@@ static int fuse_flush(struct file *file
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
        req->force = 1;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS) {
@@@ -344,7 -353,7 +353,7 @@@ int fuse_fsync_common(struct file *file
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS) {
@@@ -396,7 -405,7 +405,7 @@@ static size_t fuse_send_read(struct fus
                inarg->read_flags |= FUSE_READ_LOCKOWNER;
                inarg->lock_owner = fuse_lock_owner_id(fc, owner);
        }
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        return req->out.args[0].size;
  }
  
@@@ -493,7 -502,6 +502,6 @@@ static void fuse_readpages_end(struct f
        }
        if (req->ff)
                fuse_file_put(req->ff);
-       fuse_put_request(fc, req);
  }
  
  static void fuse_send_readpages(struct fuse_req *req, struct file *file,
                struct fuse_file *ff = file->private_data;
                req->ff = fuse_file_get(ff);
                req->end = fuse_readpages_end;
-               request_send_background(fc, req);
+               fuse_request_send_background(fc, req);
        } else {
-               request_send(fc, req);
+               fuse_request_send(fc, req);
                fuse_readpages_end(fc, req);
+               fuse_put_request(fc, req);
        }
  }
  
@@@ -543,7 -552,7 +552,7 @@@ static int fuse_readpages_fill(void *_d
                }
        }
        req->pages[req->num_pages] = page;
-       req->num_pages ++;
+       req->num_pages++;
        return 0;
  }
  
@@@ -636,7 -645,7 +645,7 @@@ static size_t fuse_send_write(struct fu
                inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
                inarg->lock_owner = fuse_lock_owner_id(fc, owner);
        }
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        return req->misc.write.out.size;
  }
  
@@@ -646,7 -655,7 +655,7 @@@ static int fuse_write_begin(struct fil
  {
        pgoff_t index = pos >> PAGE_CACHE_SHIFT;
  
 -      *pagep = __grab_cache_page(mapping, index);
 +      *pagep = grab_cache_page_write_begin(mapping, index, flags);
        if (!*pagep)
                return -ENOMEM;
        return 0;
@@@ -779,7 -788,7 +788,7 @@@ static ssize_t fuse_fill_write_pages(st
                        break;
  
                err = -ENOMEM;
 -              page = __grab_cache_page(mapping, index);
 +              page = grab_cache_page_write_begin(mapping, index, 0);
                if (!page)
                        break;
  
@@@ -1042,7 -1051,6 +1051,6 @@@ static void fuse_writepage_free(struct 
  {
        __free_page(req->pages[0]);
        fuse_file_put(req->ff);
-       fuse_put_request(fc, req);
  }
  
  static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req)
  
  /* Called under fc->lock, may release and reacquire it */
  static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        struct fuse_inode *fi = get_fuse_inode(req->inode);
        loff_t size = i_size_read(req->inode);
  
        req->in.args[1].size = inarg->size;
        fi->writectr++;
-       request_send_background_locked(fc, req);
+       fuse_request_send_background_locked(fc, req);
        return;
  
   out_free:
        fuse_writepage_finish(fc, req);
        spin_unlock(&fc->lock);
        fuse_writepage_free(fc, req);
+       fuse_put_request(fc, req);
        spin_lock(&fc->lock);
  }
  
   * Called with fc->lock
   */
  void fuse_flush_writepages(struct inode *inode)
+ __releases(&fc->lock)
+ __acquires(&fc->lock)
  {
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_inode *fi = get_fuse_inode(inode);
@@@ -1325,7 -1338,7 +1338,7 @@@ static int fuse_getlk(struct file *file
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(outarg);
        req->out.args[0].value = &outarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err)
@@@ -1357,7 -1370,7 +1370,7 @@@ static int fuse_setlk(struct file *file
                return PTR_ERR(req);
  
        fuse_lk_fill(req, file, fl, opcode, pid, flock);
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        /* locking is restartable */
        if (err == -EINTR)
@@@ -1433,7 -1446,7 +1446,7 @@@ static sector_t fuse_bmap(struct addres
        req->out.numargs = 1;
        req->out.args[0].size = sizeof(outarg);
        req->out.args[0].value = &outarg;
-       request_send(fc, req);
+       fuse_request_send(fc, req);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (err == -ENOSYS)
@@@ -1470,6 -1483,406 +1483,406 @@@ static loff_t fuse_file_llseek(struct f
        return retval;
  }
  
+ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
+                       unsigned int nr_segs, size_t bytes, bool to_user)
+ {
+       struct iov_iter ii;
+       int page_idx = 0;
+       if (!bytes)
+               return 0;
+       iov_iter_init(&ii, iov, nr_segs, bytes, 0);
+       while (iov_iter_count(&ii)) {
+               struct page *page = pages[page_idx++];
+               size_t todo = min_t(size_t, PAGE_SIZE, iov_iter_count(&ii));
+               void *kaddr, *map;
+               kaddr = map = kmap(page);
+               while (todo) {
+                       char __user *uaddr = ii.iov->iov_base + ii.iov_offset;
+                       size_t iov_len = ii.iov->iov_len - ii.iov_offset;
+                       size_t copy = min(todo, iov_len);
+                       size_t left;
+                       if (!to_user)
+                               left = copy_from_user(kaddr, uaddr, copy);
+                       else
+                               left = copy_to_user(uaddr, kaddr, copy);
+                       if (unlikely(left))
+                               return -EFAULT;
+                       iov_iter_advance(&ii, copy);
+                       todo -= copy;
+                       kaddr += copy;
+               }
+               kunmap(map);
+       }
+       return 0;
+ }
+ /*
+  * For ioctls, there is no generic way to determine how much memory
+  * needs to be read and/or written.  Furthermore, ioctls are allowed
+  * to dereference the passed pointer, so the parameter requires deep
+  * copying but FUSE has no idea whatsoever about what to copy in or
+  * out.
+  *
+  * This is solved by allowing FUSE server to retry ioctl with
+  * necessary in/out iovecs.  Let's assume the ioctl implementation
+  * needs to read in the following structure.
+  *
+  * struct a {
+  *    char    *buf;
+  *    size_t  buflen;
+  * }
+  *
+  * On the first callout to FUSE server, inarg->in_size and
+  * inarg->out_size will be NULL; then, the server completes the ioctl
+  * with FUSE_IOCTL_RETRY set in out->flags, out->in_iovs set to 1 and
+  * the actual iov array to
+  *
+  * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) } }
+  *
+  * which tells FUSE to copy in the requested area and retry the ioctl.
+  * On the second round, the server has access to the structure and
+  * from that it can tell what to look for next, so on the invocation,
+  * it sets FUSE_IOCTL_RETRY, out->in_iovs to 2 and iov array to
+  *
+  * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a)     },
+  *   { .iov_base = a.buf,     .iov_len = a.buflen             } }
+  *
+  * FUSE will copy both struct a and the pointed buffer from the
+  * process doing the ioctl and retry ioctl with both struct a and the
+  * buffer.
+  *
+  * This time, FUSE server has everything it needs and completes ioctl
+  * without FUSE_IOCTL_RETRY which finishes the ioctl call.
+  *
+  * Copying data out works the same way.
+  *
+  * Note that if FUSE_IOCTL_UNRESTRICTED is clear, the kernel
+  * automatically initializes in and out iovs by decoding @cmd with
+  * _IOC_* macros and the server is not allowed to request RETRY.  This
+  * limits ioctl data transfers to well-formed ioctls and is the forced
+  * behavior for all FUSE servers.
+  */
+ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
+                              unsigned long arg, unsigned int flags)
+ {
+       struct inode *inode = file->f_dentry->d_inode;
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_ioctl_in inarg = {
+               .fh = ff->fh,
+               .cmd = cmd,
+               .arg = arg,
+               .flags = flags
+       };
+       struct fuse_ioctl_out outarg;
+       struct fuse_req *req = NULL;
+       struct page **pages = NULL;
+       struct page *iov_page = NULL;
+       struct iovec *in_iov = NULL, *out_iov = NULL;
+       unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages;
+       size_t in_size, out_size, transferred;
+       int err;
+       /* assume all the iovs returned by client always fits in a page */
+       BUILD_BUG_ON(sizeof(struct iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE);
+       if (!fuse_allow_task(fc, current))
+               return -EACCES;
+       err = -EIO;
+       if (is_bad_inode(inode))
+               goto out;
+       err = -ENOMEM;
+       pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL);
+       iov_page = alloc_page(GFP_KERNEL);
+       if (!pages || !iov_page)
+               goto out;
+       /*
+        * If restricted, initialize IO parameters as encoded in @cmd.
+        * RETRY from server is not allowed.
+        */
+       if (!(flags & FUSE_IOCTL_UNRESTRICTED)) {
+               struct iovec *iov = page_address(iov_page);
+               iov->iov_base = (void __user *)arg;
+               iov->iov_len = _IOC_SIZE(cmd);
+               if (_IOC_DIR(cmd) & _IOC_WRITE) {
+                       in_iov = iov;
+                       in_iovs = 1;
+               }
+               if (_IOC_DIR(cmd) & _IOC_READ) {
+                       out_iov = iov;
+                       out_iovs = 1;
+               }
+       }
+  retry:
+       inarg.in_size = in_size = iov_length(in_iov, in_iovs);
+       inarg.out_size = out_size = iov_length(out_iov, out_iovs);
+       /*
+        * Out data can be used either for actual out data or iovs,
+        * make sure there always is at least one page.
+        */
+       out_size = max_t(size_t, out_size, PAGE_SIZE);
+       max_pages = DIV_ROUND_UP(max(in_size, out_size), PAGE_SIZE);
+       /* make sure there are enough buffer pages and init request with them */
+       err = -ENOMEM;
+       if (max_pages > FUSE_MAX_PAGES_PER_REQ)
+               goto out;
+       while (num_pages < max_pages) {
+               pages[num_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+               if (!pages[num_pages])
+                       goto out;
+               num_pages++;
+       }
+       req = fuse_get_req(fc);
+       if (IS_ERR(req)) {
+               err = PTR_ERR(req);
+               req = NULL;
+               goto out;
+       }
+       memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages);
+       req->num_pages = num_pages;
+       /* okay, let's send it to the client */
+       req->in.h.opcode = FUSE_IOCTL;
+       req->in.h.nodeid = get_node_id(inode);
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(inarg);
+       req->in.args[0].value = &inarg;
+       if (in_size) {
+               req->in.numargs++;
+               req->in.args[1].size = in_size;
+               req->in.argpages = 1;
+               err = fuse_ioctl_copy_user(pages, in_iov, in_iovs, in_size,
+                                          false);
+               if (err)
+                       goto out;
+       }
+       req->out.numargs = 2;
+       req->out.args[0].size = sizeof(outarg);
+       req->out.args[0].value = &outarg;
+       req->out.args[1].size = out_size;
+       req->out.argpages = 1;
+       req->out.argvar = 1;
+       fuse_request_send(fc, req);
+       err = req->out.h.error;
+       transferred = req->out.args[1].size;
+       fuse_put_request(fc, req);
+       req = NULL;
+       if (err)
+               goto out;
+       /* did it ask for retry? */
+       if (outarg.flags & FUSE_IOCTL_RETRY) {
+               char *vaddr;
+               /* no retry if in restricted mode */
+               err = -EIO;
+               if (!(flags & FUSE_IOCTL_UNRESTRICTED))
+                       goto out;
+               in_iovs = outarg.in_iovs;
+               out_iovs = outarg.out_iovs;
+               /*
+                * Make sure things are in boundary, separate checks
+                * are to protect against overflow.
+                */
+               err = -ENOMEM;
+               if (in_iovs > FUSE_IOCTL_MAX_IOV ||
+                   out_iovs > FUSE_IOCTL_MAX_IOV ||
+                   in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
+                       goto out;
+               err = -EIO;
+               if ((in_iovs + out_iovs) * sizeof(struct iovec) != transferred)
+                       goto out;
+               /* okay, copy in iovs and retry */
+               vaddr = kmap_atomic(pages[0], KM_USER0);
+               memcpy(page_address(iov_page), vaddr, transferred);
+               kunmap_atomic(vaddr, KM_USER0);
+               in_iov = page_address(iov_page);
+               out_iov = in_iov + in_iovs;
+               goto retry;
+       }
+       err = -EIO;
+       if (transferred > inarg.out_size)
+               goto out;
+       err = fuse_ioctl_copy_user(pages, out_iov, out_iovs, transferred, true);
+  out:
+       if (req)
+               fuse_put_request(fc, req);
+       if (iov_page)
+               __free_page(iov_page);
+       while (num_pages)
+               __free_page(pages[--num_pages]);
+       kfree(pages);
+       return err ? err : outarg.result;
+ }
+ static long fuse_file_ioctl(struct file *file, unsigned int cmd,
+                           unsigned long arg)
+ {
+       return fuse_file_do_ioctl(file, cmd, arg, 0);
+ }
+ static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
+                                  unsigned long arg)
+ {
+       return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT);
+ }
+ /*
+  * All files which have been polled are linked to RB tree
+  * fuse_conn->polled_files which is indexed by kh.  Walk the tree and
+  * find the matching one.
+  */
+ static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh,
+                                             struct rb_node **parent_out)
+ {
+       struct rb_node **link = &fc->polled_files.rb_node;
+       struct rb_node *last = NULL;
+       while (*link) {
+               struct fuse_file *ff;
+               last = *link;
+               ff = rb_entry(last, struct fuse_file, polled_node);
+               if (kh < ff->kh)
+                       link = &last->rb_left;
+               else if (kh > ff->kh)
+                       link = &last->rb_right;
+               else
+                       return link;
+       }
+       if (parent_out)
+               *parent_out = last;
+       return link;
+ }
+ /*
+  * The file is about to be polled.  Make sure it's on the polled_files
+  * RB tree.  Note that files once added to the polled_files tree are
+  * not removed before the file is released.  This is because a file
+  * polled once is likely to be polled again.
+  */
+ static void fuse_register_polled_file(struct fuse_conn *fc,
+                                     struct fuse_file *ff)
+ {
+       spin_lock(&fc->lock);
+       if (RB_EMPTY_NODE(&ff->polled_node)) {
+               struct rb_node **link, *parent;
+               link = fuse_find_polled_node(fc, ff->kh, &parent);
+               BUG_ON(*link);
+               rb_link_node(&ff->polled_node, parent, link);
+               rb_insert_color(&ff->polled_node, &fc->polled_files);
+       }
+       spin_unlock(&fc->lock);
+ }
+ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
+ {
+       struct inode *inode = file->f_dentry->d_inode;
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh };
+       struct fuse_poll_out outarg;
+       struct fuse_req *req;
+       int err;
+       if (fc->no_poll)
+               return DEFAULT_POLLMASK;
+       poll_wait(file, &ff->poll_wait, wait);
+       /*
+        * Ask for notification iff there's someone waiting for it.
+        * The client may ignore the flag and always notify.
+        */
+       if (waitqueue_active(&ff->poll_wait)) {
+               inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY;
+               fuse_register_polled_file(fc, ff);
+       }
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
+       req->in.h.opcode = FUSE_POLL;
+       req->in.h.nodeid = get_node_id(inode);
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(inarg);
+       req->in.args[0].value = &inarg;
+       req->out.numargs = 1;
+       req->out.args[0].size = sizeof(outarg);
+       req->out.args[0].value = &outarg;
+       fuse_request_send(fc, req);
+       err = req->out.h.error;
+       fuse_put_request(fc, req);
+       if (!err)
+               return outarg.revents;
+       if (err == -ENOSYS) {
+               fc->no_poll = 1;
+               return DEFAULT_POLLMASK;
+       }
+       return POLLERR;
+ }
+ /*
+  * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
+  * wakes up the poll waiters.
+  */
+ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
+                           struct fuse_notify_poll_wakeup_out *outarg)
+ {
+       u64 kh = outarg->kh;
+       struct rb_node **link;
+       spin_lock(&fc->lock);
+       link = fuse_find_polled_node(fc, kh, NULL);
+       if (*link) {
+               struct fuse_file *ff;
+               ff = rb_entry(*link, struct fuse_file, polled_node);
+               wake_up_interruptible_sync(&ff->poll_wait);
+       }
+       spin_unlock(&fc->lock);
+       return 0;
+ }
  static const struct file_operations fuse_file_operations = {
        .llseek         = fuse_file_llseek,
        .read           = do_sync_read,
        .lock           = fuse_file_lock,
        .flock          = fuse_file_flock,
        .splice_read    = generic_file_splice_read,
+       .unlocked_ioctl = fuse_file_ioctl,
+       .compat_ioctl   = fuse_file_compat_ioctl,
+       .poll           = fuse_file_poll,
  };
  
  static const struct file_operations fuse_direct_io_file_operations = {
        .fsync          = fuse_fsync,
        .lock           = fuse_file_lock,
        .flock          = fuse_file_flock,
+       .unlocked_ioctl = fuse_file_ioctl,
+       .compat_ioctl   = fuse_file_compat_ioctl,
+       .poll           = fuse_file_poll,
        /* no mmap and splice_read */
  };