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