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