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