Need to set the tagging too so disk limits are enforceable.
[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 static int
52 create_context(xid_t ctx, uint64_t bcaps)
53 {
54   struct vc_ctx_caps  vc_caps;
55   struct vc_net_flags  vc_nf;
56
57   /* Create network context */
58   if (vc_net_create(ctx) == VC_NOCTX) {
59     if (errno == EEXIST)
60       goto tag;
61     return -1;
62   }
63
64   /* Make the network context persistent */
65   vc_nf.mask = vc_nf.flagword = VC_NXF_PERSISTENT;
66   if (vc_set_nflags(ctx, &vc_nf))
67     return -1;
68
69 tag:
70   /* Create tag context */
71   if (vc_tag_create(ctx) == VC_NOCTX)
72     return -1;
73
74 process:
75   /*
76    * Create context info - this sets the STATE_SETUP and STATE_INIT flags.
77    */
78   if (vc_ctx_create(ctx, 0) == 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   if (pl_setsched(ctx, 1, 0) < 0) {
90     PERROR("pl_setsched(%u)", ctx);
91     exit(1);
92   }
93
94   return 0;
95 }
96
97 int
98 pl_setup_done(xid_t ctx)
99 {
100   struct vc_ctx_flags  vc_flags;
101
102   /* unset SETUP flag - this allows other processes to migrate */
103   /* set the PERSISTENT flag - so the context doesn't vanish */
104   /* Don't clear the STATE_INIT flag, as that would make us the init task. */
105   vc_flags.mask = VC_VXF_STATE_SETUP|VC_VXF_PERSISTENT;
106   vc_flags.flagword = VC_VXF_PERSISTENT;
107   if (vc_set_cflags(ctx, &vc_flags))
108     return -1;
109
110   return 0;
111 }
112
113 #define RETRY_LIMIT  10
114
115 int
116 pl_chcontext(xid_t ctx, uint64_t bcaps, const struct sliver_resources *slr)
117 {
118   int  retry_count = 0;
119   int  net_migrated = 0;
120
121   pl_set_ulimits(slr);
122
123   for (;;)
124     {
125       struct vc_ctx_flags  vc_flags;
126
127       if (vc_get_cflags(ctx, &vc_flags))
128         {
129           if (errno != ESRCH)
130             return -1;
131
132           /* context doesn't exist - create it */
133           if (create_context(ctx, bcaps))
134             {
135               if (errno == EEXIST)
136                 /* another process beat us in a race */
137                 goto migrate;
138               if (errno == EBUSY)
139                 /* another process is creating - poll the SETUP flag */
140                 continue;
141               return -1;
142             }
143
144           /* created context and migrated to it i.e., we're done */
145           return 1;
146         }
147
148       /* check the SETUP flag */
149       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
150         {
151           /* context is still being setup - wait a while then retry */
152           if (retry_count++ >= RETRY_LIMIT)
153             {
154               errno = EBUSY;
155               return -1;
156             }
157           sleep(1);
158           continue;
159         }
160
161       /* context has been setup */
162     migrate:
163       if (net_migrated || !vc_net_migrate(ctx))
164         {
165           if (!vc_tag_migrate(ctx) && !vc_ctx_migrate(ctx, 0))
166             break;  /* done */
167           net_migrated = 1;
168         }
169
170       /* context disappeared - retry */
171     }
172
173   return 0;
174 }
175
176 /* it's okay for a syscall to fail because the context doesn't exist */
177 #define VC_SYSCALL(x)                           \
178 do                                              \
179 {                                               \
180   if (x)                                        \
181     return errno == ESRCH ? 0 : -1;             \
182 }                                               \
183 while (0)
184
185 int
186 pl_setsched(xid_t ctx, uint32_t cpu_share, uint32_t cpu_sched_flags)
187 {
188   struct vc_set_sched  vc_sched;
189   struct vc_ctx_flags  vc_flags;
190   uint32_t  new_flags;
191
192   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
193                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX | VC_VXSM_MSEC |
194                        VC_VXSM_FILL_RATE2 | VC_VXSM_INTERVAL2 | VC_VXSM_FORCE |
195                        VC_VXSM_IDLE_TIME);
196   vc_sched.fill_rate = 0;
197   vc_sched.fill_rate2 = cpu_share;  /* tokens accumulated per interval */
198   vc_sched.interval = vc_sched.interval2 = 1000;  /* milliseconds */
199   vc_sched.tokens = 100;     /* initial allocation of tokens */
200   vc_sched.tokens_min = 50;  /* need this many tokens to run */
201   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
202
203   if (cpu_share == (uint32_t)VC_LIM_KEEP)
204     vc_sched.set_mask &= ~(VC_VXSM_FILL_RATE|VC_VXSM_FILL_RATE2);
205
206   /* guaranteed CPU corresponds to SCHED_SHARE flag being cleared */
207   if (cpu_sched_flags & VS_SCHED_CPU_GUARANTEED) {
208     new_flags = 0;
209     vc_sched.fill_rate = vc_sched.fill_rate2;
210   }
211   else
212     new_flags = VC_VXF_SCHED_SHARE;
213
214   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
215
216   vc_flags.mask = VC_VXF_SCHED_FLAGS;
217   vc_flags.flagword = new_flags | VC_VXF_SCHED_HARD;
218   VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
219
220   return 0;
221 }
222
223 struct pl_resources {
224         char *name;
225         unsigned long long *limit;
226 };
227
228 #define WHITESPACE(buffer,index,len)     \
229   while(isspace((int)buffer[index])) \
230         if (index < len) index++; else goto out;
231
232 #define VSERVERCONF "/etc/vservers/"
233 void
234 pl_get_limits(const char *context, struct sliver_resources *slr)
235 {
236   FILE *fb;
237   int cwd;
238   size_t len = strlen(VSERVERCONF) + strlen(context) + NULLBYTE_SIZE;
239   char *conf = (char *)malloc(len + strlen("rlimits/openfd.hard"));
240   struct pl_resources *r;
241   struct pl_resources sliver_list[] = {
242     {"sched/fill-rate2", &slr->vs_cpu},
243
244     {"rlimits/nproc.hard", &slr->vs_nproc.hard},
245     {"rlimits/nproc.soft", &slr->vs_nproc.soft},
246     {"rlimits/nproc.min", &slr->vs_nproc.min},
247   
248     {"rlimits/rss.hard", &slr->vs_rss.hard},
249     {"rlimits/rss.soft", &slr->vs_rss.soft},
250     {"rlimits/rss.min", &slr->vs_rss.min},
251   
252     {"rlimits/as.hard", &slr->vs_as.hard},
253     {"rlimits/as.soft", &slr->vs_as.soft},
254     {"rlimits/as.min", &slr->vs_as.min},
255   
256     {"rlimits/openfd.hard", &slr->vs_openfd.hard},
257     {"rlimits/openfd.soft", &slr->vs_openfd.soft},
258     {"rlimits/openfd.min", &slr->vs_openfd.min},
259
260     {0,0}
261   };
262
263   sprintf(conf, "%s%s", VSERVERCONF, context);
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   slr->vs_nproc.hard = VC_LIM_KEEP;
274   slr->vs_nproc.soft = VC_LIM_KEEP;
275   slr->vs_nproc.min = VC_LIM_KEEP;
276
277   slr->vs_openfd.hard = VC_LIM_KEEP;
278   slr->vs_openfd.soft = VC_LIM_KEEP;
279   slr->vs_openfd.min = VC_LIM_KEEP;
280
281   cwd = open(".", O_RDONLY);
282   if (cwd == -1) {
283     perror("cannot get a handle on .");
284     goto out;
285   }
286   if (chdir(conf) == -1) {
287     fprintf(stderr, "cannot chdir to ");
288     perror(conf);
289     goto out_fd;
290   }
291
292   for (r = &sliver_list[0]; r->name; r++) {
293     char buf[1000];
294     fb = fopen(r->name, "r");
295     if (fb == NULL)
296       continue;
297     if (fgets(buf, sizeof(buf), fb) != NULL && isdigit(*buf))
298       *r->limit = atoi(buf);
299     fclose(fb);
300   }
301
302   fchdir(cwd);
303 out_fd:
304   close(cwd);
305 out:
306   free(conf);
307 }
308
309 int
310 adjust_lim(const struct vc_rlimit *vcr, struct rlimit *lim)
311 {
312   int adjusted = 0;
313   if (vcr->min != VC_LIM_KEEP) {
314     if (vcr->min > lim->rlim_cur) {
315       lim->rlim_cur = vcr->min;
316       adjusted = 1;
317     }
318     if (vcr->min > lim->rlim_max) {
319       lim->rlim_max = vcr->min;
320       adjusted = 1;
321     }
322   }
323
324   if (vcr->soft != VC_LIM_KEEP) {
325     switch (vcr->min != VC_LIM_KEEP) {
326     case 1:
327       if (vcr->soft < vcr->min)
328         break;
329     case 0:
330         lim->rlim_cur = vcr->soft;
331         adjusted = 1;
332     }
333   }
334
335   if (vcr->hard != VC_LIM_KEEP) {
336     switch (vcr->min != VC_LIM_KEEP) {
337     case 1:
338       if (vcr->hard < vcr->min)
339         break;
340     case 0:
341         lim->rlim_cur = vcr->hard;
342         adjusted = 1;
343     }
344   }
345   return adjusted;
346 }
347
348 static inline void
349 set_one_ulimit(int resource, const struct vc_rlimit *limit)
350 {
351   struct rlimit lim;
352   getrlimit(resource, &lim);
353   adjust_lim(limit, &lim);
354   setrlimit(resource, &lim);
355 }
356
357 void
358 pl_set_ulimits(const struct sliver_resources *slr)
359 {
360   if (!slr)
361     return;
362
363   set_one_ulimit(RLIMIT_RSS, &slr->vs_rss);
364   set_one_ulimit(RLIMIT_AS, &slr->vs_as);
365   set_one_ulimit(RLIMIT_NPROC, &slr->vs_nproc);
366   set_one_ulimit(RLIMIT_NOFILE, &slr->vs_openfd);
367 }