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 ignoreRange If given, this wired range of the area is not checked 189 whether it intersects with the given address range. Useful when the 190 caller has added the range and only wants to check intersection with 191 other ranges. 192 \return \c true, if the waiter has been added, \c false otherwise. 193 */ 194 bool 195 VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter, addr_t base, size_t size, 196 VMAreaWiredRange* ignoreRange) 197 { 198 for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator(); 199 VMAreaWiredRange* range = it.Next();) { 200 if (range != ignoreRange && range->IntersectsWith(base, size)) { 201 waiter->area = this; 202 waiter->base = base; 203 waiter->size = size; 204 waiter->condition.Init(this, "area unwired"); 205 waiter->condition.Add(&waiter->waitEntry); 206 207 range->waiters.Add(waiter); 208 209 return true; 210 } 211 } 212 213 return false; 214 } 215 216 217 // #pragma mark - VMAreaHash 218 219 220 /*static*/ status_t 221 VMAreaHash::Init() 222 { 223 return sTable.Init(AREA_HASH_TABLE_SIZE); 224 } 225 226 227 /*static*/ VMArea* 228 VMAreaHash::Lookup(area_id id) 229 { 230 ReadLock(); 231 VMArea* area = LookupLocked(id); 232 ReadUnlock(); 233 return area; 234 } 235 236 237 /*static*/ area_id 238 VMAreaHash::Find(const char* name) 239 { 240 ReadLock(); 241 242 area_id id = B_NAME_NOT_FOUND; 243 244 // TODO: Iterating through the whole table can be very slow and the whole 245 // time we're holding the lock! Use a second hash table! 246 247 for (VMAreaHashTable::Iterator it = sTable.GetIterator(); 248 VMArea* area = it.Next();) { 249 if (strcmp(area->name, name) == 0) { 250 id = area->id; 251 break; 252 } 253 } 254 255 ReadUnlock(); 256 257 return id; 258 } 259 260 261 /*static*/ void 262 VMAreaHash::Insert(VMArea* area) 263 { 264 WriteLock(); 265 sTable.InsertUnchecked(area); 266 WriteUnlock(); 267 } 268 269 270 /*static*/ void 271 VMAreaHash::Remove(VMArea* area) 272 { 273 WriteLock(); 274 sTable.RemoveUnchecked(area); 275 WriteUnlock(); 276 } 277