| /* |
| * linux/mm/vcache.c |
| * |
| * virtual => physical page mapping cache. Users of this mechanism |
| * register callbacks for a given (virt,mm,phys) page mapping, and |
| * the kernel guarantees to call back when this mapping is invalidated. |
| * (ie. upon COW or unmap.) |
| * |
| * Started by Ingo Molnar, Copyright (C) 2002 |
| */ |
| |
| #include <linux/mm.h> |
| #include <linux/init.h> |
| #include <linux/hash.h> |
| #include <linux/vcache.h> |
| |
| #define VCACHE_HASHBITS 8 |
| #define VCACHE_HASHSIZE (1 << VCACHE_HASHBITS) |
| |
| spinlock_t vcache_lock = SPIN_LOCK_UNLOCKED; |
| |
| static struct list_head hash[VCACHE_HASHSIZE]; |
| |
| static struct list_head *hash_vcache(unsigned long address, |
| struct mm_struct *mm) |
| { |
| return &hash[hash_long(address + (unsigned long)mm, VCACHE_HASHBITS)]; |
| } |
| |
| void __attach_vcache(vcache_t *vcache, |
| unsigned long address, |
| struct mm_struct *mm, |
| void (*callback)(struct vcache_s *data, struct page *new)) |
| { |
| struct list_head *hash_head; |
| |
| address &= PAGE_MASK; |
| vcache->address = address; |
| vcache->mm = mm; |
| vcache->callback = callback; |
| |
| hash_head = hash_vcache(address, mm); |
| |
| list_add_tail(&vcache->hash_entry, hash_head); |
| } |
| |
| void __detach_vcache(vcache_t *vcache) |
| { |
| list_del_init(&vcache->hash_entry); |
| } |
| |
| void invalidate_vcache(unsigned long address, struct mm_struct *mm, |
| struct page *new_page) |
| { |
| struct list_head *l, *hash_head; |
| vcache_t *vcache; |
| |
| address &= PAGE_MASK; |
| |
| hash_head = hash_vcache(address, mm); |
| /* |
| * This is safe, because this path is called with the pagetable |
| * lock held. So while other mm's might add new entries in |
| * parallel, *this* mm is locked out, so if the list is empty |
| * now then we do not have to take the vcache lock to see it's |
| * really empty. |
| */ |
| if (likely(list_empty(hash_head))) |
| return; |
| |
| spin_lock(&vcache_lock); |
| list_for_each(l, hash_head) { |
| vcache = list_entry(l, vcache_t, hash_entry); |
| if (vcache->address != address || vcache->mm != mm) |
| continue; |
| vcache->callback(vcache, new_page); |
| } |
| spin_unlock(&vcache_lock); |
| } |
| |
| static int __init vcache_init(void) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < VCACHE_HASHSIZE; i++) |
| INIT_LIST_HEAD(hash + i); |
| return 0; |
| } |
| __initcall(vcache_init); |
| |