um: UBD Improvements
authorAnton Ivanov <aivanov@kot-begemot.co.uk>
Wed, 9 Nov 2016 20:43:25 +0000 (20:43 +0000)
committerRichard Weinberger <richard@nod.at>
Wed, 14 Dec 2016 21:46:55 +0000 (22:46 +0100)
UBD at present is extremely slow because it handles only
one request at a time in the IO thread and IRQ handler.

The single request at a time is replaced by handling multiple
requests as well as necessary workarounds for short reads/writes.

Resulting performance improvement in disk IO - 30%

Signed-off-by: Anton Ivanov <aivanov@kot-begemot.co.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
arch/um/drivers/ubd.h
arch/um/drivers/ubd_kern.c
arch/um/drivers/ubd_user.c

index 3b48cd2081ee112cb9eabdb3e2fab145c2084c06..cc1cc85f5afc25ab5d0a53778856cb58b1c3f9a0 100644 (file)
@@ -11,5 +11,10 @@ extern int start_io_thread(unsigned long sp, int *fds_out);
 extern int io_thread(void *arg);
 extern int kernel_fd;
 
+extern int ubd_read_poll(int timeout);
+extern int ubd_write_poll(int timeout);
+
+#define UBD_REQ_BUFFER_SIZE 64
+
 #endif
 
index f3540270d09686d6ca6884c05f15481ae831ee0e..85410279beab63d611af485b27f729727aa5988d 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015-2016 Anton Ivanov (aivanov@brocade.com)
  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
  */
@@ -58,6 +59,17 @@ struct io_thread_req {
        int error;
 };
 
