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