diff options
Diffstat (limited to 'fs/fuse/dev.c')
| -rw-r--r-- | fs/fuse/dev.c | 78 | 
1 files changed, 31 insertions, 47 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ef74ad5fd36..ca887314aba 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -643,9 +643,8 @@ struct fuse_copy_state {  	unsigned long seglen;  	unsigned long addr;  	struct page *pg; -	void *mapaddr; -	void *buf;  	unsigned len; +	unsigned offset;  	unsigned move_pages:1;  }; @@ -666,23 +665,17 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)  	if (cs->currbuf) {  		struct pipe_buffer *buf = cs->currbuf; -		if (!cs->write) { -			buf->ops->unmap(cs->pipe, buf, cs->mapaddr); -		} else { -			kunmap(buf->page); +		if (cs->write)  			buf->len = PAGE_SIZE - cs->len; -		}  		cs->currbuf = NULL; -		cs->mapaddr = NULL; -	} else if (cs->mapaddr) { -		kunmap(cs->pg); +	} else if (cs->pg) {  		if (cs->write) {  			flush_dcache_page(cs->pg);  			set_page_dirty_lock(cs->pg);  		}  		put_page(cs->pg); -		cs->mapaddr = NULL;  	} +	cs->pg = NULL;  }  /* @@ -691,7 +684,7 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)   */  static int fuse_copy_fill(struct fuse_copy_state *cs)  { -	unsigned long offset; +	struct page *page;  	int err;  	unlock_request(cs->fc, cs->req); @@ -706,14 +699,12 @@ 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, 0); +			cs->pg = buf->page; +			cs->offset = buf->offset;  			cs->len = buf->len; -			cs->buf = cs->mapaddr + buf->offset;  			cs->pipebufs++;  			cs->nr_segs--;  		} else { -			struct page *page; -  			if (cs->nr_segs == cs->pipe->buffers)  				return -EIO; @@ -726,8 +717,8 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)  			buf->len = 0;  			cs->currbuf = buf; -			cs->mapaddr = kmap(page); -			cs->buf = cs->mapaddr; +			cs->pg = page; +			cs->offset = 0;  			cs->len = PAGE_SIZE;  			cs->pipebufs++;  			cs->nr_segs++; @@ -740,14 +731,13 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)  			cs->iov++;  			cs->nr_segs--;  		} -		err = get_user_pages_fast(cs->addr, 1, cs->write, &cs->pg); +		err = get_user_pages_fast(cs->addr, 1, cs->write, &page);  		if (err < 0)  			return err;  		BUG_ON(err != 1); -		offset = cs->addr % PAGE_SIZE; -		cs->mapaddr = kmap(cs->pg); -		cs->buf = cs->mapaddr + offset; -		cs->len = min(PAGE_SIZE - offset, cs->seglen); +		cs->pg = page; +		cs->offset = cs->addr % PAGE_SIZE; +		cs->len = min(PAGE_SIZE - cs->offset, cs->seglen);  		cs->seglen -= cs->len;  		cs->addr += cs->len;  	} @@ -760,15 +750,20 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)  {  	unsigned ncpy = min(*size, cs->len);  	if (val) { +		void *pgaddr = kmap_atomic(cs->pg); +		void *buf = pgaddr + cs->offset; +  		if (cs->write) -			memcpy(cs->buf, *val, ncpy); +			memcpy(buf, *val, ncpy);  		else -			memcpy(*val, cs->buf, ncpy); +			memcpy(*val, buf, ncpy); + +		kunmap_atomic(pgaddr);  		*val += ncpy;  	}  	*size -= ncpy;  	cs->len -= ncpy; -	cs->buf += ncpy; +	cs->offset += ncpy;  	return ncpy;  } @@ -874,8 +869,8 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)  out_fallback_unlock:  	unlock_page(newpage);  out_fallback: -	cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); -	cs->buf = cs->mapaddr + buf->offset; +	cs->pg = buf->page; +	cs->offset = buf->offset;  	err = lock_request(cs->fc, cs->req);  	if (err) @@ -1296,22 +1291,6 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,  	return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs));  } -static int fuse_dev_pipe_buf_steal(struct pipe_inode_info *pipe, -				   struct pipe_buffer *buf) -{ -	return 1; -} - -static const struct pipe_buf_operations fuse_dev_pipe_buf_ops = { -	.can_merge = 0, -	.map = generic_pipe_buf_map, -	.unmap = generic_pipe_buf_unmap, -	.confirm = generic_pipe_buf_confirm, -	.release = generic_pipe_buf_release, -	.steal = fuse_dev_pipe_buf_steal, -	.get = generic_pipe_buf_get, -}; -  static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,  				    struct pipe_inode_info *pipe,  				    size_t len, unsigned int flags) @@ -1358,7 +1337,11 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,  		buf->page = bufs[page_nr].page;  		buf->offset = bufs[page_nr].offset;  		buf->len = bufs[page_nr].len; -		buf->ops = &fuse_dev_pipe_buf_ops; +		/* +		 * Need to be careful about this.  Having buf->ops in module +		 * code can Oops if the buffer persists after module unload. +		 */ +		buf->ops = &nosteal_pipe_buf_ops;  		pipe->nrbufs++;  		page_nr++; @@ -1599,7 +1582,8 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,  		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)) +		if (!err && offset == 0 && +		    (this_num == PAGE_CACHE_SIZE || file_size == end))  			SetPageUptodate(page);  		unlock_page(page);  		page_cache_release(page); @@ -1625,7 +1609,7 @@ out_finish:  static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req)  { -	release_pages(req->pages, req->num_pages, 0); +	release_pages(req->pages, req->num_pages, false);  }  static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,  | 
