// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. namespace FASTER.core { using System; using System.Runtime.InteropServices; using System.Security; using Microsoft.Win32.SafeHandles; using System.Threading; using System.IO; /// /// Interop with WINAPI for file I/O, threading, and NUMA functions. /// public static unsafe class Native32 { #region Native structs [StructLayout(LayoutKind.Sequential)] private struct LUID { public uint lp; public int hp; } [StructLayout(LayoutKind.Sequential)] private struct LUID_AND_ATTRIBUTES { public LUID Luid; public uint Attributes; } [StructLayout(LayoutKind.Sequential)] private struct TOKEN_PRIVILEGES { public uint PrivilegeCount; public LUID_AND_ATTRIBUTES Privileges; } [StructLayout(LayoutKind.Sequential)] private struct MARK_HANDLE_INFO { public uint UsnSourceInfo; public IntPtr VolumeHandle; public uint HandleInfo; } #endregion #region io constants and flags internal const int ERROR_IO_PENDING = 997; internal const uint GENERIC_READ = 0x80000000; internal const uint GENERIC_WRITE = 0x40000000; internal const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000; internal const uint FILE_FLAG_OVERLAPPED = 0x40000000; internal const uint FILE_SHARE_DELETE = 0x00000004; #endregion #region io functions [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeFileHandle CreateFileW( [In] string lpFileName, [In] UInt32 dwDesiredAccess, [In] UInt32 dwShareMode, [In] IntPtr lpSecurityAttributes, [In] UInt32 dwCreationDisposition, [In] UInt32 dwFlagsAndAttributes, [In] IntPtr hTemplateFile); [DllImport("Kernel32.dll", SetLastError = true)] internal static extern bool ReadFile( [In] SafeFileHandle hFile, [Out] IntPtr lpBuffer, [In] UInt32 nNumberOfBytesToRead, [Out] out UInt32 lpNumberOfBytesRead, [In] NativeOverlapped* lpOverlapped); [DllImport("Kernel32.dll", SetLastError = true)] internal static extern bool WriteFile( [In] SafeFileHandle hFile, [In] IntPtr lpBuffer, [In] UInt32 nNumberOfBytesToWrite, [Out] out UInt32 lpNumberOfBytesWritten, [In] NativeOverlapped* lpOverlapped); internal enum EMoveMethod : uint { Begin = 0, Current = 1, End = 2 } [DllImport("kernel32.dll", SetLastError = true)] internal static extern uint SetFilePointer( [In] SafeFileHandle hFile, [In] int lDistanceToMove, [In, Out] ref int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod); [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool SetEndOfFile( [In] SafeFileHandle hFile); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool GetDiskFreeSpace(string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters); [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName); #endregion #region Thread and NUMA functions [DllImport("kernel32.dll")] private static extern IntPtr GetCurrentThread(); [DllImport("kernel32")] internal static extern uint GetCurrentThreadId(); [DllImport("kernel32.dll", SetLastError = true)] private static extern uint GetCurrentProcessorNumber(); [DllImport("kernel32.dll", SetLastError = true)] private static extern uint GetActiveProcessorCount(uint count); [DllImport("kernel32.dll", SetLastError = true)] private static extern ushort GetActiveProcessorGroupCount(); [DllImport("kernel32.dll", SetLastError = true)] private static extern int SetThreadGroupAffinity(IntPtr hThread, ref GROUP_AFFINITY GroupAffinity, ref GROUP_AFFINITY PreviousGroupAffinity); [DllImport("kernel32.dll", SetLastError = true)] private static extern int GetThreadGroupAffinity(IntPtr hThread, ref GROUP_AFFINITY PreviousGroupAffinity); private static readonly uint ALL_PROCESSOR_GROUPS = 0xffff; [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] private struct GROUP_AFFINITY { public ulong Mask; public uint Group; public uint Reserved1; public uint Reserved2; public uint Reserved3; } /// /// Accepts thread id = 0, 1, 2, ... and sprays them round-robin /// across all cores (viewed as a flat space). On NUMA machines, /// this gives us [socket, core] ordering of affinitization. That is, /// if there are N cores per socket, then thread indices of 0 to N-1 map /// to the range [socket 0, core 0] to [socket 0, core N-1]. /// /// Index of thread (from 0 onwards) public static void AffinitizeThreadRoundRobin(uint threadIdx) { uint nrOfProcessors = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); ushort nrOfProcessorGroups = GetActiveProcessorGroupCount(); uint nrOfProcsPerGroup = nrOfProcessors / nrOfProcessorGroups; GROUP_AFFINITY groupAffinityThread = default(GROUP_AFFINITY); GROUP_AFFINITY oldAffinityThread = default(GROUP_AFFINITY); IntPtr thread = GetCurrentThread(); GetThreadGroupAffinity(thread, ref groupAffinityThread); threadIdx = threadIdx % nrOfProcessors; groupAffinityThread.Mask = (ulong)1L << ((int)(threadIdx % (int)nrOfProcsPerGroup)); groupAffinityThread.Group = (uint)(threadIdx / nrOfProcsPerGroup); if (SetThreadGroupAffinity(thread, ref groupAffinityThread, ref oldAffinityThread) == 0) { throw new Exception("Unable to affinitize thread"); } } /// /// Accepts thread id = 0, 1, 2, ... and sprays them round-robin /// across all cores (viewed as a flat space). On NUMA machines, /// this gives us [core, socket] ordering of affinitization. That is, /// if there are N cores per socket, then thread indices of 0 to N-1 map /// to the range [socket 0, core 0] to [socket N-1, core 0]. /// /// Index of thread (from 0 onwards) /// Number of NUMA sockets public static void AffinitizeThreadShardedNuma(uint threadIdx, ushort nrOfProcessorGroups) { uint nrOfProcessors = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); uint nrOfProcsPerGroup = nrOfProcessors / nrOfProcessorGroups; threadIdx = nrOfProcsPerGroup * (threadIdx % nrOfProcessorGroups) + (threadIdx / nrOfProcessorGroups); AffinitizeThreadRoundRobin(threadIdx); return; } #endregion #region Advanced file ops [DllImport("advapi32.dll", SetLastError = true)] private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetCurrentProcess(); [DllImport("advapi32", SetLastError = true)] private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool AdjustTokenPrivileges(IntPtr tokenhandle, int disableprivs, ref TOKEN_PRIVILEGES Newstate, int BufferLengthInBytes, int PreviousState, int ReturnLengthInBytes); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); [DllImport("Kernel32.dll", SetLastError = true)] private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint IoControlCode, void* InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize, ref uint pBytesReturned, IntPtr Overlapped); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetFileValidData(SafeFileHandle hFile, long ValidDataLength); [DllImport("kernel32.dll", SetLastError = true)] private static extern SafeFileHandle CreateFile(string filename, uint access, uint share, IntPtr securityAttributes, uint creationDisposition, uint flagsAndAttributes, IntPtr templateFile); /// /// Enable privilege for process /// /// public static bool EnableProcessPrivileges() { #if DOTNETCORE if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return false; #endif TOKEN_PRIVILEGES token_privileges = default(TOKEN_PRIVILEGES); token_privileges.PrivilegeCount = 1; token_privileges.Privileges.Attributes = 0x2; if (!LookupPrivilegeValue(null, "SeManageVolumePrivilege", ref token_privileges.Privileges.Luid)) return false; if (!OpenProcessToken(GetCurrentProcess(), 0x20, out IntPtr token)) return false; if (!AdjustTokenPrivileges(token, 0, ref token_privileges, 0, 0, 0)) return false; if (Marshal.GetLastWin32Error() != 0) return false; CloseHandle(token); return true; } private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access) { return (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)); } internal static bool EnableVolumePrivileges(string filename, SafeFileHandle handle) { #if DOTNETCORE if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return false; #endif string volume_string = "\\\\.\\" + filename.Substring(0, 2); uint fileCreation = unchecked((uint)FileMode.Open); SafeFileHandle volume_handle = CreateFile(volume_string, 0, 0, IntPtr.Zero, fileCreation, 0x80, IntPtr.Zero); if (volume_handle == null) { return false; } MARK_HANDLE_INFO mhi; mhi.UsnSourceInfo = 0x1; mhi.VolumeHandle = volume_handle.DangerousGetHandle(); mhi.HandleInfo = 0x1; uint bytes_returned = 0; bool result = DeviceIoControl(handle, CTL_CODE(0x9, 63, 0, 0), (void*)&mhi, sizeof(MARK_HANDLE_INFO), IntPtr.Zero, 0, ref bytes_returned, IntPtr.Zero); if (!result) { return false; } volume_handle.Close(); return true; } /// /// Set file size /// /// /// /// public static bool SetFileSize(SafeFileHandle file_handle, long file_size) { #if DOTNETCORE if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return false; #endif if (!SetFilePointerEx(file_handle, file_size, out long newFilePtr, 0)) { return false; } // Set a fixed file length if (!SetEndOfFile(file_handle)) { return false; } if (!SetFileValidData(file_handle, file_size)) { return false; } return true; } internal static int MakeHRFromErrorCode(int errorCode) { return unchecked(((int)0x80070000) | errorCode); } #endregion } }