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