fs/poll: Fix poll_notify for CONFIG_BUILD_KERNEL
With flat addressing the user pollfd list is given directly to the drivers that perform the notification. This is fine when the addressing is flat, as there is no ambiguity on who gets notified and the fds memory is always mapped. For kernel mode with MMU this does not work. The notification must be done via a temporary buffer allocated from kernel memory. Thus, create a copy of the user fds and pass the copy to the drivers. Afterwards copy the output events back to the user.
This commit is contained in:
parent
3ea7a6fb77
commit
f730cf8ad8
@ -441,6 +441,7 @@ int file_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
|
|||||||
|
|
||||||
int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
|
int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
|
||||||
{
|
{
|
||||||
|
struct pollfd *kfds;
|
||||||
sem_t sem;
|
sem_t sem;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int ret2;
|
int ret2;
|
||||||
@ -452,8 +453,29 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
|
|||||||
|
|
||||||
enter_cancellation_point();
|
enter_cancellation_point();
|
||||||
|
|
||||||
|
#ifdef CONFIG_BUILD_KERNEL
|
||||||
|
/* Allocate kernel memory for the fds */
|
||||||
|
|
||||||
|
kfds = kmm_malloc(nfds * sizeof(struct pollfd));
|
||||||
|
if (!kfds)
|
||||||
|
{
|
||||||
|
/* Out of memory */
|
||||||
|
|
||||||
|
ret = ENOMEM;
|
||||||
|
goto out_with_cancelpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the user fds to neutral kernel memory */
|
||||||
|
|
||||||
|
memcpy(kfds, fds, nfds * sizeof(struct pollfd));
|
||||||
|
#else
|
||||||
|
/* Can use the user fds directly */
|
||||||
|
|
||||||
|
kfds = fds;
|
||||||
|
#endif
|
||||||
|
|
||||||
nxsem_init(&sem, 0, 0);
|
nxsem_init(&sem, 0, 0);
|
||||||
ret = poll_setup(fds, nfds, &sem);
|
ret = poll_setup(kfds, nfds, &sem);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
{
|
{
|
||||||
if (timeout == 0)
|
if (timeout == 0)
|
||||||
@ -519,7 +541,7 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
|
|||||||
* Preserve ret, if negative, since it holds the result of the wait.
|
* Preserve ret, if negative, since it holds the result of the wait.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ret2 = poll_teardown(fds, nfds, &count, ret);
|
ret2 = poll_teardown(kfds, nfds, &count, ret);
|
||||||
if (ret2 < 0 && ret >= 0)
|
if (ret2 < 0 && ret >= 0)
|
||||||
{
|
{
|
||||||
ret = ret2;
|
ret = ret2;
|
||||||
@ -527,6 +549,26 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
nxsem_destroy(&sem);
|
nxsem_destroy(&sem);
|
||||||
|
|
||||||
|
#ifdef CONFIG_BUILD_KERNEL
|
||||||
|
/* Copy the events back to user */
|
||||||
|
|
||||||
|
if (ret == OK)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < nfds; i++)
|
||||||
|
{
|
||||||
|
fds[i].revents = kfds[i].revents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the temporary buffer */
|
||||||
|
|
||||||
|
kmm_free(kfds);
|
||||||
|
|
||||||
|
out_with_cancelpt:
|
||||||
|
#endif
|
||||||
|
|
||||||
leave_cancellation_point();
|
leave_cancellation_point();
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user