]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - fs/pipe.c
Merge branch 'page-refs' (page ref overflow)
[thirdparty/linux.git] / fs / pipe.c
index b1543b85c14a4427a8659f1656ea140cd913f9e6..41065901106b09d4365ebc13ee6cfa7b6465339b 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -140,8 +140,7 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe,
        struct page *page = buf->page;
 
        if (page_count(page) == 1) {
-               if (memcg_kmem_enabled())
-                       memcg_kmem_uncharge(page, 0);
+               memcg_kmem_uncharge(page, 0);
                __SetPageLocked(page);
                return 0;
        }
@@ -226,8 +225,15 @@ void generic_pipe_buf_release(struct pipe_inode_info *pipe,
 }
 EXPORT_SYMBOL(generic_pipe_buf_release);
 
+/* New data written to a pipe may be appended to a buffer with this type. */
 static const struct pipe_buf_operations anon_pipe_buf_ops = {
-       .can_merge = 1,
+       .confirm = generic_pipe_buf_confirm,
+       .release = anon_pipe_buf_release,
+       .steal = anon_pipe_buf_steal,
+       .get = generic_pipe_buf_get,
+};
+
+static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = {
        .confirm = generic_pipe_buf_confirm,
        .release = anon_pipe_buf_release,
        .steal = anon_pipe_buf_steal,
@@ -235,13 +241,32 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = {
 };
 
 static const struct pipe_buf_operations packet_pipe_buf_ops = {
-       .can_merge = 0,
        .confirm = generic_pipe_buf_confirm,
        .release = anon_pipe_buf_release,
        .steal = anon_pipe_buf_steal,
        .get = generic_pipe_buf_get,
 };
 
+/**
+ * pipe_buf_mark_unmergeable - mark a &struct pipe_buffer as unmergeable
+ * @buf:       the buffer to mark
+ *
+ * Description:
+ *     This function ensures that no future writes will be merged into the
+ *     given &struct pipe_buffer. This is necessary when multiple pipe buffers
+ *     share the same backing page.
+ */
+void pipe_buf_mark_unmergeable(struct pipe_buffer *buf)
+{
+       if (buf->ops == &anon_pipe_buf_ops)
+               buf->ops = &anon_pipe_buf_nomerge_ops;
+}
+
+static bool pipe_buf_can_merge(struct pipe_buffer *buf)
+{
+       return buf->ops == &anon_pipe_buf_ops;
+}
+
 static ssize_t
 pipe_read(struct kiocb *iocb, struct iov_iter *to)
 {
@@ -379,7 +404,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                struct pipe_buffer *buf = pipe->bufs + lastbuf;
                int offset = buf->offset + buf->len;
 
-               if (buf->ops->can_merge && offset + chars <= PAGE_SIZE) {
+               if (pipe_buf_can_merge(buf) && offset + chars <= PAGE_SIZE) {
                        ret = pipe_buf_confirm(pipe, buf);
                        if (ret)
                                goto out;