Merge branch 'for-linus-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml

Pull UML update from Richard Weinberger:
 "A performance enhancement for UML's block driver"

* 'for-linus-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
  um: UBD Improvements
This commit is contained in:
Linus Torvalds 2016-12-16 09:35:03 -08:00
commit 9936f44add
3 changed files with 167 additions and 27 deletions

View File

@ -11,5 +11,10 @@ extern int start_io_thread(unsigned long sp, int *fds_out);
extern int io_thread(void *arg); extern int io_thread(void *arg);
extern int kernel_fd; 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 #endif

View File

@ -1,4 +1,5 @@
/* /*
* Copyright (C) 2015-2016 Anton Ivanov (aivanov@brocade.com)
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com) * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL * Licensed under the GPL
*/ */
@ -58,6 +59,17 @@ struct io_thread_req {
int error; 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) static inline int ubd_test_bit(__u64 bit, unsigned char *data)
{ {
__u64 n; __u64 n;
@ -442,29 +454,91 @@ static void do_ubd_request(struct request_queue * q);
static int thread_fd = -1; static int thread_fd = -1;
static LIST_HEAD(restart); 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. */ /* Called without dev->lock held, and only in interrupt context. */
static void ubd_handler(void) static void ubd_handler(void)
{ {
struct io_thread_req *req;
struct ubd *ubd; struct ubd *ubd;
struct list_head *list, *next_ele; struct list_head *list, *next_ele;
unsigned long flags; unsigned long flags;
int n; int n;
int count;
while(1){ while(1){
n = os_read_file(thread_fd, &req, n = bulk_req_safe_read(
sizeof(struct io_thread_req *)); thread_fd,
if(n != sizeof(req)){ irq_req_buffer,
&irq_remainder,
&irq_remainder_size,
UBD_REQ_BUFFER_SIZE
);
if (n < 0) {
if(n == -EAGAIN) if(n == -EAGAIN)
break; break;
printk(KERN_ERR "spurious interrupt in ubd_handler, " printk(KERN_ERR "spurious interrupt in ubd_handler, "
"err = %d\n", -n); "err = %d\n", -n);
return; return;
} }
for (count = 0; count < n/sizeof(struct io_thread_req *); count++) {
blk_end_request(req->req, 0, req->length); blk_end_request(
kfree(req); (*irq_req_buffer)[count]->req,
0,
(*irq_req_buffer)[count]->length
);
kfree((*irq_req_buffer)[count]);
}
} }
reactivate_fd(thread_fd, UBD_IRQ); reactivate_fd(thread_fd, UBD_IRQ);
@ -1064,6 +1138,28 @@ static int __init ubd_init(void)
if (register_blkdev(fake_major, "ubd")) if (register_blkdev(fake_major, "ubd"))
return -1; 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); platform_driver_register(&ubd_driver);
mutex_lock(&ubd_lock); mutex_lock(&ubd_lock);
for (i = 0; i < MAX_DEV; i++){ for (i = 0; i < MAX_DEV; i++){
@ -1458,31 +1554,51 @@ static int io_count = 0;
int io_thread(void *arg) int io_thread(void *arg)
{ {
struct io_thread_req *req; int n, count, written, res;
int n;
os_fix_helper_signals(); os_fix_helper_signals();
while(1){ while(1){
n = os_read_file(kernel_fd, &req, n = bulk_req_safe_read(
sizeof(struct io_thread_req *)); kernel_fd,
if(n != sizeof(struct io_thread_req *)){ io_req_buffer,
if(n < 0) &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, " printk("io_thread - read failed, fd = %d, "
"err = %d\n", kernel_fd, -n); "err = %d,"
else { "reminder = %d\n",
printk("io_thread - short read, fd = %d, " kernel_fd, -n, io_remainder_size);
"length = %d\n", kernel_fd, n);
} }
continue;
} }
io_count++;
do_io(req); for (count = 0; count < n/sizeof(struct io_thread_req *); count++) {
n = os_write_file(kernel_fd, &req, io_count++;
sizeof(struct io_thread_req *)); do_io((*io_req_buffer)[count]);
if(n != sizeof(struct io_thread_req *)) }
printk("io_thread - write failed, fd = %d, err = %d\n",
kernel_fd, -n); 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; return 0;

View 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) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com) * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
* Licensed under the GPL * Licensed under the GPL
@ -20,6 +21,9 @@
#include "ubd.h" #include "ubd.h"
#include <os.h> #include <os.h>
#include <poll.h>
struct pollfd kernel_pollfd;
int start_io_thread(unsigned long sp, int *fd_out) 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_fd = fds[0];
kernel_pollfd.fd = kernel_fd;
kernel_pollfd.events = POLLIN;
*fd_out = fds[1]; *fd_out = fds[1];
err = os_set_fd_block(*fd_out, 0); err = os_set_fd_block(*fd_out, 0);
err = os_set_fd_block(kernel_fd, 0);
if (err) { if (err) {
printk("start_io_thread - failed to set nonblocking I/O.\n"); printk("start_io_thread - failed to set nonblocking I/O.\n");
goto out_close; goto out_close;
@ -57,3 +64,15 @@ int start_io_thread(unsigned long sp, int *fd_out)
out: out:
return err; 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);
}