aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/xenbus
diff options
context:
space:
mode:
authorDaniel De Graaf <dgdegra@tycho.nsa.gov>2011-12-19 14:55:14 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-12-20 17:02:35 -0500
commit2c5d37d30fbd27d424a18abc16786cb152a37017 (patch)
treea721776de9157570120057a31de7e271cbe56b28 /drivers/xen/xenbus
parentcb85f123cd2393581bcffad335bfc6bcdd58569c (diff)
xenbus: Support HVM backends
Add HVM implementations of xenbus_(map,unmap)_ring_v(alloc,free) so that ring mappings can be done without using GNTMAP_contains_pte which is not supported on HVM. This also removes the need to use vmlist_lock on PV by tracking the allocated xenbus rings. Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov> [v1: Fix compile error when XENBUS_FRONTEND is defined as module] Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen/xenbus')
-rw-r--r--drivers/xen/xenbus/xenbus_client.c176
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c2
-rw-r--r--drivers/xen/xenbus/xenbus_probe.h2
3 files changed, 159 insertions, 21 deletions
diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c
index 1906125eab49..0fa52916ad05 100644
--- a/drivers/xen/xenbus/xenbus_client.c
+++ b/drivers/xen/xenbus/xenbus_client.c
@@ -32,15 +32,39 @@
32 32
33#include <linux/slab.h> 33#include <linux/slab.h>
34#include <linux/types.h> 34#include <linux/types.h>
35#include <linux/spinlock.h>
35#include <linux/vmalloc.h> 36#include <linux/vmalloc.h>
36#include <linux/export.h> 37#include <linux/export.h>
37#include <asm/xen/hypervisor.h> 38#include <asm/xen/hypervisor.h>
38#include <asm/xen/page.h> 39#include <asm/xen/page.h>
39#include <xen/interface/xen.h> 40#include <xen/interface/xen.h>
40#include <xen/interface/event_channel.h> 41#include <xen/interface/event_channel.h>
42#include <xen/balloon.h>
41#include <xen/events.h> 43#include <xen/events.h>
42#include <xen/grant_table.h> 44#include <xen/grant_table.h>
43#include <xen/xenbus.h> 45#include <xen/xenbus.h>
46#include <xen/xen.h>
47
48#include "xenbus_probe.h"
49
50struct xenbus_map_node {
51 struct list_head next;
52 union {
53 struct vm_struct *area; /* PV */
54 struct page *page; /* HVM */
55 };
56 grant_handle_t handle;
57};
58
59static DEFINE_SPINLOCK(xenbus_valloc_lock);
60static LIST_HEAD(xenbus_valloc_pages);
61
62struct xenbus_ring_ops {
63 int (*map)(struct xenbus_device *dev, int gnt, void **vaddr);
64 int (*unmap)(struct xenbus_device *dev, void *vaddr);
65};
66
67static const struct xenbus_ring_ops *ring_ops __read_mostly;
44 68
45const char *xenbus_strstate(enum xenbus_state state) 69const char *xenbus_strstate(enum xenbus_state state)
46{ 70{
@@ -436,19 +460,33 @@ EXPORT_SYMBOL_GPL(xenbus_free_evtchn);
436 */ 460 */
437int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) 461int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr)
438{ 462{
463 return ring_ops->map(dev, gnt_ref, vaddr);
464}
465EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc);
466
467static int xenbus_map_ring_valloc_pv(struct xenbus_device *dev,
468 int gnt_ref, void **vaddr)
469{
439 struct gnttab_map_grant_ref op = { 470 struct gnttab_map_grant_ref op = {
440 .flags = GNTMAP_host_map | GNTMAP_contains_pte, 471 .flags = GNTMAP_host_map | GNTMAP_contains_pte,
441 .ref = gnt_ref, 472 .ref = gnt_ref,
442 .dom = dev->otherend_id, 473 .dom = dev->otherend_id,
443 }; 474 };
475 struct xenbus_map_node *node;
444 struct vm_struct *area; 476 struct vm_struct *area;
445 pte_t *pte; 477 pte_t *pte;
446 478
447 *vaddr = NULL; 479 *vaddr = NULL;
448 480
481 node = kzalloc(sizeof(*node), GFP_KERNEL);
482 if (!node)
483 return -ENOMEM;
484
449 area = alloc_vm_area(PAGE_SIZE, &pte); 485 area = alloc_vm_area(PAGE_SIZE, &pte);
450 if (!area) 486 if (!area) {
487 kfree(node);
451 return -ENOMEM; 488 return -ENOMEM;
489 }
452 490
453 op.host_addr = arbitrary_virt_to_machine(pte).maddr; 491 op.host_addr = arbitrary_virt_to_machine(pte).maddr;
454 492
@@ -457,19 +495,59 @@ int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr)
457 495
458 if (op.status != GNTST_okay) { 496 if (op.status != GNTST_okay) {
459 free_vm_area(area); 497 free_vm_area(area);
498 kfree(node);
460 xenbus_dev_fatal(dev, op.status, 499 xenbus_dev_fatal(dev, op.status,
461 "mapping in shared page %d from domain %d", 500 "mapping in shared page %d from domain %d",
462 gnt_ref, dev->otherend_id); 501 gnt_ref, dev->otherend_id);
463 return op.status; 502 return op.status;
464 } 503 }
465 504
466 /* Stuff the handle in an unused field */ 505 node->handle = op.handle;
467 area->phys_addr = (unsigned long)op.handle; 506 node->area = area;
507
508 spin_lock(&xenbus_valloc_lock);
509 list_add(&node->next, &xenbus_valloc_pages);
510 spin_unlock(&xenbus_valloc_lock);
468 511
469 *vaddr = area->addr; 512 *vaddr = area->addr;
470 return 0; 513 return 0;
471} 514}
472EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc); 515
516static int xenbus_map_ring_valloc_hvm(struct xenbus_device *dev,
517 int gnt_ref, void **vaddr)
518{
519 struct xenbus_map_node *node;
520 int err;
521 void *addr;
522
523 *vaddr = NULL;
524
525 node = kzalloc(sizeof(*node), GFP_KERNEL);
526 if (!node)
527 return -ENOMEM;
528
529 err = alloc_xenballooned_pages(1, &node->page, false /* lowmem */);
530 if (err)
531 goto out_err;
532
533 addr = pfn_to_kaddr(page_to_pfn(node->page));
534
535 err = xenbus_map_ring(dev, gnt_ref, &node->handle, addr);
536 if (err)
537 goto out_err;
538
539 spin_lock(&xenbus_valloc_lock);
540 list_add(&node->next, &xenbus_valloc_pages);
541 spin_unlock(&xenbus_valloc_lock);
542
543 *vaddr = addr;
544 return 0;
545
546 out_err:
547 free_xenballooned_pages(1, &node->page);
548 kfree(node);
549 return err;
550}
473 551
474 552
475/** 553/**
@@ -525,32 +603,36 @@ EXPORT_SYMBOL_GPL(xenbus_map_ring);
525 */ 603 */
526int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr) 604int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr)
527{ 605{
528 struct vm_struct *area; 606 return ring_ops->unmap(dev, vaddr);
607}
608EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree);
609
610static int xenbus_unmap_ring_vfree_pv(struct xenbus_device *dev, void *vaddr)
611{
612 struct xenbus_map_node *node;
529 struct gnttab_unmap_grant_ref op = { 613 struct gnttab_unmap_grant_ref op = {
530 .host_addr = (unsigned long)vaddr, 614 .host_addr = (unsigned long)vaddr,
531 }; 615 };
532 unsigned int level; 616 unsigned int level;
533 617
534 /* It'd be nice if linux/vmalloc.h provided a find_vm_area(void *addr) 618 spin_lock(&xenbus_valloc_lock);
535 * method so that we don't have to muck with vmalloc internals here. 619 list_for_each_entry(node, &xenbus_valloc_pages, next) {
536 * We could force the user to hang on to their struct vm_struct from 620 if (node->area->addr == vaddr) {
537 * xenbus_map_ring_valloc, but these 6 lines considerably simplify 621 list_del(&node->next);
538 * this API. 622 goto found;
539 */ 623 }
540 read_lock(&vmlist_lock);
541 for (area = vmlist; area != NULL; area = area->next) {
542 if (area->addr == vaddr)
543 break;
544 } 624 }
545 read_unlock(&vmlist_lock); 625 node = NULL;
626 found:
627 spin_unlock(&xenbus_valloc_lock);
546 628
547 if (!area) { 629 if (!node) {
548 xenbus_dev_error(dev, -ENOENT, 630 xenbus_dev_error(dev, -ENOENT,
549 "can't find mapped virtual address %p", vaddr); 631 "can't find mapped virtual address %p", vaddr);
550 return GNTST_bad_virt_addr; 632 return GNTST_bad_virt_addr;
551 } 633 }
552 634
553 op.handle = (grant_handle_t)area->phys_addr; 635 op.handle = node->handle;
554 op.host_addr = arbitrary_virt_to_machine( 636 op.host_addr = arbitrary_virt_to_machine(
555 lookup_address((unsigned long)vaddr, &level)).maddr; 637 lookup_address((unsigned long)vaddr, &level)).maddr;
556 638
@@ -558,16 +640,50 @@ int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr)
558 BUG(); 640 BUG();
559 641
560 if (op.status == GNTST_okay) 642 if (op.status == GNTST_okay)
561 free_vm_area(area); 643 free_vm_area(node->area);
562 else 644 else
563 xenbus_dev_error(dev, op.status, 645 xenbus_dev_error(dev, op.status,
564 "unmapping page at handle %d error %d", 646 "unmapping page at handle %d error %d",
565 (int16_t)area->phys_addr, op.status); 647 node->handle, op.status);
566 648
649 kfree(node);
567 return op.status; 650 return op.status;
568} 651}
569EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree);
570 652
653static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
654{
655 int rv;
656 struct xenbus_map_node *node;
657 void *addr;
658
659 spin_lock(&xenbus_valloc_lock);
660 list_for_each_entry(node, &xenbus_valloc_pages, next) {
661 addr = pfn_to_kaddr(page_to_pfn(node->page));
662 if (addr == vaddr) {
663 list_del(&node->next);
664 goto found;
665 }
666 }
667 node = NULL;
668 found:
669 spin_unlock(&xenbus_valloc_lock);
670
671 if (!node) {
672 xenbus_dev_error(dev, -ENOENT,
673 "can't find mapped virtual address %p", vaddr);
674 return GNTST_bad_virt_addr;
675 }
676
677 rv = xenbus_unmap_ring(dev, node->handle, addr);
678
679 if (!rv)
680 free_xenballooned_pages(1, &node->page);
681 else
682 WARN(1, "Leaking %p\n", vaddr);
683
684 kfree(node);
685 return rv;
686}
571 687
572/** 688/**
573 * xenbus_unmap_ring 689 * xenbus_unmap_ring
@@ -617,3 +733,21 @@ enum xenbus_state xenbus_read_driver_state(const char *path)
617 return result; 733 return result;
618} 734}
619EXPORT_SYMBOL_GPL(xenbus_read_driver_state); 735EXPORT_SYMBOL_GPL(xenbus_read_driver_state);
736
737static const struct xenbus_ring_ops ring_ops_pv = {
738 .map = xenbus_map_ring_valloc_pv,
739 .unmap = xenbus_unmap_ring_vfree_pv,
740};
741
742static const struct xenbus_ring_ops ring_ops_hvm = {
743 .map = xenbus_map_ring_valloc_hvm,
744 .unmap = xenbus_unmap_ring_vfree_hvm,
745};
746
747void __init xenbus_ring_ops_init(void)
748{
749 if (xen_pv_domain())
750 ring_ops = &ring_ops_pv;
751 else
752 ring_ops = &ring_ops_hvm;
753}
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 1b178c6e8937..1c05b2508ae8 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -730,6 +730,8 @@ static int __init xenbus_init(void)
730 if (!xen_domain()) 730 if (!xen_domain())
731 return -ENODEV; 731 return -ENODEV;
732 732
733 xenbus_ring_ops_init();
734
733 if (xen_hvm_domain()) { 735 if (xen_hvm_domain()) {
734 uint64_t v = 0; 736 uint64_t v = 0;
735 err = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v); 737 err = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v);
diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h
index 9b1de4e34c64..460d784a769a 100644
--- a/drivers/xen/xenbus/xenbus_probe.h
+++ b/drivers/xen/xenbus/xenbus_probe.h
@@ -76,4 +76,6 @@ extern void xenbus_otherend_changed(struct xenbus_watch *watch,
76extern int xenbus_read_otherend_details(struct xenbus_device *xendev, 76extern int xenbus_read_otherend_details(struct xenbus_device *xendev,
77 char *id_node, char *path_node); 77 char *id_node, char *path_node);
78 78
79void xenbus_ring_ops_init(void);
80
79#endif 81#endif