]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] ia64: sys_ia32.c fix for epoll
authorJanet Morgan <janetmor@us.ibm.com>
Wed, 8 Oct 2003 10:45:05 +0000 (03:45 -0700)
committerChristoph Hellwig <hch@lst.de>
Wed, 8 Oct 2003 10:45:05 +0000 (03:45 -0700)
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.

arch/ia64/ia32/sys_ia32.c

index d4931dc98dbd497810557c1279bf488849275188..d0e2182b2aead2480d63886c575bc7ca43201997 100644 (file)
@@ -2722,8 +2722,8 @@ out_error:
 struct epoll_event32
 {
        u32 events;
-       u64 data;
-} __attribute__((packed));
+       u32 data[2];
+}
 
 asmlinkage long
 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;
 
        __get_user(event64.events, &event->events);
-       __get_user(data_halfword, (u32*)(&event->data));
+       __get_user(data_halfword, &event->data[0]);
        event64.data = data_halfword;
-       __get_user(data_halfword, ((u32*)(&event->data) + 1));
-       event64.data |= ((u64)data_halfword) << 32;
+       __get_user(data_halfword, &event->data[1]);
+       event64.data |= (u64)data_halfword << 32;
 
        set_fs(KERNEL_DS);
        error = sys_epoll_ctl(epfd, op, fd, &event64);
@@ -2756,8 +2756,9 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents,
 {
        struct epoll_event *events64 = NULL;
        mm_segment_t old_fs = get_fs();
-       int error;
+       int error, numevents, size;
        int evt_idx;
+       int do_free_pages = 0;
 
        if (maxevents <= 0) {
                return -EINVAL;
@@ -2768,43 +2769,44 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents,
                                 maxevents * sizeof(struct epoll_event32))))
                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) {
-               return -ENOMEM;
-       }
-
-       /* Expand the 32-bit structures into the 64-bit structures */
-       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;
+               events64 = __get_free_pages(GFP_KERNEL, get_order(size));
+               if (events64 == NULL) 
+                       return -ENOMEM;
+               do_free_pages = 1;
        }
 
        /* Do the system call */
        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);
 
        /* 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
                   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,
                                   &events[evt_idx].events);
-                       __put_user((u32)(events64[evt_idx].data),
-                                  (u32*)(&events[evt_idx].data));
+                       __put_user((u32)events64[evt_idx].data,
+                                  &events[evt_idx].data[0]);
                        __put_user((u32)(events64[evt_idx].data >> 32),
-                                  ((u32*)(&events[evt_idx].data) + 1));
+                                  &events[evt_idx].data[1]);
                }
        }
 
-       kfree(events64);
-       return error;
+       if (do_free_pages)
+               free_pages(events64, get_order(size));
+       else
+               kfree(events64);
+       return numevents;
 }
 
 #ifdef NOTYET  /* UNTESTED FOR IA64 FROM HERE DOWN */