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