xref: /haiku/src/system/kernel/vm/VMArea.cpp (revision 46d4471af7fad4e52cfbd09174598cf5318aceed)
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 flags
189 		- \c IGNORE_WRITE_WIRED_RANGES: Ignore ranges wired for writing.
190 	\return \c true, if the waiter has been added, \c false otherwise.
191 */
192 bool
193 VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter, addr_t base, size_t size,
194 	uint32 flags)
195 {
196 	for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
197 			VMAreaWiredRange* range = it.Next();) {
198 		if ((flags & IGNORE_WRITE_WIRED_RANGES) != 0 && range->writable)
199 			continue;
200 
201 		if (range->IntersectsWith(base, size)) {
202 			waiter->area = this;
203 			waiter->base = base;
204 			waiter->size = size;
205 			waiter->condition.Init(this, "area unwired");
206 			waiter->condition.Add(&waiter->waitEntry);
207 
208 			range->waiters.Add(waiter);
209 
210 			return true;
211 		}
212 	}
213 
214 	return false;
215 }
216 
217 
218 // #pragma mark - VMAreaHash
219 
220 
221 /*static*/ status_t
222 VMAreaHash::Init()
223 {
224 	return sTable.Init(AREA_HASH_TABLE_SIZE);
225 }
226 
227 
228 /*static*/ VMArea*
229 VMAreaHash::Lookup(area_id id)
230 {
231 	ReadLock();
232 	VMArea* area = LookupLocked(id);
233 	ReadUnlock();
234 	return area;
235 }
236 
237 
238 /*static*/ area_id
239 VMAreaHash::Find(const char* name)
240 {
241 	ReadLock();
242 
243 	area_id id = B_NAME_NOT_FOUND;
244 
245 	// TODO: Iterating through the whole table can be very slow and the whole
246 	// time we're holding the lock! Use a second hash table!
247 
248 	for (VMAreaHashTable::Iterator it = sTable.GetIterator();
249 			VMArea* area = it.Next();) {
250 		if (strcmp(area->name, name) == 0) {
251 			id = area->id;
252 			break;
253 		}
254 	}
255 
256 	ReadUnlock();
257 
258 	return id;
259 }
260 
261 
262 /*static*/ void
263 VMAreaHash::Insert(VMArea* area)
264 {
265 	WriteLock();
266 	sTable.InsertUnchecked(area);
267 	WriteUnlock();
268 }
269 
270 
271 /*static*/ void
272 VMAreaHash::Remove(VMArea* area)
273 {
274 	WriteLock();
275 	sTable.RemoveUnchecked(area);
276 	WriteUnlock();
277 }
278