This isn't terribly useful at present, but will make it much easier to
forward mapped packets between domains when there are multiple drivers
loaded which can produce such packets (e.g. netback1 and netback2).
Signed-off-by: Steven Smith <steven.smith@xxxxxxxxxx>
---
drivers/xen/Makefile | 2 +-
drivers/xen/grant-table.c | 1 +
drivers/xen/live_maps.c | 61 +++++++++++++++++
include/xen/live_maps.h | 165 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 228 insertions(+), 1 deletions(-)
create mode 100644 drivers/xen/live_maps.c
create mode 100644 include/xen/live_maps.h
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 8e7ce48..39530bd 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,5 +1,5 @@
obj-y += grant-table.o features.o events.o manage.o biomerge.o
-obj-y += xenbus/
+obj-y += xenbus/ live_maps.o
nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_features.o := $(nostackp)
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 58996c9..cd82d22 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -556,6 +556,7 @@ int gnttab_copy_grant_page(grant_ref_t ref, struct page
**pagep)
new_page->mapping = page->mapping;
new_page->index = page->index;
+ new_page->private = page->private;
set_bit(PG_foreign, &new_page->flags);
*pagep = new_page;
diff --git a/drivers/xen/live_maps.c b/drivers/xen/live_maps.c
new file mode 100644
index 0000000..010682a
--- /dev/null
+++ b/drivers/xen/live_maps.c
@@ -0,0 +1,61 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <xen/grant_table.h>
+#include <xen/live_maps.h>
+
+/* This lock protects allocation and release of trackers, but is not
+ held when we're actually looking stuff up. The caller is
+ responsible for making sure that suitable locks are held around
+ data path operations. */
+static DEFINE_SPINLOCK(tracker_lock);
+
+struct page_foreign_tracker *foreign_trackers[LIVE_MAP_NR_TRACKERS];
+EXPORT_SYMBOL(foreign_trackers);
+
+/* Allocate a foreign page tracker. @size is the maximum index in the
+ tracker. Returns NULL on error. */
+struct page_foreign_tracker *alloc_page_foreign_tracker(unsigned size)
+{
+ struct page_foreign_tracker *work;
+ unsigned x;
+
+ BUG_ON(size & ~LIVE_MAP_TRACKER_IDX_MASK);
+
+ work = kzalloc(sizeof(*work) +
+ size * sizeof(struct page_foreign_tracked),
+ GFP_KERNEL);
+ if (!work)
+ return work;
+ work->size = size;
+
+ spin_lock(&tracker_lock);
+ for (x = 0; x < LIVE_MAP_NR_TRACKERS; x++) {
+ if (foreign_trackers[x] == NULL) {
+ work->id = x;
+ foreign_trackers[x] = work;
+ break;
+ }
+ }
+ spin_unlock(&tracker_lock);
+ if (x == LIVE_MAP_NR_TRACKERS) {
+ printk(KERN_WARNING "Out of foreign page trackers!\n");
+ kfree(work);
+ return NULL;
+ }
+ return work;
+}
+
+/* Release a tracker allocated with alloc_page_foreign_tracker. There
+ should be no tracked pages when this is called. */
+void free_page_foreign_tracker(struct page_foreign_tracker *pft)
+{
+ spin_lock(&tracker_lock);
+ BUG_ON(foreign_trackers[pft->id] != pft);
+ foreign_trackers[pft->id] = NULL;
+ spin_unlock(&tracker_lock);
+ kfree(pft);
+}
+
+EXPORT_SYMBOL(alloc_page_foreign_tracker);
+EXPORT_SYMBOL(free_page_foreign_tracker);
diff --git a/include/xen/live_maps.h b/include/xen/live_maps.h
new file mode 100644
index 0000000..6223ca4
--- /dev/null
+++ b/include/xen/live_maps.h
@@ -0,0 +1,165 @@
+#ifndef XEN_LIVE_MAPS_H__
+#define XEN_LIVE_MAPS_H__
+
+/* A mechanism for tracking where pages have been grant mapped from.
+ Anything which can map pages through a grant reference is supposed
+ to allocate a page_tracker and then, whenever they map a grant:
+
+ a) Flag the page as foreign with SetPageForeign(), and
+ b) Register the struct page with a tracker through start_tracking_page().
+
+ If you later need to grant access to the page (either with a normal
+ grant or implicitly in a copy grant operation), you should use
+ lookup_tracker_page() to find out what domain and grant reference
+ it was mapped from.
+
+ Obviously, if a backend knows that the page will never need to be
+ re-granted once it's been mapped, it can avoid doing all this
+ stuff.
+
+ The number of trackers is quite limited, so they shouldn't be
+ allocated unnecessarily. One per backend class is reasonable
+ (i.e. netback, blkback, etc.), but one per backend device probably
+ isn't.
+*/
+
+#include <linux/mm.h>
+#include <xen/grant_table.h>
+
+#ifdef CONFIG_XEN
+
+/* We use page->private to store some index information so that we can
+ find the tracking information later. The top few bits are used to
+ identify the tracker, and the rest are used as an index into that
+ tracker. */
+
+/* How many bits to use for tracker IDs. */
+#define LIVE_MAP_TRACKER_BITS 2
+
+/* How many bits to use for tracker indexes. */
+#define LIVE_MAP_TRACKER_IDX_BITS (32 - LIVE_MAP_TRACKER_BITS)
+
+/* Maximum number of trackers */
+#define LIVE_MAP_NR_TRACKERS (1 << LIVE_MAP_TRACKER_BITS)
+
+/* Bitmask of index inside tracker */
+#define LIVE_MAP_TRACKER_IDX_MASK (~0u >> LIVE_MAP_TRACKER_BITS)
+
+/* Turn off some moderately expensive debug checks. */
+#undef LIVE_MAPS_DEBUG
+
+struct page_foreign_tracked {
+ domid_t dom;
+ grant_ref_t gref;
+ void *ctxt;
+#ifdef LIVE_MAPS_DEBUG
+ unsigned in_use;
+#endif
+};
+
+struct page_foreign_tracker {
+ unsigned size;
+ unsigned id;
+ struct page_foreign_tracked contents[];
+};
+
+extern struct page_foreign_tracker *foreign_trackers[LIVE_MAP_NR_TRACKERS];
+
+/* Allocate a foreign page tracker. @size is the maximum index in the
+ tracker. Returns NULL on error. */
+struct page_foreign_tracker *alloc_page_foreign_tracker(unsigned size);
+
+/* Release a tracker allocated with alloc_page_foreign_tracker. There
+ should be no tracked pages when this is called. */
+void free_page_foreign_tracker(struct page_foreign_tracker *pft);
+
+static inline struct page_foreign_tracker *tracker_for_page(struct page *p)
+{
+ unsigned idx = page_private(p);
+ return foreign_trackers[idx >> LIVE_MAP_TRACKER_IDX_BITS];
+}
+
+static inline void *get_page_tracker_ctxt(struct page *p)
+{
+ struct page_foreign_tracker *pft = tracker_for_page(p);
+ unsigned idx = page_private(p);
+ return pft->contents[idx & LIVE_MAP_TRACKER_IDX_MASK].ctxt;
+}
+
+/* Start tracking a page. @idx is an index in the tracker which is
+ not currently in use, and must be less than the size of the
+ tracker. The page must be marked as foreign before this is called.
+ The caller is expected to make sure that the page is not a
+ simulataneous target of lookup_tracker_page(). The page should be
+ passed to stop_tracking_page() when the grant is unmapped. */
+static inline void start_tracking_page(struct page_foreign_tracker *pft,
+ struct page *p,
+ domid_t dom,
+ grant_ref_t gref,
+ unsigned idx,
+ void *ctxt)
+{
+ BUG_ON(!PageForeign(p));
+#ifdef LIVE_MAPS_DEBUG
+ BUG_ON(idx > pft->size);
+ BUG_ON(pft->contents[idx].in_use);
+ pft->contents[idx].in_use = 1;
+#endif
+ pft->contents[idx].dom = dom;
+ pft->contents[idx].gref = gref;
+ pft->contents[idx].ctxt = ctxt;
+ set_page_private(p, idx | (pft->id << LIVE_MAP_TRACKER_IDX_BITS));
+}
+
+static inline void stop_tracking_page(struct page *p)
+{
+#ifdef LIVE_MAPS_DEBUG
+ struct page_foreign_tracker *pft;
+ unsigned idx = page_private(p);
+ BUG_ON(!PageForeign(p));
+ pft = tracker_for_page(p);
+ BUG_ON((idx & LIVE_MAP_TRACKER_IDX_MASK) >= pft->size);
+ BUG_ON(!pft->contents[idx & LIVE_MAP_TRACKER_IDX_MASK].in_use);
+ pft->contents[idx & LIVE_MAP_TRACKER_IDX_MASK].in_use = 0;
+ set_page_private(p, 0);
+#endif
+}
+
+/* Lookup a page which is tracked in some tracker.
+ start_tracking_page() must have been called previously. *@dom and
+ *@gref will be set to the values which were specified when
+ start_tracking_page() was called. */
+static inline void lookup_tracker_page(struct page *p, domid_t *dom,
+ grant_ref_t *gref)
+{
+ struct page_foreign_tracker *pft;
+ unsigned idx = page_private(p);
+ BUG_ON(!PageForeign(p));
+ pft = tracker_for_page(p);
+#ifdef LIVE_MAPS_DEBUG
+ BUG_ON(!pft);
+ BUG_ON((idx & LIVE_MAP_TRACKER_IDX_MASK) >= pft->size);
+ BUG_ON(!pft->contents[idx & LIVE_MAP_TRACKER_IDX_MASK].in_use);
+#endif
+ *dom = pft->contents[idx & LIVE_MAP_TRACKER_IDX_MASK].dom;
+ *gref = pft->contents[idx & LIVE_MAP_TRACKER_IDX_MASK].gref;
+}
+
+static inline int page_is_tracked(struct page *p)
+{
+ return PageForeign(p) && p->mapping;
+}
+
+#else /* !CONFIG_XEN */
+static inline int page_is_tracked(struct page *p)
+{
+ return 0;
+}
+static void lookup_tracker_page(struct page *p, domid_t *domid,
+ grant_ref_t *gref)
+{
+ BUG();
+}
+#endif
+
+#endif /* !XEN_LIVE_MAPS_H__ */
--
1.6.3.1
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|