summaryrefslogtreecommitdiff
path: root/minix/servers/vm/rs.c
blob: 9801a39e7eb696bcd09fe128274457e287e00d96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

#define _SYSTEM 1

#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/ds.h>
#include <minix/endpoint.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <minix/rs.h>

#include <sys/mman.h>

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#include "glo.h"
#include "proto.h"
#include "util.h"
#include "region.h"

/*===========================================================================*
 *				do_rs_set_priv				     *
 *===========================================================================*/
int do_rs_set_priv(message *m)
{
	int r, n, nr;
	struct vmproc *vmp;
	bitchunk_t call_mask[VM_CALL_MASK_SIZE], *call_mask_p;

	nr = m->VM_RS_NR;

	if ((r = vm_isokendpt(nr, &n)) != OK) {
		printf("do_rs_set_priv: bad endpoint %d\n", nr);
		return EINVAL;
	}

	vmp = &vmproc[n];

	if (m->VM_RS_BUF) {
		r = sys_datacopy(m->m_source, (vir_bytes) m->VM_RS_BUF, SELF,
			(vir_bytes) call_mask, sizeof(call_mask));
		if (r != OK)
			return r;
		call_mask_p = call_mask;
	} else {
		if (m->VM_RS_SYS) {
			printf("VM: do_rs_set_priv: sys procs don't share!\n");
			return EINVAL;
		}
		call_mask_p = NULL;
	}

	acl_set(vmp, call_mask_p, m->VM_RS_SYS);

	return OK;
}

/*===========================================================================*
 *				do_rs_prepare	     			     *
 *===========================================================================*/
int do_rs_prepare(message *m_ptr)
{
	/* Prepare a new instance of a service for an upcoming live-update
	 * switch, based on the old instance of this service.  This call is
	 * used only by RS and only for a multicomponent live update which
	 * includes VM.  In this case, all processes need to be prepared such
	 * that they don't require the new VM instance to perform actions
	 * during live update that cannot be undone in the case of a rollback.
	 */
	endpoint_t src_e, dst_e;
	int src_p, dst_p;
	struct vmproc *src_vmp, *dst_vmp;
	struct vir_region *src_data_vr, *dst_data_vr;
	vir_bytes src_addr, dst_addr;
	int sys_upd_flags;

	src_e = m_ptr->m_lsys_vm_update.src;
	dst_e = m_ptr->m_lsys_vm_update.dst;
        sys_upd_flags = m_ptr->m_lsys_vm_update.flags;

	/* Lookup slots for source and destination process. */
	if(vm_isokendpt(src_e, &src_p) != OK) {
		printf("VM: do_rs_prepare: bad src endpoint %d\n", src_e);
		return EINVAL;
	}
	src_vmp = &vmproc[src_p];
	if(vm_isokendpt(dst_e, &dst_p) != OK) {
		printf("VM: do_rs_prepare: bad dst endpoint %d\n", dst_e);
		return EINVAL;
	}
	dst_vmp = &vmproc[dst_p];

	/* Pin memory for the source process. */
	map_pin_memory(src_vmp);

	/* See if the source process has a larger heap than the destination
	 * process.  If so, extend the heap of the destination process to
	 * match the source's.  While this may end up wasting quite some
	 * memory, it is absolutely essential that the destination process
	 * does not run out of heap memory during the live update window,
	 * and since most processes will be doing an identity transfer, they
	 * are likely to require as much heap as their previous instances.
	 * Better safe than sorry.  TODO: prevent wasting memory somehow;
	 * this seems particularly relevant for RS.
	 */
	src_data_vr = region_search(&src_vmp->vm_regions_avl, VM_MMAPBASE,
	    AVL_LESS);
	assert(src_data_vr);
	dst_data_vr = region_search(&dst_vmp->vm_regions_avl, VM_MMAPBASE,
	    AVL_LESS);
	assert(dst_data_vr);

	src_addr = src_data_vr->vaddr + src_data_vr->length;
	dst_addr = dst_data_vr->vaddr + dst_data_vr->length;
	if (src_addr > dst_addr)
		real_brk(dst_vmp, src_addr);

	/* Now also pin memory for the destination process. */
	map_pin_memory(dst_vmp);

	/* Finally, map the source process's memory-mapped regions into the
	 * destination process.  This needs to happen now, because VM may not
	 * allocate any objects during the live update window, since this
	 * would prevent successful rollback of VM afterwards.  The
	 * destination may not actually touch these regions during the live
	 * update window either, because they are mapped copy-on-write and a
	 * pagefault would also cause object allocation.  Objects are pages,
	 * slab objects, anything in the new VM instance to which changes are
	 * visible in the old VM basically.
	 */
	if (!(sys_upd_flags & SF_VM_NOMMAP))
		map_proc_dyn_data(src_vmp, dst_vmp);

	return OK;
}

