diff options
Diffstat (limited to 'net/socket.c')
-rw-r--r-- | net/socket.c | 62 |
1 files changed, 47 insertions, 15 deletions
diff --git a/net/socket.c b/net/socket.c index f10f1d947c78..af57d85bcb48 100644 --- a/net/socket.c +++ b/net/socket.c @@ -117,8 +117,10 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from); static int sock_mmap(struct file *file, struct vm_area_struct *vma); static int sock_close(struct inode *inode, struct file *file); -static __poll_t sock_poll(struct file *file, - struct poll_table_struct *wait); +static struct wait_queue_head *sock_get_poll_head(struct file *file, + __poll_t events); +static __poll_t sock_poll_mask(struct file *file, __poll_t); +static __poll_t sock_poll(struct file *file, struct poll_table_struct *wait); static long sock_ioctl(struct file *file, unsigned int cmd, unsigned long arg); #ifdef CONFIG_COMPAT static long compat_sock_ioctl(struct file *file, @@ -141,6 +143,8 @@ static const struct file_operations socket_file_ops = { .llseek = no_llseek, .read_iter = sock_read_iter, .write_iter = sock_write_iter, + .get_poll_head = sock_get_poll_head, + .poll_mask = sock_poll_mask, .poll = sock_poll, .unlocked_ioctl = sock_ioctl, #ifdef CONFIG_COMPAT @@ -1114,27 +1118,48 @@ out_release: } EXPORT_SYMBOL(sock_create_lite); -/* No kernel lock held - perfect */ -static __poll_t sock_poll(struct file *file, poll_table *wait) +static struct wait_queue_head *sock_get_poll_head(struct file *file, + __poll_t events) { - __poll_t busy_flag = 0; - struct socket *sock; + struct socket *sock = file->private_data; + + if (!sock->ops->poll_mask) + return NULL; + sock_poll_busy_loop(sock, events); + return sk_sleep(sock->sk); +} + +static __poll_t sock_poll_mask(struct file *file, __poll_t events) +{ + struct socket *sock = file->private_data; /* - * We can't return errors to poll, so it's either yes or no. + * We need to be sure we are in sync with the socket flags modification. + * + * This memory barrier is paired in the wq_has_sleeper. */ - sock = file->private_data; + smp_mb(); - if (sk_can_busy_loop(sock->sk)) { - /* this socket can poll_ll so tell the system call */ - busy_flag = POLL_BUSY_LOOP; + /* this socket can poll_ll so tell the system call */ + return sock->ops->poll_mask(sock, events) | + (sk_can_busy_loop(sock->sk) ? POLL_BUSY_LOOP : 0); +} + +/* No kernel lock held - perfect */ +static __poll_t sock_poll(struct file *file, poll_table *wait) +{ + struct socket *sock = file->private_data; + __poll_t events = poll_requested_events(wait), mask = 0; - /* once, only if requested by syscall */ - if (wait && (wait->_key & POLL_BUSY_LOOP)) - sk_busy_loop(sock->sk, 1); + if (sock->ops->poll) { + sock_poll_busy_loop(sock, events); + mask = sock->ops->poll(file, sock, wait); + } else if (sock->ops->poll_mask) { + sock_poll_wait(file, sock_get_poll_head(file, events), wait); + mask = sock->ops->poll_mask(sock, events); } - return busy_flag | sock->ops->poll(file, sock, wait); + return mask | sock_poll_busy_flag(sock); } static int sock_mmap(struct file *file, struct vm_area_struct *vma) @@ -1420,6 +1445,13 @@ int __sys_socketpair(int family, int type, int protocol, int __user *usockvec) goto out; } + err = security_socket_socketpair(sock1, sock2); + if (unlikely(err)) { + sock_release(sock2); + sock_release(sock1); + goto out; + } + err = sock1->ops->socketpair(sock1, sock2); if (unlikely(err < 0)) { sock_release(sock2); |