+
+static struct io_thread_req * (*irq_req_buffer)[];
+static struct io_thread_req *irq_remainder;
+static int irq_remainder_size;
+
+static struct io_thread_req * (*io_req_buffer)[];
+static struct io_thread_req *io_remainder;
+static int io_remainder_size;
+
+
+
 static inline int ubd_test_bit(__u64 bit, unsigned char *data)
 {
        __u64 n;
@@ -442,29 +454,91 @@ static void do_ubd_request(struct request_queue * q);
 static int thread_fd = -1;
 static LIST_HEAD(restart);
 
-/* XXX - move this inside ubd_intr. */
+/* Function to read several request pointers at a time
+* handling fractional reads if (and as) needed
+*/
+
+static int bulk_req_safe_read(
+       int fd,
+       struct io_thread_req * (*request_buffer)[],
+       struct io_thread_req **remainder,
+       int *remainder_size,
+       int max_recs
+       )
+{
+       int n = 0;
+       int res = 0;
+
+       if (*remainder_size > 0) {
+               memmove(
+                       (char *) request_buffer,
+                       (char *) remainder, *remainder_size
+               );
+               n = *remainder_size;
+       }
+
+       res = os_read_file(
+                       fd,
+                       ((char *) request_buffer) + *remainder_size,
+                       sizeof(struct io_thread_req *)*max_recs
+                               - *remainder_size
+               );
+       if (res > 0) {
+               n += res;
+               if ((n % sizeof(struct io_thread_req *)) > 0) {
+                       /*
+                       * Read somehow returned not a multiple of dword
+                       * theoretically possible, but never observed in the
+                       * wild, so read routine must be able to handle it
+                       */
+                       *remainder_size = n % sizeof(struct io_thread_req *);
+                       WARN(*remainder_size > 0, "UBD IPC read returned a partial result");
+                       memmove(
+                               remainder,
+                               ((char *) request_buffer) +
+                                       (n/sizeof(struct io_thread_req *))*sizeof(struct io_thread_req *),
+                               *remainder_size
+                       );
+                       n = n - *remainder_size;
+               }
+       } else {
+               n = res;
+       }
+       return n;
+}
+
 /* Called without dev->lock held, and only in interrupt context. */
 static void ubd_handler(void)
 {
-       struct io_thread_req *req;
        struct ubd *ubd;
        struct list_head *list, *next_ele;
        unsigned long flags;
        int n;
+       int count;
 
        while(1){
-               n = os_read_file(thread_fd, &req,
-                                sizeof(struct io_thread_req *));
-               if(n != sizeof(req)){
+               n = bulk_req_safe_read(
+                       thread_fd,
+                       irq_req_buffer,
+                       &irq_remainder,
+                       &irq_remainder_size,
+                       UBD_REQ_BUFFER_SIZE
+               );
+               if (n < 0) {
                        if(n == -EAGAIN)
                                break;
                        printk(KERN_ERR "spurious interrupt in ubd_handler, "
                               "err = %d\n", -n);
                        return;
                }
-
-               blk_end_request(req->req, 0, req->length);
-               kfree(req);
+               for (count = 0; count < n/sizeof(struct io_thread_req *); count++) {
+                       blk_end_request(
+                               (*irq_req_buffer)[count]->req,
+                               0,
+                               (*irq_req_buffer)[count]->length
+                       );
+                       kfree((*irq_req_buffer)[count]);
+               }
        }
        reactivate_fd(thread_fd, UBD_IRQ);
 
@@ -1064,6 +1138,28 @@ static int __init ubd_init(void)
                if (register_blkdev(fake_major, "ubd"))
                        return -1;
        }
+
+       irq_req_buffer = kmalloc(
+                       sizeof(struct io_thread_req *) * UBD_REQ_BUFFER_SIZE,
+                       GFP_KERNEL
+               );
+       irq_remainder = 0;
+
+       if (irq_req_buffer == NULL) {
+               printk(KERN_ERR "Failed to initialize ubd buffering\n");
+               return -1;
+       }
+       io_req_buffer = kmalloc(
+                       sizeof(struct io_thread_req *) * UBD_REQ_BUFFER_SIZE,
+                       GFP_KERNEL
+               );
+
+       io_remainder = 0;
+
+       if (io_req_buffer == NULL) {
+               printk(KERN_ERR "Failed to initialize ubd buffering\n");
+               return -1;
+       }
        platform_driver_register(&ubd_driver);
        mutex_lock(&ubd_lock);
        for (i = 0; i < MAX_DEV; i++){
@@ -1458,31 +1554,51 @@ static int io_count = 0;
 
 int io_thread(void *arg)
 {
-       struct io_thread_req *req;
-       int n;
+       int n, count, written, res;
 
        os_fix_helper_signals();
 
        while(1){
-               n = os_read_file(kernel_fd, &req,
-                                sizeof(struct io_thread_req *));
-               if(n != sizeof(struct io_thread_req *)){
-                       if(n < 0)
+               n = bulk_req_safe_read(
+                       kernel_fd,
+                       io_req_buffer,
+                       &io_remainder,
+                       &io_remainder_size,
+                       UBD_REQ_BUFFER_SIZE
+               );
+               if (n < 0) {
+                       if (n == -EAGAIN) {
+                               ubd_read_poll(-1);
+                               continue;
+                       } else {
                                printk("io_thread - read failed, fd = %d, "
-                                      "err = %d\n", kernel_fd, -n);
-                       else {
-                               printk("io_thread - short read, fd = %d, "
-                                      "length = %d\n", kernel_fd, n);
+                                      "err = %d,"
+                                      "reminder = %d\n",
+                                      kernel_fd, -n, io_remainder_size);
                        }
-                       continue;
                }
-               io_count++;
-               do_io(req);
-               n = os_write_file(kernel_fd, &req,
-                                 sizeof(struct io_thread_req *));
-               if(n != sizeof(struct io_thread_req *))
-                       printk("io_thread - write failed, fd = %d, err = %d\n",
-                              kernel_fd, -n);
+
+               for (count = 0; count < n/sizeof(struct io_thread_req *); count++) {
+                       io_count++;
+                       do_io((*io_req_buffer)[count]);
+               }
+
+               written = 0;
+
+               do {
+                       res = os_write_file(kernel_fd, ((char *) io_req_buffer) + written, n);
+                       if (res > 0) {
+                               written += res;
+                       } else {
+                               if (res != -EAGAIN) {
+                                       printk("io_thread - read failed, fd = %d, "
+                                              "err = %d\n", kernel_fd, -n);
+                               }
+                       }
+                       if (written < n) {
+                               ubd_write_poll(-1);
+                       }
+               } while (written < n);
        }
 
        return 0;
index e376f9b9c68d8986f5a74536916d34c08848758d..6f744794d1416e795e5727065998f0b702c6343c 100644 (file)
@@ -1,4 +1,5 @@
-/* 
+/*
+ * Copyright (C) 2016 Anton Ivanov (aivanov@brocade.com)
  * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
  * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
  * Licensed under the GPL
@@ -20,6 +21,9 @@
 
 #include "ubd.h"
 #include <os.h>
+#include <poll.h>
+
+struct pollfd kernel_pollfd;
 
 int start_io_thread(unsigned long sp, int *fd_out)
 {
@@ -32,9 +36,12 @@ int start_io_thread(unsigned long sp, int *fd_out)
        }
 
        kernel_fd = fds[0];
+       kernel_pollfd.fd = kernel_fd;
+       kernel_pollfd.events = POLLIN;
        *fd_out = fds[1];
 
        err = os_set_fd_block(*fd_out, 0);
+       err = os_set_fd_block(kernel_fd, 0);
        if (err) {
                printk("start_io_thread - failed to set nonblocking I/O.\n");
                goto out_close;
@@ -57,3 +64,15 @@ int start_io_thread(unsigned long sp, int *fd_out)
  out:
        return err;
 }
+
+int ubd_read_poll(int timeout)
+{
+       kernel_pollfd.events = POLLIN;
+       return poll(&kernel_pollfd, 1, timeout);
+}
+int ubd_write_poll(int timeout)
+{
+       kernel_pollfd.events = POLLOUT;
+       return poll(&kernel_pollfd, 1, timeout);
+}
+