xref: /haiku/src/system/kernel/vm/VMAddressSpace.cpp (revision b3de82492af3b6412ffaf7eb87fd6e1995755685)
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);
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(nogrow) VMKernelAddressSpace(teamID, base, size)
196 		: (VMAddressSpace*)new(nogrow) VMUserAddressSpace(teamID, base, size);
197 	if (addressSpace == NULL)
198 		return B_NO_MEMORY;
199 
200 	status_t status = addressSpace->InitObject();
201 	if (status != B_OK) {
202 		delete addressSpace;
203 		return status;
204 	}
205 
206 	TRACE(("vm_create_aspace: team %ld (%skernel): %#lx bytes starting at "
207 		"%#lx => %p\n", id, kernel ? "!" : "", size, base, addressSpace));
208 
209 	// create the corresponding translation map
210 	status = arch_vm_translation_map_create_map(kernel,
211 		&addressSpace->fTranslationMap);
212 	if (status != B_OK) {
213 		delete addressSpace;
214 		return status;
215 	}
216 
217 	// add the aspace to the global hash table
218 	rw_lock_write_lock(&sAddressSpaceTableLock);
219 	sAddressSpaceTable.InsertUnchecked(addressSpace);
220 	rw_lock_write_unlock(&sAddressSpaceTableLock);
221 
222 	*_addressSpace = addressSpace;
223 	return B_OK;
224 }
225 
226 
227 /*static*/ VMAddressSpace*
228 VMAddressSpace::GetKernel()
229 {
230 	// we can treat this one a little differently since it can't be deleted
231 	sKernelAddressSpace->Get();
232 	return sKernelAddressSpace;
233 }
234 
235 
236 /*static*/ team_id
237 VMAddressSpace::CurrentID()
238 {
239 	struct thread* thread = thread_get_current_thread();
240 
241 	if (thread != NULL && thread->team->address_space != NULL)
242 		return thread->team->id;
243 
244 	return B_ERROR;
245 }
246 
247 
248 /*static*/ VMAddressSpace*
249 VMAddressSpace::GetCurrent()
250 {
251 	struct thread* thread = thread_get_current_thread();
252 
253 	if (thread != NULL) {
254 		VMAddressSpace* addressSpace = thread->team->address_space;
255 		if (addressSpace != NULL) {
256 			addressSpace->Get();
257 			return addressSpace;
258 		}
259 	}
260 
261 	return NULL;
262 }
263 
264 
265 /*static*/ VMAddressSpace*
266 VMAddressSpace::Get(team_id teamID)
267 {
268 	rw_lock_read_lock(&sAddressSpaceTableLock);
269 	VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID);
270 	if (addressSpace)
271 		addressSpace->Get();
272 	rw_lock_read_unlock(&sAddressSpaceTableLock);
273 
274 	return addressSpace;
275 }
276 
277 
278 /*static*/ VMAddressSpace*
279 VMAddressSpace::DebugFirst()
280 {
281 	return sAddressSpaceTable.GetIterator().Next();
282 }
283 
284 
285 /*static*/ VMAddressSpace*
286 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace)
287 {
288 	if (addressSpace == NULL)
289 		return NULL;
290 
291 	AddressSpaceTable::Iterator it
292 		= sAddressSpaceTable.GetIterator(addressSpace->ID());
293 	it.Next();
294 	return it.Next();
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("   address      id         base          end   area count   "
348 		" area size\n");
349 
350 	AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator();
351 	while (VMAddressSpace* space = it.Next()) {
352 		int32 areaCount = 0;
353 		off_t areaSize = 0;
354 		for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
355 				VMArea* area = areaIt.Next();) {
356 			if (area->cache->type != CACHE_TYPE_NULL) {
357 				areaCount++;
358 				areaSize += area->Size();
359 			}
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