]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/xen/gnttab.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / sys / xen / 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 <sys/cdefs.h>
14 __FBSDID("$FreeBSD$");
15
16 #include "opt_global.h"
17 #include "opt_pmap.h"
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/bus.h>
22 #include <sys/conf.h>
23 #include <sys/module.h>
24 #include <sys/kernel.h>
25 #include <sys/lock.h>
26 #include <sys/malloc.h>
27 #include <sys/mman.h>
28
29 #include <machine/xen/hypervisor.h>
30 #include <machine/xen/synch_bitops.h>
31 #include <xen/gnttab.h>
32
33 #define cmpxchg(a, b, c) atomic_cmpset_int((volatile u_int *)(a),(b),(c))
34
35 #if 1
36 #define ASSERT(_p) \
37     if ( !(_p) ) { printk("Assertion '%s': line %d, file %s\n", \
38     #_p , __LINE__, __FILE__); *(int*)0=0; }
39 #else
40 #define ASSERT(_p) ((void)0)
41 #endif
42
43 #define WPRINTK(fmt, args...) \
44     printk("xen_grant: " fmt, ##args)
45
46 /* External tools reserve first few grant table entries. */
47 #define NR_RESERVED_ENTRIES 8
48 #define GNTTAB_LIST_END 0xffffffff
49 #define GREFS_PER_GRANT_FRAME (PAGE_SIZE / sizeof(grant_entry_t))
50
51 static grant_ref_t **gnttab_list;
52 static unsigned int nr_grant_frames;
53 static unsigned int boot_max_nr_grant_frames;
54 static int gnttab_free_count;
55 static grant_ref_t gnttab_free_head;
56 static struct mtx gnttab_list_lock;
57
58 static grant_entry_t *shared;
59
60 static struct gnttab_free_callback *gnttab_free_callback_list = NULL;
61
62 static int gnttab_expand(unsigned int req_entries);
63
64 #define RPP (PAGE_SIZE / sizeof(grant_ref_t))
65 #define gnttab_entry(entry) (gnttab_list[(entry) / RPP][(entry) % RPP])
66
67 static int
68 get_free_entries(int count)
69 {
70         int ref, rc;
71         grant_ref_t head;
72         
73         mtx_lock(&gnttab_list_lock);
74         if ((gnttab_free_count < count) &&
75             ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
76                 mtx_unlock(&gnttab_list_lock);
77                 return (rc);
78         }
79         ref = head = gnttab_free_head;
80         gnttab_free_count -= count;
81         while (count-- > 1)
82                 head = gnttab_entry(head);
83         gnttab_free_head = gnttab_entry(head);
84         gnttab_entry(head) = GNTTAB_LIST_END;
85         mtx_unlock(&gnttab_list_lock);  
86         return (ref);
87 }
88
89 #define get_free_entry() get_free_entries(1)
90
91 static void
92 do_free_callbacks(void)
93 {
94         struct gnttab_free_callback *callback, *next;
95
96         callback = gnttab_free_callback_list;
97         gnttab_free_callback_list = NULL;
98
99         while (callback != NULL) {
100                 next = callback->next;
101                 if (gnttab_free_count >= callback->count) {
102                         callback->next = NULL;
103                         callback->fn(callback->arg);
104                 } else {
105                         callback->next = gnttab_free_callback_list;
106                         gnttab_free_callback_list = callback;
107                 }
108                 callback = next;
109         }
110 }
111
112 static inline void
113 check_free_callbacks(void)
114 {
115         if (unlikely(gnttab_free_callback_list != NULL))
116                 do_free_callbacks();
117 }
118
119 static void
120 put_free_entry(grant_ref_t ref)
121 {
122
123         mtx_lock(&gnttab_list_lock);
124         gnttab_entry(ref) = gnttab_free_head;
125         gnttab_free_head = ref;
126         gnttab_free_count++;
127         check_free_callbacks();
128         mtx_unlock(&gnttab_list_lock);  
129 }
130
131 /*
132  * Public grant-issuing interface functions
133  */
134
135 int
136 gnttab_grant_foreign_access(domid_t domid, unsigned long frame, int readonly)
137 {
138         int ref;
139
140         if (unlikely((ref = get_free_entry()) == -1))
141                 return -ENOSPC;
142
143         shared[ref].frame = frame;
144         shared[ref].domid = domid;
145         wmb();
146         shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
147
148         return ref;
149 }
150
151 void
152 gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
153                                 unsigned long frame, int readonly)
154 {
155         shared[ref].frame = frame;
156         shared[ref].domid = domid;
157         wmb();
158         shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
159 }
160
161 int
162 gnttab_query_foreign_access(grant_ref_t ref)
163 {
164         uint16_t nflags;
165         
166         nflags = shared[ref].flags;
167         
168         return (nflags & (GTF_reading|GTF_writing));
169 }
170
171 int
172 gnttab_end_foreign_access_ref(grant_ref_t ref)
173 {
174         uint16_t flags, nflags;
175
176         nflags = shared[ref].flags;
177         do {
178                 if ( (flags = nflags) & (GTF_reading|GTF_writing) ) {
179                         printf("WARNING: g.e. still in use!\n");
180                         return (0);
181                 }
182         } while ((nflags = synch_cmpxchg(&shared[ref].flags, flags, 0)) !=
183                flags);
184
185         return (1);
186 }
187
188 void
189 gnttab_end_foreign_access(grant_ref_t ref, void *page)
190 {
191         if (gnttab_end_foreign_access_ref(ref)) {
192                 put_free_entry(ref);
193                 if (page != NULL) {
194                         free(page, M_DEVBUF);
195                 }
196         }
197         else {
198                 /* XXX This needs to be fixed so that the ref and page are
199                    placed on a list to be freed up later. */
200                 printf("WARNING: leaking g.e. and page still in use!\n");
201         }
202 }
203
204 int
205 gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
206 {
207         int ref;
208         
209         if (unlikely((ref = get_free_entry()) == -1))
210                 return -ENOSPC;
211
212         gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
213         
214         return (ref);
215 }
216
217 void
218 gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
219         unsigned long pfn)
220 {
221         shared[ref].frame = pfn;
222         shared[ref].domid = domid;
223         wmb();
224         shared[ref].flags = GTF_accept_transfer;
225 }
226
227 unsigned long
228 gnttab_end_foreign_transfer_ref(grant_ref_t ref)
229 {
230         unsigned long frame;
231         uint16_t      flags;
232
233         /*
234          * If a transfer is not even yet started, try to reclaim the grant
235          * reference and return failure (== 0).
236          */
237         while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
238                 if ( synch_cmpxchg(&shared[ref].flags, flags, 0) == flags )
239                         return (0);
240                 cpu_relax();
241         }
242
243         /* If a transfer is in progress then wait until it is completed. */
244         while (!(flags & GTF_transfer_completed)) {
245                 flags = shared[ref].flags;
246                 cpu_relax();
247         }
248
249         /* Read the frame number /after/ reading completion status. */
250         rmb();
251         frame = shared[ref].frame;
252         PANIC_IF(frame == 0);
253
254         return (frame);
255 }
256
257 unsigned long
258 gnttab_end_foreign_transfer(grant_ref_t ref)
259 {
260         unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
261
262         put_free_entry(ref);
263         return (frame);
264 }
265
266 void
267 gnttab_free_grant_reference(grant_ref_t ref)
268 {
269
270         put_free_entry(ref);
271 }
272
273 void
274 gnttab_free_grant_references(grant_ref_t head)
275 {
276         grant_ref_t ref;
277         int count = 1;
278         
279         if (head == GNTTAB_LIST_END)
280                 return;
281         
282         mtx_lock(&gnttab_list_lock);
283         ref = head;
284         while (gnttab_entry(ref) != GNTTAB_LIST_END) {
285                 ref = gnttab_entry(ref);
286                 count++;
287         }
288         gnttab_entry(ref) = gnttab_free_head;
289         gnttab_free_head = head;
290         gnttab_free_count += count;
291         check_free_callbacks();
292         mtx_unlock(&gnttab_list_lock);
293 }
294
295 int
296 gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head)
297 {
298         int h = get_free_entries(count);
299
300         if (h == -1)
301                 return -ENOSPC;
302
303         *head = h;
304
305         return 0;
306 }
307
308 int
309 gnttab_empty_grant_references(const grant_ref_t *private_head)
310 {
311         return (*private_head == GNTTAB_LIST_END);
312 }
313
314 int
315 gnttab_claim_grant_reference(grant_ref_t *private_head)
316 {
317         grant_ref_t g = *private_head;
318
319         if (unlikely(g == GNTTAB_LIST_END))
320                 return -ENOSPC;
321         *private_head = gnttab_entry(g);
322
323         return (g);
324 }
325
326 void
327 gnttab_release_grant_reference(grant_ref_t *private_head, grant_ref_t  release)
328 {
329         gnttab_entry(release) = *private_head;
330         *private_head = release;
331 }
332
333 void
334 gnttab_request_free_callback(struct gnttab_free_callback *callback,
335                              void (*fn)(void *), void *arg, uint16_t count)
336 {
337
338         mtx_lock(&gnttab_list_lock);
339         if (callback->next)
340                 goto out;
341         callback->fn = fn;
342         callback->arg = arg;
343         callback->count = count;
344         callback->next = gnttab_free_callback_list;
345         gnttab_free_callback_list = callback;
346         check_free_callbacks();
347  out:
348         mtx_unlock(&gnttab_list_lock);
349
350 }
351
352 void
353 gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
354 {
355         struct gnttab_free_callback **pcb;
356
357         mtx_lock(&gnttab_list_lock);
358         for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
359                 if (*pcb == callback) {
360                         *pcb = callback->next;
361                         break;
362                 }
363         }
364         mtx_unlock(&gnttab_list_lock);
365 }
366
367
368 static int
369 grow_gnttab_list(unsigned int more_frames)
370 {
371         unsigned int new_nr_grant_frames, extra_entries, i;
372
373         new_nr_grant_frames = nr_grant_frames + more_frames;
374         extra_entries       = more_frames * GREFS_PER_GRANT_FRAME;
375
376         for (i = nr_grant_frames; i < new_nr_grant_frames; i++)
377         {
378                 gnttab_list[i] = (grant_ref_t *)malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
379
380                 if (!gnttab_list[i])
381                         goto grow_nomem;
382         }
383
384         for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
385              i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
386                 gnttab_entry(i) = i + 1;
387
388         gnttab_entry(i) = gnttab_free_head;
389         gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
390         gnttab_free_count += extra_entries;
391
392         nr_grant_frames = new_nr_grant_frames;
393
394         check_free_callbacks();
395
396         return 0;
397         
398 grow_nomem:
399         for ( ; i >= nr_grant_frames; i--)
400                 free(gnttab_list[i], M_DEVBUF);
401         return (-ENOMEM);
402 }
403
404 static unsigned int
405 __max_nr_grant_frames(void)
406 {
407         struct gnttab_query_size query;
408         int rc;
409
410         query.dom = DOMID_SELF;
411
412         rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
413         if ((rc < 0) || (query.status != GNTST_okay))
414                 return (4); /* Legacy max supported number of frames */
415
416         return (query.max_nr_frames);
417 }
418
419 static inline
420 unsigned int max_nr_grant_frames(void)
421 {
422         unsigned int xen_max = __max_nr_grant_frames();
423
424         if (xen_max > boot_max_nr_grant_frames)
425                 return (boot_max_nr_grant_frames);
426         return (xen_max);
427 }
428
429 #ifdef notyet
430 /*
431  * XXX needed for backend support
432  *
433  */
434 static int
435 map_pte_fn(pte_t *pte, struct page *pmd_page,
436                       unsigned long addr, void *data)
437 {
438         unsigned long **frames = (unsigned long **)data;
439
440         set_pte_at(&init_mm, addr, pte, pfn_pte_ma((*frames)[0], PAGE_KERNEL));
441         (*frames)++;
442         return 0;
443 }
444
445 static int
446 unmap_pte_fn(pte_t *pte, struct page *pmd_page,
447                         unsigned long addr, void *data)
448 {
449
450         set_pte_at(&init_mm, addr, pte, __pte(0));
451         return 0;
452 }
453 #endif
454
455 static int
456 gnttab_map(unsigned int start_idx, unsigned int end_idx)
457 {
458         struct gnttab_setup_table setup;
459         u_long *frames;
460
461         unsigned int nr_gframes = end_idx + 1;
462         int i, rc;
463
464         frames = malloc(nr_gframes * sizeof(unsigned long), M_DEVBUF, M_NOWAIT);
465         if (!frames)
466                 return -ENOMEM;
467
468         setup.dom        = DOMID_SELF;
469         setup.nr_frames  = nr_gframes;
470         set_xen_guest_handle(setup.frame_list, frames);
471
472         rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
473         if (rc == -ENOSYS) {
474                 free(frames, M_DEVBUF);
475                 return -ENOSYS;
476         }
477         PANIC_IF(rc || setup.status);
478
479         if (shared == NULL) {
480                 vm_offset_t area;
481                 
482                 area = kmem_alloc_nofault(kernel_map,
483                     PAGE_SIZE * max_nr_grant_frames());
484                 PANIC_IF(area == 0);
485                 shared = (grant_entry_t *)area;
486         }
487         for (i = 0; i < nr_gframes; i++)
488                 PT_SET_MA(((caddr_t)shared) + i*PAGE_SIZE, 
489                     ((vm_paddr_t)frames[i]) << PAGE_SHIFT | PG_RW | PG_V);
490
491         free(frames, M_DEVBUF);
492         
493         return 0;
494 }
495
496 int
497 gnttab_resume(void)
498 {
499         if (max_nr_grant_frames() < nr_grant_frames)
500                 return -ENOSYS;
501         return gnttab_map(0, nr_grant_frames - 1);
502 }
503
504 int
505 gnttab_suspend(void)
506 {       
507         int i, pages;
508
509         pages = (PAGE_SIZE*nr_grant_frames) >> PAGE_SHIFT;
510
511         for (i = 0; i < pages; i++)
512                 PT_SET_MA(shared + (i*PAGE_SIZE), (vm_paddr_t)0);
513
514         return (0);
515 }
516
517 static int
518 gnttab_expand(unsigned int req_entries)
519 {
520         int rc;
521         unsigned int cur, extra;
522
523         cur = nr_grant_frames;
524         extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
525                  GREFS_PER_GRANT_FRAME);
526         if (cur + extra > max_nr_grant_frames())
527                 return -ENOSPC;
528
529         if ((rc = gnttab_map(cur, cur + extra - 1)) == 0)
530                 rc = grow_gnttab_list(extra);
531
532         return rc;
533 }
534
535 static int 
536 gnttab_init(void *unused)
537 {
538         int i;
539         unsigned int max_nr_glist_frames;
540         unsigned int nr_init_grefs;
541
542         if (!is_running_on_xen())
543                 return -ENODEV;
544
545         nr_grant_frames = 1;
546         boot_max_nr_grant_frames = __max_nr_grant_frames();
547
548         /* Determine the maximum number of frames required for the
549          * grant reference free list on the current hypervisor.
550          */
551         max_nr_glist_frames = (boot_max_nr_grant_frames *
552                                GREFS_PER_GRANT_FRAME /
553                                (PAGE_SIZE / sizeof(grant_ref_t)));
554
555         gnttab_list = malloc(max_nr_glist_frames * sizeof(grant_ref_t *),
556             M_DEVBUF, M_NOWAIT);
557
558         if (gnttab_list == NULL)
559                 return -ENOMEM;
560
561         for (i = 0; i < nr_grant_frames; i++) {
562                 gnttab_list[i] = (grant_ref_t *)malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
563                 if (gnttab_list[i] == NULL)
564                         goto ini_nomem;
565         }
566         
567         if (gnttab_resume() < 0)
568                 return -ENODEV;
569         
570         nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
571
572         for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
573                 gnttab_entry(i) = i + 1;
574
575         gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
576         gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
577         gnttab_free_head  = NR_RESERVED_ENTRIES;
578
579         printk("Grant table initialized\n");
580         return 0;
581
582 ini_nomem:
583         for (i--; i >= 0; i--)
584                 free(gnttab_list[i], M_DEVBUF);
585         free(gnttab_list, M_DEVBUF);
586         return -ENOMEM;
587
588 }
589
590 MTX_SYSINIT(gnttab, &gnttab_list_lock, "GNTTAB LOCK", MTX_DEF); 
591 SYSINIT(gnttab, SI_SUB_PSEUDO, SI_ORDER_FIRST, gnttab_init, NULL);