xref: /haiku/src/system/kernel/vm/VMAddressSpace.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
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 %ld (%skernel): %#lx bytes starting at "
199 		"%#lx => %p\n", teamID, kernel ? "" : "!", size, base, addressSpace));
200 
201 	// create the corresponding translation map
202 	status = arch_vm_translation_map_create_map(kernel,
203 		&addressSpace->fTranslationMap);
204 	if (status != B_OK) {
205 		delete addressSpace;
206 		return status;
207 	}
208 
209 	// add the aspace to the global hash table
210 	rw_lock_write_lock(&sAddressSpaceTableLock);
211 	sAddressSpaceTable.InsertUnchecked(addressSpace);
212 	rw_lock_write_unlock(&sAddressSpaceTableLock);
213 
214 	*_addressSpace = addressSpace;
215 	return B_OK;
216 }
217 
218 
219 /*static*/ VMAddressSpace*
220 VMAddressSpace::GetKernel()
221 {
222 	// we can treat this one a little differently since it can't be deleted
223 	sKernelAddressSpace->Get();
224 	return sKernelAddressSpace;
225 }
226 
227 
228 /*static*/ team_id
229 VMAddressSpace::CurrentID()
230 {
231 	Thread* thread = thread_get_current_thread();
232 
233 	if (thread != NULL && thread->team->address_space != NULL)
234 		return thread->team->id;
235 
236 	return B_ERROR;
237 }
238 
239 
240 /*static*/ VMAddressSpace*
241 VMAddressSpace::GetCurrent()
242 {
243 	Thread* thread = thread_get_current_thread();
244 
245 	if (thread != NULL) {
246 		VMAddressSpace* addressSpace = thread->team->address_space;
247 		if (addressSpace != NULL) {
248 			addressSpace->Get();
249 			return addressSpace;
250 		}
251 	}
252 
253 	return NULL;
254 }
255 
256 
257 /*static*/ VMAddressSpace*
258 VMAddressSpace::Get(team_id teamID)
259 {
260 	rw_lock_read_lock(&sAddressSpaceTableLock);
261 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID);
262 	if (addressSpace)
263 		addressSpace->Get();
264 	rw_lock_read_unlock(&sAddressSpaceTableLock);
265 
266 	return addressSpace;
267 }
268 
269 
270 /*static*/ VMAddressSpace*
271 VMAddressSpace::DebugFirst()
272 {
273 	return sAddressSpaceTable.GetIterator().Next();
274 }
275 
276 
277 /*static*/ VMAddressSpace*
278 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace)
279 {
280 	if (addressSpace == NULL)
281 		return NULL;
282 
283 	AddressSpaceTable::Iterator it
284 		= sAddressSpaceTable.GetIterator(addressSpace->ID());
285 	it.Next();
286 	return it.Next();
287 }
288 
289 
290 /*static*/ VMAddressSpace*
291 VMAddressSpace::DebugGet(team_id teamID)
292 {
293 	return sAddressSpaceTable.Lookup(teamID);
294 }
295 
296 
297 /*static*/ void
298 VMAddressSpace::_DeleteIfUnreferenced(team_id id)
299 {
300 	rw_lock_write_lock(&sAddressSpaceTableLock);
301 
302 	bool remove = false;
303 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(id);
304 	if (addressSpace != NULL && addressSpace->fRefCount == 0) {
305 		sAddressSpaceTable.RemoveUnchecked(addressSpace);
306 		remove = true;
307 	}
308 
309 	rw_lock_write_unlock(&sAddressSpaceTableLock);
310 
311 	if (remove)
312 		delete addressSpace;
313 }
314 
315 
316 /*static*/ int
317 VMAddressSpace::_DumpCommand(int argc, char** argv)
318 {
319 	VMAddressSpace* aspace;
320 
321 	if (argc < 2) {
322 		kprintf("aspace: not enough arguments\n");
323 		return 0;
324 	}
325 
326 	// if the argument looks like a number, treat it as such
327 
328 	{
329 		team_id id = strtoul(argv[1], NULL, 0);
330 
331 		aspace = sAddressSpaceTable.Lookup(id);
332 		if (aspace == NULL) {
333 			kprintf("invalid aspace id\n");
334 		} else {
335 			aspace->Dump();
336 		}
337 		return 0;
338 	}
339 	return 0;
340 }
341 
342 
343 /*static*/ int
344 VMAddressSpace::_DumpListCommand(int argc, char** argv)
345 {
346 	kprintf("   address      id         base          end   area count   "
347 		" area size\n");
348 
349 	AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator();
350 	while (VMAddressSpace* space = it.Next()) {
351 		int32 areaCount = 0;
352 		off_t areaSize = 0;
353 		for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
354 				VMArea* area = areaIt.Next();) {
355 			areaCount++;
356 			areaSize += area->Size();
357 		}
358 		kprintf("%p  %6" B_PRId32 "   %#010" B_PRIxADDR "   %#10" B_PRIxADDR
359 			"   %10" B_PRId32 "   %10" B_PRIdOFF "\n", space, space->ID(),
360 			space->Base(), space->EndAddress(), areaCount, areaSize);
361 	}
362 
363 	return 0;
364 }
365