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