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