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 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 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 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 68 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 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 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* 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 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 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 206 VMAreas::Init() 207 { 208 new(&sTree) VMAreasTree; 209 return B_OK; 210 } 211 212 213 /*static*/ VMArea* 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 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 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 258 VMAreas::Remove(VMArea* area) 259 { 260 WriteLock(); 261 sTree.Remove(area); 262 WriteUnlock(); 263 } 264