diff options
| -rw-r--r-- | arch/x86/include/asm/xen/page.h | 16 | ||||
| -rw-r--r-- | arch/x86/xen/p2m.c | 80 |
2 files changed, 93 insertions, 3 deletions
diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h index 8760cc60a21c..50f0a0f6bd6a 100644 --- a/arch/x86/include/asm/xen/page.h +++ b/arch/x86/include/asm/xen/page.h | |||
| @@ -42,6 +42,11 @@ extern unsigned int machine_to_phys_order; | |||
| 42 | extern unsigned long get_phys_to_machine(unsigned long pfn); | 42 | extern unsigned long get_phys_to_machine(unsigned long pfn); |
| 43 | extern bool set_phys_to_machine(unsigned long pfn, unsigned long mfn); | 43 | extern bool set_phys_to_machine(unsigned long pfn, unsigned long mfn); |
| 44 | 44 | ||
| 45 | extern void m2p_add_override(unsigned long mfn, struct page *page); | ||
| 46 | extern void m2p_remove_override(struct page *page); | ||
| 47 | extern struct page *m2p_find_override(unsigned long mfn); | ||
| 48 | extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn); | ||
| 49 | |||
| 45 | static inline unsigned long pfn_to_mfn(unsigned long pfn) | 50 | static inline unsigned long pfn_to_mfn(unsigned long pfn) |
| 46 | { | 51 | { |
| 47 | unsigned long mfn; | 52 | unsigned long mfn; |
| @@ -72,9 +77,6 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn) | |||
| 72 | if (xen_feature(XENFEAT_auto_translated_physmap)) | 77 | if (xen_feature(XENFEAT_auto_translated_physmap)) |
| 73 | return mfn; | 78 | return mfn; |
| 74 | 79 | ||
| 75 | if (unlikely((mfn >> machine_to_phys_order) != 0)) | ||
| 76 | return ~0; | ||
| 77 | |||
| 78 | pfn = 0; | 80 | pfn = 0; |
| 79 | /* | 81 | /* |
| 80 | * The array access can fail (e.g., device space beyond end of RAM). | 82 | * The array access can fail (e.g., device space beyond end of RAM). |
| @@ -83,6 +85,14 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn) | |||
| 83 | */ | 85 | */ |
| 84 | __get_user(pfn, &machine_to_phys_mapping[mfn]); | 86 | __get_user(pfn, &machine_to_phys_mapping[mfn]); |
| 85 | 87 | ||
| 88 | /* | ||
| 89 | * If this appears to be a foreign mfn (because the pfn | ||
| 90 | * doesn't map back to the mfn), then check the local override | ||
| 91 | * table to see if there's a better pfn to use. | ||
| 92 | */ | ||
| 93 | if (get_phys_to_machine(pfn) != mfn) | ||
| 94 | pfn = m2p_find_override_pfn(mfn, pfn); | ||
| 95 | |||
| 86 | return pfn; | 96 | return pfn; |
| 87 | } | 97 | } |
| 88 | 98 | ||
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 259ec3bb8b6f..8db19d50c467 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c | |||
| @@ -27,6 +27,8 @@ | |||
| 27 | 27 | ||
| 28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
| 29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
| 30 | #include <linux/list.h> | ||
| 31 | #include <linux/hash.h> | ||
| 30 | 32 | ||
| 31 | #include <asm/cache.h> | 33 | #include <asm/cache.h> |
| 32 | #include <asm/setup.h> | 34 | #include <asm/setup.h> |
| @@ -37,6 +39,8 @@ | |||
| 37 | 39 | ||
| 38 | #include "xen-ops.h" | 40 | #include "xen-ops.h" |
| 39 | 41 | ||
| 42 | static void __init m2p_override_init(void); | ||
| 43 | |||
| 40 | unsigned long xen_max_p2m_pfn __read_mostly; | 44 | unsigned long xen_max_p2m_pfn __read_mostly; |
| 41 | 45 | ||
| 42 | #define P2M_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) | 46 | #define P2M_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) |
| @@ -234,6 +238,8 @@ void __init xen_build_dynamic_phys_to_machine(void) | |||
| 234 | 238 | ||
| 235 | p2m_top[topidx][mididx] = &mfn_list[pfn]; | 239 | p2m_top[topidx][mididx] = &mfn_list[pfn]; |
| 236 | } | 240 | } |
| 241 | |||
| 242 | m2p_override_init(); | ||
| 237 | } | 243 | } |
| 238 | 244 | ||
| 239 | unsigned long get_phys_to_machine(unsigned long pfn) | 245 | unsigned long get_phys_to_machine(unsigned long pfn) |
| @@ -374,3 +380,77 @@ bool set_phys_to_machine(unsigned long pfn, unsigned long mfn) | |||
| 374 | 380 | ||
| 375 | return true; | 381 | return true; |
| 376 | } | 382 | } |
| 383 | |||
| 384 | #define M2P_OVERRIDE_HASH_SHIFT 10 | ||
| 385 | #define M2P_OVERRIDE_HASH (1 << M2P_OVERRIDE_HASH_SHIFT) | ||
| 386 | |||
| 387 | static RESERVE_BRK_ARRAY(struct list_head, m2p_overrides, M2P_OVERRIDE_HASH); | ||
| 388 | static DEFINE_SPINLOCK(m2p_override_lock); | ||
| 389 | |||
| 390 | static void __init m2p_override_init(void) | ||
| 391 | { | ||
| 392 | unsigned i; | ||
| 393 | |||
| 394 | m2p_overrides = extend_brk(sizeof(*m2p_overrides) * M2P_OVERRIDE_HASH, | ||
| 395 | sizeof(unsigned long)); | ||
| 396 | |||
| 397 | for (i = 0; i < M2P_OVERRIDE_HASH; i++) | ||
| 398 | INIT_LIST_HEAD(&m2p_overrides[i]); | ||
| 399 | } | ||
| 400 | |||
| 401 | static unsigned long mfn_hash(unsigned long mfn) | ||
| 402 | { | ||
| 403 | return hash_long(mfn, M2P_OVERRIDE_HASH_SHIFT); | ||
| 404 | } | ||
| 405 | |||
| 406 | /* Add an MFN override for a particular page */ | ||
| 407 | void m2p_add_override(unsigned long mfn, struct page *page) | ||
| 408 | { | ||
| 409 | unsigned long flags; | ||
| 410 | page->private = mfn; | ||
| 411 | |||
| 412 | spin_lock_irqsave(&m2p_override_lock, flags); | ||
| 413 | list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]); | ||
| 414 | spin_unlock_irqrestore(&m2p_override_lock, flags); | ||
| 415 | } | ||
| 416 | |||
| 417 | void m2p_remove_override(struct page *page) | ||
| 418 | { | ||
| 419 | unsigned long flags; | ||
| 420 | spin_lock_irqsave(&m2p_override_lock, flags); | ||
| 421 | list_del(&page->lru); | ||
| 422 | spin_unlock_irqrestore(&m2p_override_lock, flags); | ||
| 423 | } | ||
| 424 | |||
| 425 | struct page *m2p_find_override(unsigned long mfn) | ||
| 426 | { | ||
| 427 | unsigned long flags; | ||
| 428 | struct list_head *bucket = &m2p_overrides[mfn_hash(mfn)]; | ||
| 429 | struct page *p, *ret; | ||
| 430 | |||
| 431 | ret = NULL; | ||
| 432 | |||
| 433 | spin_lock_irqsave(&m2p_override_lock, flags); | ||
| 434 | |||
| 435 | list_for_each_entry(p, bucket, lru) { | ||
| 436 | if (p->private == mfn) { | ||
| 437 | ret = p; | ||
| 438 | break; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | spin_unlock_irqrestore(&m2p_override_lock, flags); | ||
| 443 | |||
| 444 | return ret; | ||
| 445 | } | ||
| 446 | |||
| 447 | unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn) | ||
| 448 | { | ||
| 449 | struct page *p = m2p_find_override(mfn); | ||
| 450 | unsigned long ret = pfn; | ||
| 451 | |||
| 452 | if (p) | ||
| 453 | ret = page_to_pfn(p); | ||
| 454 | |||
| 455 | return ret; | ||
| 456 | } | ||