/*===========================================================================*
 *				do_rs_update	     			     *
 *===========================================================================*/
int do_rs_update(message *m_ptr)
{
	endpoint_t src_e, dst_e, reply_e;
	int src_p, dst_p;
	struct vmproc *src_vmp, *dst_vmp;
	int r, sys_upd_flags;

	src_e = m_ptr->m_lsys_vm_update.src;
	dst_e = m_ptr->m_lsys_vm_update.dst;
        sys_upd_flags = m_ptr->m_lsys_vm_update.flags;
        reply_e = m_ptr->m_source;

	/* Lookup slots for source and destination process. */
	if(vm_isokendpt(src_e, &src_p) != OK) {
		printf("do_rs_update: bad src endpoint %d\n", src_e);
		return EINVAL;
	}
	src_vmp = &vmproc[src_p];
	if(vm_isokendpt(dst_e, &dst_p) != OK) {
		printf("do_rs_update: bad dst endpoint %d\n", dst_e);
		return EINVAL;
	}
	dst_vmp = &vmproc[dst_p];

	/* Check flags. */
	if((sys_upd_flags & (SF_VM_ROLLBACK|SF_VM_NOMMAP)) == 0) {
	        /* Can't preallocate when transfering mmapped regions. */
	        if(map_region_lookup_type(dst_vmp, VR_PREALLOC_MAP)) {
			return ENOSYS;
	        }
	}

	/* Let the kernel do the update first. */
	r = sys_update(src_e, dst_e,
	    sys_upd_flags & SF_VM_ROLLBACK ? SYS_UPD_ROLLBACK : 0);
	if(r != OK) {
		return r;
	}

	/* Do the update in VM now. */
	r = swap_proc_slot(src_vmp, dst_vmp);
	if(r != OK) {
		return r;
	}
	r = swap_proc_dyn_data(src_vmp, dst_vmp, sys_upd_flags);
	if(r != OK) {
		return r;
	}
	pt_bind(&src_vmp->vm_pt, src_vmp);
	pt_bind(&dst_vmp->vm_pt, dst_vmp);

	/* Reply in case of external request, update-aware. */
	if(reply_e != VM_PROC_NR) {
            if(reply_e == src_e) reply_e = dst_e;
            else if(reply_e == dst_e) reply_e = src_e;
            m_ptr->m_type = OK;
            r = ipc_send(reply_e, m_ptr);
            if(r != OK) {
                    panic("ipc_send() error");
            }
	}

	return SUSPEND;
}

/*===========================================================================*
 *		           rs_memctl_make_vm_instance			     *
 *===========================================================================*/
static int rs_memctl_make_vm_instance(struct vmproc *new_vm_vmp)
{
	int r;
	u32_t flags;
	int verify;
	struct vmproc *this_vm_vmp;

	this_vm_vmp = &vmproc[VM_PROC_NR];

	pt_assert(&this_vm_vmp->vm_pt);

	/* Check if the operation is allowed. */
	assert(num_vm_instances == 1 || num_vm_instances == 2);
	if(num_vm_instances == 2) {
		printf("VM can currently support no more than 2 VM instances at the time.");
		return EPERM;
	}

	/* Copy settings from current VM. */
	new_vm_vmp->vm_flags |= VMF_VM_INSTANCE;
	num_vm_instances++;

	/* Pin memory for the new VM instance. */
	r = map_pin_memory(new_vm_vmp);
	if(r != OK) {
		return r;
	}

	/* Preallocate page tables for the entire address space for both
	 * VM and the new VM instance.
	 */
	flags = 0;
	verify = FALSE;
	r = pt_ptalloc_in_range(&this_vm_vmp->vm_pt,
		VM_OWN_HEAPBASE, VM_DATATOP, flags, verify);
	if(r != OK) {
		return r;
	}
	r = pt_ptalloc_in_range(&new_vm_vmp->vm_pt,
		VM_OWN_HEAPBASE, VM_DATATOP, flags, verify);
	if(r != OK) {
		return r;
	}

	/* Let the new VM instance map VM's page tables and its own. */
	r = pt_ptmap(this_vm_vmp, new_vm_vmp);
	if(r != OK) {
		return r;
	}
	r = pt_ptmap(new_vm_vmp, new_vm_vmp);
	if(r != OK) {
		return r;
	}

	pt_assert(&this_vm_vmp->vm_pt);
	pt_assert(&new_vm_vmp->vm_pt);

	return OK;
}

