dynamic pipe resizing
RFC PATCH 1/1 dynamic pipe resizing
Hi,
Dabbling around with splice a bit, I added some code to change the size of a pipe. Currently it's hardcoded as 16 pages, with this patch you can shrink (if you wanted) or grow (the likely scenario) if you want to increase the size of your in-kernel buffer for splice operations.
Like with my original splice patches from 2005, I used fcntl() F_GETPIPE_SZ and F_SETPIPE_SZ to change the size of the pipe. I'm not particularly fond of that interface, so suggestions on how to improve it would be appreciated. Even if fcntl() should be the preferred approach, I think it would be better to pass in a byte based value instead of a number of pages.
fs/fcntl.c +5 -0
@@ -18,6 +18,7 @@#include <linux/ptrace.h> #include <linux/signal.h> #include <linux/rcupdate.h> +#include <linux/pipe_fs_i.h> #include <asm/poll.h> #include <asm/siginfo.h>
@@ -376,6 +377,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,case F_NOTIFY : err =fcntl_dirnotify (fd, filp, arg);break ; +case F_SETPIPE_SZ : +case F_GETPIPE_SZ : + err =pipe_fcntl (filp, cmd, arg); +break ;default :break ; }
fs/pipe.c +94 -12
168 lines not on this page · raw message
fs/splice.c +73 -30
236 lines not on this page · raw message
include/asm-generic/fcntl.h +7 -0
@@ -73,6 +73,13 @@#define F_SETSIG 10/* for sockets. */ #define F_GETSIG 11/* for sockets. */ #endif +#ifndef F_SETPIPE_SZ /* for growing pipe sizes */ +#define F_SETPIPE_SZ 12 +#endif +#ifndef F_GETPIPE_SZ +#define F_GETPIPE_SZ 13 +#endif +/* for F_[GET|SET]FL */ #define FD_CLOEXEC 1/* actually anything with low bit set goes */
include/linux/pipe_fs_i.h +7 -4
@@ -3,7 +3,7 @@#define PIPEFS_MAGIC 0x50495045 -#define PIPE_BUFFERS (16) +#define PIPE_DEF_BUFFERS 16#define PIPE_BUF_FLAG_LRU 0x01/* page is on the LRU */ #define PIPE_BUF_FLAG_ATOMIC 0x02/* was atomically mapped */
@@ -44,17 +44,17 @@ struct pipe_buffer {**/ struct pipe_inode_info {wait_queue_head_t wait; -unsigned int nrbufs, curbuf; -struct page *tmp_page; +unsigned int nrbufs, curbuf, buffers;unsigned int readers;unsigned int writers;unsigned int waiting_writers;unsigned int r_counter;unsigned int w_counter; +struct page *tmp_page;struct fasync_struct *fasync_readers;struct fasync_struct *fasync_writers;struct inode *inode; -struct pipe_buffer bufs[PIPE_BUFFERS ]; +struct pipe_buffer *bufs; };/*
@@ -148,4 +148,7 @@ void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);int generic_pipe_buf_confirm (struct pipe_inode_info *,struct pipe_buffer *);int generic_pipe_buf_steal (struct pipe_inode_info *,struct pipe_buffer *); +/* for F_SETPIPE_SZ and F_GETPIPE_SZ */ +long pipe_fcntl (struct file *,unsigned int ,unsigned long arg); +#endif
include/linux/splice.h +7 -0
@@ -70,4 +70,11 @@ extern ssize_t splice_to_pipe(struct pipe_inode_info *,extern ssize_t splice_direct_to_actor (struct file *,struct splice_desc *,splice_direct_actor *); +/* +* for dynamic pipe sizing +*/ +extern int splice_grow_spd (struct pipe_inode_info *,struct splice_pipe_desc *); +extern void splice_shrink_spd (struct pipe_inode_info *, +struct splice_pipe_desc *); +#endif
kernel/relay.c +9 -4
@@ -1080,8 +1080,8 @@ static int subbuf_splice_actor(struct file *in,size_t read_subbuf = read_start / subbuf_size;size_t padding = rbuf->padding[read_subbuf];size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding; -struct page *pages[PIPE_BUFFERS ]; -struct partial_page partial[PIPE_BUFFERS ]; +struct page *pages[PIPE_DEF_BUFFERS ]; +struct partial_page partial[PIPE_DEF_BUFFERS ];struct splice_pipe_desc spd = { .pages = pages, .nr_pages =0 ,
@@ -1092,6 +1092,8 @@ static int subbuf_splice_actor(struct file *in,if (rbuf->subbufs_produced == rbuf->subbufs_consumed)return 0 ; +if (splice_grow_spd (pipe, &spd)) +return -ENOMEM ;/* * Adjust read len, if longer than what is available
@@ -1135,16 +1137,19 @@ static int subbuf_splice_actor(struct file *in, } } + ret =0 ;if (!spd.nr_pages) -return 0 ; + goto out; ret = *nonpad_ret =splice_to_pipe (pipe, &spd);if (ret <0 || ret < total_len) -return ret; + goto out;if (read_start + ret == nonpad_end) ret += padding; +out: +splice_shrink_spd (pipe, &spd);return ret; }
Signature · 1 line
Jens Axboe
Quoted 6 lines · Jens Axboe
Hi,
Dabbling around with splice a bit, I added some code to change the size of a pipe. Currently it's hardcoded as 16 pages, with this patch you can shrink (if you wanted) or grow (the likely scenario) if you want to increase the size of your in-kernel buffer for splice operations.
Wonderful, I just did the same {-.-}
(but then dropped the patch because it did not help my application at all, so never mind).
Quoted 6 lines
Like with my original splice patches from 2005, I used
fcntl()F_GETPIPE_SZandF_SETPIPE_SZto change the size of the pipe. I'm not particularly fond of that interface, so suggestions on how to improve it would be appreciated. Even iffcntl()should be the preferred approach, I think it would be better to pass in a byte based value instead of a number of pages.
And using % pipe->buffers then?
Jan
Quoted 13 lines · Jan Engelhardt
Quoted 6 lines · Jens Axboe
Hi,
Dabbling around with splice a bit, I added some code to change the size of a pipe. Currently it's hardcoded as 16 pages, with this patch you can shrink (if you wanted) or grow (the likely scenario) if you want to increase the size of your in-kernel buffer for splice operations.
Wonderful, I just did the same {-.-}
(but then dropped the patch because it did not help my application at all, so never mind).
Quoted 8 lines
Quoted 6 lines
Like with my original splice patches from 2005, I used
fcntl()F_GETPIPE_SZandF_SETPIPE_SZto change the size of the pipe. I'm not particularly fond of that interface, so suggestions on how to improve it would be appreciated. Even iffcntl()should be the preferred approach, I think it would be better to pass in a byte based value instead of a number of pages.And using %
pipe->buffersthen?
I'd probably prefer sticking to power-of-2 array sizes, so just round it up appropriately. But it's likely not a big deal.
Signature · 1 line
Jens Axboe
Quoted 9 lines · Jens Axboe
Subject: [PATCH][RFC] dynamic pipe resizing
Hi,
Dabbling around with splice a bit, I added some code to change the size of a pipe. Currently it's hardcoded as 16 pages, with this patch you can shrink (if you wanted) or grow (the likely scenario) if you want to increase the size of your in-kernel buffer for splice operations.
Quoted 7 lines
Like with my original splice patches from 2005, I used
fcntl()F_GETPIPE_SZandF_SETPIPE_SZto change the size of the pipe. I'm not particularly fond of that interface, so suggestions on how to improve it would be appreciated. Even iffcntl()should be the preferred approach, I think it would be better to pass in a byte based value instead of a number of pages.
Could this patch still make it in?
Yes, I think its set() and get() parts should use bytes and convert to/from pages.
Quoted 4 lines
/*
+ * Allocate a new array of pipe buffers and copy the info over. Returns the
+ * pipe size if successful, or return-ERRORon error.
+ */
fs/pipe.c int do_pipe(int *fd) · line 1099
Quoted 10 lines
fs/pipe.c +10
-0+static long pipe_set_size (struct pipe_inode_info *pipe,unsigned long arg) +{ +struct pipe_buffer *bufs; + +/* +* Must be a power-of-2 current +*/ +if (arg & (arg -1 )) +return -EINVAL ; +
Perhaps just round up, and mention the rounding in the manpage, so that noone gets a shock when the pipe is not exactly as small as requested.
fs/pipe.c int do_pipe(int *fd) · line 1107
Quoted 8 lines
fs/pipe.c +8
-0+/* +* We can shrink the pipe, if arg >= pipe->nrbufs. Since we don't +* expect a lot of shrink+grow operations, just free and allocate +* again like we would do for growing. If the pipe currently +* contains more buffers than arg, then return busy. +*/ +if (arg < pipe->nrbufs) +return -EBUSY ;
How big can a pipe become? k*alloc has a certain limit, so should perhaps vmalloc be used for when kalloc fails?
Quoted 9 lines · Jan Engelhardt
Quoted 8 lines · Jens Axboe
Subject: [PATCH][RFC] dynamic pipe resizing
Like with my original splice patches from 2005, I usedfcntl()F_GETPIPE_SZandF_SETPIPE_SZto change the size of the pipe. I'm not particularly fond of that interface, so suggestions on how to improve it would be appreciated. Even iffcntl()should be the preferred approach, I think it would be better to pass in a byte based value instead of a number of pages.
Quoted 6 lines
Could this patch still make it in?
Yes, I think itsset()andget()parts should use bytes and convert to/from pages.Perhaps just round up, and mention the rounding in the manpage, so that noone gets a shock when the pipe is not exactly as small as requested.
Yes, but document only that it is rounding and make the unit arbitrary. That reduces the ABI requirements.
Best Regards
Ingo Oeser