[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] Re: Ungranting



On Thu, 2005-11-03 at 08:42 +0000, Keir Fraser wrote:
> On 3 Nov 2005, at 03:03, Rusty Russell wrote:
> 
> >     The simplest implementation I can think of, is to provide a mechanism
> > in Xen to query the reference count of a page (1 meaning no other
> > mappings).  Linux would simply have a thread which, if any pages were 
> > in
> > the "pending" queue, would query them all once a second.  Since speed
> > isn't important, this would be fine IMHO.
> Linux can already tell whether a grant reference is 'in use' and then 
> atomically free the reference while it is not in use, using cmpxchg.
> 
> I agree polling is the right answer -- explicit notification is 
> probably overkill and might slow down fast paths in Xen.

OK, here's a (lightly) tested implementation.  I use a separate bitmap,
although we could probably use a reserved bit in the grant table flags.
OTOH, if we wanted an arbitrary callback (rather than hardcoding
free_page) we'd need an array of pointers.

For the list: we have a problem in that if a driver wants to unload, it
cannot simply ungrant a page then free it, as it may still be in use by
the other side.  The current code prints out a warning in this case and
just returns without ungranting the page.  This patch provides a
mechanism drivers can use to get and grant a page, and to release that
page.  If it can't be release immediately, it's queued internally.

Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>

diff -r 270469d40f02 linux-2.6-xen-sparse/arch/xen/kernel/gnttab.c
--- a/linux-2.6-xen-sparse/arch/xen/kernel/gnttab.c     Sun Nov  6 18:50:33 2005
+++ b/linux-2.6-xen-sparse/arch/xen/kernel/gnttab.c     Mon Nov  7 14:22:13 2005
@@ -10,6 +10,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/kthread.h>
 #include <asm/pgtable.h>
 #include <asm-xen/xen-public/xen.h>
 #include <asm/fixmap.h>
@@ -56,6 +57,8 @@
 static int gnttab_free_count;
 static grant_ref_t gnttab_free_head;
 static spinlock_t gnttab_list_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_BITMAP(gnttab_freeing, NR_GRANT_ENTRIES);
+static struct task_struct *gnttab_freer;
 
 static grant_entry_t *shared;
 
@@ -454,6 +457,71 @@
        return 0;
 }
 
+void *alloc_granted_page(grant_ref_t *ref, domid_t dom, int readonly)
+{
+       int err;
+       void *page;
+
+       page = (void *)get_zeroed_page(GFP_KERNEL);
+       if (!page)
+               return ERR_PTR(-ENOMEM);
+
+       err = gnttab_grant_foreign_access(dom, virt_to_mfn(page), readonly);
+       if (err == -ENOSPC) {
+               free_page((unsigned long)page);
+               return ERR_PTR(err);
+       }
+       *ref = err;
+       return page;
+}
+
+static int try_free_grant(grant_ref_t ref)
+{
+       u16 flags = shared[ref].flags;
+
+       if (!(flags & (GTF_reading|GTF_writing)) &&
+           synch_cmpxchg(&shared[ref].flags, flags, 0)) {
+               free_page((unsigned long)mfn_to_virt(shared[ref].frame));
+               /* Clear this if set, before it can be reused. */
+               clear_bit(ref, gnttab_freeing);
+               put_free_entry(ref);
+               printk("Grant %i freed\n", ref);
+               return 1;
+       }
+       return 0;
+}
+
+/* Simplest possible code to poll while bits are pending... */
+static int gnttab_try_free(void *unused)
+{
+       for (;;) {
+               int bit, pending = 0;
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               for (bit = find_first_bit(gnttab_freeing, NR_GRANT_ENTRIES);
+                    bit < NR_GRANT_ENTRIES;
+                    bit=find_next_bit(gnttab_freeing,bit+1,NR_GRANT_ENTRIES))
+                       if (!try_free_grant(bit))
+                               pending = 1;
+
+               if (pending)
+                       schedule_timeout(HZ);
+               else
+                       schedule();
+       }
+}
+
+void free_granted_page(void *page, grant_ref_t ref)
+{
+       if (try_free_grant(ref))
+               return;
+
+       /* Deferred freeing. */
+       set_bit(ref, gnttab_freeing);
+       wake_up_process(gnttab_freer);
+}
+
 static int __init
 gnttab_init(void)
 {
@@ -488,6 +556,8 @@
        grant_pde->read_proc  = &grant_read;
        grant_pde->write_proc = &grant_write;
 #endif
+
+       gnttab_freer = kthread_run(gnttab_try_free, NULL, "xengnttabd");
 
        printk("Grant table initialized\n");
        return 0;
diff -r 270469d40f02 linux-2.6-xen-sparse/include/asm-xen/gnttab.h
--- a/linux-2.6-xen-sparse/include/asm-xen/gnttab.h     Sun Nov  6 18:50:33 2005
+++ b/linux-2.6-xen-sparse/include/asm-xen/gnttab.h     Mon Nov  7 14:22:13 2005
@@ -57,6 +57,10 @@
 
 int gnttab_query_foreign_access(grant_ref_t ref);
 
+/* Convenient and safe helpers for granting/ungranting page. */
+void *alloc_granted_page(grant_ref_t *ref, domid_t dom, int readonly);
+void free_granted_page(void *page, grant_ref_t ref);
+
 /*
  * operations on reserved batches of grant references
  */

-- 
A bad analogy is like a leaky screwdriver -- Richard Braakman


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.