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