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