xref: /haiku/src/system/kernel/vm/VMAddressSpace.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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 }
102 
103 
104 VMAddressSpace::~VMAddressSpace()
105 {
106 	TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n",
107 		ID()));
108 
109 	WriteLock();
110 
111 	delete fTranslationMap;
112 
113 	rw_lock_destroy(&fLock);
114 }
115 
116 
117 /*static*/ status_t
118 VMAddressSpace::Init()
119 {
120 	rw_lock_init(&sAddressSpaceTableLock, "address spaces table");
121 
122 	// create the area and address space hash tables
123 	{
124 		new(&sAddressSpaceTable) AddressSpaceTable;
125 		status_t error = sAddressSpaceTable.Init(ASPACE_HASH_TABLE_SIZE);
126 		if (error != B_OK)
127 			panic("vm_init: error creating aspace hash table\n");
128 	}
129 
130 	// create the initial kernel address space
131 	if (Create(B_SYSTEM_TEAM, KERNEL_BASE, KERNEL_SIZE, true,
132 			&sKernelAddressSpace) != B_OK) {
133 		panic("vm_init: error creating kernel address space!\n");
134 	}
135 
136 	add_debugger_command("aspaces", &_DumpListCommand,
137 		"Dump a list of all address spaces");
138 	add_debugger_command("aspace", &_DumpCommand,
139 		"Dump info about a particular address space");
140 
141 	return B_OK;
142 }
143 
144 
145 /*! Deletes all areas in the specified address space, and the address
146 	space by decreasing all reference counters. It also marks the
147 	address space of being in deletion state, so that no more areas
148 	can be created in it.
149 	After this, the address space is not operational anymore, but might
150 	still be in memory until the last reference has been released.
151 */
152 void
153 VMAddressSpace::RemoveAndPut()
154 {
155 	WriteLock();
156 	fDeleting = true;
157 	WriteUnlock();
158 
159 	vm_delete_areas(this, true);
160 	Put();
161 }
162 
163 
164 status_t
165 VMAddressSpace::InitObject()
166 {
167 	return B_OK;
168 }
169 
170 
171 void
172 VMAddressSpace::Dump() const
173 {
174 	kprintf("dump of address space at %p:\n", this);
175 	kprintf("id: %" B_PRId32 "\n", fID);
176 	kprintf("ref_count: %" B_PRId32 "\n", fRefCount);
177 	kprintf("fault_count: %" B_PRId32 "\n", fFaultCount);
178 	kprintf("translation_map: %p\n", fTranslationMap);
179 	kprintf("base: %#" B_PRIxADDR "\n", fBase);
180 	kprintf("end: %#" B_PRIxADDR "\n", fEndAddress);
181 	kprintf("change_count: %" B_PRId32 "\n", fChangeCount);
182 }
183 
184 
185 /*static*/ status_t
186 VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
187 	VMAddressSpace** _addressSpace)
188 {
189 	VMAddressSpace* addressSpace = kernel
190 		? (VMAddressSpace*)new(std::nothrow) VMKernelAddressSpace(teamID, base,
191 			size)
192 		: (VMAddressSpace*)new(std::nothrow) VMUserAddressSpace(teamID, base,
193 			size);
194 	if (addressSpace == NULL)
195 		return B_NO_MEMORY;
196 
197 	status_t status = addressSpace->InitObject();
198 	if (status != B_OK) {
199 		delete addressSpace;
200 		return status;
201 	}
202 
203 	TRACE(("VMAddressSpace::Create(): team %" B_PRId32 " (%skernel): %#lx "
204 		"bytes starting at %#lx => %p\n", teamID, kernel ? "" : "!", size,
205 		base, addressSpace));
206 
207 	// create the corresponding translation map
208 	status = arch_vm_translation_map_create_map(kernel,
209 		&addressSpace->fTranslationMap);
210 	if (status != B_OK) {
211 		delete addressSpace;
212 		return status;
213 	}
214 
215 	// add the aspace to the global hash table
216 	rw_lock_write_lock(&sAddressSpaceTableLock);
217 	sAddressSpaceTable.InsertUnchecked(addressSpace);
218 	rw_lock_write_unlock(&sAddressSpaceTableLock);
219 
220 	*_addressSpace = addressSpace;
221 	return B_OK;
222 }
223 
224 
225 /*static*/ VMAddressSpace*
226 VMAddressSpace::GetKernel()
227 {
228 	// we can treat this one a little differently since it can't be deleted
229 	sKernelAddressSpace->Get();
230 	return sKernelAddressSpace;
231 }
232 
233 
234 /*static*/ team_id
235 VMAddressSpace::CurrentID()
236 {
237 	Thread* thread = thread_get_current_thread();
238 
239 	if (thread != NULL && thread->team->address_space != NULL)
240 		return thread->team->id;
241 
242 	return B_ERROR;
243 }
244 
245 
246 /*static*/ VMAddressSpace*
247 VMAddressSpace::GetCurrent()
248 {
249 	Thread* thread = thread_get_current_thread();
250 
251 	if (thread != NULL) {
252 		VMAddressSpace* addressSpace = thread->team->address_space;
253 		if (addressSpace != NULL) {
254 			addressSpace->Get();
255 			return addressSpace;
256 		}
257 	}
258 
259 	return NULL;
260 }
261 
262 
263 /*static*/ VMAddressSpace*
264 VMAddressSpace::Get(team_id teamID)
265 {
266 	rw_lock_read_lock(&sAddressSpaceTableLock);
267 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID);
268 	if (addressSpace)
269 		addressSpace->Get();
270 	rw_lock_read_unlock(&sAddressSpaceTableLock);
271 
272 	return addressSpace;
273 }
274 
275 
276 /*static*/ VMAddressSpace*
277 VMAddressSpace::DebugFirst()
278 {
279 	return sAddressSpaceTable.GetIterator().Next();
280 }
281 
282 
283 /*static*/ VMAddressSpace*
284 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace)
285 {
286 	if (addressSpace == NULL)
287 		return NULL;
288 
289 	AddressSpaceTable::Iterator it
290 		= sAddressSpaceTable.GetIterator(addressSpace->ID());
291 	it.Next();
292 	return it.Next();
293 }
294 
295 
296 /*static*/ VMAddressSpace*
297 VMAddressSpace::DebugGet(team_id teamID)
298 {
299 	return sAddressSpaceTable.Lookup(teamID);
300 }
301 
302 
303 /*static*/ void
304 VMAddressSpace::_DeleteIfUnreferenced(team_id id)
305 {
306 	rw_lock_write_lock(&sAddressSpaceTableLock);
307 
308 	bool remove = false;
309 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(id);
310 	if (addressSpace != NULL && addressSpace->fRefCount == 0) {
311 		sAddressSpaceTable.RemoveUnchecked(addressSpace);
312 		remove = true;
313 	}
314 
315 	rw_lock_write_unlock(&sAddressSpaceTableLock);
316 
317 	if (remove)
318 		delete addressSpace;
319 }
320 
321 
322 /*static*/ int
323 VMAddressSpace::_DumpCommand(int argc, char** argv)
324 {
325 	VMAddressSpace* aspace;
326 
327 	if (argc < 2) {
328 		kprintf("aspace: not enough arguments\n");
329 		return 0;
330 	}
331 
332 	// if the argument looks like a number, treat it as such
333 
334 	{
335 		team_id id = strtoul(argv[1], NULL, 0);
336 
337 		aspace = sAddressSpaceTable.Lookup(id);
338 		if (aspace == NULL) {
339 			kprintf("invalid aspace id\n");
340 		} else {
341 			aspace->Dump();
342 		}
343 	}
344 
345 	return 0;
346 }
347 
348 
349 /*static*/ int
350 VMAddressSpace::_DumpListCommand(int argc, char** argv)
351 {
352 	kprintf("  %*s      id     %*s     %*s   area count    area size\n",
353 		B_PRINTF_POINTER_WIDTH, "address", B_PRINTF_POINTER_WIDTH, "base",
354 		B_PRINTF_POINTER_WIDTH, "end");
355 
356 	AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator();
357 	while (VMAddressSpace* space = it.Next()) {
358 		int32 areaCount = 0;
359 		off_t areaSize = 0;
360 		for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
361 				VMArea* area = areaIt.Next();) {
362 			areaCount++;
363 			areaSize += area->Size();
364 		}
365 		kprintf("%p  %6" B_PRId32 "   %#010" B_PRIxADDR "   %#10" B_PRIxADDR
366 			"   %10" B_PRId32 "   %10" B_PRIdOFF "\n", space, space->ID(),
367 			space->Base(), space->EndAddress(), areaCount, areaSize);
368 	}
369 
370 	return 0;
371 }
372