xref: /haiku/src/system/kernel/vm/VMArea.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
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 
22 rw_lock VMAreaHash::sLock = RW_LOCK_INITIALIZER("area hash");
23 VMAreaHashTable VMAreaHash::sTable;
24 static area_id sNextAreaID = 1;
25 
26 
27 // #pragma mark - VMArea
28 
29 VMArea::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 
49 VMArea::~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 
60 status_t
61 VMArea::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 */
83 bool
84 VMArea::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 */
99 void
100 VMArea::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 */
113 void
114 VMArea::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 */
139 VMAreaWiredRange*
140 VMArea::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 */
162 bool
163 VMArea::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 */
194 bool
195 VMArea::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
221 VMAreaHash::Init()
222 {
223 	return sTable.Init(AREA_HASH_TABLE_SIZE);
224 }
225 
226 
227 /*static*/ VMArea*
228 VMAreaHash::Lookup(area_id id)
229 {
230 	ReadLock();
231 	VMArea* area = LookupLocked(id);
232 	ReadUnlock();
233 	return area;
234 }
235 
236 
237 /*static*/ area_id
238 VMAreaHash::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
262 VMAreaHash::Insert(VMArea* area)
263 {
264 	WriteLock();
265 	sTable.InsertUnchecked(area);
266 	WriteUnlock();
267 }
268 
269 
270 /*static*/ void
271 VMAreaHash::Remove(VMArea* area)
272 {
273 	WriteLock();
274 	sTable.RemoveUnchecked(area);
275 	WriteUnlock();
276 }
277