b1a4e8edec939760ef1d19547cb6f052b1f090db
[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     if (errno == EEXIST)
61       goto process;
62     return -1;
63   }
64
65   /* Make the network context persistent */
66   vc_nf.mask = vc_nf.flagword = VC_NXF_PERSISTENT;
67   if (vc_set_nflags(ctx, &vc_nf))
68     return -1;
69
70 process:
71   /*
72    * Create context info - this sets the STATE_SETUP and STATE_INIT flags.
73    */
74   if (vc_ctx_create(ctx) == VC_NOCTX)
75     return -1;
76
77   /* Set capabilities - these don't take effect until SETUP flag is unset */
78   vc_caps.bcaps = bcaps;
79   vc_caps.bmask = ~0ULL;  /* currently unused */
80   vc_caps.ccaps = 0;      /* don't want any of these */
81   vc_caps.cmask = ~0ULL;
82   if (vc_set_ccaps(ctx, &vc_caps))
83     return -1;
84
85   pl_set_limits(ctx, slr);
86
87   return 0;
88 }
89
90 int
91 pl_setup_done(xid_t ctx)
92 {
93   struct vc_ctx_flags  vc_flags;
94
95   /* unset SETUP flag - this allows other processes to migrate */
96   /* set the PERSISTENT flag - so the context doesn't vanish */
97   /* Don't clear the STATE_INIT flag, as that would make us the init task. */
98   vc_flags.mask = VC_VXF_STATE_SETUP|VC_VXF_PERSISTENT;
99   vc_flags.flagword = VC_VXF_PERSISTENT;
100   if (vc_set_cflags(ctx, &vc_flags))
101     return -1;
102
103   return 0;
104 }
105
106 #define RETRY_LIMIT  10
107
108 int
109 pl_chcontext(xid_t ctx, uint64_t bcaps, struct sliver_resources *slr)
110 {
111   int  retry_count = 0;
112   int  net_migrated = 0;
113
114   for (;;)
115     {
116       struct vc_ctx_flags  vc_flags;
117
118       if (vc_get_cflags(ctx, &vc_flags))
119         {
120           if (errno != ESRCH)
121             return -1;
122
123           /* context doesn't exist - create it */
124           if (create_context(ctx, bcaps, slr))
125             {
126               if (errno == EEXIST)
127                 /* another process beat us in a race */
128                 goto migrate;
129               if (errno == EBUSY)
130                 /* another process is creating - poll the SETUP flag */
131                 continue;
132               return -1;
133             }
134
135           /* created context and migrated to it i.e., we're done */
136           return 1;
137         }
138
139       /* check the SETUP flag */
140       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
141         {
142           /* context is still being setup - wait a while then retry */
143           if (retry_count++ >= RETRY_LIMIT)
144             {
145               errno = EBUSY;
146               return -1;
147             }
148           sleep(1);
149           continue;
150         }
151
152       /* context has been setup */
153     migrate:
154       if (net_migrated || !vc_net_migrate(ctx))
155         {
156           if (!vc_ctx_migrate(ctx, 0))
157             break;  /* done */
158           net_migrated = 1;
159         }
160
161       /* context disappeared - retry */
162     }
163
164   return 0;
165 }
166
167 /* it's okay for a syscall to fail because the context doesn't exist */
168 #define VC_SYSCALL(x)                           \
169 do                                              \
170 {                                               \
171   if (x)                                        \
172     return errno == ESRCH ? 0 : -1;             \
173 }                                               \
174 while (0)
175
176 int
177 pl_setsched(xid_t ctx, uint32_t cpu_share, uint32_t cpu_sched_flags)
178 {
179   struct vc_set_sched  vc_sched;
180   struct vc_ctx_flags  vc_flags;
181   uint32_t  new_flags;
182
183   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
184                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX | VC_VXSM_MSEC |
185                        VC_VXSM_FILL_RATE2 | VC_VXSM_INTERVAL2 | VC_VXSM_FORCE |
186                        VC_VXSM_IDLE_TIME);
187   vc_sched.fill_rate = 0;
188   vc_sched.fill_rate2 = cpu_share;  /* tokens accumulated per interval */
189   vc_sched.interval = vc_sched.interval2 = 1000;  /* milliseconds */
190   vc_sched.tokens = 100;     /* initial allocation of tokens */
191   vc_sched.tokens_min = 50;  /* need this many tokens to run */
192   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
193
194   if (cpu_share == (uint32_t)VC_LIM_KEEP)
195     vc_sched.set_mask &= ~(VC_VXSM_FILL_RATE|VC_VXSM_FILL_RATE2);
196
197   /* guaranteed CPU corresponds to SCHED_SHARE flag being cleared */
198   if (cpu_sched_flags & VS_SCHED_CPU_GUARANTEED) {
199     new_flags = 0;
200     vc_sched.fill_rate = vc_sched.fill_rate2;
201   }
202   else
203     new_flags = VC_VXF_SCHED_SHARE;
204
205   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
206
207   vc_flags.mask = VC_VXF_SCHED_FLAGS;
208   vc_flags.flagword = new_flags | VC_VXF_SCHED_HARD;
209   VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
210
211   return 0;
212 }
213
214 struct pl_resources {
215         char *name;
216         unsigned long long *limit;
217 };
218
219 #define WHITESPACE(buffer,index,len)     \
220   while(isspace((int)buffer[index])) \
221         if (index < len) index++; else goto out;
222
223 #define VSERVERCONF "/etc/vservers/"
224 void
225 pl_get_limits(char *context, struct sliver_resources *slr)
226 {
227   FILE *fb;
228   int cwd;
229   size_t len = strlen(VSERVERCONF) + strlen(context) + NULLBYTE_SIZE;
230   char *conf = (char *)malloc(len + strlen("rlimits/openfd.hard"));
231   struct pl_resources *r;
232   struct pl_resources sliver_list[] = {
233     {"sched/fill-rate2", &slr->vs_cpu},
234     {"sched/fill-rate", &slr->vs_cpuguaranteed},
235   
236     {"rlimits/nproc.hard", &slr->vs_nproc.hard},
237     {"rlimits/nproc.soft", &slr->vs_nproc.soft},
238     {"rlimits/nproc.min", &slr->vs_nproc.min},
239   
240     {"rlimits/rss.hard", &slr->vs_rss.hard},
241     {"rlimits/rss.soft", &slr->vs_rss.soft},
242     {"rlimits/rss.min", &slr->vs_rss.min},
243   
244     {"rlimits/as.hard", &slr->vs_as.hard},
245     {"rlimits/as.soft", &slr->vs_as.soft},
246     {"rlimits/as.min", &slr->vs_as.min},
247   
248     {"rlimits/openfd.hard", &slr->vs_openfd.hard},
249     {"rlimits/openfd.soft", &slr->vs_openfd.soft},
250     {"rlimits/openfd.min", &slr->vs_openfd.min},
251
252     {"bcapabilities", NULL},
253     {0,0}
254   };
255
256   sprintf(conf, "%s%s", VSERVERCONF, context);
257
258   slr->vs_cpu = VC_LIM_KEEP;
259   slr->vs_cpuguaranteed = 0;
260
261   slr->vs_rss.hard = VC_LIM_KEEP;
262   slr->vs_rss.soft = VC_LIM_KEEP;
263   slr->vs_rss.min = VC_LIM_KEEP;
264
265   slr->vs_as.hard = VC_LIM_KEEP;
266   slr->vs_as.soft = VC_LIM_KEEP;
267   slr->vs_as.min = VC_LIM_KEEP;
268
269
270   slr->vs_nproc.hard = VC_LIM_KEEP;
271   slr->vs_nproc.soft = VC_LIM_KEEP;
272   slr->vs_nproc.min = VC_LIM_KEEP;
273
274   slr->vs_openfd.hard = VC_LIM_KEEP;
275   slr->vs_openfd.soft = VC_LIM_KEEP;
276   slr->vs_openfd.min = VC_LIM_KEEP;
277
278   slr->vs_capabilities.bcaps = 0;
279   slr->vs_capabilities.bmask = 0;
280   slr->vs_capabilities.ccaps = 0;
281   slr->vs_capabilities.cmask = 0;
282
283   cwd = open(".", O_RDONLY);
284   if (cwd == -1) {
285     perror("cannot get a handle on .");
286     goto out;
287   }
288   if (chdir(conf) == -1) {
289     fprintf(stderr, "cannot chdir to ");
290     perror(conf);
291     goto out_fd;
292   }
293
294   for (r = &sliver_list[0]; r->name; r++) {
295     char buf[1000];
296     fb = fopen(r->name, "r");
297     if (fb == NULL)
298       continue;
299     /* XXX: UGLY. */
300     if (strcmp(r->name, "bcapabilities") == 0) {
301       size_t len, i;
302       struct vc_err_listparser err;
303
304       len = fread(buf, 1, sizeof(buf), fb);
305       for (i = 0; i < len; i++) {
306         if (buf[i] == '\n')
307           buf[i] = ',';
308       }
309       vc_list2bcap(buf, len, &err, &slr->vs_capabilities);
310     }
311     else
312       if (fgets(buf, sizeof(buf), fb) != NULL && isdigit(*buf))
313         *r->limit = atoi(buf);
314     fclose(fb);
315   }
316
317   fchdir(cwd);
318 out_fd:
319   close(cwd);
320 out:
321   free(conf);
322 }
323
324 int
325 adjust_lim(struct vc_rlimit *vcr, struct rlimit *lim)
326 {
327   int adjusted = 0;
328   if (vcr->min != VC_LIM_KEEP) {
329     if (vcr->min > lim->rlim_cur) {
330       lim->rlim_cur = vcr->min;
331       adjusted = 1;
332     }
333     if (vcr->min > lim->rlim_max) {
334       lim->rlim_max = vcr->min;
335       adjusted = 1;
336     }
337   }
338
339   if (vcr->soft != VC_LIM_KEEP) {
340     switch (vcr->min != VC_LIM_KEEP) {
341     case 1:
342       if (vcr->soft < vcr->min)
343         break;
344     case 0:
345         lim->rlim_cur = vcr->soft;
346         adjusted = 1;
347     }
348   }
349
350   if (vcr->hard != VC_LIM_KEEP) {
351     switch (vcr->min != VC_LIM_KEEP) {
352     case 1:
353       if (vcr->hard < vcr->min)
354         break;
355     case 0:
356         lim->rlim_cur = vcr->hard;
357         adjusted = 1;
358     }
359   }
360   return adjusted;
361 }
362
363 void
364 pl_set_limits(xid_t ctx, struct sliver_resources *slr)
365 {
366   struct rlimit lim; /* getrlimit values */
367   unsigned long long vs_cpu;
368   uint32_t cpu_sched_flags;
369
370   if (slr != 0) {
371     /* set memory limits */
372     getrlimit(RLIMIT_RSS,&lim);
373     if (adjust_lim(&slr->vs_rss, &lim)) {
374       setrlimit(RLIMIT_RSS, &lim);
375       if (vc_set_rlimit(ctx, RLIMIT_RSS, &slr->vs_rss))
376         {
377           PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
378           exit(1);
379         }
380     }
381
382     /* set address space limits */
383     getrlimit(RLIMIT_AS,&lim);
384     if (adjust_lim(&slr->vs_as, &lim)) {
385       setrlimit(RLIMIT_AS, &lim);
386       if (vc_set_rlimit(ctx, RLIMIT_AS, &slr->vs_as))
387         {
388           PERROR("pl_setrlimit(%u, RLIMIT_AS)", ctx);
389           exit(1);
390         }
391     }
392     /* set nrpoc limit */
393     getrlimit(RLIMIT_NPROC,&lim);
394     if (adjust_lim(&slr->vs_nproc, &lim)) {
395       setrlimit(RLIMIT_NPROC, &lim);
396       if (vc_set_rlimit(ctx, RLIMIT_NPROC, &slr->vs_nproc))
397         {
398           PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
399           exit(1);
400         }
401     }
402
403     /* set openfd limit */
404     getrlimit(RLIMIT_NOFILE,&lim);
405     if (adjust_lim(&slr->vs_openfd, &lim)) {
406       setrlimit(RLIMIT_NOFILE, &lim);
407       if (vc_set_rlimit(ctx, RLIMIT_NOFILE, &slr->vs_openfd))
408         {
409           PERROR("pl_setrlimit(%u, RLIMIT_NOFILE)", ctx);
410           exit(1);
411         }
412       if (vc_set_rlimit(ctx, VC_VLIMIT_OPENFD, &slr->vs_openfd))
413         {
414           PERROR("pl_setrlimit(%u, VLIMIT_OPENFD)", ctx);
415           exit(1);
416         }
417     }
418     vs_cpu = slr->vs_cpu;    
419     cpu_sched_flags = slr->vs_cpuguaranteed & VS_SCHED_CPU_GUARANTEED;
420
421     slr->vs_capabilities.bmask = vc_get_insecurebcaps();
422     if (vc_set_ccaps(ctx, &slr->vs_capabilities) < 0) {
423       PERROR("pl_setcaps(%u)", ctx);
424       exit(1);
425     }
426   } else {
427     vs_cpu = 1;
428     cpu_sched_flags = 0;
429   }
430   
431   if (pl_setsched(ctx, vs_cpu, cpu_sched_flags) < 0) {
432     PERROR("pl_setsched(%u)", ctx);
433     exit(1);
434   }
435 }