struct pipe_buffer *buf)
{
struct page *page = buf->page;
- struct address_space *mapping = page_mapping(page);
+ struct address_space *mapping;
lock_page(page);
- WARN_ON(!PageUptodate(page));
+ mapping = page_mapping(page);
+ if (mapping) {
+ WARN_ON(!PageUptodate(page));
- /*
- * At least for ext2 with nobh option, we need to wait on writeback
- * completing on this page, since we'll remove it from the pagecache.
- * Otherwise truncate wont wait on the page, allowing the disk
- * blocks to be reused by someone else before we actually wrote our
- * data to them. fs corruption ensues.
- */
- wait_on_page_writeback(page);
+ /*
+ * At least for ext2 with nobh option, we need to wait on
+ * writeback completing on this page, since we'll remove it
+ * from the pagecache. Otherwise truncate wont wait on the
+ * page, allowing the disk blocks to be reused by someone else
+ * before we actually wrote our data to them. fs corruption
+ * ensues.
+ */
+ wait_on_page_writeback(page);
- if (PagePrivate(page))
- try_to_release_page(page, mapping_gfp_mask(mapping));
+ if (PagePrivate(page))
+ try_to_release_page(page, mapping_gfp_mask(mapping));
- if (!remove_mapping(mapping, page)) {
- unlock_page(page);
- return 1;
+ /*
+ * If we succeeded in removing the mapping, set LRU flag
+ * and return good.
+ */
+ if (remove_mapping(mapping, page)) {
+ buf->flags |= PIPE_BUF_FLAG_LRU;
+ return 0;
+ }
}
- buf->flags |= PIPE_BUF_FLAG_LRU;
- return 0;
+ /*
+ * Raced with truncate or failed to remove page from current
+ * address space, unlock and return failure.
+ */
+ unlock_page(page);
+ return 1;
}
static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
ret = -ENOMEM;
page = page_cache_alloc_cold(mapping);
if (unlikely(!page))
- goto out_nomem;
+ goto out_ret;
/*
* This will also lock the page
if (sd->pos + this_len > isize)
vmtruncate(mapping->host, isize);
- goto out;
+ goto out_ret;
}
if (buf->page != page) {
out:
page_cache_release(page);
unlock_page(page);
-out_nomem:
+out_ret:
return ret;
}
EXPORT_SYMBOL(do_splice_direct);
+/*
+ * After the inode slimming patch, i_pipe/i_bdev/i_cdev share the same
+ * location, so checking ->i_pipe is not enough to verify that this is a
+ * pipe.
+ */
+static inline int is_pipe(struct inode *inode)
+{
+ if (inode->i_pipe && S_ISFIFO(inode->i_mode))
+ return 1;
+
+ return 0;
+}
+
/*
* Determine where to splice to/from.
*/
loff_t offset, *off;
long ret;
- pipe = in->f_dentry->d_inode->i_pipe;
- if (pipe) {
+ if (is_pipe(in->f_dentry->d_inode)) {
+ pipe = in->f_dentry->d_inode->i_pipe;
if (off_in)
return -ESPIPE;
if (off_out) {
return ret;
}
- pipe = out->f_dentry->d_inode->i_pipe;
- if (pipe) {
+ if (is_pipe(out->f_dentry->d_inode)) {
+ pipe = out->f_dentry->d_inode->i_pipe;
if (off_out)
return -ESPIPE;
if (off_in) {
static long do_vmsplice(struct file *file, const struct iovec __user *iov,
unsigned long nr_segs, unsigned int flags)
{
- struct pipe_inode_info *pipe = file->f_dentry->d_inode->i_pipe;
+ struct pipe_inode_info *pipe;
struct page *pages[PIPE_BUFFERS];
struct partial_page partial[PIPE_BUFFERS];
struct splice_pipe_desc spd = {
.ops = &user_page_pipe_buf_ops,
};
- if (unlikely(!pipe))
+ if (!is_pipe(file->f_dentry->d_inode))
return -EBADF;
if (unlikely(nr_segs > UIO_MAXIOV))
return -EINVAL;
if (spd.nr_pages <= 0)
return spd.nr_pages;
+ pipe = file->f_dentry->d_inode->i_pipe;
return splice_to_pipe(pipe, &spd);
}
* could deadlock (one doing tee from A -> B, the other from B -> A).
*/
if (ipipe->inode < opipe->inode) {
- mutex_lock(&ipipe->inode->i_mutex);
- mutex_lock(&opipe->inode->i_mutex);
+ mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_CHILD);
} else {
- mutex_lock(&opipe->inode->i_mutex);
- mutex_lock(&ipipe->inode->i_mutex);
+ mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_CHILD);
}
do {
static long do_tee(struct file *in, struct file *out, size_t len,
unsigned int flags)
{
- struct pipe_inode_info *ipipe = in->f_dentry->d_inode->i_pipe;
- struct pipe_inode_info *opipe = out->f_dentry->d_inode->i_pipe;
+ struct pipe_inode_info *ipipe;
+ struct pipe_inode_info *opipe;
int ret = -EINVAL;
+ if (!is_pipe(in->f_dentry->d_inode) || !is_pipe(out->f_dentry->d_inode))
+ return ret;
+
/*
* Duplicate the contents of ipipe to opipe without actually
* copying the data.
*/
- if (ipipe && opipe && ipipe != opipe) {
+ ipipe = in->f_dentry->d_inode->i_pipe;
+ opipe = out->f_dentry->d_inode->i_pipe;
+ if (ipipe != opipe) {
/*
* Keep going, unless we encounter an error. The ipipe/opipe
* ordering doesn't really matter.