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