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/VMArea.h>
12
13#include <new>
14
15#include <heap.h>
16#include <vm/VMAddressSpace.h>
17
18
19#define AREA_HASH_TABLE_SIZE 1024
20
21
22rw_lock VMAreaHash::sLock = RW_LOCK_INITIALIZER("area hash");
23VMAreaHashTable VMAreaHash::sTable;
24static area_id sNextAreaID = 1;
25
26
27// #pragma mark - VMArea
28
29VMArea::VMArea(VMAddressSpace* addressSpace, uint32 wiring, uint32 protection)
30	:
31	name(NULL),
32	protection(protection),
33	wiring(wiring),
34	memory_type(0),
35	cache(NULL),
36	no_cache_change(0),
37	cache_offset(0),
38	cache_type(0),
39	page_protections(NULL),
40	address_space(addressSpace),
41	cache_next(NULL),
42	cache_prev(NULL),
43	hash_next(NULL)
44{
45	new (&mappings) VMAreaMappings;
46}
47
48
49VMArea::~VMArea()
50{
51	const uint32 flags = HEAP_DONT_WAIT_FOR_MEMORY
52		| HEAP_DONT_LOCK_KERNEL_SPACE;
53		// TODO: This might be stricter than necessary.
54
55	free_etc(page_protections, flags);
56	free_etc(name, flags);
57}
58
59
60status_t
61VMArea::Init(const char* name, uint32 allocationFlags)
62{
63	// restrict the area name to B_OS_NAME_LENGTH
64	size_t length = strlen(name) + 1;
65	if (length > B_OS_NAME_LENGTH)
66		length = B_OS_NAME_LENGTH;
67
68	// clone the name
69	this->name = (char*)malloc_etc(length, allocationFlags);
70	if (this->name == NULL)
71		return B_NO_MEMORY;
72	strlcpy(this->name, name, length);
73
74	id = atomic_add(&sNextAreaID, 1);
75	return B_OK;
76}
77
78
79/*!	Returns whether any part of the given address range intersects with a wired
80	range of this area.
81	The area's top cache must be locked.
82*/
83bool
84VMArea::IsWired(addr_t base, size_t size) const
85{
86	for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
87			VMAreaWiredRange* range = it.Next();) {
88		if (range->IntersectsWith(base, size))
89			return true;
90	}
91
92	return false;
93}
94
95
96/*!	Adds the given wired range to this area.
97	The area's top cache must be locked.
98*/
99void
100VMArea::Wire(VMAreaWiredRange* range)
101{
102	ASSERT(range->area == NULL);
103
104	range->area = this;
105	fWiredRanges.Add(range);
106}
107
108
109/*!	Removes the given wired range from this area.
110	Must balance a previous Wire() call.
111	The area's top cache must be locked.
112*/
113void
114VMArea::Unwire(VMAreaWiredRange* range)
115{
116	ASSERT(range->area == this);
117
118	// remove the range
119	range->area = NULL;
120	fWiredRanges.Remove(range);
121
122	// wake up waiters
123	for (VMAreaUnwiredWaiterList::Iterator it = range->waiters.GetIterator();
124			VMAreaUnwiredWaiter* waiter = it.Next();) {
125		waiter->condition.NotifyAll();
126	}
127
128	range->waiters.MakeEmpty();
129}
130
131
132/*!	Removes a wired range from this area.
133
134	Must balance a previous Wire() call. The first implicit range with matching
135	\a base, \a size, and \a writable attributes is removed and returned. It's
136	waiters are woken up as well.
137	The area's top cache must be locked.
138*/
139VMAreaWiredRange*
140VMArea::Unwire(addr_t base, size_t size, bool writable)
141{
142	for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
143			VMAreaWiredRange* range = it.Next();) {
144		if (range->implicit && range->base == base && range->size == size
145				&& range->writable == writable) {
146			Unwire(range);
147			return range;
148		}
149	}
150
151	panic("VMArea::Unwire(%#" B_PRIxADDR ", %#" B_PRIxADDR ", %d): no such "
152		"range", base, size, writable);
153	return NULL;
154}
155
156
157/*!	If the area has any wired range, the given waiter is added to the range and
158	prepared for waiting.
159
160	\return \c true, if the waiter has been added, \c false otherwise.
161*/
162bool
163VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter)
164{
165	VMAreaWiredRange* range = fWiredRanges.Head();
166	if (range == NULL)
167		return false;
168
169	waiter->area = this;
170	waiter->base = fBase;
171	waiter->size = fSize;
172	waiter->condition.Init(this, "area unwired");
173	waiter->condition.Add(&waiter->waitEntry);
174
175	range->waiters.Add(waiter);
176
177	return true;
178}
179
180
181/*!	If the given address range intersect with a wired range of this area, the
182	given waiter is added to the range and prepared for waiting.
183
184	\param waiter The waiter structure that will be added to the wired range
185		that intersects with the given address range.
186	\param base The base of the address range to check.
187	\param size The size of the address range to check.
188	\param ignoreRange If given, this wired range of the area is not checked
189		whether it intersects with the given address range. Useful when the
190		caller has added the range and only wants to check intersection with
191		other ranges.
192	\return \c true, if the waiter has been added, \c false otherwise.
193*/
194bool
195VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter, addr_t base, size_t size,
196	VMAreaWiredRange* ignoreRange)
197{
198	for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
199			VMAreaWiredRange* range = it.Next();) {
200		if (range != ignoreRange && range->IntersectsWith(base, size)) {
201			waiter->area = this;
202			waiter->base = base;
203			waiter->size = size;
204			waiter->condition.Init(this, "area unwired");
205			waiter->condition.Add(&waiter->waitEntry);
206
207			range->waiters.Add(waiter);
208
209			return true;
210		}
211	}
212
213	return false;
214}
215
216
217// #pragma mark - VMAreaHash
218
219
220/*static*/ status_t
221VMAreaHash::Init()
222{
223	return sTable.Init(AREA_HASH_TABLE_SIZE);
224}
225
226
227/*static*/ VMArea*
228VMAreaHash::Lookup(area_id id)
229{
230	ReadLock();
231	VMArea* area = LookupLocked(id);
232	ReadUnlock();
233	return area;
234}
235
236
237/*static*/ area_id
238VMAreaHash::Find(const char* name)
239{
240	ReadLock();
241
242	area_id id = B_NAME_NOT_FOUND;
243
244	// TODO: Iterating through the whole table can be very slow and the whole
245	// time we're holding the lock! Use a second hash table!
246
247	for (VMAreaHashTable::Iterator it = sTable.GetIterator();
248			VMArea* area = it.Next();) {
249		if (strcmp(area->name, name) == 0) {
250			id = area->id;
251			break;
252		}
253	}
254
255	ReadUnlock();
256
257	return id;
258}
259
260
261/*static*/ void
262VMAreaHash::Insert(VMArea* area)
263{
264	WriteLock();
265	sTable.InsertUnchecked(area);
266	WriteUnlock();
267}
268
269
270/*static*/ void
271VMAreaHash::Remove(VMArea* area)
272{
273	WriteLock();
274	sTable.RemoveUnchecked(area);
275	WriteUnlock();
276}
277