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