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