f48f6079b145742375086ef9fc1b41e1fc1c7acc
[util-vserver.git] / lib / planetlab.c
1 /* Copyright 2005 Princeton University
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
5 are met: 
6
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9       
10     * Redistributions in binary form must reproduce the above
11       copyright notice, this list of conditions and the following
12       disclaimer in the documentation and/or other materials provided
13       with the distribution.
14       
15     * Neither the name of the copyright holder nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18       
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
23 UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE. 
31
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #  include <config.h>
36 #endif
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <stdint.h>
42 #include <stdarg.h>
43 #include <unistd.h>
44 #include <ctype.h>
45 #include <sys/resource.h>
46 #include <fcntl.h>
47
48 #include "vserver.h"
49 #include "planetlab.h"
50
51 static int
52 create_context(xid_t ctx, uint64_t bcaps, struct sliver_resources *slr)
53 {
54   struct vc_ctx_caps  vc_caps;
55   struct vc_net_nx  vc_net;
56   struct vc_net_flags  vc_nf;
57
58   /* Create network context */
59   if (vc_net_create(ctx) == VC_NOCTX)
60     return -1;
61
62   /* Make the network context persistent */
63   vc_nf.mask = vc_nf.flagword = VC_NXF_PERSISTENT;
64   if (vc_set_nflags(ctx, &vc_nf))
65     return -1;
66
67   /* XXX: Allow access to all IPv4 addresses (for now) */
68   vc_net.type = vcNET_IPV4;
69   vc_net.count = 1;
70   vc_net.ip[0] = 0;
71   vc_net.mask[0] = 0;
72   if (vc_net_add(ctx, &vc_net) == -1)
73     return -1;
74
75   /*
76    * Create context info - this sets the STATE_SETUP and STATE_INIT flags.
77    */
78   if (vc_ctx_create(ctx) == VC_NOCTX)
79     return -1;
80
81   /* Set capabilities - these don't take effect until SETUP flag is unset */
82   vc_caps.bcaps = bcaps;
83   vc_caps.bmask = ~0ULL;  /* currently unused */
84   vc_caps.ccaps = 0;      /* don't want any of these */
85   vc_caps.cmask = ~0ULL;
86   if (vc_set_ccaps(ctx, &vc_caps))
87     return -1;
88
89   pl_set_limits(ctx, slr);
90
91   return 0;
92 }
93
94 int
95 pl_setup_done(xid_t ctx)
96 {
97   struct vc_ctx_flags  vc_flags;
98
99   /* unset SETUP flag - this allows other processes to migrate */
100   /* set the PERSISTENT flag - so the context doesn't vanish */
101   /* Don't clear the STATE_INIT flag, as that would make us the init task. */
102   vc_flags.mask = VC_VXF_STATE_SETUP|VC_VXF_PERSISTENT;
103   vc_flags.flagword = VC_VXF_PERSISTENT;
104   if (vc_set_cflags(ctx, &vc_flags))
105     return -1;
106
107   return 0;
108 }
109
110 #define RETRY_LIMIT  10
111
112 int
113 pl_chcontext(xid_t ctx, uint64_t bcaps, struct sliver_resources *slr)
114 {
115   int  retry_count = 0;
116   int  net_migrated = 0;
117
118   for (;;)
119     {
120       struct vc_ctx_flags  vc_flags;
121
122       if (vc_get_cflags(ctx, &vc_flags))
123         {
124           if (errno != ESRCH)
125             return -1;
126
127           /* context doesn't exist - create it */
128           if (create_context(ctx, bcaps,slr))
129             {
130               if (errno == EEXIST)
131                 /* another process beat us in a race */
132                 goto migrate;
133               if (errno == EBUSY)
134                 /* another process is creating - poll the SETUP flag */
135                 continue;
136               return -1;
137             }
138
139           /* created context and migrated to it i.e., we're done */
140           return 1;
141         }
142
143       /* check the SETUP flag */
144       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
145         {
146           /* context is still being setup - wait a while then retry */
147           if (retry_count++ >= RETRY_LIMIT)
148             {
149               errno = EBUSY;
150               return -1;
151             }
152           sleep(1);
153           continue;
154         }
155
156       /* context has been setup */
157     migrate:
158       if (net_migrated || !vc_net_migrate(ctx))
159         {
160           if (!vc_ctx_migrate(ctx, 0))
161             break;  /* done */
162           net_migrated = 1;
163         }
164
165       /* context disappeared - retry */
166     }
167
168   return 0;
169 }
170
171 /* it's okay for a syscall to fail because the context doesn't exist */
172 #define VC_SYSCALL(x)                           \
173 do                                              \
174 {                                               \
175   if (x)                                        \
176     return errno == ESRCH ? 0 : -1;             \
177 }                                               \
178 while (0)
179
180 int
181 pl_setsched(xid_t ctx, uint32_t cpu_share, uint32_t cpu_sched_flags)
182 {
183   struct vc_set_sched  vc_sched;
184   struct vc_ctx_flags  vc_flags;
185   uint32_t  new_flags;
186
187   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
188                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX | VC_VXSM_MSEC |
189                        VC_VXSM_FILL_RATE2 | VC_VXSM_INTERVAL2 | VC_VXSM_FORCE |
190                        VC_VXSM_IDLE_TIME);
191   vc_sched.fill_rate = 0;
192   vc_sched.fill_rate2 = cpu_share;  /* tokens accumulated per interval */
193   vc_sched.interval = vc_sched.interval2 = 1000;  /* milliseconds */
194   vc_sched.tokens = 100;     /* initial allocation of tokens */
195   vc_sched.tokens_min = 50;  /* need this many tokens to run */
196   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
197
198   if (cpu_share == VC_LIM_KEEP)
199     vc_sched.set_mask &= ~(VC_VXSM_FILL_RATE|VC_VXSM_FILL_RATE2);
200
201   /* guaranteed CPU corresponds to SCHED_SHARE flag being cleared */
202   if (cpu_sched_flags & VS_SCHED_CPU_GUARANTEED) {
203     new_flags = 0;
204     vc_sched.fill_rate = vc_sched.fill_rate2;
205   }
206   else
207     new_flags = VC_VXF_SCHED_SHARE;
208
209   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
210
211   vc_flags.mask = VC_VXF_SCHED_FLAGS;
212   vc_flags.flagword = new_flags | VC_VXF_SCHED_HARD;
213   VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
214
215   return 0;
216 }
217
218 struct pl_resources {
219         char *name;
220         unsigned long long *limit;
221 };
222
223 #define WHITESPACE(buffer,index,len)     \
224   while(isspace((int)buffer[index])) \
225         if (index < len) index++; else goto out;
226
227 #define VSERVERCONF "/etc/vservers/"
228 void
229 pl_get_limits(char *context, struct sliver_resources *slr)
230 {
231   FILE *fb;
232   int cwd;
233   size_t len = strlen(VSERVERCONF) + strlen(context) + NULLBYTE_SIZE;
234   char *conf = (char *)malloc(len + strlen("rlimits/openfd.hard"));
235   struct pl_resources *r;
236   struct pl_resources sliver_list[] = {
237     {"sched/fill-rate2", &slr->vs_cpu},
238     {"sched/fill-rate", &slr->vs_cpuguaranteed},
239   
240     {"rlimits/nproc.hard", &slr->vs_nproc.hard},
241     {"rlimits/nproc.soft", &slr->vs_nproc.soft},
242     {"rlimits/nproc.min", &slr->vs_nproc.min},
243   
244     {"rlimits/rss.hard", &slr->vs_rss.hard},
245     {"rlimits/rss.soft", &slr->vs_rss.soft},
246     {"rlimits/rss.min", &slr->vs_rss.min},
247   
248     {"rlimits/as.hard", &slr->vs_as.hard},
249     {"rlimits/as.soft", &slr->vs_as.soft},
250     {"rlimits/as.min", &slr->vs_as.min},
251   
252     {"rlimits/openfd.hard", &slr->vs_openfd.hard},
253     {"rlimits/openfd.soft", &slr->vs_openfd.soft},
254     {"rlimits/openfd.min", &slr->vs_openfd.min},
255
256     {"whitelisted", &slr->vs_whitelisted},
257     {0,0}
258   };
259
260   sprintf(conf, "%s%s", VSERVERCONF, context);
261
262   slr->vs_cpu = VC_LIM_KEEP;
263   slr->vs_cpuguaranteed = 0;
264
265   slr->vs_rss.hard = VC_LIM_KEEP;
266   slr->vs_rss.soft = VC_LIM_KEEP;
267   slr->vs_rss.min = VC_LIM_KEEP;
268
269   slr->vs_as.hard = VC_LIM_KEEP;
270   slr->vs_as.soft = VC_LIM_KEEP;
271   slr->vs_as.min = VC_LIM_KEEP;
272
273
274   slr->vs_nproc.hard = VC_LIM_KEEP;
275   slr->vs_nproc.soft = VC_LIM_KEEP;
276   slr->vs_nproc.min = VC_LIM_KEEP;
277
278   slr->vs_openfd.hard = VC_LIM_KEEP;
279   slr->vs_openfd.soft = VC_LIM_KEEP;
280   slr->vs_openfd.min = VC_LIM_KEEP;
281
282   slr->vs_whitelisted = 1;
283
284   cwd = open(".", O_RDONLY);
285   if (cwd == -1) {
286     perror("cannot get a handle on .");
287     goto out;
288   }
289   if (chdir(conf) == -1) {
290     fprintf(stderr, "cannot chdir to ");
291     perror(conf);
292     goto out_fd;
293   }
294
295   for (r = &sliver_list[0]; r->name; r++) {
296     char buf[1000];
297     fb = fopen(r->name, "r");
298     if (fb == NULL)
299       continue;
300     if (fgets(buf, sizeof(buf), fb) != NULL && isdigit(*buf)) {
301       *r->limit = atoi(buf);
302     }
303     fclose(fb);
304   }
305
306   fchdir(cwd);
307 out_fd:
308   close(cwd);
309 out:
310 #if 0
311   /* open the conf file for reading */
312   fb = fopen(conf,"r");
313   if (fb != NULL) {
314     size_t index;
315     char *buffer = malloc(1000);
316     char *p;
317     
318     /* the conf file exist */ 
319     while((p=fgets(buffer,1000-1,fb))!=NULL) {
320       index = 0;
321       len = strnlen(buffer,1000);
322       WHITESPACE(buffer,index,len);
323       if (buffer[index] == '#') 
324         continue;
325       
326       for (r=&sliver_list[0]; r->name; r++)
327         if ((p=strstr(&buffer[index],r->name))!=NULL) {
328           /* adjust index into buffer */
329           index+= (p-&buffer[index])+strlen(r->name);
330           
331           /* skip over whitespace */
332           WHITESPACE(buffer,index,len);
333           
334           /* expecting to see = sign */
335           if (buffer[index++]!='=') goto out;
336           
337           /* skip over whitespace */
338           WHITESPACE(buffer,index,len);
339           
340           /* expecting to see a digit for number */
341           if (!isdigit((int)buffer[index])) goto out;
342           
343           *r->limit = atoi(&buffer[index]);
344           if (0) /* for debugging only */
345             fprintf(stderr,"pl_get_limits found %s=%lld\n",
346                     r->name,*r->limit);
347           break;
348         }
349     }
350   out:
351     fclose(fb);
352     free(buffer);
353   } else {
354     fprintf(stderr,"cannot open %s\n",conf);
355   }
356 #endif
357   free(conf);
358 }
359
360 int
361 adjust_lim(struct vc_rlimit *vcr, struct rlimit *lim)
362 {
363   int adjusted = 0;
364   if (vcr->min != VC_LIM_KEEP) {
365     if (vcr->min > lim->rlim_cur) {
366       lim->rlim_cur = vcr->min;
367       adjusted = 1;
368     }
369     if (vcr->min > lim->rlim_max) {
370       lim->rlim_max = vcr->min;
371       adjusted = 1;
372     }
373   }
374
375   if (vcr->soft != VC_LIM_KEEP) {
376     switch (vcr->min != VC_LIM_KEEP) {
377     case 1:
378       if (vcr->soft < vcr->min)
379         break;
380     case 0:
381         lim->rlim_cur = vcr->soft;
382         adjusted = 1;
383     }
384   }
385
386   if (vcr->hard != VC_LIM_KEEP) {
387     switch (vcr->min != VC_LIM_KEEP) {
388     case 1:
389       if (vcr->hard < vcr->min)
390         break;
391     case 0:
392         lim->rlim_cur = vcr->hard;
393         adjusted = 1;
394     }
395   }
396   return adjusted;
397 }
398
399
400 void
401 pl_set_limits(xid_t ctx, struct sliver_resources *slr)
402 {
403   struct rlimit lim; /* getrlimit values */
404   unsigned long long vs_cpu;
405   uint32_t cpu_sched_flags;
406
407   if (slr != 0) {
408     /* set memory limits */
409     getrlimit(RLIMIT_RSS,&lim);
410     if (adjust_lim(&slr->vs_rss, &lim)) {
411       setrlimit(RLIMIT_RSS, &lim);
412       if (vc_set_rlimit(ctx, RLIMIT_RSS, &slr->vs_rss))
413         {
414           PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
415           exit(1);
416         }
417     }
418
419     /* set address space limits */
420     getrlimit(RLIMIT_AS,&lim);
421     if (adjust_lim(&slr->vs_as, &lim)) {
422       setrlimit(RLIMIT_AS, &lim);
423       if (vc_set_rlimit(ctx, RLIMIT_AS, &slr->vs_as))
424         {
425           PERROR("pl_setrlimit(%u, RLIMIT_AS)", ctx);
426           exit(1);
427         }
428     }
429     /* set nrpoc limit */
430     getrlimit(RLIMIT_NPROC,&lim);
431     if (adjust_lim(&slr->vs_nproc, &lim)) {
432       setrlimit(RLIMIT_NPROC, &lim);
433       if (vc_set_rlimit(ctx, RLIMIT_NPROC, &slr->vs_nproc))
434         {
435           PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
436           exit(1);
437         }
438     }
439
440     /* set openfd limit */
441     getrlimit(RLIMIT_NOFILE,&lim);
442     if (adjust_lim(&slr->vs_openfd, &lim)) {
443       setrlimit(RLIMIT_NOFILE, &lim);
444       if (vc_set_rlimit(ctx, RLIMIT_NOFILE, &slr->vs_openfd))
445         {
446           PERROR("pl_setrlimit(%u, RLIMIT_NOFILE)", ctx);
447           exit(1);
448         }
449       if (vc_set_rlimit(ctx, VC_VLIMIT_OPENFD, &slr->vs_openfd))
450         {
451           PERROR("pl_setrlimit(%u, VLIMIT_OPENFD)", ctx);
452           exit(1);
453         }
454     }
455     vs_cpu = slr->vs_cpu;    
456     cpu_sched_flags = slr->vs_cpuguaranteed & VS_SCHED_CPU_GUARANTEED;
457   } else {
458     vs_cpu = 1;
459     cpu_sched_flags = 0;
460   }
461   
462   if (pl_setsched(ctx, vs_cpu, cpu_sched_flags) < 0) {
463     PERROR("pl_setsched(&u)", ctx);
464     exit(1);
465   }
466 }