diff options
Diffstat (limited to 'fs/select.c')
| -rw-r--r-- | fs/select.c | 87 | 
1 files changed, 50 insertions, 37 deletions
diff --git a/fs/select.c b/fs/select.c index ba879c51288f..317891ff8165 100644 --- a/fs/select.c +++ b/fs/select.c @@ -34,6 +34,29 @@  #include <linux/uaccess.h> +__poll_t vfs_poll(struct file *file, struct poll_table_struct *pt) +{ +	if (file->f_op->poll) { +		return file->f_op->poll(file, pt); +	} else if (file_has_poll_mask(file)) { +		unsigned int events = poll_requested_events(pt); +		struct wait_queue_head *head; + +		if (pt && pt->_qproc) { +			head = file->f_op->get_poll_head(file, events); +			if (!head) +				return DEFAULT_POLLMASK; +			if (IS_ERR(head)) +				return EPOLLERR; +			pt->_qproc(file, head, pt); +		} + +		return file->f_op->poll_mask(file, events); +	} else { +		return DEFAULT_POLLMASK; +	} +} +EXPORT_SYMBOL_GPL(vfs_poll);  /*   * Estimate expected accuracy in ns from a timeval. @@ -233,7 +256,7 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,  	add_wait_queue(wait_address, &entry->wait);  } -int poll_schedule_timeout(struct poll_wqueues *pwq, int state, +static int poll_schedule_timeout(struct poll_wqueues *pwq, int state,  			  ktime_t *expires, unsigned long slack)  {  	int rc = -EINTR; @@ -258,7 +281,6 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,  	return rc;  } -EXPORT_SYMBOL(poll_schedule_timeout);  /**   * poll_select_set_timeout - helper function to setup the timeout value @@ -503,14 +525,10 @@ static int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)  					continue;  				f = fdget(i);  				if (f.file) { -					const struct file_operations *f_op; -					f_op = f.file->f_op; -					mask = DEFAULT_POLLMASK; -					if (f_op->poll) { -						wait_key_set(wait, in, out, -							     bit, busy_flag); -						mask = (*f_op->poll)(f.file, wait); -					} +					wait_key_set(wait, in, out, bit, +						     busy_flag); +					mask = vfs_poll(f.file, wait); +  					fdput(f);  					if ((mask & POLLIN_SET) && (in & bit)) {  						res_in |= bit; @@ -813,34 +831,29 @@ static inline __poll_t do_pollfd(struct pollfd *pollfd, poll_table *pwait,  				     bool *can_busy_poll,  				     __poll_t busy_flag)  { -	__poll_t mask; -	int fd; - -	mask = 0; -	fd = pollfd->fd; -	if (fd >= 0) { -		struct fd f = fdget(fd); -		mask = EPOLLNVAL; -		if (f.file) { -			/* userland u16 ->events contains POLL... bitmap */ -			__poll_t filter = demangle_poll(pollfd->events) | -						EPOLLERR | EPOLLHUP; -			mask = DEFAULT_POLLMASK; -			if (f.file->f_op->poll) { -				pwait->_key = filter; -				pwait->_key |= busy_flag; -				mask = f.file->f_op->poll(f.file, pwait); -				if (mask & busy_flag) -					*can_busy_poll = true; -			} -			/* Mask out unneeded events. */ -			mask &= filter; -			fdput(f); -		} -	} +	int fd = pollfd->fd; +	__poll_t mask = 0, filter; +	struct fd f; + +	if (fd < 0) +		goto out; +	mask = EPOLLNVAL; +	f = fdget(fd); +	if (!f.file) +		goto out; + +	/* userland u16 ->events contains POLL... bitmap */ +	filter = demangle_poll(pollfd->events) | EPOLLERR | EPOLLHUP; +	pwait->_key = filter | busy_flag; +	mask = vfs_poll(f.file, pwait); +	if (mask & busy_flag) +		*can_busy_poll = true; +	mask &= filter;		/* Mask out unneeded events. */ +	fdput(f); + +out:  	/* ... and so does ->revents */  	pollfd->revents = mangle_poll(mask); -  	return mask;  } @@ -1223,7 +1236,7 @@ static int compat_core_sys_select(int n, compat_ulong_t __user *inp,  	size = FDS_BYTES(n);  	bits = stack_fds;  	if (size > sizeof(stack_fds) / 6) { -		bits = kmalloc(6 * size, GFP_KERNEL); +		bits = kmalloc_array(6, size, GFP_KERNEL);  		ret = -ENOMEM;  		if (!bits)  			goto out_nofds;  | 
