1 #include <xenevtchn.h>
2 #include <xenctrl.h>
3 #include <xc_private.h>
4 #include <xc_core.h>
5 #include <xenstore.h>
6 #include <unistd.h>
7 
8 #undef ARRAY_SIZE /* We shouldn't be including xc_private.h */
9 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
10 
11 static xc_interface *xch;
12 
show_help(void)13 void show_help(void)
14 {
15     fprintf(stderr,
16             "xen-hptool: Xen CPU/memory hotplug tool\n"
17             "Usage: xen-hptool <command> [args]\n"
18             "Commands:\n"
19             "  help                     display this help\n"
20             "  cpu-online    <cpuid>    online CPU <cpuid>\n"
21             "  cpu-offline   <cpuid>    offline CPU <cpuid>\n"
22             "  mem-online    <mfn>      online MEMORY <mfn>\n"
23             "  mem-offline   <mfn>      offline MEMORY <mfn>\n"
24             "  mem-status    <mfn>      query Memory status<mfn>\n"
25            );
26 }
27 
28 /* wrapper function */
help_func(int argc,char * argv[])29 static int help_func(int argc, char *argv[])
30 {
31     show_help();
32     return 0;
33 }
34 
hp_mem_online_func(int argc,char * argv[])35 static int hp_mem_online_func(int argc, char *argv[])
36 {
37     uint32_t status;
38     int ret;
39     unsigned long mfn;
40 
41     if (argc != 1)
42     {
43         show_help();
44         return -1;
45     }
46 
47     sscanf(argv[0], "%lx", &mfn);
48     printf("Prepare to online MEMORY mfn %lx\n", mfn);
49 
50     ret = xc_mark_page_online(xch, mfn, mfn, &status);
51 
52     if (ret < 0)
53         fprintf(stderr, "Onlining page mfn %lx failed, error %x", mfn, errno);
54     else if (status & (PG_ONLINE_FAILED |PG_ONLINE_BROKEN)) {
55         fprintf(stderr, "Onlining page mfn %lx is broken, "
56                         "Memory online failed\n", mfn);
57         ret = -1;
58 	}
59     else if (status & PG_ONLINE_ONLINED)
60         printf("Memory mfn %lx onlined successfully\n", mfn);
61     else
62         printf("Memory is already onlined!\n");
63 
64     return ret;
65 }
66 
hp_mem_query_func(int argc,char * argv[])67 static int hp_mem_query_func(int argc, char *argv[])
68 {
69     uint32_t status;
70     int ret;
71     unsigned long mfn;
72 
73     if (argc != 1)
74     {
75         show_help();
76         return -1;
77     }
78 
79     sscanf(argv[0], "%lx", &mfn);
80     printf("Querying MEMORY mfn %lx status\n", mfn);
81     ret = xc_query_page_offline_status(xch, mfn, mfn, &status);
82 
83     if (ret < 0)
84         fprintf(stderr, "Querying page mfn %lx failed, error %x", mfn, errno);
85     else
86     {
87 		printf("Memory Status %x: [", status);
88         if ( status & PG_OFFLINE_STATUS_OFFLINE_PENDING)
89             printf(" PAGE_OFFLINE_PENDING ");
90         if ( status & PG_OFFLINE_STATUS_BROKEN )
91             printf(" PAGE_BROKEND  ");
92         if ( status & PG_OFFLINE_STATUS_OFFLINED )
93             printf(" PAGE_OFFLINED ");
94 		else
95             printf(" PAGE_ONLINED ");
96         printf("]\n");
97     }
98 
99     return ret;
100 }
101 
suspend_guest(xc_interface * xch,xenevtchn_handle * xce,int domid,int * evtchn,int * lockfd)102 static int suspend_guest(xc_interface *xch, xenevtchn_handle *xce, int domid,
103                          int *evtchn, int *lockfd)
104 {
105     int port, rc, suspend_evtchn = -1;
106 
107     *lockfd = -1;
108 
109     if (!evtchn)
110         return -1;
111 
112     port = xs_suspend_evtchn_port(domid);
113     if (port < 0)
114     {
115         fprintf(stderr, "DOM%d: No suspend port, try live migration\n", domid);
116         goto failed;
117     }
118     suspend_evtchn = xc_suspend_evtchn_init_exclusive(xch, xce, domid,
119                                                       port, lockfd);
120     if (suspend_evtchn < 0)
121     {
122         fprintf(stderr, "Suspend evtchn initialization failed\n");
123         goto failed;
124     }
125     *evtchn = suspend_evtchn;
126 
127     rc = xenevtchn_notify(xce, suspend_evtchn);
128     if (rc < 0)
129     {
130         fprintf(stderr, "Failed to notify suspend channel: errno %d\n", rc);
131         goto failed;
132     }
133     if (xc_await_suspend(xch, xce, suspend_evtchn) < 0)
134     {
135         fprintf(stderr, "Suspend Failed\n");
136         goto failed;
137     }
138     return 0;
139 
140 failed:
141     if (suspend_evtchn != -1)
142         xc_suspend_evtchn_release(xch, xce, domid,
143                                   suspend_evtchn, lockfd);
144 
145     return -1;
146 }
147 
hp_mem_offline_func(int argc,char * argv[])148 static int hp_mem_offline_func(int argc, char *argv[])
149 {
150     uint32_t status, domid;
151     int ret;
152     unsigned long mfn;
153 
154     if (argc != 1)
155     {
156         show_help();
157         return -1;
158     }
159 
160     sscanf(argv[0], "%lx", &mfn);
161     printf("Prepare to offline MEMORY mfn %lx\n", mfn);
162     ret = xc_mark_page_offline(xch, mfn, mfn, &status);
163     if (ret < 0) {
164         fprintf(stderr, "Offlining page mfn %lx failed, error %x\n", mfn, errno);
165         if (status & (PG_OFFLINE_XENPAGE | PG_OFFLINE_FAILED))
166             fprintf(stderr, "XEN_PAGE is not permitted be offlined\n");
167         else if (status & (PG_OFFLINE_FAILED | PG_OFFLINE_NOT_CONV_RAM))
168             fprintf(stderr, "RESERVED RAM is not permitted to be offlined\n");
169     }
170     else
171     {
172         switch(status & PG_OFFLINE_STATUS_MASK)
173         {
174             case PG_OFFLINE_OFFLINED:
175             {
176                 printf("Memory mfn %lx offlined successfully, current state is"
177                        " [PG_OFFLINE_OFFLINED]\n", mfn);
178                 if (status & PG_OFFLINE_BROKEN)
179                     printf("And this offlined PAGE is already marked broken"
180                         " before!\n");
181                 break;
182             }
183             case PG_OFFLINE_FAILED:
184             {
185                 fprintf(stderr, "Memory mfn %lx offline failed\n", mfn);
186                 if ( status & PG_OFFLINE_ANONYMOUS)
187                     fprintf(stderr, "the memory is an anonymous page!\n");
188                 ret = -1;
189                 break;
190             }
191             case PG_OFFLINE_PENDING:
192             {
193                 if (status & PG_OFFLINE_XENPAGE) {
194                     ret = -1;
195                     fprintf(stderr, "Memory mfn %lx offlined succssefully,"
196                             "this page is xen page, current state is"
197                             " [PG_OFFLINE_PENDING, PG_OFFLINE_XENPAGE]\n", mfn);
198                 }
199                 else if (status & PG_OFFLINE_OWNED)
200                 {
201                     int result, suspend_evtchn = -1, suspend_lockfd = -1;
202                     xenevtchn_handle *xce;
203                     xce = xenevtchn_open(NULL, 0);
204 
205                     if (xce == NULL)
206                     {
207                         fprintf(stderr, "When exchange page, fail"
208                                 " to open evtchn\n");
209                         return -1;
210                     }
211 
212                     domid = status >> PG_OFFLINE_OWNER_SHIFT;
213                     if (suspend_guest(xch, xce, domid,
214                                       &suspend_evtchn, &suspend_lockfd))
215                     {
216                         fprintf(stderr, "Failed to suspend guest %d for"
217                                 " mfn %lx\n", domid, mfn);
218                         xenevtchn_close(xce);
219                         return -1;
220                     }
221 
222                     result = xc_exchange_page(xch, domid, mfn);
223 
224                     /* Exchange page successfully */
225                     if (result == 0)
226                         printf("Memory mfn %lx offlined successfully, this "
227                                 "page is DOM%d page and being swapped "
228                                 "successfully, current state is "
229                                 "[PG_OFFLINE_OFFLINED, PG_OFFLINE_OWNED]\n",
230                                 mfn, domid);
231                     else {
232                         ret = -1;
233                         fprintf(stderr, "Memory mfn %lx offlined successfully"
234                                 " , this page is DOM%d page yet failed to be "
235                                 "exchanged. current state is "
236                                 "[PG_OFFLINE_PENDING, PG_OFFLINE_OWNED]\n",
237                                 mfn, domid);
238                     }
239                     xc_domain_resume(xch, domid, 1);
240                     xc_suspend_evtchn_release(xch, xce, domid,
241                                               suspend_evtchn, &suspend_lockfd);
242                     xenevtchn_close(xce);
243                 }
244                 break;
245             }
246         }//end of switch
247     }//end of if
248 
249     return ret;
250 }
251 
exec_cpu_hp_fn(int (* hp_fn)(xc_interface *,int),int cpu)252 static int exec_cpu_hp_fn(int (*hp_fn)(xc_interface *, int), int cpu)
253 {
254     int ret;
255 
256     for ( ; ; )
257     {
258         ret = (*hp_fn)(xch, cpu);
259         if ( (ret >= 0) || (errno != EBUSY) )
260             break;
261         usleep(100000); /* 100ms */
262     }
263 
264     return ret;
265 }
266 
hp_cpu_online_func(int argc,char * argv[])267 static int hp_cpu_online_func(int argc, char *argv[])
268 {
269     int cpu, ret;
270 
271     if ( argc != 1 )
272     {
273         show_help();
274         return -1;
275     }
276 
277     cpu = atoi(argv[0]);
278     printf("Prepare to online CPU %d\n", cpu);
279     ret = exec_cpu_hp_fn(xc_cpu_online, cpu);
280     if (ret < 0)
281         fprintf(stderr, "CPU %d online failed (error %d: %s)\n",
282                 cpu, errno, strerror(errno));
283     else
284         printf("CPU %d onlined successfully\n", cpu);
285 
286     return ret;
287 
288 }
hp_cpu_offline_func(int argc,char * argv[])289 static int hp_cpu_offline_func(int argc, char *argv[])
290 {
291     int cpu, ret;
292 
293     if (argc != 1 )
294     {
295         show_help();
296         return -1;
297     }
298     cpu = atoi(argv[0]);
299     printf("Prepare to offline CPU %d\n", cpu);
300     ret = exec_cpu_hp_fn(xc_cpu_offline, cpu);
301     if (ret < 0)
302         fprintf(stderr, "CPU %d offline failed (error %d: %s)\n",
303                 cpu, errno, strerror(errno));
304     else
305         printf("CPU %d offlined successfully\n", cpu);
306 
307     return ret;
308 }
309 
310 struct {
311     const char *name;
312     int (*function)(int argc, char *argv[]);
313 } main_options[] = {
314     { "help", help_func },
315     { "cpu-online", hp_cpu_online_func },
316     { "cpu-offline", hp_cpu_offline_func },
317     { "mem-status", hp_mem_query_func},
318     { "mem-online", hp_mem_online_func},
319     { "mem-offline", hp_mem_offline_func},
320 };
321 
322 
main(int argc,char * argv[])323 int main(int argc, char *argv[])
324 {
325     int i, ret;
326 
327     if (argc < 2)
328     {
329         show_help();
330         return 0;
331     }
332 
333     xch = xc_interface_open(0,0,0);
334     if ( !xch )
335     {
336         fprintf(stderr, "failed to get the handler\n");
337         return 0;
338     }
339 
340     for ( i = 0; i < ARRAY_SIZE(main_options); i++ )
341         if (!strncmp(main_options[i].name, argv[1], strlen(argv[1])))
342             break;
343     if ( i == ARRAY_SIZE(main_options) )
344     {
345         fprintf(stderr, "Unrecognised command '%s' -- try "
346                 "'xen-hptool help'\n", argv[1]);
347         return 1;
348     }
349 
350     ret = main_options[i].function(argc -2, argv + 2);
351 
352     xc_interface_close(xch);
353 
354     return !!ret;
355 }
356