X-Git-Url: http://git.cascardo.info/?a=blobdiff_plain;f=fs%2Fpipe.c;h=8e0d9f26dfadc4b5849a9f46a933a89ea2aa49ca;hb=a442950d4af036006757bc3da00a2cb939fb2f59;hp=dc5f4c040890e9c53ff888623068cc376b2da496;hpb=b0b91d18e2e97b741b294af9333824ecc3fadfd8;p=cascardo%2Flinux.git diff --git a/fs/pipe.c b/fs/pipe.c index dc5f4c040890..8e0d9f26dfad 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -601,54 +601,63 @@ pipe_fasync(int fd, struct file *filp, int on) return retval; } -static void account_pipe_buffers(struct user_struct *user, +static unsigned long account_pipe_buffers(struct user_struct *user, unsigned long old, unsigned long new) { - atomic_long_add(new - old, &user->pipe_bufs); + return atomic_long_add_return(new - old, &user->pipe_bufs); } -static bool too_many_pipe_buffers_soft(struct user_struct *user) +static bool too_many_pipe_buffers_soft(unsigned long user_bufs) { - return pipe_user_pages_soft && - atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; + return pipe_user_pages_soft && user_bufs >= pipe_user_pages_soft; } -static bool too_many_pipe_buffers_hard(struct user_struct *user) +static bool too_many_pipe_buffers_hard(unsigned long user_bufs) { - return pipe_user_pages_hard && - atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; + return pipe_user_pages_hard && user_bufs >= pipe_user_pages_hard; } struct pipe_inode_info *alloc_pipe_info(void) { struct pipe_inode_info *pipe; + unsigned long pipe_bufs = PIPE_DEF_BUFFERS; + struct user_struct *user = get_current_user(); + unsigned long user_bufs; pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL_ACCOUNT); - if (pipe) { - unsigned long pipe_bufs = PIPE_DEF_BUFFERS; - struct user_struct *user = get_current_user(); - - if (!too_many_pipe_buffers_hard(user)) { - if (too_many_pipe_buffers_soft(user)) - pipe_bufs = 1; - pipe->bufs = kcalloc(pipe_bufs, - sizeof(struct pipe_buffer), - GFP_KERNEL_ACCOUNT); - } + if (pipe == NULL) + goto out_free_uid; - if (pipe->bufs) { - init_waitqueue_head(&pipe->wait); - pipe->r_counter = pipe->w_counter = 1; - pipe->buffers = pipe_bufs; - pipe->user = user; - account_pipe_buffers(user, 0, pipe_bufs); - mutex_init(&pipe->mutex); - return pipe; - } - free_uid(user); - kfree(pipe); + if (pipe_bufs * PAGE_SIZE > pipe_max_size && !capable(CAP_SYS_RESOURCE)) + pipe_bufs = pipe_max_size >> PAGE_SHIFT; + + user_bufs = account_pipe_buffers(user, 0, pipe_bufs); + + if (too_many_pipe_buffers_soft(user_bufs)) { + user_bufs = account_pipe_buffers(user, pipe_bufs, 1); + pipe_bufs = 1; } + if (too_many_pipe_buffers_hard(user_bufs)) + goto out_revert_acct; + + pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer), + GFP_KERNEL_ACCOUNT); + + if (pipe->bufs) { + init_waitqueue_head(&pipe->wait); + pipe->r_counter = pipe->w_counter = 1; + pipe->buffers = pipe_bufs; + pipe->user = user; + mutex_init(&pipe->mutex); + return pipe; + } + +out_revert_acct: + (void) account_pipe_buffers(user, pipe_bufs, 0); + kfree(pipe); +out_free_uid: + free_uid(user); return NULL; } @@ -656,7 +665,7 @@ void free_pipe_info(struct pipe_inode_info *pipe) { int i; - account_pipe_buffers(pipe->user, pipe->buffers, 0); + (void) account_pipe_buffers(pipe->user, pipe->buffers, 0); free_uid(pipe->user); for (i = 0; i < pipe->buffers; i++) { struct pipe_buffer *buf = pipe->bufs + i; @@ -1027,6 +1036,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) { struct pipe_buffer *bufs; unsigned int size, nr_pages; + unsigned long user_bufs; long ret = 0; size = round_pipe_size(arg); @@ -1046,11 +1056,11 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) size > pipe_max_size && !capable(CAP_SYS_RESOURCE)) return -EPERM; - account_pipe_buffers(pipe->user, pipe->buffers, nr_pages); + user_bufs = account_pipe_buffers(pipe->user, pipe->buffers, nr_pages); if (nr_pages > pipe->buffers && - (too_many_pipe_buffers_hard(pipe->user) || - too_many_pipe_buffers_soft(pipe->user)) && + (too_many_pipe_buffers_hard(user_bufs) || + too_many_pipe_buffers_soft(user_bufs)) && !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { ret = -EPERM; goto out_revert_acct; @@ -1102,7 +1112,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) return nr_pages * PAGE_SIZE; out_revert_acct: - account_pipe_buffers(pipe->user, nr_pages, pipe->buffers); + (void) account_pipe_buffers(pipe->user, nr_pages, pipe->buffers); return ret; }