Skip to contentSkip to conversation

lguest: block device speedup

PATCH In review Rusty Russell 3 participants 11 messages Mar 9, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/9 lguest: block device speedup

Jens Axboe pointed out that end_request() does not end the entire request. Go figure. On the upside, he wrote the replacement for me! Now we do far less block traffic, and our performance sucks less.

drivers/block/lguest_blk.c +12 -2
diff -r fdc8cbc1fd61 drivers/block/lguest_blk.c
@@ -45,6 +45,16 @@ struct blockdev
 	struct request *req;
 };
 
+/* Jens gave me this nice helper to end all chunks of a request. */
+static void end_entire_request(struct request *req, int uptodate)
+{
+	if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
+		BUG();
+	add_disk_randomness(req->rq_disk);
+	blkdev_dequeue_request(req);
+	end_that_request_last(req, uptodate);
+}
+
 static irqreturn_t lgb_irq(int irq, void *_bd)
 {
 	struct blockdev *bd = _bd;
@@ -61,7 +71,7 @@ static irqreturn_t lgb_irq(int irq, void
 	}
 
 	spin_lock_irqsave(&bd->lock, flags);
-	end_request(bd->req, bd->lb_page->result == 1);
+	end_entire_request(bd->req, bd->lb_page->result == 1);
 	bd->req = NULL;
 	bd->dma.used_len = 0;
 	blk_start_queue(bd->disk->queue);
@@ -149,7 +159,7 @@ again:
 		pr_debug("Got non-command 0x%08x\n", req->cmd_type);
 	error:
 		req->errors++;
-		end_request(req, 0);
+		end_entire_request(req, 0);
 		goto again;
 	} else {
 		if (rq_data_dir(req) == WRITE)
#1 lore raw

PATCH 2/9 lguest: bridging support in example code

Expand the --tunnet option to take a bridge name as an argument, so that the tap interface is added to the specified bridge. This makes it convenient to use bridging for connecting the guest to external networks.

Documentation/lguest/lguest.c +45 -12
diff -r cff3d561d1b0 Documentation/lguest/lguest.c
@@ -23,7 +23,8 @@
 #include <sys/time.h>
 #include <time.h>
 #include <netinet/in.h>
-#include <linux/if.h>
+#include <net/if.h>
+#include <linux/sockios.h>
 #include <linux/if_tun.h>
 #include <sys/uio.h>
 #include <termios.h>
@@ -36,6 +37,7 @@ typedef uint8_t u8;
 
 #define PAGE_PRESENT 0x7 	/* Present, RW, Execute */
 #define NET_PEERNUM 1
+#define BRIDGE_PFX "bridge:"
 
 static bool verbose;
 #define verbose(args...) \
@@ -582,20 +584,16 @@ static u32 handle_block_output(int fd, c
 	((u8)(ip >> 8)),			\
 	((u8)(ip))
 
-static void configure_device(const char *devname, u32 ipaddr,
+static void configure_device(int fd, const char *devname, u32 ipaddr,
 			     unsigned char hwaddr[6])
 {
 	struct ifreq ifr;
-	int fd;
 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
 
 	memset(&ifr, 0, sizeof(ifr));
 	strcpy(ifr.ifr_name, devname);
 	sin->sin_family = AF_INET;
 	sin->sin_addr.s_addr = htonl(ipaddr);
-	fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
-	if (fd < 0)
-		err(1, "opening IP socket");
 	if (ioctl(fd, SIOCSIFADDR, &ifr) != 0)
 		err(1, "Setting %s interface address", devname);
 	ifr.ifr_flags = IFF_UP;
@@ -724,13 +722,34 @@ static u32 str2ip(const char *ipaddr)
 	return (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3];
 }
 
-static void setup_tun_net(const char *ipaddr,
+/* adapted from libbridge */
+static void add_to_bridge(int fd, const char *if_name, const char *br_name)
+{
+	int ifidx;
+	struct ifreq ifr;
+
+	if (!*br_name)
+		errx(1, "must specify bridge name");
+
+	ifidx = if_nametoindex(if_name);
+	if (!ifidx)
+		errx(1, "interface %s does not exist!", if_name);
+
+	strncpy(ifr.ifr_name, br_name, IFNAMSIZ);
+	ifr.ifr_ifindex = ifidx;
+	if (ioctl(fd, SIOCBRADDIF, &ifr) < 0)
+		err(1, "can't add %s to bridge %s", if_name, br_name);
+}
+
+static void setup_tun_net(const char *arg,
 			  struct lguest_device_desc *descs,
 			  struct devices *devices)
 {
 	struct device *dev;
 	struct ifreq ifr;
-	int netfd;
+	int netfd, ipfd;
+	u32 ipaddr;
+	const char *br_name = NULL;
 
 	netfd = open("/dev/net/tun", O_RDWR);
 	if (netfd < 0)
@@ -748,15 +767,29 @@ static void setup_tun_net(const char *ip
 	dev->priv = malloc(sizeof(bool));
 	*(bool *)dev->priv = false;
 
+	ipfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+	if (ipfd < 0)
+		err(1, "opening IP socket");
+
+	if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) {
+		ipaddr = INADDR_ANY;
+		br_name = arg + strlen(BRIDGE_PFX);
+		add_to_bridge(ipfd, ifr.ifr_name, br_name);
+	} else
+		ipaddr = str2ip(arg);
+
 	/* We are peer 0, rest is all NO_GUEST */
 	memset(dev->mem, 0xFF, getpagesize());
-	configure_device(ifr.ifr_name, str2ip(ipaddr), dev->mem);
+	configure_device(ipfd, ifr.ifr_name, ipaddr, dev->mem);
+	close(ipfd);
 
 	/* You will be peer 1: we should create enough jitter to randomize */
 	dev->desc->features = NET_PEERNUM|LGUEST_DEVICE_F_RANDOMNESS;
 	verbose("device %p@%p: tun net %u.%u.%u.%u\n", dev->desc,
 		(void *)(dev->desc->pfn * getpagesize()),
-		HIPQUAD(str2ip(ipaddr)));
+		HIPQUAD(ipaddr));
+	if (br_name)
+		verbose("attched to bridge: %s\n", br_name);
 }
 
 static void setup_block_file(const char *filename,
@@ -887,8 +920,8 @@ int main(int argc, char *argv[])
 
 	if (argc < 4)
 		errx(1, "Usage: lguest [--verbose] <mem> vmlinux "
-			"[--sharenet=<filename>|--tunnet=<ipaddr>|--block=<filename>"
-			"|--initrd=<filename>]... [args...]");
+			"[--sharenet=<filename>|--tunnet=(<ipaddr>|bridge:<bridgename>)"
+			"|--block=<filename>|--initrd=<filename>]... [args...]");
 
 	zero_fd = open("/dev/zero", O_RDONLY, 0);
 	if (zero_fd < 0)
Documentation/lguest/lguest.txt +17 -1
diff -r cff3d561d1b0 Documentation/lguest/lguest.txt
@@ -77,10 +77,26 @@ Running Lguest:
   /proc/sys/net/ipv4/ip_forward".  In this example, I would configure
   eth0 inside the guest at 192.168.19.2.
 
+  Another method is to bridge the tap device to an external interface
+  using --tunnet=bridge:<bridgename>, and perhaps run dhcp on the guest
+  to obtain an IP address.  The bridge needs to be configured first:
+  this option simply adds the tap interface to it.
+  
+  A simple example on my system:
+  
+    ifconfig eth0 0.0.0.0
+    brctl addbr lg0
+    ifconfig lg0 up
+    dhclient lg0
+    
+  Then use --tunnet=bridge:lg0 when launching the guest.
+
+  See http://linux-net.osdl.org/index.php/Bridge for general information
+  on how to get bridging working.
+
 - You can also create an inter-guest network using
   "--sharenet=<filename>": any two guests using the same file are on
   the same network.  This file is created if it does not exist.
-
 
 Lguest I/O model:
 
#2 lore raw

PATCH 3/9 lguest: cleanup: allocate separate pages for switcher code

We don't need physically-contiguous pages for the hypervisor, since we use map_vm_area anyway.

Two other related cleanups: pass the number of pages to init_pagetables() so we can remove the constant from the header, and call populate_hypervisor_pte_page() on each page as we allocate it, rather than as a separate loop.

arch/i386/lguest/core.c +29 -18
diff -r 9fea34a28460 arch/i386/lguest/core.c
@@ -24,17 +24,21 @@ static char __initdata hypervisor_blob[]
 #include "hypervisor-blob.c"
 };
 
-#define MAX_LGUEST_GUESTS						  \
-	(((PAGE_SIZE << HYPERVISOR_PAGE_ORDER) - sizeof(hypervisor_blob)) \
+/* 64k ought to be enough for anybody! */
+#define HYPERVISOR_PAGES (65536 / PAGE_SIZE)
+
+#define MAX_LGUEST_GUESTS						\
+	(((HYPERVISOR_PAGES * PAGE_SIZE) - sizeof(hypervisor_blob))	\
 	 / sizeof(struct lguest_state))
 
 static struct vm_struct *hypervisor_vma;
+/* Pages for hypervisor itself */
+static struct page *hype_page[HYPERVISOR_PAGES];
 static int cpu_had_pge;
 static struct {
 	unsigned long offset;
 	unsigned short segment;
 } lguest_entry __attribute_used__;
-struct page *hype_pages; /* Contiguous pages. */
 struct lguest lguests[MAX_LGUEST_GUESTS];
 DEFINE_MUTEX(lguest_lock);
 
@@ -58,15 +62,19 @@ struct lguest_state *__lguest_states(voi
 
 static __init int map_hypervisor(void)
 {
-	unsigned int i;
-	int err;
-	struct page *pages[HYPERVISOR_PAGES], **pagep = pages;
-
-	hype_pages = alloc_pages(GFP_KERNEL|__GFP_ZERO, HYPERVISOR_PAGE_ORDER);
-	if (!hype_pages)
-		return -ENOMEM;
-
-	hypervisor_vma = __get_vm_area(PAGE_SIZE << HYPERVISOR_PAGE_ORDER,
+	int i, err;
+	struct page **pagep = hype_page;
+
+	for (i = 0; i < ARRAY_SIZE(hype_page); i++) {
+		unsigned long addr = get_zeroed_page(GFP_KERNEL);
+		if (!addr) {
+			err = -ENOMEM;
+			goto free_some_pages;
+		}
+		hype_page[i] = virt_to_page(addr);
+	}
+
+	hypervisor_vma = __get_vm_area(ARRAY_SIZE(hype_page) * PAGE_SIZE,
 				       VM_ALLOC, HYPE_ADDR, VMALLOC_END);
 	if (!hypervisor_vma) {
 		err = -ENOMEM;
@@ -74,9 +82,6 @@ static __init int map_hypervisor(void)
 		goto free_pages;
 	}
 
-	for (i = 0; i < HYPERVISOR_PAGES; i++)
-		pages[i] = hype_pages + i;
-
 	err = map_vm_area(hypervisor_vma, PAGE_KERNEL, &pagep);
 	if (err) {
 		printk("lguest: map_vm_area failed: %i\n", err);
@@ -100,14 +105,20 @@ free_vma:
 free_vma:
 	vunmap(hypervisor_vma->addr);
 free_pages:
-	__free_pages(hype_pages, HYPERVISOR_PAGE_ORDER);
+	i = ARRAY_SIZE(hype_page);
+free_some_pages:
+	for (--i; i >= 0; i--)
+		__free_pages(hype_page[i], 0);
 	return err;
 }
 
 static __exit void unmap_hypervisor(void)
 {
+	unsigned int i;
+
 	vunmap(hypervisor_vma->addr);
-	__free_pages(hype_pages, HYPERVISOR_PAGE_ORDER);
+	for (i = 0; i < ARRAY_SIZE(hype_page); i++)
+		__free_pages(hype_page[i], 0);
 }
 
 /* IN/OUT insns: enough to get us past boot-time probing. */
@@ -390,7 +401,7 @@ static int __init init(void)
 	if (err)
 		return err;
 
-	err = init_pagetables(hype_pages);
+	err = init_pagetables(hype_page, HYPERVISOR_PAGES);
 	if (err) {
 		unmap_hypervisor();
 		return err;
arch/i386/lguest/lg.h +1 -5
diff -r 9fea34a28460 arch/i386/lguest/lg.h
@@ -2,9 +2,6 @@
 #define _LGUEST_H
 
 #include <asm/desc.h>
-/* 64k ought to be enough for anybody! */
-#define HYPERVISOR_PAGE_ORDER (16 - PAGE_SHIFT)
-#define HYPERVISOR_PAGES (1 << HYPERVISOR_PAGE_ORDER)
 
 #define GDT_ENTRY_LGUEST_CS	10
 #define GDT_ENTRY_LGUEST_DS	11
@@ -43,7 +40,7 @@ struct lguest_regs
 };
 
 __exit void free_pagetables(void);
-__init int init_pagetables(struct page *hype_pages);
+__init int init_pagetables(struct page **hype_page, int pages);
 
 /* Full 4G segment descriptors, suitable for CS and DS. */
 #define FULL_EXEC_SEGMENT ((struct desc_struct){0x0000ffff, 0x00cf9b00})
@@ -122,7 +119,6 @@ struct lguest
 	struct host_trap interrupt[LGUEST_IRQS];
 };
 
-extern struct page *hype_pages; /* Contiguous pages. */
 extern struct lguest lguests[];
 extern struct mutex lguest_lock;
 
arch/i386/lguest/page_tables.c +16 -27

61 lines not on this page · raw message

#3 lore raw

PATCH 4/9 lguest: cleanup: clean up regs save/restore

We previously put "cr3" in the guest regs restored and saved: the guest cannot change cr3, so saving it it silly. Hand it across to the host<->guest switcher in ebx.

While we're there, only save the host registers we need to; tell GCC we clobber everything we can.

Finally, and trap 2 (NMI) doesn't supply a error code (we don't handle NMI yet, but the test is wrong, so fix it before I get confused).

arch/i386/lguest/core.c +4 -4
diff -r 6efda2f8ac22 arch/i386/lguest/core.c
@@ -260,11 +260,11 @@ static void run_guest_once(struct lguest
 {
 	unsigned int clobber;
 
-	/* Put eflags on stack, lcall does rest. */
+	/* Put eflags on stack, lcall does rest: suitable for iret return. */
 	asm volatile("pushf; lcall *lguest_entry"
-		     : "=a"(clobber), "=d"(clobber)
-		     : "0"(lg->state), "1"(get_idt_table())
-		     : "memory");
+		     : "=a"(clobber), "=d"(clobber), "=b"(clobber)
+		     : "0"(lg->state), "1"(get_idt_table()), "2"(lg->cr3)
+		     : "memory", "%ecx", "%edi", "%esi");
 }
 
 int run_guest(struct lguest *lg, char *__user user)
arch/i386/lguest/hypervisor.S +30 -38

111 lines not on this page · raw message

arch/i386/lguest/lg.h +1 -1
diff -r 6efda2f8ac22 arch/i386/lguest/lg.h
@@ -24,7 +24,6 @@ struct lguest_regs
 struct lguest_regs
 {
 	/* Manually saved part. */
-	u32 cr3;
 	u32 ebx, ecx, edx;
 	u32 esi, edi, ebp;
 	u32 gs;
@@ -81,6 +80,7 @@ struct lguest
 	u32 pfn_limit;
 	u32 page_offset;
 	u32 cr2;
+	u32 cr3;
 	int timer_on;
 	int halted;
 	int ts;
arch/i386/lguest/lguest_user.c +2 -3
diff -r 6efda2f8ac22 arch/i386/lguest/lguest_user.c
@@ -4,7 +4,7 @@
 #include <linux/fs.h>
 #include "lg.h"
 
-static struct lguest_state *setup_guest_state(unsigned int num, void *pgdir,
+static struct lguest_state *setup_guest_state(unsigned int num,
 					      unsigned long start)
 {
 	struct lguest_state *guest = &__lguest_states()[num];
@@ -38,7 +38,6 @@ static struct lguest_state *setup_guest_
 
 	/* Write out stack in format lguest expects, so we can switch to it. */
 	regs = &guest->regs;
-	regs->cr3 = __pa(pgdir);
 	regs->eax = regs->ebx = regs->ecx = regs->edx = regs->esp = 0;
 	regs->edi = LGUEST_MAGIC_EDI;
 	regs->ebp = LGUEST_MAGIC_EBP;
@@ -149,7 +148,7 @@ static int initialize(struct file *file,
 	if (err)
 		goto free_trap_page;
 
-	lg->state = setup_guest_state(i, lg->pgdirs[lg->pgdidx].pgdir,args[2]);
+	lg->state = setup_guest_state(i, args[2]);
 	if (!lg->state) {
 		err = -ENOEXEC;
 		goto release_pgtable;
arch/i386/lguest/page_tables.c +2 -1
diff -r 6efda2f8ac22 arch/i386/lguest/page_tables.c
@@ -223,7 +223,7 @@ void guest_new_pagetable(struct lguest *
 	if (newpgdir == ARRAY_SIZE(lg->pgdirs))
 		newpgdir = new_pgdir(lg, pgtable);
 	lg->pgdidx = newpgdir;
-	lg->state->regs.cr3 = __pa(lg->pgdirs[lg->pgdidx].pgdir);
+	lg->cr3 = __pa(lg->pgdirs[lg->pgdidx].pgdir);
 	pin_stack_pages(lg);
 }
 
@@ -296,6 +296,7 @@ int init_guest_pagetable(struct lguest *
 	lg->pgdirs[lg->pgdidx].pgdir = (u32*)get_zeroed_page(GFP_KERNEL);
 	if (!lg->pgdirs[lg->pgdidx].pgdir)
 		return -ENOMEM;
+	lg->cr3 = __pa(lg->pgdirs[lg->pgdidx].pgdir);
 	return 0;
 }
 
#4 lore raw

PATCH 5/9 lguest: documentation fixes

1: It helps if you connect the bridge to a link.

2: You can theoretically run lguest with no boot parameters.

Documentation/lguest/lguest.c +1 -1
diff -r 90134cf1fe0a Documentation/lguest/lguest.c
@@ -938,7 +938,7 @@ int main(int argc, char *argv[])
 		argc--;
 	}
 
-	if (argc < 4)
+	if (argc < 3)
 		errx(1, "Usage: lguest [--verbose] <mem> vmlinux "
 			"[--sharenet=<filename>|--tunnet=(<ipaddr>|bridge:<bridgename>)"
 			"|--block=<filename>|--initrd=<filename>]... [args...]");
Documentation/lguest/lguest.txt +1 -0
diff -r 90134cf1fe0a Documentation/lguest/lguest.txt
@@ -90,6 +90,7 @@ Running Lguest:
     ifconfig eth0 0.0.0.0
     brctl addbr lg0
     ifconfig lg0 up
+    brctl addif lg0 eth0
     dhclient lg0
     
   Then use --tunnet=bridge:lg0 when launching the guest.
#5 lore raw

PATCH 6/9 lguest: pin stack page optimization

We make sure that the stack is always mapped in pin_stack_pages by simply calling demand_page, but that calls get_user_pages() to find the pfn, which is way overkill since the page is almost certainly already mapped. So don't call pin_stack_pages every context switch (unless genuinely a completely clean context, all the kernel mappings are kept in sync), and when we do call it, have it check if it needs to call demand_page().

This speeds guest context switch by 25%:

Before:
Time for one context switch via pipe: 10606 nsec
After:
Time for one context switch via pipe: 7805 nsec
Native:
Time for one context switch via pipe: 4701 nsec

arch/i386/lguest/page_tables.c +25 -6
diff -r 06b3a533da77 arch/i386/lguest/page_tables.c
@@ -155,14 +158,29 @@ int demand_page(struct lguest *lg, u32 v
 	return page_in(lg, vaddr, (write ? _PAGE_DIRTY : 0)|_PAGE_ACCESSED);
 }
 
+/* This is much faster than the full demand_page logic. */
+static int page_writable(struct lguest *lg, unsigned long vaddr)
+{
+	u32 *top, *pte;
+
+	top = toplev(lg, lg->pgdidx, vaddr);
+	if (!(*top & _PAGE_PRESENT))
+		return 0;
+
+	pte = pteof(lg, *top, vaddr);
+	return (*pte & (_PAGE_PRESENT|_PAGE_RW)) == (_PAGE_PRESENT|_PAGE_RW);
+}
+
 void pin_stack_pages(struct lguest *lg)
 {
 	unsigned int i;
 	u32 stack = lg->state->tss.esp1;
 
-	for (i = 0; i < lg->stack_pages; i++)
-		if (!demand_page(lg, stack - i*PAGE_SIZE, 1))
+	for (i = 0; i < lg->stack_pages; i++) {
+		if (!page_writable(lg, stack - i * PAGE_SIZE)
+		    && !demand_page(lg, stack - i * PAGE_SIZE, 1))
 			kill_guest(lg, "bad stack page %i@%#x", i, stack);
+	}
 }
 
 static unsigned int find_pgdir(struct lguest *lg, u32 pgtable)
@@ -198,7 +216,7 @@ void guest_pagetable_flush_user(struct l
 	flush_user_mappings(lg, lg->pgdidx);
 }
 
-static unsigned int new_pgdir(struct lguest *lg, u32 cr3)
+static unsigned int new_pgdir(struct lguest *lg, u32 cr3, int *blank_pgdir)
 {
 	unsigned int next;
 
@@ -207,6 +225,9 @@ static unsigned int new_pgdir(struct lgu
 		lg->pgdirs[next].pgdir = (u32 *)get_zeroed_page(GFP_KERNEL);
 		if (!lg->pgdirs[next].pgdir)
 			next = lg->pgdidx;
+		else
+			/* There are no mappings: you'll need to re-pin */
+			*blank_pgdir = 1;
 	}
 	lg->pgdirs[next].cr3 = cr3;
 	/* Release all the non-kernel mappings. */
@@ -217,14 +238,15 @@ static unsigned int new_pgdir(struct lgu
 
 void guest_new_pagetable(struct lguest *lg, u32 pgtable)
 {
-	int newpgdir;
+	int newpgdir, repin = 0;
 
 	newpgdir = find_pgdir(lg, pgtable);
 	if (newpgdir == ARRAY_SIZE(lg->pgdirs))
-		newpgdir = new_pgdir(lg, pgtable);
+		newpgdir = new_pgdir(lg, pgtable, &repin);
 	lg->pgdidx = newpgdir;
 	lg->cr3 = __pa(lg->pgdirs[lg->pgdidx].pgdir);
-	pin_stack_pages(lg);
+	if (repin)
+		pin_stack_pages(lg);
 }
 
 static void release_all_pagetables(struct lguest *lg)
#6 lore raw

PATCH 7/9 lguest: use read-only pages rather than segments to protect high-mapped switcher

The current lguest uses segment limits to ensure that the guest cannot reach the switcher code at the top of virtual memory. This is bad for two reasons:

1) It introduces complexity when the guest wants to use 4G segments (ie. glibc's __thread support).
2) It doesn't work on x86-64 boxes.

The alternative is used here: in the host we map the actual switcher code, two per-cpu pages. The switcher code and one per-cpu page are
read-only: the read-only page contains the saved host state and the GDT, IDT and TSS the guest is using. The other per-cpu page is the stack page for the hypervisor, which is writable by the guest. This is where we save the guest registers: it's safe because while we're doing this we know the (UP) guest isn't running.

Switching into the guest involves copying in the registers, GDT and IDT to this cpu's pages, the copying the registers out on the way back. This is optimized in another patch.

arch/i386/kernel/asm-offsets.c +10 -10
diff -r 7a963f6eef0a arch/i386/kernel/asm-offsets.c
@@ -122,15 +122,15 @@ void foo(void)
 #ifdef CONFIG_LGUEST_GUEST
 	BLANK();
 	OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled);
-	OFFSET(LGUEST_STATE_host_stackptr, lguest_state, host.stackptr);
-	OFFSET(LGUEST_STATE_host_pgdir, lguest_state, host.pgdir);
-	OFFSET(LGUEST_STATE_host_gdt, lguest_state, host.gdt);
-	OFFSET(LGUEST_STATE_host_idt, lguest_state, host.idt);
-	OFFSET(LGUEST_STATE_regs, lguest_state, regs);
-	OFFSET(LGUEST_STATE_gdt, lguest_state, gdt);
-	OFFSET(LGUEST_STATE_idt, lguest_state, idt);
-	OFFSET(LGUEST_STATE_gdt_table, lguest_state, gdt_table);
-	OFFSET(LGUEST_STATE_trapnum, lguest_state, regs.trapnum);
-	OFFSET(LGUEST_STATE_errcode, lguest_state, regs.errcode);
+	OFFSET(LGUEST_PAGES_host_gdt_desc, lguest_pages, state.host_gdt_desc);
+	OFFSET(LGUEST_PAGES_host_idt_desc, lguest_pages, state.host_idt_desc);
+	OFFSET(LGUEST_PAGES_host_cr3, lguest_pages, state.host_cr3);
+	OFFSET(LGUEST_PAGES_host_sp, lguest_pages, state.host_sp);
+	OFFSET(LGUEST_PAGES_guest_gdt_desc, lguest_pages,state.guest_gdt_desc);
+	OFFSET(LGUEST_PAGES_guest_idt_desc, lguest_pages,state.guest_idt_desc);
+	OFFSET(LGUEST_PAGES_guest_gdt, lguest_pages, state.guest_gdt);
+	OFFSET(LGUEST_PAGES_regs_trapnum, lguest_pages, regs.trapnum);
+	OFFSET(LGUEST_PAGES_regs_errcode, lguest_pages, regs.errcode);
+	OFFSET(LGUEST_PAGES_regs, lguest_pages, regs);
 #endif
 }
arch/i386/lguest/Makefile +2 -2
diff -r 7a963f6eef0a arch/i386/lguest/Makefile
@@ -6,8 +6,8 @@ lg-objs := core.o hypercalls.o page_tabl
 lg-objs := core.o hypercalls.o page_tables.o interrupts_and_traps.o \
 	segments.o io.o lguest_user.o
 
-# We use top 4MB for guest traps page, then hypervisor. */
-HYPE_ADDR := (0xFFC00000+4096)
+# We use top 4MB for hypervisor. */
+HYPE_ADDR := 0xFFC00000
 # The data is only 1k (256 interrupt handler pointers)
 HYPE_DATA_SIZE := 1024
 CFLAGS += -DHYPE_ADDR="$(HYPE_ADDR)" -DHYPE_DATA_SIZE="$(HYPE_DATA_SIZE)"
arch/i386/lguest/core.c +86 -89

296 lines not on this page · raw message

arch/i386/lguest/hypercalls.c +2 -2
diff -r 7a963f6eef0a arch/i386/lguest/hypercalls.c
@@ -32,8 +32,8 @@ static void guest_set_stack(struct lgues
 		kill_guest(lg, "bad stack segment %i", seg);
 	if (pages > 2)
 		kill_guest(lg, "bad stack pages %u", pages);
-	lg->state->tss.ss1 = seg;
-	lg->state->tss.esp1 = esp;
+	lg->ss1 = seg;
+	lg->esp1 = esp;
 	lg->stack_pages = pages;
 	pin_stack_pages(lg);
 }
arch/i386/lguest/hypervisor.S +46 -44

141 lines not on this page · raw message

arch/i386/lguest/interrupts_and_traps.c +163 -124

334 lines not on this page · raw message

arch/i386/lguest/lg.h +86 -85

245 lines not on this page · raw message

arch/i386/lguest/lguest_user.c +5 -58

101 lines not on this page · raw message

arch/i386/lguest/page_tables.c +19 -19
diff -r 7a963f6eef0a arch/i386/lguest/page_tables.c
@@ -168,16 +168,10 @@ static int page_writable(struct lguest *
 	return (*pte & (_PAGE_PRESENT|_PAGE_RW)) == (_PAGE_PRESENT|_PAGE_RW);
 }
 
-void pin_stack_pages(struct lguest *lg)
-{
-	unsigned int i;
-	u32 stack = lg->state->tss.esp1;
-
-	for (i = 0; i < lg->stack_pages; i++) {
-		if (!page_writable(lg, stack - i * PAGE_SIZE)
-		    && !demand_page(lg, stack - i * PAGE_SIZE, 1))
-			kill_guest(lg, "bad stack page %i@%#x", i, stack);
-	}
+void pin_page(struct lguest *lg, u32 addr)
+{
+	if (!page_writable(lg, addr) && !demand_page(lg, addr, 0))
+		kill_guest(lg, "bad trap page %#x", addr);
 }
 
 static unsigned int find_pgdir(struct lguest *lg, u32 pgtable)
@@ -243,7 +237,7 @@ void guest_new_pagetable(struct lguest *
 	lg->pgdidx = newpgdir;
 	lg->cr3 = __pa(lg->pgdirs[lg->pgdidx].pgdir);
 	if (repin)
-		pin_stack_pages(lg);
+		pin_trap_pages(lg);
 }
 
 static void release_all_pagetables(struct lguest *lg)
@@ -259,7 +253,7 @@ void guest_pagetable_clear_all(struct lg
 void guest_pagetable_clear_all(struct lguest *lg)
 {
 	release_all_pagetables(lg);
-	pin_stack_pages(lg);
+	pin_trap_pages(lg);
 }
 
 static void do_set_pte(struct lguest *lg, int idx,
@@ -329,11 +323,9 @@ void free_guest_pagetable(struct lguest 
 }
 
 /* Caller must be preempt-safe */
-void map_trap_page(struct lguest *lg)
+void map_hypervisor_in_guest(struct lguest *lg)
 {
 	int cpu = smp_processor_id();
-
-	hypervisor_pte_page(cpu)[0] = (__pa(lg->trap_page)|_PAGE_PRESENT);
 
 	/* Since hypervisor less that 4MB, we simply mug top pte page. */
 	lg->pgdirs[lg->pgdidx].pgdir[HYPERVISOR_PGD_ENTRY] =
@@ -356,10 +348,18 @@ static __init void populate_hypervisor_p
 	u32 *pte = hypervisor_pte_page(cpu);
 
 	for (i = 0; i < pages; i++) {
-		/* First entry set dynamically in map_trap_page */
-		pte[i+1] = ((page_to_pfn(hype_page[i]) << PAGE_SHIFT) 
-			    | _PAGE_KERNEL_EXEC);
-	}
+		pte[i] = ((page_to_pfn(hype_page[i]) << PAGE_SHIFT) 
+			    | _PAGE_PRESENT|_PAGE_ACCESSED);
+	}
+
+	/* We only map this CPU's pages, so guest can't see others. */
+	i = pages + cpu*2;
+
+	/* First page (regs) is rw, second (state) is ro. */
+	pte[i] = ((page_to_pfn(hype_page[i]) << PAGE_SHIFT)
+		  | _PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW);
+	pte[i+1] = ((page_to_pfn(hype_page[i+1]) << PAGE_SHIFT)
+		    | _PAGE_PRESENT|_PAGE_ACCESSED);
 }
 
 __init int init_pagetables(struct page **hype_page, int pages)
arch/i386/lguest/segments.c +78 -135

249 lines not on this page · raw message

include/asm-i386/lguest.h +0 -3
diff -r 7a963f6eef0a include/asm-i386/lguest.h
@@ -59,9 +59,6 @@ struct lguest_data
 	/* Blocked interrupts. */
 	DECLARE_BITMAP(interrupts, LGUEST_IRQS);
 
-	/* Last (userspace) address we got a GPF & reloaded gs. */
-	unsigned int gs_gpf_eip;
-
 	/* Virtual address of page fault. */
 	unsigned long cr2;
 
#7 lore raw

PATCH 8/9 lguest: Optimize away copy in and out of per-cpu guest pages

Rather than copy in IDT, GDT and TSS every time, we only need do it when something has changed (ie. guest IDT/GDT/TSS has changed, or guest has changed CPU, or CPU has just run another guest).

For the registers, we simply allocate them an entire page and map that over the stack page in the guest.

This restores context switch speed to be comparable to the old segment-using lguest.

arch/i386/lguest/core.c +26 -28
diff -r 8286b7923a5b arch/i386/lguest/core.c
@@ -37,6 +37,7 @@ static struct {
 	unsigned short segment;
 } lguest_entry __attribute_used__;
 DEFINE_MUTEX(lguest_lock);
+static DEFINE_PER_CPU(struct lguest *, last_guest);
 
 /* FIXME: Make dynamic. */
 #define MAX_LGUEST_GUESTS 16
@@ -144,10 +145,10 @@ static int emulate_insn(struct lguest *l
 {
 	u8 insn;
 	unsigned int insnlen = 0, in = 0, shift = 0;
-	unsigned long physaddr = guest_pa(lg, lg->regs.eip);
+	unsigned long physaddr = guest_pa(lg, lg->regs->eip);
 
 	/* This only works for addresses in linear mapping... */
-	if (lg->regs.eip < lg->page_offset)
+	if (lg->regs->eip < lg->page_offset)
 		return 0;
 	lhread(lg, &insn, physaddr, 1);
 
@@ -180,11 +181,11 @@ static int emulate_insn(struct lguest *l
 	if (in) {
 		/* Lower bit tells is whether it's a 16 or 32 bit access */
 		if (insn & 0x1)
-			lg->regs.eax = 0xFFFFFFFF;
+			lg->regs->eax = 0xFFFFFFFF;
 		else
-			lg->regs.eax |= (0xFFFF << shift);
-	}
-	lg->regs.eip += insnlen;
+			lg->regs->eax |= (0xFFFF << shift);
+	}
+	lg->regs->eip += insnlen;
 	return 1;
 }
 
@@ -260,36 +261,35 @@ static void run_guest_once(struct lguest
 		     : "memory", "%edx", "%ecx", "%edi", "%esi");
 }
 
-static void copy_in_guest_info(struct lguest_pages *pages,
-			       struct lguest *lg)
-{
-	/* Copy in regs. */
-	pages->regs = lg->regs;
-
-	/* TSS entries for direct traps. */
+static void copy_in_guest_info(struct lguest_pages *pages, struct lguest *lg)
+{
+	if (__get_cpu_var(last_guest) != lg || lg->last_pages != pages) {
+		__get_cpu_var(last_guest) = lg;
+		lg->last_pages = pages;
+		lg->changed = CHANGED_ALL;
+	}
+
+	/* These are pretty cheap, so we do them unconditionally. */
+	pages->state.host_cr3 = __pa(current->mm->pgd);
+	map_hypervisor_in_guest(lg, pages);
 	pages->state.guest_tss.esp1 = lg->esp1;
 	pages->state.guest_tss.ss1 = lg->ss1;
 
-	/* CR3 */
-	pages->state.host_cr3 = __pa(current->mm->pgd);
-
 	/* Copy direct trap entries. */
-	copy_traps(lg, pages->state.guest_idt, lguest_default_idt_entries());
+	if (lg->changed & CHANGED_IDT)
+		copy_traps(lg, pages->state.guest_idt,
+			   lguest_default_idt_entries());
 
 	/* Copy all GDT entries but the TSS. */
-	copy_gdt(lg, pages->state.guest_gdt);
-}
-
-static void copy_out_guest_info(struct lguest *lg,
-				const struct lguest_pages *pages)
-{
-	/* We just want the regs back. */
-	lg->regs = pages->regs;
+	if (lg->changed & CHANGED_GDT)
+		copy_gdt(lg, pages->state.guest_gdt);
+
+	lg->changed = 0;
 }
 
 int run_guest(struct lguest *lg, char *__user user)
 {
-	struct lguest_regs *regs = &lg->regs;
+	struct lguest_regs *regs = lg->regs;
 
 	while (!lg->dead) {
 		unsigned int cr2 = 0; /* Damn gcc */
@@ -327,10 +327,8 @@ int run_guest(struct lguest *lg, char *_
 		set_ts(lg->ts);
 
 		pages = lguest_pages(raw_smp_processor_id());
-		map_hypervisor_in_guest(lg);
 		copy_in_guest_info(pages, lg);
 		run_guest_once(lg, pages);
-		copy_out_guest_info(lg, pages);
 
 		/* Save cr2 now if we page-faulted. */
 		if (regs->trapnum == 14)
arch/i386/lguest/hypervisor.S +5 -10
diff -r 8286b7923a5b arch/i386/lguest/hypervisor.S
@@ -76,6 +76,8 @@ switch_to_guest:
 	/* Figure out where we are, based on stack (at top of regs). */	\
 	movl	%esp, %eax;						\
 	subl	$LGUEST_PAGES_regs, %eax;				\
+	/* Put trap number in %ebx before we switch cr3 and lose it. */ \
+	movl	LGUEST_PAGES_regs_trapnum(%eax), %ebx;			\
 	/* Switch to host page tables (host GDT, IDT and stack are in host   \
 	   mem, so need this first) */					\
 	movl	LGUEST_PAGES_host_cr3(%eax), %edx;			\
@@ -104,23 +106,15 @@ return_to_host:
 
 deliver_to_host:
 	SWITCH_TO_HOST
-decode_idt_and_jmp:
 	/* Decode IDT and jump to hosts' irq handler.  When that does iret, it
 	 * will return to run_guest_once.  This is a feature. */
 	movl	(LGUEST_PAGES_host_idt_desc+2)(%eax), %edx
-	movl	LGUEST_PAGES_regs_trapnum(%eax), %eax
-	leal	(%edx,%eax,8), %eax
+	leal	(%edx,%ebx,8), %eax
 	movzwl	(%eax),%edx
 	movl	4(%eax), %eax
 	xorw	%ax, %ax
 	orl	%eax, %edx
 	jmp	*%edx
-
-/* FIXME: NMI needs something completely different.  Don't SWITCH_TO_HOST. */
-deliver_to_host_with_errcode:
-	SWITCH_TO_HOST
-	pushl	LGUEST_PAGES_regs_errcode(%eax)
-	jmp decode_idt_and_jmp
 
 /* Real hardware interrupts are delivered straight to the host.  Others
    cause us to return to run_guest_once so it can decide what to do.  Note
@@ -154,7 +148,8 @@ default_idt_entries:
 default_idt_entries:
 .text
 	IRQ_STUBS 0 1 return_to_host		/* First two traps */
-	IRQ_STUB 2 deliver_to_host_with_errcode	/* NMI */
+/* FIXME: NMI needs something completely different.  Don't SWITCH_TO_HOST. */
+	IRQ_STUB 2 deliver_to_host		/* NMI */
 	IRQ_STUBS 3 31 return_to_host		/* Rest of traps */
 	IRQ_STUBS 32 127 deliver_to_host	/* Real interrupts */
 	IRQ_STUB 128 return_to_host		/* System call (overridden) */
arch/i386/lguest/interrupts_and_traps.c +4 -3
diff -r 8286b7923a5b arch/i386/lguest/interrupts_and_traps.c
@@ -25,7 +25,7 @@ static void reflect_trap(struct lguest *
 {
 	u32 __user *gstack;
 	u32 eflags, ss, irq_enable;
-	struct lguest_regs *regs = &lg->regs;
+	struct lguest_regs *regs = lg->regs;
 
 	/* If they want a ring change, we use new stack and push old ss/esp */
 	if ((regs->ss&0x3) != GUEST_DPL) {
@@ -121,11 +121,11 @@ void check_bug_kill(struct lguest *lg)
 void check_bug_kill(struct lguest *lg)
 {
 #ifdef CONFIG_BUG
-	u32 eip = lg->regs.eip - PAGE_OFFSET;
+	u32 eip = lg->regs->eip - PAGE_OFFSET;
 	u16 insn;
 
 	/* This only works for addresses in linear mapping... */
-	if (lg->regs.eip < PAGE_OFFSET)
+	if (lg->regs->eip < PAGE_OFFSET)
 		return;
 	lhread(lg, &insn, eip, sizeof(insn));
 	if (insn == 0x0b0f) {
@@ -219,6 +219,7 @@ void load_guest_idt_entry(struct lguest 
 	if (num == 2 || num == 8 || num == 15 || num == LGUEST_TRAP_ENTRY)
 		return;
 
+	lg->changed |= CHANGED_IDT;
 	if (num < ARRAY_SIZE(lg->idt))
 		set_trap(lg, &lg->idt[num], num, lo, hi);
 	else if (num == SYSCALL_VECTOR)
arch/i386/lguest/lg.h +12 -2
diff -r 8286b7923a5b arch/i386/lguest/lg.h
@@ -118,10 +118,16 @@ struct lguest_pages
 	struct lguest_ro_state state;
 } __attribute__((aligned(PAGE_SIZE)));
 
+#define CHANGED_IDT		1
+#define CHANGED_GDT		2
+#define CHANGED_ALL	        3
+
 /* The private info the thread maintains about the guest. */
 struct lguest
 {
-	struct lguest_regs regs;
+	/* At end of a page shared mapped over lguest_pages in guest.  */
+	unsigned long regs_page;
+	struct lguest_regs *regs;
 	struct lguest_data __user *lguest_data;
 	struct task_struct *tsk;
 	struct mm_struct *mm; 	/* == tsk->mm, but that becomes NULL on exit */
@@ -138,6 +144,10 @@ struct lguest
 	u32 esp1;
 	u8 ss1;
 
+	/* Bitmap of what has changed: see CHANGED_* above. */
+	int changed;
+	struct lguest_pages *last_pages;
+
 	/* We keep a small number of these. */
 	u32 pgdidx;
 	struct pgdir pgdirs[4];
@@ -210,7 +220,7 @@ void guest_pagetable_flush_user(struct l
 void guest_pagetable_flush_user(struct lguest *lg);
 void guest_set_pte(struct lguest *lg, unsigned long cr3,
 		   unsigned long vaddr, u32 val);
-void map_hypervisor_in_guest(struct lguest *lg);
+void map_hypervisor_in_guest(struct lguest *lg, struct lguest_pages *pages);
 int demand_page(struct lguest *info, u32 cr2, int write);
 void pin_page(struct lguest *lg, u32 addr);
 
arch/i386/lguest/lguest_user.c +12 -2
diff -r 8286b7923a5b arch/i386/lguest/lguest_user.c
@@ -100,19 +100,28 @@ static int initialize(struct file *file,
 	lg->guestid = i;
 	lg->pfn_limit = args[0];
 	lg->page_offset = args[3];
+	lg->regs_page = get_zeroed_page(GFP_KERNEL);
+	if (!lg->regs_page) {
+		err = -ENOMEM;
+		goto release_guest;
+	}
+	lg->regs = (void *)lg->regs_page + PAGE_SIZE - sizeof(*lg->regs);
 
 	err = init_guest_pagetable(lg, args[1]);
 	if (err)
-		goto release_guest;
+		goto free_regs;
 
-	setup_regs(&lg->regs, args[2]);
+	setup_regs(lg->regs, args[2]);
 	lg->tsk = current;
 	lg->mm = get_task_mm(current);
+	lg->last_pages = NULL;
 	mutex_unlock(&lguest_lock);
 
 	file->private_data = lg;
 	return sizeof(args);
 
+free_regs:
+	free_page(lg->regs_page);
 release_guest:
 	memset(lg, 0, sizeof(*lg));
 unlock:
@@ -160,6 +169,7 @@ static int close(struct inode *inode, st
 	mmput(lg->mm);
 	if (lg->dead != (void *)1)
 		kfree(lg->dead);
+	free_page(lg->regs_page);
 	memset(lg, 0, sizeof(*lg));
 	mutex_unlock(&lguest_lock);
 	return 0;
arch/i386/lguest/page_tables.c +9 -5
diff -r 8286b7923a5b arch/i386/lguest/page_tables.c
@@ -99,7 +99,7 @@ static u32 get_pte(struct lguest *lg, u3
    swapped.  It'd be nice to have a callback when Linux wants to swap out. */
 
 /* We fault pages in, which allows us to update accessed/dirty bits.
- * Return NULL or the pte page. */
+ * Return true if we got page. */
 static int page_in(struct lguest *lg, u32 vaddr, unsigned flags)
 {
 	u32 gtop, gpte;
@@ -323,13 +323,17 @@ void free_guest_pagetable(struct lguest 
 }
 
 /* Caller must be preempt-safe */
-void map_hypervisor_in_guest(struct lguest *lg)
-{
-	int cpu = smp_processor_id();
+void map_hypervisor_in_guest(struct lguest *lg, struct lguest_pages *pages)
+{
+	u32 *hype_pte_page = __get_cpu_var(hypervisor_pte_pages);
 
 	/* Since hypervisor less that 4MB, we simply mug top pte page. */
 	lg->pgdirs[lg->pgdidx].pgdir[HYPERVISOR_PGD_ENTRY] =
-		(__pa(hypervisor_pte_page(cpu))| _PAGE_KERNEL);
+		(__pa(hype_pte_page) | _PAGE_KERNEL);
+
+	/* Map our regs page over stack page. */
+	hype_pte_page[(unsigned long)pages / PAGE_SIZE % PTES_PER_PAGE]
+		= (__pa(lg->regs_page) | _PAGE_KERNEL);
 }
 
 static void free_hypervisor_pte_pages(void)
arch/i386/lguest/segments.c +11 -9
diff -r 8286b7923a5b arch/i386/lguest/segments.c
@@ -24,15 +24,15 @@ static int ignored_gdt(unsigned int num)
 /* We don't allow removal of CS, DS or SS; it doesn't make sense. */
 static void check_segment_use(struct lguest *lg, unsigned int desc)
 {
-	if (lg->regs.gs / 8 == desc)
-		lg->regs.gs = 0;
-	if (lg->regs.fs / 8 == desc)
-		lg->regs.fs = 0;
-	if (lg->regs.es / 8 == desc)
-		lg->regs.es = 0;
-	if (lg->regs.ds / 8 == desc
-	    || lg->regs.cs / 8 == desc
-	    || lg->regs.ss / 8 == desc)
+	if (lg->regs->gs / 8 == desc)
+		lg->regs->gs = 0;
+	if (lg->regs->fs / 8 == desc)
+		lg->regs->fs = 0;
+	if (lg->regs->es / 8 == desc)
+		lg->regs->es = 0;
+	if (lg->regs->ds / 8 == desc
+	    || lg->regs->cs / 8 == desc
+	    || lg->regs->ss / 8 == desc)
 		kill_guest(lg, "Removed live GDT entry %u", desc);
 }
 
@@ -103,6 +103,7 @@ void load_guest_gdt(struct lguest *lg, u
 
 	lhread(lg, lg->gdt, table, num * sizeof(lg->gdt[0]));
 	fixup_gdt_table(lg);
+	lg->changed |= CHANGED_GDT;
 }
 
 void guest_load_tls(struct lguest *lg, const struct desc_struct __user *gtls)
@@ -111,4 +112,5 @@ void guest_load_tls(struct lguest *lg, c
 
 	lhread(lg, tls, (u32)gtls, sizeof(*tls)*GDT_ENTRY_TLS_ENTRIES);
 	fixup_gdt_table(lg);
+	lg->changed |= CHANGED_GDT;
 }
#8 lore raw

PATCH 9/9 lguest: don't crash host on NMI

"handle" NMI by ignoring it. Can't have been important, right? As the lguest64 hackers explained, handling NMI is a PITA. Now oprofile does not crash machine.

arch/i386/lguest/hypervisor.S +6 -2
diff -r 5beeb29ed3a3 arch/i386/lguest/hypervisor.S
@@ -116,6 +116,11 @@ deliver_to_host:
 	orl	%eax, %edx
 	jmp	*%edx
 
+/* We ignore NMI and return. */
+handle_nmi:
+	addl	$8, %esp
+	iret
+
 /* Real hardware interrupts are delivered straight to the host.  Others
    cause us to return to run_guest_once so it can decide what to do.  Note
    that some of these are overridden by the guest to deliver directly, and
@@ -148,8 +153,7 @@ default_idt_entries:
 default_idt_entries:
 .text
 	IRQ_STUBS 0 1 return_to_host		/* First two traps */
-/* FIXME: NMI needs something completely different.  Don't SWITCH_TO_HOST. */
-	IRQ_STUB 2 deliver_to_host		/* NMI */
+	IRQ_STUB 2 handle_nmi			/* NMI */
 	IRQ_STUBS 3 31 return_to_host		/* Rest of traps */
 	IRQ_STUBS 32 127 deliver_to_host	/* Real interrupts */
 	IRQ_STUB 128 return_to_host		/* System call (overridden) */
#9 lore raw
In reply to Rusty Russell
Quoted 6 lines · Rusty Russell

diff -r fdc8cbc1fd61 drivers/block/lguest_blk.c

drivers/block/lguest_blk.c +0 -0
@@ -45,6 +45,16 @@ struct blockdev
 	struct request *req;
 };

drivers/block/lguest_blk.c struct blockdev · line 56

Quoted 10 lines
drivers/block/lguest_blk.c +9 -0
 
+/* Jens gave me this nice helper to end all chunks of a request. */
+static void end_entire_request(struct request *req, int uptodate)
+{
+	if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
+		BUG();
+	add_disk_randomness(req->rq_disk);
+	blkdev_dequeue_request(req);
+	end_that_request_last(req, uptodate);
+}

I think we really want this in common code, ll_rw_blk.c should have:

static int __end_request(struct request *req, int uptodate,
unsigned int sectors)

{
        if (!end_that_request_first(req, uptodate, sectors)) {
		add_disk_randomness(req->rq_disk);
		blkdev_dequeue_request(req);
		end_that_request_last(req, uptodate);
		return 1;
	}
	return 0;
}

/* TODO: add kerneldoc comment */
/* XXX: should be called end_partial_request */ void end_request(struct request *req, int uptodate)

{
	__end_request(req, uptodate, req->hard_cur_sectors);
}

/* TODO: add kerneldoc comment */
void end_entired_request(struct request *req, int uptodate)

{
	if (!__end_request(req, uptodate, req->hard_nr_sectors))
		BUG();
}

the latter two maybe as inlines

#10 lore raw
Quoted 8 lines · Christoph Hellwig
Quoted 7 lines · Rusty Russell

diff -r fdc8cbc1fd61 drivers/block/lguest_blk.c

drivers/block/lguest_blk.c +0 -0
@@ -45,6 +45,16 @@ struct blockdev
 	struct request *req;
 };
 
Quoted 11 lines

drivers/block/lguest_blk.c struct blockdev · line 56

Quoted 9 lines
drivers/block/lguest_blk.c +9 -0
+/* Jens gave me this nice helper to end all chunks of a request. */
+static void end_entire_request(struct request *req, int uptodate)
+{
+	if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
+		BUG();
+	add_disk_randomness(req->rq_disk);
+	blkdev_dequeue_request(req);
+	end_that_request_last(req, uptodate);
+}

I think we really want this in common code, ll_rw_blk.c should have:

Yeah I know, this is also what I wrote to Rusty when I reviewed this code and made the suggestion. The kernel also needs a full sweep to get rid of end_request() entirely, it's really a relic from ancient times.

Signature · 1 line
Jens Axboe