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