//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: ZoneAllocation.cpp // // Note: Non-blocking zone allocation for variable size // elements for eventing and tracing infrastructure. // #include "hal.h" #include "eventing.h" // // Utility routines for buffer validation // #ifdef BUFFER_VALIDATION #define BOB_SIGNATURE 0xabababab // begining of buffer signature #define EOB_SIGNATURE 0xcdcdcdcd // end of buffer signature void SetEndOfBuffer(PMEMORY_ZONE Zone) { uint32 * ptr = (uint32 *)((ULONG_PTR)Zone + Zone->ZoneSize); *ptr = EOB_SIGNATURE; ptr = (uint32 *)((ULONG_PTR)Zone) - 1; *ptr = BOB_SIGNATURE; } void CheckEndOfBuffer(PMEMORY_ZONE Zone) { uint32 * ptr = (uint32 *)((ULONG_PTR)Zone + Zone->ZoneSize); EV_ASSERT(*ptr == EOB_SIGNATURE); ptr = (uint32 *)((ULONG_PTR)Zone) - 1; EV_ASSERT(*ptr == BOB_SIGNATURE); } #else // !BUFFER_VALIDATION #define SetEndOfBuffer(x) #define CheckEndOfBuffer(x) #endif // BUFFER_VALIDATION PMEMORY_ZONE InitializeMemoryZone(void * Buffer, uint16 Size, UIntPtr storageHandle) { PMEMORY_ZONE Zone; #ifdef BUFFER_VALIDATION // In buffer validation mode, add extra space for the end signature // to detect potential buffer overruns Size = Size - 2 * sizeof(uint32); Buffer = (void *)((ULONG_PTR)Buffer + sizeof(uint32)); #endif // BUFFER_VALIDATION if (Size <= sizeof(MEMORY_ZONE)) { return NULL; } Zone = (PMEMORY_ZONE)Buffer; Zone->ZoneSize = Size; Zone->Link = NULL; Zone->StorageHandle = storageHandle; Zone->LastSyncPoint = 0; Zone->Allocation.AtomicValue32 = 0; Zone->ReadyList.AtomicValue32 = 0; Zone->Allocation.FreeOffset = sizeof(MEMORY_ZONE); // Assert the assumptions regarding the bit values that have been initialized // with the dword write above EV_ASSERT(Zone->Allocation.Filled == 0); EV_ASSERT(Zone->Allocation.Committed == 0); Zone->Generation = 0; SetEndOfBuffer(Zone); return Zone; } bool RecycleZone(PMEMORY_ZONE Zone) { CheckEndOfBuffer(Zone); ZONE_ALLOCATION_POINTER CapturedValue; ZONE_ALLOCATION_POINTER NextValue; do { CapturedValue.AtomicValue32 = Zone->Allocation.AtomicValue32; if (CapturedValue.Recycling) { // Someone is already recycling this zone. Nothing left to do, just return return false; } // Also test whether the zone has been already recycled and in is in use if ((CapturedValue.Filled == 0) || (CapturedValue.Committed == 0)) { return false; } NextValue.AtomicValue32 = CapturedValue.AtomicValue32; NextValue.Recycling = 1; // Atomically also set the recycling flag to prevent other concurrent recycling } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, NextValue.AtomicValue32, CapturedValue.AtomicValue32) != CapturedValue.AtomicValue32); // We sucessfully too ownership on recycling phase. We can now reinitialize the fields // The last operation should be atomically updating the flags and offset in // the allocation field EV_ASSERT(IsZoneCompleted(Zone)); EV_ASSERT(Zone->Allocation.Count == Zone->ReadyList.Count); ZONE_ALLOCATION_POINTER Allocation; Allocation.AtomicValue32 = 0; Allocation.FreeOffset = sizeof(MEMORY_ZONE); Zone->LastSyncPoint = Allocation.FreeOffset; Zone->Generation = MemoryStorageGetNextGeneration(HANDLE_TO_STORAGE(Zone->StorageHandle)); InterlockedExchange(&Zone->ReadyList.AtomicValue32, 0); InterlockedExchange(&Zone->Allocation.AtomicValue32, Allocation.AtomicValue32); return true; } void MarkZoneFull(PMEMORY_ZONE Zone) { ZONE_ALLOCATION_POINTER CapturedOffset; ZONE_ALLOCATION_POINTER NextValueOffset; CheckEndOfBuffer(Zone); do { CapturedOffset.AtomicValue32 = Zone->Allocation.AtomicValue32; if (CapturedOffset.Filled) { // Someone already did it. We are done here. return; } NextValueOffset.AtomicValue32 = CapturedOffset.AtomicValue32; NextValueOffset.Filled = 1; // // Atomically also set the committed flag if all entries have been commited // if (Zone->ReadyList.Count == CapturedOffset.Count) { NextValueOffset.Committed = 1; } } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, NextValueOffset.AtomicValue32, CapturedOffset.AtomicValue32) != CapturedOffset.AtomicValue32); } void MarkZoneCommited(PMEMORY_ZONE Zone) { ZONE_ALLOCATION_POINTER CapturedValue; ZONE_ALLOCATION_POINTER NextValue; CheckEndOfBuffer(Zone); do { CapturedValue.AtomicValue32 = Zone->Allocation.AtomicValue32; if ((CapturedValue.Committed == 1) || (CapturedValue.Recycling == 1) || (CapturedValue.Filled == 0) || (Zone->ReadyList.Count != CapturedValue.Count)) { return; } NextValue.AtomicValue32 = CapturedValue.AtomicValue32; NextValue.Committed = 1; } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, NextValue.AtomicValue32, CapturedValue.AtomicValue32) != CapturedValue.AtomicValue32); } PMEMORY_HEADER AllocateEventEntry(PMEMORY_ZONE Zone, uint16 size) { PMEMORY_HEADER ReturnBuffer; ZONE_ALLOCATION_POINTER CapturedOffset; ZONE_ALLOCATION_POINTER NextValueOffset; uint16 Reqsize = size; CheckEndOfBuffer(Zone); size += sizeof(MEMORY_HEADER); size = (uint16)ROUND_UP_TO_POWER2(size, sizeof(uint64)); do { CapturedOffset.AtomicValue32 = Zone->Allocation.AtomicValue32; ReturnBuffer = GetMemoryHeader(Zone, CapturedOffset.FreeOffset); if (CapturedOffset.Filled) { return NULL; } NextValueOffset.AtomicValue32 = CapturedOffset.AtomicValue32; NextValueOffset.FreeOffset += size; NextValueOffset.Count += 1; if ((NextValueOffset.FreeOffset >= Zone->ZoneSize) || (NextValueOffset.FreeOffset < CapturedOffset.FreeOffset )) { MarkZoneFull(Zone); // After that call this zone might have been recycled, no operations return NULL; } } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, NextValueOffset.AtomicValue32, CapturedOffset.AtomicValue32) != CapturedOffset.AtomicValue32); EV_ASSERT(((ULONG_PTR)ReturnBuffer + size) <= ((ULONG_PTR)Zone + Zone->ZoneSize)); EV_ASSERT((ULONG_PTR)ReturnBuffer >= ((ULONG_PTR)(Zone + 1))); #ifdef BUFFER_VALIDATION ReturnBuffer->Link = 0xffff; #endif ReturnBuffer->Size = (uint16)size; ReturnBuffer->Offset = CapturedOffset.FreeOffset; ReturnBuffer->Flags = 0; Struct_Microsoft_Singularity_ThreadContext *threadContext = Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); Struct_Microsoft_Singularity_ProcessorContext *processorContext = Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext(); #if SINGULARITY_KERNEL ReturnBuffer->TID = threadContext->threadIndex; #else ReturnBuffer->TID = threadContext->kernelThreadIndex; #endif ReturnBuffer->Cpu = processorContext->cpuRecord.id; #if ISA_XSCALE ReturnBuffer->Timestamp = Class_Microsoft_Singularity_Isal_Isa::g_GetCycleCount(); #else // ISA_XSCALE // Guarantee strict ordering across all CPUs (which RDTSC does not provide) static long s_timestamp; ReturnBuffer->Timestamp = ::InterlockedIncrement(&s_timestamp); #endif // ISA_XSCALE CheckEndOfBuffer(Zone); return ReturnBuffer; } void CommitEventEntry(PMEMORY_HEADER Entry) { PMEMORY_ZONE Zone = (PMEMORY_ZONE)((ULONG_PTR)Entry - Entry->Offset); ZONE_ALLOCATION_POINTER CapturedAllocationInfo; ZONE_READY_LIST CapturedReadyList; ZONE_READY_LIST NextReadyList; EV_ASSERT(Entry->Link == 0xffff); CheckEndOfBuffer(Zone); do { CapturedReadyList.AtomicValue32 = Zone->ReadyList.AtomicValue32; CapturedAllocationInfo.AtomicValue32 = Zone->Allocation.AtomicValue32; EV_ASSERT(CapturedAllocationInfo.Committed == 0); EV_ASSERT(CapturedAllocationInfo.Count > CapturedReadyList.Count); Entry->Link = CapturedReadyList.ReadyList; NextReadyList.Count = CapturedReadyList.Count + 1; NextReadyList.ReadyList = Entry->Offset; if (NextReadyList.Count == CapturedAllocationInfo.Count) { // Remember the new high watermark for forward zone walking Zone->LastSyncPoint = CapturedAllocationInfo.FreeOffset; } } while (InterlockedCompareExchange(&Zone->ReadyList.AtomicValue32, NextReadyList.AtomicValue32, CapturedReadyList.AtomicValue32) != CapturedReadyList.AtomicValue32); if (Zone->Allocation.Filled) { // The thread that took the last entry from the zone, did not had // a chance to see this last commit. We need to update also the // commit flag MarkZoneCommited(Zone); } } bool IsZoneCompleted(PMEMORY_ZONE Zone) { ZONE_ALLOCATION_POINTER CapturedValue; CapturedValue.AtomicValue32 = Zone->Allocation.AtomicValue32; return ((CapturedValue.Filled != 0) && (CapturedValue.Committed != 0)); } bool IsEntryCommited(PMEMORY_ZONE Zone, PMEMORY_HEADER Entry) { // This function assumes the zone is locked for read so it does not get // recycled during this test uint16 offsetKey = Entry->Offset; if (offsetKey < Zone->LastSyncPoint) { return true; } uint16 crtOffset = Zone->ReadyList.ReadyList; while (crtOffset) { if (crtOffset == offsetKey) { return true; } Entry = (PMEMORY_HEADER)((ULONG_PTR)Zone + crtOffset); crtOffset = Entry->Link; } return false; } PMEMORY_HEADER GetFirstReadyEntry(PMEMORY_ZONE Zone, uint16 offset) { PMEMORY_HEADER Entry; // This function assumes the zone is locked for read so it does not get // recycled during this test uint16 offsetFound = Zone->ZoneSize; uint16 crtOffset = Zone->ReadyList.ReadyList; while (crtOffset) { if ((crtOffset > offset) && (crtOffset < offsetFound)) { offsetFound = crtOffset; } Entry = (PMEMORY_HEADER)((ULONG_PTR)Zone + crtOffset); crtOffset = Entry->Link; } if (offsetFound != Zone->ZoneSize) { return (PMEMORY_HEADER)((ULONG_PTR)Zone + offsetFound); } return NULL; } PMEMORY_HEADER GetFirstEntry(PMEMORY_ZONE Zone, bool forward) { if (forward) { return GetFirstReadyEntry(Zone, 0); } uint16 crtOffset = Zone->ReadyList.ReadyList; if (crtOffset == 0) { return NULL; } return (PMEMORY_HEADER)((ULONG_PTR)Zone + crtOffset); } PMEMORY_HEADER GetNextEntry(PQUERY_VIEW view) { PMEMORY_ZONE Zone = view->CurrentZone; PMEMORY_HEADER Entry = view->CurrentEntry; if (!Zone->Allocation.Filled) { // The Zone is during allocations if (view->ZoneGeneration != Zone->Generation) { // We lost the context as the zone has been recycled return NULL; } } if (view->Forward) { view->CurrentEntryIndex += 1; if (view->CurrentEntryIndex >= Zone->Allocation.Count) { return NULL; } view->CurrentEntry = (PMEMORY_HEADER)((ULONG_PTR)Entry + Entry->Size); if ((Zone->Allocation.Filled != 0) || IsEntryCommited(Zone, Entry)) { // Valid entry Entry = view->CurrentEntry; } else { view->CurrentEntry = GetFirstReadyEntry(Zone, Entry->Offset); Entry = view->CurrentEntry; } } else { if (Entry->Link == 0) { // We finished to walk the chain backwards return NULL; } view->CurrentEntry = (PMEMORY_HEADER)((ULONG_PTR)Zone + Entry->Link); Entry = view->CurrentEntry; } return Entry; } // ///////////////////////////////////////////////////////////////// End of File.