Commit fd61c73e authored by Janet Morgan's avatar Janet Morgan Committed by Christoph Hellwig

[PATCH] ia64: sys_ia32.c fix for epoll

I ran into some bugs testing epoll in ia32-emulation mode on ia64.
The attached patch fixes the problems and is well tested.
Here is a summary of the changes:

Changes to sys32_epoll_ctl() and sys32_epoll_wait():
- changed epoll_event32.data to an array (this is subjective, but seems
to make the code more readable)

Changes to sys32_epoll_wait():
- added call to __get_free_pages if kmalloc(epoll_event array) fails.  This
  provides needed scalability and fixes the -ENOMEM failure during
epoll-pipetest.
- deleted copy-in of epoll_event array since this is not a user-inputsyscall
- changed to check numevents > 0 as indicator of success on call to
  sys_epoll_wait.
- changed to loop on numevents not maxevents when copying out to userspace.
parent 6853823b
...@@ -2722,8 +2722,8 @@ sys32_open (const char * filename, int flags, int mode) ...@@ -2722,8 +2722,8 @@ sys32_open (const char * filename, int flags, int mode)
struct epoll_event32 struct epoll_event32
{ {
u32 events; u32 events;
u64 data; u32 data[2];
} __attribute__((packed)); };
asmlinkage long asmlinkage long
sys32_epoll_ctl(int epfd, int op, int fd, struct epoll_event32 *event) sys32_epoll_ctl(int epfd, int op, int fd, struct epoll_event32 *event)
...@@ -2738,10 +2738,10 @@ sys32_epoll_ctl(int epfd, int op, int fd, struct epoll_event32 *event) ...@@ -2738,10 +2738,10 @@ sys32_epoll_ctl(int epfd, int op, int fd, struct epoll_event32 *event)
return error; return error;
__get_user(event64.events, &event->events); __get_user(event64.events, &event->events);
__get_user(data_halfword, (u32*)(&event->data)); __get_user(data_halfword, &event->data[0]);
event64.data = data_halfword; event64.data = data_halfword;
__get_user(data_halfword, ((u32*)(&event->data) + 1)); __get_user(data_halfword, &event->data[1]);
event64.data |= ((u64)data_halfword) << 32; event64.data |= (u64)data_halfword << 32;
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
error = sys_epoll_ctl(epfd, op, fd, &event64); error = sys_epoll_ctl(epfd, op, fd, &event64);
...@@ -2756,8 +2756,9 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents, ...@@ -2756,8 +2756,9 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents,
{ {
struct epoll_event *events64 = NULL; struct epoll_event *events64 = NULL;
mm_segment_t old_fs = get_fs(); mm_segment_t old_fs = get_fs();
int error; int error, numevents, size;
int evt_idx; int evt_idx;
int do_free_pages = 0;
if (maxevents <= 0) { if (maxevents <= 0) {
return -EINVAL; return -EINVAL;
...@@ -2768,43 +2769,44 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents, ...@@ -2768,43 +2769,44 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents,
maxevents * sizeof(struct epoll_event32)))) maxevents * sizeof(struct epoll_event32))))
return error; return error;
/* Allocate the space needed for the intermediate copy */ /*
events64 = kmalloc(maxevents * sizeof(struct epoll_event), GFP_KERNEL); * Allocate space for the intermediate copy. If the space needed
* is large enough to cause kmalloc to fail, then try again with
* __get_free_pages.
*/
size = maxevents * sizeof(struct epoll_event);
events64 = kmalloc(size, GFP_KERNEL);
if (events64 == NULL) { if (events64 == NULL) {
return -ENOMEM; events64 = __get_free_pages(GFP_KERNEL, get_order(size));
} if (events64 == NULL)
return -ENOMEM;
/* Expand the 32-bit structures into the 64-bit structures */ do_free_pages = 1;
for (evt_idx = 0; evt_idx < maxevents; evt_idx++) {
u32 data_halfword;
__get_user(events64[evt_idx].events, &events[evt_idx].events);
__get_user(data_halfword, (u32*)(&events[evt_idx].data));
events64[evt_idx].data = data_halfword;
__get_user(data_halfword, ((u32*)(&events[evt_idx].data) + 1));
events64[evt_idx].data |= ((u64)data_halfword) << 32;
} }
/* Do the system call */ /* Do the system call */
set_fs(KERNEL_DS); /* copy_to/from_user should work on kernel mem*/ set_fs(KERNEL_DS); /* copy_to/from_user should work on kernel mem*/
error = sys_epoll_wait(epfd, events64, maxevents, timeout); numevents = sys_epoll_wait(epfd, events64, maxevents, timeout);
set_fs(old_fs); set_fs(old_fs);
/* Don't modify userspace memory if we're returning an error */ /* Don't modify userspace memory if we're returning an error */
if (!error) { if (numevents > 0) {
/* Translate the 64-bit structures back into the 32-bit /* Translate the 64-bit structures back into the 32-bit
structures */ structures */
for (evt_idx = 0; evt_idx < maxevents; evt_idx++) { for (evt_idx = 0; evt_idx < numevents; evt_idx++) {
__put_user(events64[evt_idx].events, __put_user(events64[evt_idx].events,
&events[evt_idx].events); &events[evt_idx].events);
__put_user((u32)(events64[evt_idx].data), __put_user((u32)events64[evt_idx].data,
(u32*)(&events[evt_idx].data)); &events[evt_idx].data[0]);
__put_user((u32)(events64[evt_idx].data >> 32), __put_user((u32)(events64[evt_idx].data >> 32),
((u32*)(&events[evt_idx].data) + 1)); &events[evt_idx].data[1]);
} }
} }
kfree(events64); if (do_free_pages)
return error; free_pages(events64, get_order(size));
else
kfree(events64);
return numevents;
} }
#ifdef NOTYET /* UNTESTED FOR IA64 FROM HERE DOWN */ #ifdef NOTYET /* UNTESTED FOR IA64 FROM HERE DOWN */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment