]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/um/drivers/ubd_kern.c
Merge branch 'x86-mm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / arch / um / drivers / ubd_kern.c
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;