Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Fix for floating point save/restore in case where fp context switching is turned off
[palacios.git] / palacios / src / palacios / vmm_barrier.c
1 /*
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2011, Jack Lange <jacklange@cs.pitt.edu> 
11  * Copyright (c) 2011, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jacklangel@cs.pitt.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20
21 #include <palacios/vmm_barrier.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vm_guest.h>
24
25 int v3_init_barrier(struct v3_vm_info * vm_info) {
26     struct v3_barrier * barrier = &(vm_info->barrier);
27
28     memset(barrier, 0, sizeof(struct v3_barrier));
29     v3_bitmap_init(&(barrier->cpu_map), vm_info->num_cores); 
30     v3_lock_init(&(barrier->lock));
31
32     return 0;
33 }
34
35 int v3_deinit_barrier(struct v3_vm_info * vm_info) {
36     struct v3_barrier * barrier = &(vm_info->barrier);
37
38     v3_bitmap_deinit(&(barrier->cpu_map));
39     v3_lock_deinit(&(barrier->lock));
40
41     return 0;
42 }
43
44 int v3_raise_barrier_nowait(struct v3_vm_info * vm_info, struct guest_info * local_core) {
45     struct v3_barrier * barrier = &(vm_info->barrier);
46     addr_t flag;
47     int acquired = 0;
48
49     int local_vcpu = -1;
50     int i = 0;
51
52     flag = v3_lock_irqsave(barrier->lock);
53
54     if (barrier->active == 0) {
55         barrier->active = 1;
56         acquired = 1;
57     }
58
59     v3_unlock_irqrestore(barrier->lock, flag);
60
61     if (acquired == 0) {
62         /* If we are in a core context and the barrier has already been acquired 
63            we'll be safe and let the other barrier proceed. We will still report an error 
64            though to allow possible cleanups to occur at the call site.
65         */
66         if (local_core != NULL) {
67             v3_wait_at_barrier(local_core);
68         }
69
70         return -1;
71     }
72
73     // If we are raising the barrier from a core context
74     //   we have to mark ourselves blocked first to avoid deadlock
75     if (local_core != NULL) {
76         local_vcpu = local_core->vcpu_id;
77         v3_bitmap_set(&(barrier->cpu_map), local_vcpu);
78     }
79
80
81     // send out interrupts to force exits on all cores
82     for (i = 0; i < vm_info->num_cores; i++) {
83         if (vm_info->cores[i].vcpu_id != local_vcpu) {
84             v3_interrupt_cpu(vm_info, vm_info->cores[i].pcpu_id, 0);
85         }
86     }
87
88     return 0;
89 }
90
91 int v3_wait_for_barrier(struct v3_vm_info * vm_info, struct guest_info * local_core) {
92     struct v3_barrier * barrier = &(vm_info->barrier);
93     int all_blocked = 0;
94     int i = 0;
95
96     if (barrier->active == 0) {
97         return -1;
98     }
99
100     // wait for barrier catch on all cores
101     while (all_blocked == 0) {
102         all_blocked = 1;
103
104         for (i = 0; i < vm_info->num_cores; i++) {
105             
106             // Tricky: If a core is not running then it is safe to ignore it. 
107             // Whenever we transition a core to the RUNNING state we MUST immediately wait on the barrier. 
108             // TODO: Wrap the state transitions in functions that do this automatically
109             if (vm_info->cores[i].core_run_state != CORE_RUNNING) {
110                 continue;
111             }
112
113             if (v3_bitmap_check(&(barrier->cpu_map), i) == 0) {
114                 // There is still a core that is not waiting at the barrier
115                 all_blocked = 0;
116             }
117         }
118
119         if (all_blocked == 1) {
120             break;
121         }
122
123         // return immediately and spin if there is no one to yield to 
124         v3_yield(local_core,-1);
125     }
126
127     return 0;
128 }
129
130
131
132 /* Barrier synchronization primitive
133  *   -- This call will block until all the guest cores are waiting at a common synchronization point
134  *      in a yield loop. The core will block at the sync point until the barrier is lowered.
135  * 
136  *   ARGUMENTS: 
137  *       vm_info -- The VM for which the barrier is being activated
138  *       local_core -- The core whose thread this function is being called from, or NULL 
139  *                     if the calling thread is not associated with a VM's core context
140  */
141
142 int v3_raise_barrier(struct v3_vm_info * vm_info, struct guest_info * local_core) {
143     int ret = 0;
144
145
146     if ((vm_info->run_state != VM_RUNNING) && 
147         (vm_info->run_state != VM_SIMULATING)) {
148         return 0;
149     }
150
151     ret = v3_raise_barrier_nowait(vm_info, local_core);
152
153     if (ret != 0) {
154         return ret;
155     }
156
157     return v3_wait_for_barrier(vm_info, local_core);
158 }
159
160
161
162 /* Lowers a barrier that has already been raised
163  *    guest cores will automatically resume execution 
164  *    once this has been called
165  * 
166  *   TODO: Need someway to check that the barrier is active
167  */
168
169 int v3_lower_barrier(struct v3_vm_info * vm_info) {
170     struct v3_barrier * barrier = &(vm_info->barrier);
171
172     
173     if ((vm_info->run_state != VM_RUNNING) && 
174         (vm_info->run_state != VM_SIMULATING)) {
175         return 0;
176     }
177
178     // Clear the active flag, so cores won't wait 
179     barrier->active = 0;
180
181     // Clear all the cpu flags, so cores will proceed
182     v3_bitmap_reset(&(barrier->cpu_map));
183
184     return 0;
185 }
186
187
188 /* 
189  * Syncronization point for guest cores
190  *    -- called as part of the main VMM event loop for each core
191  *    -- if a barrier has been activated then the core will signal  
192  *       it has reached the barrier and sit in a yield loop until the 
193  *       barrier has been lowered
194  */
195 int v3_wait_at_barrier(struct guest_info * core) {
196     struct v3_barrier * barrier = &(core->vm_info->barrier);
197
198     if (barrier->active == 0) {
199         return 0;
200     }
201 #ifndef V3_CONFIG_FP_SWITCH
202     v3_get_fp_state(core); // snapshot FP state now
203 #else
204 #   ifdef V3_CONFIG_LAZY_FP_SWITCH
205     v3_get_fp_state(core); // snapshot FP state now regardless of lazy eval
206 #   endif
207 #endif
208
209     V3_Print(core->vm_info, core, "Core %d waiting at barrier\n", core->vcpu_id);
210
211     /*  Barrier has been activated. 
212      *  Wait here until it's lowered
213      */
214     
215     
216     // set cpu bit in barrier bitmap
217     v3_bitmap_set(&(barrier->cpu_map), core->vcpu_id);
218     V3_Print(core->vm_info, core, "Core %d bit set as waiting\n", core->vcpu_id);
219
220     // wait for cpu bit to clear
221     while (v3_bitmap_check(&(barrier->cpu_map), core->vcpu_id)) {
222         // Barrier wait will spin if there is no competing work
223         v3_yield(core,-1);
224     }
225
226 #ifndef V3_CONFIG_FP_SWITCH    
227     core->fp_state.need_restore=1;  // restore FP on next entry
228 #else
229 #   ifdef V3_CONFIG_LAZY_FP_SWITCH
230     core->fp_state.need_restore=1;  // restore FP on next entry
231 #   endif
232 #endif
233
234     return 0;
235 }