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