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