diff options
author | Daniel De Graaf <dgdegra@tycho.nsa.gov> | 2011-12-19 14:55:14 -0500 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-12-20 17:02:35 -0500 |
commit | 2c5d37d30fbd27d424a18abc16786cb152a37017 (patch) | |
tree | a721776de9157570120057a31de7e271cbe56b28 /drivers/xen/xenbus | |
parent | cb85f123cd2393581bcffad335bfc6bcdd58569c (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.c | 176 | ||||
-rw-r--r-- | drivers/xen/xenbus/xenbus_probe.c | 2 | ||||
-rw-r--r-- | drivers/xen/xenbus/xenbus_probe.h | 2 |
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 | |||
50 | struct 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 | |||
59 | static DEFINE_SPINLOCK(xenbus_valloc_lock); | ||
60 | static LIST_HEAD(xenbus_valloc_pages); | ||
61 | |||
62 | struct 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 | |||
67 | static const struct xenbus_ring_ops *ring_ops __read_mostly; | ||
44 | 68 | ||
45 | const char *xenbus_strstate(enum xenbus_state state) | 69 | const char *xenbus_strstate(enum xenbus_state state) |
46 | { | 70 | { |
@@ -436,19 +460,33 @@ EXPORT_SYMBOL_GPL(xenbus_free_evtchn); | |||
436 | */ | 460 | */ |
437 | int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) | 461 | int 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 | } | ||
465 | EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc); | ||
466 | |||
467 | static 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 | } |
472 | EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc); | 515 | |
516 | static 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 | */ |
526 | int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr) | 604 | int 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 | } | ||
608 | EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree); | ||
609 | |||
610 | static 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 | } |
569 | EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree); | ||
570 | 652 | ||
653 | static 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 | } |
619 | EXPORT_SYMBOL_GPL(xenbus_read_driver_state); | 735 | EXPORT_SYMBOL_GPL(xenbus_read_driver_state); |
736 | |||
737 | static const struct xenbus_ring_ops ring_ops_pv = { | ||
738 | .map = xenbus_map_ring_valloc_pv, | ||
739 | .unmap = xenbus_unmap_ring_vfree_pv, | ||
740 | }; | ||
741 | |||
742 | static const struct xenbus_ring_ops ring_ops_hvm = { | ||
743 | .map = xenbus_map_ring_valloc_hvm, | ||
744 | .unmap = xenbus_unmap_ring_vfree_hvm, | ||
745 | }; | ||
746 | |||
747 | void __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, | |||
76 | extern int xenbus_read_otherend_details(struct xenbus_device *xendev, | 76 | extern 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 | ||
79 | void xenbus_ring_ops_init(void); | ||
80 | |||
79 | #endif | 81 | #endif |