Merge git://git.infradead.org/iommu-2.6
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / fuse / dev.c
index 9424796..69ad053 100644 (file)
@@ -239,7 +239,6 @@ static u64 fuse_get_unique(struct fuse_conn *fc)
 
 static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
 {
-       req->in.h.unique = fuse_get_unique(fc);
        req->in.h.len = sizeof(struct fuse_in_header) +
                len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
        list_add_tail(&req->list, &fc->pending);
@@ -261,6 +260,7 @@ static void flush_bg_queue(struct fuse_conn *fc)
                req = list_entry(fc->bg_queue.next, struct fuse_req, list);
                list_del(&req->list);
                fc->active_background++;
+               req->in.h.unique = fuse_get_unique(fc);
                queue_request(fc, req);
        }
 }
@@ -398,6 +398,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
        else if (fc->conn_error)
                req->out.h.error = -ECONNREFUSED;
        else {
+               req->in.h.unique = fuse_get_unique(fc);
                queue_request(fc, req);
                /* acquire extra reference, since request is still needed
                   after request_end() */
@@ -450,6 +451,23 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 }
 EXPORT_SYMBOL_GPL(fuse_request_send_background);
 
+static int fuse_request_send_notify_reply(struct fuse_conn *fc,
+                                         struct fuse_req *req, u64 unique)
+{
+       int err = -ENODEV;
+
+       req->isreply = 0;
+       req->in.h.unique = unique;
+       spin_lock(&fc->lock);
+       if (fc->connected) {
+               queue_request(fc, req);
+               err = 0;
+       }
+       spin_unlock(&fc->lock);
+
+       return err;
+}
+
 /*
  * Called under fc->lock
  *
@@ -535,13 +553,13 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)
                if (!cs->write) {
                        buf->ops->unmap(cs->pipe, buf, cs->mapaddr);
                } else {
-                       kunmap_atomic(cs->mapaddr, KM_USER0);
+                       kunmap(buf->page);
                        buf->len = PAGE_SIZE - cs->len;
                }
                cs->currbuf = NULL;
                cs->mapaddr = NULL;
        } else if (cs->mapaddr) {
-               kunmap_atomic(cs->mapaddr, KM_USER0);
+               kunmap(cs->pg);
                if (cs->write) {
                        flush_dcache_page(cs->pg);
                        set_page_dirty_lock(cs->pg);
@@ -572,7 +590,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
 
                        BUG_ON(!cs->nr_segs);
                        cs->currbuf = buf;
-                       cs->mapaddr = buf->ops->map(cs->pipe, buf, 1);
+                       cs->mapaddr = buf->ops->map(cs->pipe, buf, 0);
                        cs->len = buf->len;
                        cs->buf = cs->mapaddr + buf->offset;
                        cs->pipebufs++;
@@ -592,7 +610,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
                        buf->len = 0;
 
                        cs->currbuf = buf;
-                       cs->mapaddr = kmap_atomic(page, KM_USER0);
+                       cs->mapaddr = kmap(page);
                        cs->buf = cs->mapaddr;
                        cs->len = PAGE_SIZE;
                        cs->pipebufs++;
@@ -611,7 +629,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
                        return err;
                BUG_ON(err != 1);
                offset = cs->addr % PAGE_SIZE;
-               cs->mapaddr = kmap_atomic(cs->pg, KM_USER0);
+               cs->mapaddr = kmap(cs->pg);
                cs->buf = cs->mapaddr + offset;
                cs->len = min(PAGE_SIZE - offset, cs->seglen);
                cs->seglen -= cs->len;
@@ -1231,6 +1249,199 @@ err:
        return err;
 }
 
+static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
+                            struct fuse_copy_state *cs)
+{
+       struct fuse_notify_store_out outarg;
+       struct inode *inode;
+       struct address_space *mapping;
+       u64 nodeid;
+       int err;
+       pgoff_t index;
+       unsigned int offset;
+       unsigned int num;
+       loff_t file_size;
+       loff_t end;
+
+       err = -EINVAL;
+       if (size < sizeof(outarg))
+               goto out_finish;
+
+       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
+       if (err)
+               goto out_finish;
+
+       err = -EINVAL;
+       if (size - sizeof(outarg) != outarg.size)
+               goto out_finish;
+
+       nodeid = outarg.nodeid;
+
+       down_read(&fc->killsb);
+
+       err = -ENOENT;
+       if (!fc->sb)
+               goto out_up_killsb;
+
+       inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
+       if (!inode)
+               goto out_up_killsb;
+
+       mapping = inode->i_mapping;
+       index = outarg.offset >> PAGE_CACHE_SHIFT;
+       offset = outarg.offset & ~PAGE_CACHE_MASK;
+       file_size = i_size_read(inode);
+       end = outarg.offset + outarg.size;
+       if (end > file_size) {
+               file_size = end;
+               fuse_write_update_size(inode, file_size);
+       }
+
+       num = outarg.size;
+       while (num) {
+               struct page *page;
+               unsigned int this_num;
+
+               err = -ENOMEM;
+               page = find_or_create_page(mapping, index,
+                                          mapping_gfp_mask(mapping));
+               if (!page)
+                       goto out_iput;
+
+               this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
+               err = fuse_copy_page(cs, &page, offset, this_num, 0);
+               if (!err && offset == 0 && (num != 0 || file_size == end))
+                       SetPageUptodate(page);
+               unlock_page(page);
+               page_cache_release(page);
+
+               if (err)
+                       goto out_iput;
+
+               num -= this_num;
+               offset = 0;
+               index++;
+       }
+
+       err = 0;
+
+out_iput:
+       iput(inode);
+out_up_killsb:
+       up_read(&fc->killsb);
+out_finish:
+       fuse_copy_finish(cs);
+       return err;
+}
+
+static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req)
+{
+       int i;
+
+       for (i = 0; i < req->num_pages; i++) {
+               struct page *page = req->pages[i];
+               page_cache_release(page);
+       }
+}
+
+static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
+                        struct fuse_notify_retrieve_out *outarg)
+{
+       int err;
+       struct address_space *mapping = inode->i_mapping;
+       struct fuse_req *req;
+       pgoff_t index;
+       loff_t file_size;
+       unsigned int num;
+       unsigned int offset;
+       size_t total_len;
+
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
+
+       offset = outarg->offset & ~PAGE_CACHE_MASK;
+
+       req->in.h.opcode = FUSE_NOTIFY_REPLY;
+       req->in.h.nodeid = outarg->nodeid;
+       req->in.numargs = 2;
+       req->in.argpages = 1;
+       req->page_offset = offset;
+       req->end = fuse_retrieve_end;
+
+       index = outarg->offset >> PAGE_CACHE_SHIFT;
+       file_size = i_size_read(inode);
+       num = outarg->size;
+       if (outarg->offset > file_size)
+               num = 0;
+       else if (outarg->offset + num > file_size)
+               num = file_size - outarg->offset;
+
+       while (num) {
+               struct page *page;
+               unsigned int this_num;
+
+               page = find_get_page(mapping, index);
+               if (!page)
+                       break;
+
+               this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
+               req->pages[req->num_pages] = page;
+               req->num_pages++;
+
+               num -= this_num;
+               total_len += this_num;
+       }
+       req->misc.retrieve_in.offset = outarg->offset;
+       req->misc.retrieve_in.size = total_len;
+       req->in.args[0].size = sizeof(req->misc.retrieve_in);
+       req->in.args[0].value = &req->misc.retrieve_in;
+       req->in.args[1].size = total_len;
+
+       err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique);
+       if (err)
+               fuse_retrieve_end(fc, req);
+
+       return err;
+}
+
+static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
+                               struct fuse_copy_state *cs)
+{
+       struct fuse_notify_retrieve_out outarg;
+       struct inode *inode;
+       int err;
+
+       err = -EINVAL;
+       if (size != sizeof(outarg))
+               goto copy_finish;
+
+       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
+       if (err)
+               goto copy_finish;
+
+       fuse_copy_finish(cs);
+
+       down_read(&fc->killsb);
+       err = -ENOENT;
+       if (fc->sb) {
+               u64 nodeid = outarg.nodeid;
+
+               inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
+               if (inode) {
+                       err = fuse_retrieve(fc, inode, &outarg);
+                       iput(inode);
+               }
+       }
+       up_read(&fc->killsb);
+
+       return err;
+
+copy_finish:
+       fuse_copy_finish(cs);
+       return err;
+}
+
 static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
                       unsigned int size, struct fuse_copy_state *cs)
 {
@@ -1244,6 +1455,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
        case FUSE_NOTIFY_INVAL_ENTRY:
                return fuse_notify_inval_entry(fc, size, cs);
 
+       case FUSE_NOTIFY_STORE:
+               return fuse_notify_store(fc, size, cs);
+
+       case FUSE_NOTIFY_RETRIEVE:
+               return fuse_notify_retrieve(fc, size, cs);
+
        default:
                fuse_copy_finish(cs);
                return -EINVAL;