xref: /haiku/src/system/kernel/vm/VMArea.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 rw_lock VMAreas::sLock = RW_LOCK_INITIALIZER("areas tree");
20 VMAreasTree VMAreas::sTree;
21 static area_id sNextAreaID = 1;
22 
23 
24 // #pragma mark - VMArea
25 
26 VMArea::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 
45 VMArea::~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 
52 status_t
53 VMArea::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 */
67 bool
68 VMArea::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 */
83 void
84 VMArea::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 */
97 void
98 VMArea::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 */
123 VMAreaWiredRange*
124 VMArea::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 */
146 bool
147 VMArea::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 */
176 bool
177 VMArea::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
206 VMAreas::Init()
207 {
208 	new(&sTree) VMAreasTree;
209 	return B_OK;
210 }
211 
212 
213 /*static*/ VMArea*
214 VMAreas::Lookup(area_id id)
215 {
216 	ReadLock();
217 	VMArea* area = LookupLocked(id);
218 	ReadUnlock();
219 	return area;
220 }
221 
222 
223 /*static*/ area_id
224 VMAreas::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
248 VMAreas::Insert(VMArea* area)
249 {
250 	WriteLock();
251 	sTree.Insert(area);
252 	WriteUnlock();
253 }
254 
255 
256 /*static*/ void
257 VMAreas::Remove(VMArea* area)
258 {
259 	WriteLock();
260 	sTree.Remove(area);
261 	WriteUnlock();
262 }
263