/*===========================================================================*
 *		           rs_memctl_heap_prealloc			     *
 *===========================================================================*/
static int rs_memctl_heap_prealloc(struct vmproc *vmp,
	vir_bytes *addr, size_t *len)
{
	struct vir_region *data_vr;
	vir_bytes bytes;

	if(*len <= 0) {
		return EINVAL;
	}
	data_vr = region_search(&vmp->vm_regions_avl, VM_MMAPBASE, AVL_LESS);
	*addr = data_vr->vaddr + data_vr->length;
	bytes = *addr + *len;

	return real_brk(vmp, bytes);
}

/*===========================================================================*
 *		           rs_memctl_map_prealloc			     *
 *===========================================================================*/
static int rs_memctl_map_prealloc(struct vmproc *vmp,
	vir_bytes *addr, size_t *len)
{
	struct vir_region *vr;
	vir_bytes base, top;
	int is_vm;

	if(*len <= 0) {
		return EINVAL;
	}
	*len = CLICK_CEIL(*len);

	is_vm = (vmp->vm_endpoint == VM_PROC_NR);
	base = is_vm ? VM_OWN_MMAPBASE : VM_MMAPBASE;
	top = is_vm ? VM_OWN_MMAPTOP : VM_MMAPTOP;

	if (!(vr = map_page_region(vmp, base, top, *len,
	    VR_ANON|VR_WRITABLE|VR_UNINITIALIZED, MF_PREALLOC,
	    &mem_type_anon))) {
		return ENOMEM;
	}
	vr->flags |= VR_PREALLOC_MAP;
	*addr = vr->vaddr;
	return OK;
}

/*===========================================================================*
 *		         rs_memctl_get_prealloc_map			     *
 *===========================================================================*/
static int rs_memctl_get_prealloc_map(struct vmproc *vmp,
	vir_bytes *addr, size_t *len)
{
	struct vir_region *vr;

	vr = map_region_lookup_type(vmp, VR_PREALLOC_MAP);
	if(!vr) {
		*addr = 0;
		*len = 0;
	}
	else {
		*addr = vr->vaddr;
		*len = vr->length;
	}
	return OK;
}

/*===========================================================================*
 *				do_rs_memctl	     			     *
 *===========================================================================*/
int do_rs_memctl(message *m_ptr)
{
	endpoint_t ep;
	int req, r, proc_nr;
	struct vmproc *vmp;

	ep = m_ptr->VM_RS_CTL_ENDPT;
	req = m_ptr->VM_RS_CTL_REQ;

	/* Lookup endpoint. */
	if ((r = vm_isokendpt(ep, &proc_nr)) != OK) {
		printf("do_rs_memctl: bad endpoint %d\n", ep);
		return EINVAL;
	}
	vmp = &vmproc[proc_nr];

	/* Process request. */
	switch(req)
	{
	case VM_RS_MEM_PIN:
		/* Only actually pin RS memory if VM can recover from crashes (saves memory). */
		if (num_vm_instances <= 1)
			return OK;
		r = map_pin_memory(vmp);
		return r;
	case VM_RS_MEM_MAKE_VM:
		r = rs_memctl_make_vm_instance(vmp);
		return r;
	case VM_RS_MEM_HEAP_PREALLOC:
		r = rs_memctl_heap_prealloc(vmp, (vir_bytes*) &m_ptr->VM_RS_CTL_ADDR, (size_t*) &m_ptr->VM_RS_CTL_LEN);
		return r;
	case VM_RS_MEM_MAP_PREALLOC:
		r = rs_memctl_map_prealloc(vmp, (vir_bytes*) &m_ptr->VM_RS_CTL_ADDR, (size_t*) &m_ptr->VM_RS_CTL_LEN);
		return r;
	case VM_RS_MEM_GET_PREALLOC_MAP:
		r = rs_memctl_get_prealloc_map(vmp, (vir_bytes*) &m_ptr->VM_RS_CTL_ADDR, (size_t*) &m_ptr->VM_RS_CTL_LEN);
		return r;
	default:
		printf("do_rs_memctl: bad request %d\n", req);
		return EINVAL;
	}
}