xref: /haiku/src/system/kernel/vm/VMAddressSpace.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 
11 #include <vm/VMAddressSpace.h>
12 
13 #include <stdlib.h>
14 
15 #include <new>
16 
17 #include <KernelExport.h>
18 
19 #include <util/OpenHashTable.h>
20 
21 #include <heap.h>
22 #include <thread.h>
23 #include <vm/vm.h>
24 #include <vm/VMArea.h>
25 #include <vm/VMCache.h>
26 
27 #include "VMKernelAddressSpace.h"
28 #include "VMUserAddressSpace.h"
29 
30 
31 //#define TRACE_VM
32 #ifdef TRACE_VM
33 #	define TRACE(x) dprintf x
34 #else
35 #	define TRACE(x) ;
36 #endif
37 
38 
39 #define ASPACE_HASH_TABLE_SIZE 1024
40 
41 
42 // #pragma mark - AddressSpaceHashDefinition
43 
44 
45 namespace {
46 
47 struct AddressSpaceHashDefinition {
48 	typedef team_id			KeyType;
49 	typedef VMAddressSpace	ValueType;
50 
51 	size_t HashKey(team_id key) const
52 	{
53 		return key;
54 	}
55 
56 	size_t Hash(const VMAddressSpace* value) const
57 	{
58 		return HashKey(value->ID());
59 	}
60 
61 	bool Compare(team_id key, const VMAddressSpace* value) const
62 	{
63 		return value->ID() == key;
64 	}
65 
66 	VMAddressSpace*& GetLink(VMAddressSpace* value) const
67 	{
68 		return value->HashTableLink();
69 	}
70 };
71 
72 typedef BOpenHashTable<AddressSpaceHashDefinition> AddressSpaceTable;
73 
74 } // namespace
75 
76 
77 static AddressSpaceTable	sAddressSpaceTable;
78 static rw_lock				sAddressSpaceTableLock;
79 
80 VMAddressSpace* VMAddressSpace::sKernelAddressSpace;
81 
82 
83 // #pragma mark - VMAddressSpace
84 
85 
86 VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
87 	const char* name)
88 	:
89 	fBase(base),
90 	fEndAddress(base + (size - 1)),
91 	fFreeSpace(size),
92 	fID(id),
93 	fRefCount(1),
94 	fFaultCount(0),
95 	fChangeCount(0),
96 	fTranslationMap(NULL),
97 	fRandomizingEnabled(true),
98 	fDeleting(false)
99 {
100 	rw_lock_init(&fLock, name);
101 //	rw_lock_init(&fLock, kernel ? "kernel address space" : "address space");
102 }
103 
104 
105 VMAddressSpace::~VMAddressSpace()
106 {
107 	TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n",
108 		ID()));
109 
110 	WriteLock();
111 
112 	delete fTranslationMap;
113 
114 	rw_lock_destroy(&fLock);
115 }
116 
117 
118 /*static*/ status_t
119 VMAddressSpace::Init()
120 {
121 	rw_lock_init(&sAddressSpaceTableLock, "address spaces table");
122 
123 	// create the area and address space hash tables
124 	{
125 		new(&sAddressSpaceTable) AddressSpaceTable;
126 		status_t error = sAddressSpaceTable.Init(ASPACE_HASH_TABLE_SIZE);
127 		if (error != B_OK)
128 			panic("vm_init: error creating aspace hash table\n");
129 	}
130 
131 	// create the initial kernel address space
132 	if (Create(B_SYSTEM_TEAM, KERNEL_BASE, KERNEL_SIZE, true,
133 			&sKernelAddressSpace) != B_OK) {
134 		panic("vm_init: error creating kernel address space!\n");
135 	}
136 
137 	add_debugger_command("aspaces", &_DumpListCommand,
138 		"Dump a list of all address spaces");
139 	add_debugger_command("aspace", &_DumpCommand,
140 		"Dump info about a particular address space");
141 
142 	return B_OK;
143 }
144 
145 
146 /*! Deletes all areas in the specified address space, and the address
147 	space by decreasing all reference counters. It also marks the
148 	address space of being in deletion state, so that no more areas
149 	can be created in it.
150 	After this, the address space is not operational anymore, but might
151 	still be in memory until the last reference has been released.
152 */
153 void
154 VMAddressSpace::RemoveAndPut()
155 {
156 	WriteLock();
157 	fDeleting = true;
158 	WriteUnlock();
159 
160 	vm_delete_areas(this, true);
161 	Put();
162 }
163 
164 
165 status_t
166 VMAddressSpace::InitObject()
167 {
168 	return B_OK;
169 }
170 
171 
172 void
173 VMAddressSpace::Dump() const
174 {
175 	kprintf("dump of address space at %p:\n", this);
176 	kprintf("id: %" B_PRId32 "\n", fID);
177 	kprintf("ref_count: %" B_PRId32 "\n", fRefCount);
178 	kprintf("fault_count: %" B_PRId32 "\n", fFaultCount);
179 	kprintf("translation_map: %p\n", fTranslationMap);
180 	kprintf("base: %#" B_PRIxADDR "\n", fBase);
181 	kprintf("end: %#" B_PRIxADDR "\n", fEndAddress);
182 	kprintf("change_count: %" B_PRId32 "\n", fChangeCount);
183 }
184 
185 
186 /*static*/ status_t
187 VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
188 	VMAddressSpace** _addressSpace)
189 {
190 	VMAddressSpace* addressSpace = kernel
191 		? (VMAddressSpace*)new(std::nothrow) VMKernelAddressSpace(teamID, base,
192 			size)
193 		: (VMAddressSpace*)new(std::nothrow) VMUserAddressSpace(teamID, base,
194 			size);
195 	if (addressSpace == NULL)
196 		return B_NO_MEMORY;
197 
198 	status_t status = addressSpace->InitObject();
199 	if (status != B_OK) {
200 		delete addressSpace;
201 		return status;
202 	}
203 
204 	TRACE(("VMAddressSpace::Create(): team %" B_PRId32 " (%skernel): %#lx "
205 		"bytes starting at %#lx => %p\n", teamID, kernel ? "" : "!", size,
206 		base, addressSpace));
207 
208 	// create the corresponding translation map
209 	status = arch_vm_translation_map_create_map(kernel,
210 		&addressSpace->fTranslationMap);
211 	if (status != B_OK) {
212 		delete addressSpace;
213 		return status;
214 	}
215 
216 	// add the aspace to the global hash table
217 	rw_lock_write_lock(&sAddressSpaceTableLock);
218 	sAddressSpaceTable.InsertUnchecked(addressSpace);
219 	rw_lock_write_unlock(&sAddressSpaceTableLock);
220 
221 	*_addressSpace = addressSpace;
222 	return B_OK;
223 }
224 
225 
226 /*static*/ VMAddressSpace*
227 VMAddressSpace::GetKernel()
228 {
229 	// we can treat this one a little differently since it can't be deleted
230 	sKernelAddressSpace->Get();
231 	return sKernelAddressSpace;
232 }
233 
234 
235 /*static*/ team_id
236 VMAddressSpace::CurrentID()
237 {
238 	Thread* thread = thread_get_current_thread();
239 
240 	if (thread != NULL && thread->team->address_space != NULL)
241 		return thread->team->id;
242 
243 	return B_ERROR;
244 }
245 
246 
247 /*static*/ VMAddressSpace*
248 VMAddressSpace::GetCurrent()
249 {
250 	Thread* thread = thread_get_current_thread();
251 
252 	if (thread != NULL) {
253 		VMAddressSpace* addressSpace = thread->team->address_space;
254 		if (addressSpace != NULL) {
255 			addressSpace->Get();
256 			return addressSpace;
257 		}
258 	}
259 
260 	return NULL;
261 }
262 
263 
264 /*static*/ VMAddressSpace*
265 VMAddressSpace::Get(team_id teamID)
266 {
267 	rw_lock_read_lock(&sAddressSpaceTableLock);
268 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID);
269 	if (addressSpace)
270 		addressSpace->Get();
271 	rw_lock_read_unlock(&sAddressSpaceTableLock);
272 
273 	return addressSpace;
274 }
275 
276 
277 /*static*/ VMAddressSpace*
278 VMAddressSpace::DebugFirst()
279 {
280 	return sAddressSpaceTable.GetIterator().Next();
281 }
282 
283 
284 /*static*/ VMAddressSpace*
285 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace)
286 {
287 	if (addressSpace == NULL)
288 		return NULL;
289 
290 	AddressSpaceTable::Iterator it
291 		= sAddressSpaceTable.GetIterator(addressSpace->ID());
292 	it.Next();
293 	return it.Next();
294 }
295 
296 
297 /*static*/ VMAddressSpace*
298 VMAddressSpace::DebugGet(team_id teamID)
299 {
300 	return sAddressSpaceTable.Lookup(teamID);
301 }
302 
303 
304 /*static*/ void
305 VMAddressSpace::_DeleteIfUnreferenced(team_id id)
306 {
307 	rw_lock_write_lock(&sAddressSpaceTableLock);
308 
309 	bool remove = false;
310 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(id);
311 	if (addressSpace != NULL && addressSpace->fRefCount == 0) {
312 		sAddressSpaceTable.RemoveUnchecked(addressSpace);
313 		remove = true;
314 	}
315 
316 	rw_lock_write_unlock(&sAddressSpaceTableLock);
317 
318 	if (remove)
319 		delete addressSpace;
320 }
321 
322 
323 /*static*/ int
324 VMAddressSpace::_DumpCommand(int argc, char** argv)
325 {
326 	VMAddressSpace* aspace;
327 
328 	if (argc < 2) {
329 		kprintf("aspace: not enough arguments\n");
330 		return 0;
331 	}
332 
333 	// if the argument looks like a number, treat it as such
334 
335 	{
336 		team_id id = strtoul(argv[1], NULL, 0);
337 
338 		aspace = sAddressSpaceTable.Lookup(id);
339 		if (aspace == NULL) {
340 			kprintf("invalid aspace id\n");
341 		} else {
342 			aspace->Dump();
343 		}
344 	}
345 
346 	return 0;
347 }
348 
349 
350 /*static*/ int
351 VMAddressSpace::_DumpListCommand(int argc, char** argv)
352 {
353 	kprintf("  %*s      id     %*s     %*s   area count    area size\n",
354 		B_PRINTF_POINTER_WIDTH, "address", B_PRINTF_POINTER_WIDTH, "base",
355 		B_PRINTF_POINTER_WIDTH, "end");
356 
357 	AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator();
358 	while (VMAddressSpace* space = it.Next()) {
359 		int32 areaCount = 0;
360 		off_t areaSize = 0;
361 		for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
362 				VMArea* area = areaIt.Next();) {
363 			areaCount++;
364 			areaSize += area->Size();
365 		}
366 		kprintf("%p  %6" B_PRId32 "   %#010" B_PRIxADDR "   %#10" B_PRIxADDR
367 			"   %10" B_PRId32 "   %10" B_PRIdOFF "\n", space, space->ID(),
368 			space->Base(), space->EndAddress(), areaCount, areaSize);
369 	}
370 
371 	return 0;
372 }
373