This commit was manufactured by cvs2svn to create branch
[linux-2.6.git] / arch / xen / kernel / gnttab.c
1 /******************************************************************************
2  * gnttab.c
3  * 
4  * Two sets of functionality:
5  * 1. Granting foreign access to our memory reservation.
6  * 2. Accessing others' memory reservations via grant references.
7  * (i.e., mechanisms for both sender and recipient of grant references)
8  * 
9  * Copyright (c) 2005, Christopher Clark
10  * Copyright (c) 2004, K A Fraser
11  */
12
13 #include <linux/config.h>
14 #include <linux/module.h>
15 #include <linux/sched.h>
16 #include <asm/pgtable.h>
17 #include <asm/fixmap.h>
18 #include <asm/uaccess.h>
19 #include <asm-xen/xen_proc.h>
20 #include <asm-xen/linux-public/privcmd.h>
21 #include <asm-xen/gnttab.h>
22
23 #ifndef set_fixmap_ma
24 #define set_fixmap_ma set_fixmap
25 #endif
26
27 #if 1
28 #define ASSERT(_p) \
29     if ( !(_p) ) { printk(KERN_ALERT"Assertion '%s': line %d, file %s\n", \
30     #_p , __LINE__, __FILE__); *(int*)0=0; }
31 #else
32 #define ASSERT(_p) ((void)0)
33 #endif
34
35 #define WPRINTK(fmt, args...) \
36     printk(KERN_WARNING "xen_grant: " fmt, ##args)
37
38
39 EXPORT_SYMBOL(gnttab_grant_foreign_access);
40 EXPORT_SYMBOL(gnttab_end_foreign_access);
41 EXPORT_SYMBOL(gnttab_query_foreign_access);
42 EXPORT_SYMBOL(gnttab_grant_foreign_transfer);
43 EXPORT_SYMBOL(gnttab_end_foreign_transfer);
44 EXPORT_SYMBOL(gnttab_alloc_grant_references);
45 EXPORT_SYMBOL(gnttab_free_grant_references);
46 EXPORT_SYMBOL(gnttab_claim_grant_reference);
47 EXPORT_SYMBOL(gnttab_release_grant_reference);
48 EXPORT_SYMBOL(gnttab_grant_foreign_access_ref);
49 EXPORT_SYMBOL(gnttab_grant_foreign_transfer_ref);
50
51 static grant_ref_t gnttab_free_list[NR_GRANT_ENTRIES];
52 static grant_ref_t gnttab_free_head;
53
54 static grant_entry_t *shared;
55
56 /*
57  * Lock-free grant-entry allocator
58  */
59
60 static inline int
61 get_free_entry(
62     void)
63 {
64     grant_ref_t fh, nfh = gnttab_free_head;
65     do { if ( unlikely((fh = nfh) == NR_GRANT_ENTRIES) ) return -1; }
66     while ( unlikely((nfh = cmpxchg(&gnttab_free_head, fh,
67                                     gnttab_free_list[fh])) != fh) );
68     return fh;
69 }
70
71 static inline void
72 put_free_entry(
73     grant_ref_t ref)
74 {
75     grant_ref_t fh, nfh = gnttab_free_head;
76     do { gnttab_free_list[ref] = fh = nfh; wmb(); }
77     while ( unlikely((nfh = cmpxchg(&gnttab_free_head, fh, ref)) != fh) );
78 }
79
80 /*
81  * Public grant-issuing interface functions
82  */
83
84 int
85 gnttab_grant_foreign_access(
86     domid_t domid, unsigned long frame, int readonly)
87 {
88     int ref;
89     
90     if ( unlikely((ref = get_free_entry()) == -1) )
91         return -ENOSPC;
92
93     shared[ref].frame = frame;
94     shared[ref].domid = domid;
95     wmb();
96     shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
97
98     return ref;
99 }
100
101 void
102 gnttab_grant_foreign_access_ref(
103     grant_ref_t ref, domid_t domid, unsigned long frame, int readonly)
104 {
105     shared[ref].frame = frame;
106     shared[ref].domid = domid;
107     wmb();
108     shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
109 }
110
111
112 int
113 gnttab_query_foreign_access( grant_ref_t ref )
114 {
115     u16 nflags;
116
117     nflags = shared[ref].flags;
118
119     return ( nflags & (GTF_reading|GTF_writing) );
120 }
121
122 void
123 gnttab_end_foreign_access( grant_ref_t ref, int readonly )
124 {
125     u16 flags, nflags;
126
127     nflags = shared[ref].flags;
128     do {
129         if ( (flags = nflags) & (GTF_reading|GTF_writing) )
130             printk(KERN_ALERT "WARNING: g.e. still in use!\n");
131     }
132     while ( (nflags = cmpxchg(&shared[ref].flags, flags, 0)) != flags );
133
134     put_free_entry(ref);
135 }
136
137 int
138 gnttab_grant_foreign_transfer(
139     domid_t domid, unsigned long pfn )
140 {
141     int ref;
142
143     if ( unlikely((ref = get_free_entry()) == -1) )
144         return -ENOSPC;
145
146     shared[ref].frame = pfn;
147     shared[ref].domid = domid;
148     wmb();
149     shared[ref].flags = GTF_accept_transfer;
150
151     return ref;
152 }
153
154 void
155 gnttab_grant_foreign_transfer_ref(
156     grant_ref_t ref, domid_t domid, unsigned long pfn )
157 {
158     shared[ref].frame = pfn;
159     shared[ref].domid = domid;
160     wmb();
161     shared[ref].flags = GTF_accept_transfer;
162 }
163
164 unsigned long
165 gnttab_end_foreign_transfer(
166     grant_ref_t ref)
167 {
168     unsigned long frame = 0;
169     u16           flags;
170
171     flags = shared[ref].flags;
172     ASSERT(flags == (GTF_accept_transfer | GTF_transfer_committed));
173
174     /*
175      * If a transfer is committed then wait for the frame address to appear.
176      * Otherwise invalidate the grant entry against future use.
177      */
178     if ( likely(flags != GTF_accept_transfer) ||
179          (cmpxchg(&shared[ref].flags, flags, 0) != GTF_accept_transfer) )
180         while ( unlikely((frame = shared[ref].frame) == 0) )
181             cpu_relax();
182
183     put_free_entry(ref);
184
185     return frame;
186 }
187
188 void
189 gnttab_free_grant_references( u16 count, grant_ref_t head )
190 {
191     /* TODO: O(N)...? */
192     grant_ref_t to_die = 0, next = head;
193     int i;
194
195     for ( i = 0; i < count; i++ )
196         to_die = next;
197         next = gnttab_free_list[next];
198         put_free_entry( to_die );
199 }
200
201 int
202 gnttab_alloc_grant_references( u16 count,
203                                grant_ref_t *head,
204                                grant_ref_t *terminal )
205 {
206     int i;
207     grant_ref_t h = gnttab_free_head;
208
209     for ( i = 0; i < count; i++ )
210         if ( unlikely(get_free_entry() == -1) )
211             goto not_enough_refs;
212
213     *head = h;
214     *terminal = gnttab_free_head;
215
216     return 0;
217
218 not_enough_refs:
219     gnttab_free_head = h;
220     return -ENOSPC;
221 }
222
223 int
224 gnttab_claim_grant_reference( grant_ref_t *private_head,
225                               grant_ref_t  terminal )
226 {
227     grant_ref_t g;
228     if ( unlikely((g = *private_head) == terminal) )
229         return -ENOSPC;
230     *private_head = gnttab_free_list[g];
231     return g;
232 }
233
234 void
235 gnttab_release_grant_reference( grant_ref_t *private_head,
236                                 grant_ref_t  release )
237 {
238     gnttab_free_list[release] = *private_head;
239     *private_head = release;
240 }
241
242 /*
243  * ProcFS operations
244  */
245
246 #ifdef CONFIG_PROC_FS
247
248 static struct proc_dir_entry *grant_pde;
249
250 static int grant_ioctl(struct inode *inode, struct file *file,
251                        unsigned int cmd, unsigned long data)
252 {
253     int                     ret;
254     privcmd_hypercall_t     hypercall;
255
256     /* XXX Need safety checks here if using for anything other
257      *     than debugging */
258     return -ENOSYS;
259
260     if ( cmd != IOCTL_PRIVCMD_HYPERCALL )
261         return -ENOSYS;
262
263     if ( copy_from_user(&hypercall, (void *)data, sizeof(hypercall)) )
264         return -EFAULT;
265
266     if ( hypercall.op != __HYPERVISOR_grant_table_op )
267         return -ENOSYS;
268
269     /* hypercall-invoking asm taken from privcmd.c */
270     __asm__ __volatile__ (
271         "pushl %%ebx; pushl %%ecx; pushl %%edx; pushl %%esi; pushl %%edi; "
272         "movl  4(%%eax),%%ebx ;"
273         "movl  8(%%eax),%%ecx ;"
274         "movl 12(%%eax),%%edx ;"
275         "movl 16(%%eax),%%esi ;"
276         "movl 20(%%eax),%%edi ;"
277         "movl   (%%eax),%%eax ;"
278         TRAP_INSTR "; "
279         "popl %%edi; popl %%esi; popl %%edx; popl %%ecx; popl %%ebx"
280         : "=a" (ret) : "0" (&hypercall) : "memory" );
281
282     return ret;
283 }
284
285 static struct file_operations grant_file_ops = {
286     ioctl:  grant_ioctl,
287 };
288
289 static int grant_read(char *page, char **start, off_t off,
290                       int count, int *eof, void *data)
291 {
292     int             len;
293     unsigned int    i;
294     grant_entry_t  *gt;
295
296     gt = (grant_entry_t *)shared;
297     len = 0;
298
299     for ( i = 0; i < NR_GRANT_ENTRIES; i++ )
300         /* TODO: safety catch here until this can handle >PAGE_SIZE output */
301         if (len > (PAGE_SIZE - 200))
302         {
303             len += sprintf( page + len, "Truncated.\n");
304             break;
305         }
306
307         if ( gt[i].flags )
308             len += sprintf( page + len,
309                     "Grant: ref (0x%x) flags (0x%hx) dom (0x%hx) frame (0x%x)\n", 
310                     i,
311                     gt[i].flags,
312                     gt[i].domid,
313                     gt[i].frame );
314
315     *eof = 1;
316     return len;
317 }
318
319 static int grant_write(struct file *file, const char __user *buffer,
320                        unsigned long count, void *data)
321 {
322     /* TODO: implement this */
323     return -ENOSYS;
324 }
325
326 #endif /* CONFIG_PROC_FS */
327
328 int gnttab_resume(void)
329 {
330     gnttab_setup_table_t setup;
331     unsigned long        frames[NR_GRANT_FRAMES];
332     int                  i;
333
334     setup.dom        = DOMID_SELF;
335     setup.nr_frames  = NR_GRANT_FRAMES;
336     setup.frame_list = frames;
337
338     BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0);
339     BUG_ON(setup.status != 0);
340
341     for ( i = 0; i < NR_GRANT_FRAMES; i++ )
342         set_fixmap_ma(FIX_GNTTAB_END - i, frames[i] << PAGE_SHIFT);
343
344     return 0;
345 }
346
347 int gnttab_suspend(void)
348 {
349     int i;
350
351     for ( i = 0; i < NR_GRANT_FRAMES; i++ )
352         clear_fixmap(FIX_GNTTAB_END - i);
353
354     return 0;
355 }
356
357 static int __init gnttab_init(void)
358 {
359     int i;
360
361     BUG_ON(gnttab_resume());
362
363     shared = (grant_entry_t *)fix_to_virt(FIX_GNTTAB_END);
364
365     for ( i = 0; i < NR_GRANT_ENTRIES; i++ )
366         gnttab_free_list[i] = i + 1;
367     
368 #ifdef CONFIG_PROC_FS
369     /*
370      *  /proc/xen/grant : used by libxc to access grant tables
371      */
372     if ( (grant_pde = create_xen_proc_entry("grant", 0600)) == NULL )
373     {
374         WPRINTK("Unable to create grant xen proc entry\n");
375         return -1;
376     }
377
378     grant_file_ops.read   = grant_pde->proc_fops->read;
379     grant_file_ops.write  = grant_pde->proc_fops->write;
380
381     grant_pde->proc_fops  = &grant_file_ops;
382
383     grant_pde->read_proc  = &grant_read;
384     grant_pde->write_proc = &grant_write;
385 #endif
386
387     printk("Grant table initialized\n");
388     return 0;
389 }
390
391 __initcall(gnttab_init);