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
|