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