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