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