From 06c5b26b5f2dae387dab7b803127c31aeb3d3141 Mon Sep 17 00:00:00 2001 From: Kamila Szewczyk Date: Sun, 12 Nov 2023 18:02:04 +0100 Subject: [PATCH] fix sizes --- langsmoke | 6 +- ref_c | 1739 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 1132 insertions(+), 613 deletions(-) diff --git a/langsmoke b/langsmoke index 2bedf02..7bf58c2 100755 --- a/langsmoke +++ b/langsmoke @@ -26,12 +26,12 @@ function do_stat { echo $sim } -echo "C: $(do_stat c 34248)" +echo "C: $(do_stat c 38533)" echo "C++: $(do_stat cpp 33606)" echo "Java: $(do_stat java 61196)" -echo "6502: $(do_stat 6502 7978)" +echo "6502: $(do_stat 6502 9039)" echo "Z80: $(do_stat z80 20182)" echo "APL: $(do_stat apl 29173)" echo "x86: $(do_stat x86 25753)" -echo "x64: $(do_stat x64 13671)" +echo "x64: $(do_stat x64 17809)" echo "Shell: $(do_stat sh 44144)" \ No newline at end of file diff --git a/ref_c b/ref_c index 4e9a857..8de4586 100644 --- a/ref_c +++ b/ref_c @@ -38,88 +38,6 @@ static vector(char) getlab(FILE * input) { return label; } -static struct instruction_t parse_heap(FILE * input, void (*fatal)(char * s)) { - switch(next(input)) { - case ' ': return (struct instruction_t) { STO, 0 }; - case '\t': return (struct instruction_t) { RCL, 0 }; - default: fatal("|e, IMP heap"); return (struct instruction_t) { ERR, 0 }; - } -} - -static struct instruction_t parse_arith(FILE * input, void (*fatal)(char * s)) { - switch(next(input)) { - case ' ': switch(next(input)) { - case ' ': return (struct instruction_t) { ADD, 0 }; - case '\t': return (struct instruction_t) { SUB, 0 }; - case '\n': return (struct instruction_t) { MUL, 0 }; - default: fatal("e, IMP arith"); return (struct instruction_t) { ERR, 0 }; - } - case '\t': switch(next(input)) { - case ' ': return (struct instruction_t) { DIV, 0 }; - case '\t': return (struct instruction_t) { MOD, 0 }; - default: fatal("|e, IMP arith"); return (struct instruction_t) { ERR, 0 }; - } - default: fatal("|e, IMP arith"); return (struct instruction_t) { ERR, 0 }; - } -} - -static struct instruction_t parse_io(FILE * input, void (*fatal)(char * s)) { - switch(next(input)) { - case ' ': switch(next(input)) { - case ' ': return (struct instruction_t) { PUTC, 0 }; - case '\t': return (struct instruction_t) { PUTN, 0 }; - default: fatal("|e, IMP io"); return (struct instruction_t) { ERR, 0 }; - } - case '\t': switch(next(input)) { - case ' ': return (struct instruction_t) { GETC, 0 }; - case '\t': return (struct instruction_t) { GETN, 0 }; - default: fatal("|e, IMP io"); return (struct instruction_t) { ERR, 0 }; - } - default: fatal("|e, IMP io"); return (struct instruction_t) { ERR, 0 }; - } -} - -static struct instruction_t parse_stack(FILE * input, void (*fatal)(char * s)) { - switch(next(input)) { - case ' ': return (struct instruction_t) { PSH, getnum(input) }; - case '\n': switch(next(input)) { - case ' ': return (struct instruction_t) { DUP, 0 }; - case '\t': return (struct instruction_t) { XCHG, 0 }; - case '\n': return (struct instruction_t) { DROP, 0 }; - default: fatal("e, IMP stk"); return (struct instruction_t) { ERR, 0 }; - } - case '\t': switch(next(input)) { - case ' ': return (struct instruction_t) { COPY, .data = getnum(input) }; - case '\n': return (struct instruction_t) { SLIDE, .data = getnum(input) }; - default: fatal("|e, IMP stk"); return (struct instruction_t) { ERR, 0 }; - } - default: fatal("e, IMP stk"); return (struct instruction_t) { ERR, 0 }; - } -} - -static struct instruction_t parse_flow(FILE * input, void (*fatal)(char * s)) { - switch(next(input)) { - case ' ': switch(next(input)) { - case ' ':return (struct instruction_t) { LBL, .label = getlab(input) }; - case '\t': return (struct instruction_t) { CALL, .label = getlab(input) }; - case '\n': return (struct instruction_t) { JMP, .label = getlab(input) }; - default: fatal("e, IMP flow"); return (struct instruction_t) { ERR, 0 }; - } - case '\t': switch(next(input)) { - case ' ': return (struct instruction_t) { BZ, .label = getlab(input) }; - case '\t': return (struct instruction_t) { BLTZ, .label = getlab(input) }; - case '\n': return (struct instruction_t) { RET, 0 }; - default: fatal("e, IMP flow"); return (struct instruction_t) { ERR, 0 }; - } - case '\n': - if(next(input) == '\n') - return (struct instruction_t) { STOP, 0 }; - else - { fatal("bad end, IMP flow"); return (struct instruction_t) { ERR, 0 }; } - default: fatal("e, IMP flow"); return (struct instruction_t) { ERR, 0 }; - } -} - struct _label_t { int32_t id; char * name; @@ -246,191 +164,6 @@ static void append_code(char ** buf, char * format, ...) { char * compile(struct parse_result_t program) { unsigned callid = vector_size(program.labels); char * code = NULL; - emit( - "#include \"vector.h\"\n" - "#ifndef DURING_JIT\n" - "\t#include \n" - "\t#include \n" - "\t#include \n" - "#else\n" - "\tint putchar(int);\n" - "\tint getchar(void);\n" - "\tint printf(char * fmt, ...);\n" - "\tint scanf(char * fmt, ...);\n" - "\ttypedef int int32_t;\n" - "\t#define NULL ((void *) 0)\n" - "#endif\n" - "\n" - "static vector(int32_t) stack;\n" - "static vector(int32_t) heap;\n" - "static vector(int32_t) callstack;\n" - "static int32_t lhs, rhs, tmp, ip;\n" - "\n" - "#define AT(x, y) vector_end(x)[-y]\n" - "#define BILOAD \\\n" - "\trhs = AT(stack, 1); \\\n" - "\tlhs = AT(stack, 2); \\\n" - "\tvector_pop_back(stack); \\\n" - "\tvector_pop_back(stack)\n" - "\n" - "int main(void) {\n" - "\tbranch: switch(ip) {\n" - "case 0:\n" - ); - - vector_foreach(struct instruction_t, ins, program.program) { - switch(ins->type) { - case GETC: - emit( - "\trhs = AT(stack, 1);\n" - "\tvector_pop_back(stack);\n" - "\twhile(vector_size(heap) <= (unsigned) rhs)\n" - "\t\tvector_push_back(heap, 0);\n" - "\theap[rhs] = getchar();\n" - "\tif(heap[rhs] < 0) heap[rhs] = -1;\n" - ); - break; - case PUTC: - emit( - "\tputchar(AT(stack, 1));\n" - "\tvector_pop_back(stack);\n" - ); - break; - case GETN: - emit( - "\trhs = AT(stack, 1);\n" - "\tvector_pop_back(stack);\n" - "\twhile(vector_size(heap) <= (unsigned) rhs)\n" - "\t\tvector_push_back(heap, 0);\n" - "\tscanf(\"%%d\", &tmp);\n" - "\theap[rhs] = tmp;\n" - ); - break; - case PUTN: - emit( - "\tprintf(\"%%d\", AT(stack, 1));\n" - "\tvector_pop_back(stack);\n" - ); - break; - case PSH: - emitf("\tvector_push_back(stack, %d);\n", ins->data); - break; - case DUP: - emit("\tvector_push_back(stack, AT(stack, 1));\n"); - break; - case XCHG: - emit( - "\ttmp = AT(stack, 2);\n" - "\tAT(stack, 2) = AT(stack, 1);\n" - "\tAT(stack, 1) = tmp;\n" - ); - break; - case DROP: - emit("\tvector_pop_back(stack);\n"); - break; - case ADD: - emit("\tBILOAD; vector_push_back(stack, lhs + rhs);\n"); - break; - case SUB: - emit("\tBILOAD; vector_push_back(stack, lhs - rhs);\n"); - break; - case DIV: - emit("\tBILOAD; vector_push_back(stack, lhs / rhs);\n"); - break; - case MUL: - emit("\tBILOAD; vector_push_back(stack, lhs * rhs);\n"); - break; - case MOD: - emit("\tBILOAD; vector_push_back(stack, lhs %% rhs);\n"); - break; - case STOP: - emit("\tgoto end;\n"); - break; - case STO: - emit( - "\tBILOAD;\n" - "\twhile(vector_size(heap) <= (unsigned) lhs)\n" - "\t\tvector_push_back(heap, 0);\n" - "\theap[lhs] = rhs;\n" - ); - break; - case RCL: - emit( - "\tlhs = AT(stack, 1);\n" - "\tvector_pop_back(stack);\n" - "\tif(vector_size(heap) <= (unsigned) lhs)\n" - "\t\tvector_push_back(stack, 0);\n" - "\telse\n" - "\t\tvector_push_back(stack, heap[lhs]);\n" - ); - break; - case LBL: - emitf("case %d:;\n", ins->data); - break; - case JMP: - emitf( - "\tip = %d;\n" - "\tgoto branch;\n" - , ins->data); - break; - case BZ: - emitf( - "\tlhs = AT(stack, 1);\n" - "\tvector_pop_back(stack);\n" - "\tif(lhs == 0) {\n" - "\t\tip = %d;\n" - "\t\tgoto branch;\n" - "\t}\n" - , ins->data); - break; - case BLTZ: - emitf( - "\tlhs = AT(stack, 1);\n" - "\tvector_pop_back(stack);\n" - "\tif(lhs < 0) {\n" - "\t\tip = %d;\n" - "\t\tgoto branch;\n" - "\t}\n" - , ins->data); - break; - case CALL: - emitf( - "\tvector_push_back(callstack, %d);\n" - "\tip = %d;\n" - "\tgoto branch;\n" - "case %d:\n" - , callid + 1, ins->data, callid + 1); - callid++; - break; - case RET: - emit( - "\tip = AT(callstack, 1);\n" - "\tvector_pop_back(callstack);\n" - "\tgoto branch;\n" - ); - break; - case COPY: - emitf( - "\tvector_push_back(stack, stack[vector_size(stack) - 1 - %d]);\n" - , ins->data); - break; - case SLIDE: - emitf( - "\tfor(rhs = 0; rhs < %d; rhs++)\n" - "\t\tvector_erase(stack, vector_size(stack) - 1);\n" - , ins->data); - break; - } - } - - emit( - "\t}\n" - "\tend:\n" - "\tvector_free(stack);\n" - "\tvector_free(heap);\n" - "\tvector_free(callstack);\n" - "}\n" - ); return code; } @@ -2352,341 +2085,6 @@ static inline void cksem_wait(const rzip_control * control, cksem_t * cksem) { #endif #endif - - -#if defined(RAUDIO_STANDALONE) - #include "raudio.h" -#else - #include "raylib.h" // Declares module functions - - // Check if config flags have been externally provided on compilation line - #if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags - #endif - #include "utils.h" // Required for: fopen() Android mapping -#endif - -#if defined(SUPPORT_MODULE_RAUDIO) - -#if defined(_WIN32) -// To avoid conflicting windows.h symbols with raylib, some flags are defined -// WARNING: Those flags avoid inclusion of some Win32 headers that could be required -// by user at some point and won't be included... -//------------------------------------------------------------------------------------- - -// If defined, the following flags inhibit definition of the indicated items. -#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_ -#define NOVIRTUALKEYCODES // VK_* -#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_* -#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* -#define NOSYSMETRICS // SM_* -#define NOMENUS // MF_* -#define NOICONS // IDI_* -#define NOKEYSTATES // MK_* -#define NOSYSCOMMANDS // SC_* -#define NORASTEROPS // Binary and Tertiary raster ops -#define NOSHOWWINDOW // SW_* -#define OEMRESOURCE // OEM Resource values -#define NOATOM // Atom Manager routines -#define NOCLIPBOARD // Clipboard routines -#define NOCOLOR // Screen colors -#define NOCTLMGR // Control and Dialog routines -#define NODRAWTEXT // DrawText() and DT_* -#define NOGDI // All GDI defines and routines -#define NOKERNEL // All KERNEL defines and routines -#define NOUSER // All USER defines and routines -//#define NONLS // All NLS defines and routines -#define NOMB // MB_* and MessageBox() -#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines -#define NOMETAFILE // typedef METAFILEPICT -#define NOMINMAX // Macros min(a,b) and max(a,b) -#define NOMSG // typedef MSG and associated routines -#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_* -#define NOSCROLL // SB_* and scrolling routines -#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc. -#define NOSOUND // Sound driver routines -#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines -#define NOWH // SetWindowsHook and WH_* -#define NOWINOFFSETS // GWL_*, GCL_*, associated routines -#define NOCOMM // COMM driver routines -#define NOKANJI // Kanji support stuff. -#define NOHELP // Help engine interface. -#define NOPROFILER // Profiler interface. -#define NODEFERWINDOWPOS // DeferWindowPos routines -#define NOMCX // Modem Configuration Extensions - -// Type required before windows.h inclusion -typedef struct tagMSG *LPMSG; - -#include // Windows functionality (miniaudio) - -// Type required by some unused function... -typedef struct tagBITMAPINFOHEADER { - DWORD biSize; - LONG biWidth; - LONG biHeight; - WORD biPlanes; - WORD biBitCount; - DWORD biCompression; - DWORD biSizeImage; - LONG biXPelsPerMeter; - LONG biYPelsPerMeter; - DWORD biClrUsed; - DWORD biClrImportant; -} BITMAPINFOHEADER, *PBITMAPINFOHEADER; - -#include // Component Object Model (COM) header -#include // Windows Multimedia, defines some WAVE structs -#include // Windows Multimedia, used by Windows GDI, defines DIBINDEX macro - -// Some required types defined for MSVC/TinyC compiler -#if defined(_MSC_VER) || defined(__TINYC__) - #include "propidl.h" -#endif -#endif - -#define MA_MALLOC RL_MALLOC -#define MA_FREE RL_FREE - -#define MA_NO_JACK -#define MA_NO_WAV -#define MA_NO_FLAC -#define MA_NO_MP3 - -// Threading model: Default: [0] COINIT_MULTITHREADED: COM calls objects on any thread (free threading) -#define MA_COINIT_VALUE 2 // [2] COINIT_APARTMENTTHREADED: Each object has its own thread (apartment model) - -#define MINIAUDIO_IMPLEMENTATION -//#define MA_DEBUG_OUTPUT -#include "external/miniaudio.h" // Audio device initialization and management -#undef PlaySound // Win32 API: windows.h > mmsystem.h defines PlaySound macro - -#include // Required for: malloc(), free() -#include // Required for: FILE, fopen(), fclose(), fread() -#include // Required for: strcmp() [Used in IsFileExtension(), LoadWaveFromMemory(), LoadMusicStreamFromMemory()] - -#if defined(RAUDIO_STANDALONE) - #ifndef TRACELOG - #define TRACELOG(level, ...) printf(__VA_ARGS__) - #endif - - // Allow custom memory allocators - #ifndef RL_MALLOC - #define RL_MALLOC(sz) malloc(sz) - #endif - #ifndef RL_CALLOC - #define RL_CALLOC(n,sz) calloc(n,sz) - #endif - #ifndef RL_REALLOC - #define RL_REALLOC(ptr,sz) realloc(ptr,sz) - #endif - #ifndef RL_FREE - #define RL_FREE(ptr) free(ptr) - #endif -#endif - -#if defined(SUPPORT_FILEFORMAT_WAV) - #define DRWAV_MALLOC RL_MALLOC - #define DRWAV_REALLOC RL_REALLOC - #define DRWAV_FREE RL_FREE - - #define DR_WAV_IMPLEMENTATION - #include "external/dr_wav.h" // WAV loading functions -#endif - -#if defined(SUPPORT_FILEFORMAT_OGG) - // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE - #include "external/stb_vorbis.c" // OGG loading functions -#endif - -#if defined(SUPPORT_FILEFORMAT_MP3) - #define DRMP3_MALLOC RL_MALLOC - #define DRMP3_REALLOC RL_REALLOC - #define DRMP3_FREE RL_FREE - - #define DR_MP3_IMPLEMENTATION - #include "external/dr_mp3.h" // MP3 loading functions -#endif - -#if defined(SUPPORT_FILEFORMAT_QOA) - #define QOA_MALLOC RL_MALLOC - #define QOA_FREE RL_FREE - - #if defined(_MSC_VER) // Disable some MSVC warning - #pragma warning(push) - #pragma warning(disable : 4018) - #pragma warning(disable : 4267) - #pragma warning(disable : 4244) - #endif - - #define QOA_IMPLEMENTATION - #include "external/qoa.h" // QOA loading and saving functions - #include "external/qoaplay.c" // QOA stream playing helper functions - - #if defined(_MSC_VER) - #pragma warning(pop) // Disable MSVC warning suppression - #endif -#endif - -#if defined(SUPPORT_FILEFORMAT_FLAC) - #define DRFLAC_MALLOC RL_MALLOC - #define DRFLAC_REALLOC RL_REALLOC - #define DRFLAC_FREE RL_FREE - - #define DR_FLAC_IMPLEMENTATION - #define DR_FLAC_NO_WIN32_IO - #include "external/dr_flac.h" // FLAC loading functions -#endif - -#if defined(SUPPORT_FILEFORMAT_XM) - #define JARXM_MALLOC RL_MALLOC - #define JARXM_FREE RL_FREE - - #if defined(_MSC_VER) // Disable some MSVC warning - #pragma warning(push) - #pragma warning(disable : 4244) - #endif - - #define JAR_XM_IMPLEMENTATION - #include "external/jar_xm.h" // XM loading functions - - #if defined(_MSC_VER) - #pragma warning(pop) // Disable MSVC warning suppression - #endif -#endif - -#if defined(SUPPORT_FILEFORMAT_MOD) - #define JARMOD_MALLOC RL_MALLOC - #define JARMOD_FREE RL_FREE - - #define JAR_MOD_IMPLEMENTATION - #include "external/jar_mod.h" // MOD loading functions -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef AUDIO_DEVICE_FORMAT - #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (float-32bit) -#endif -#ifndef AUDIO_DEVICE_CHANNELS - #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo -#endif -#ifndef AUDIO_DEVICE_SAMPLE_RATE - #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device output sample rate -#endif - -#ifndef MAX_AUDIO_BUFFER_POOL_CHANNELS - #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Audio pool channels -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if defined(RAUDIO_STANDALONE) -// Trace log level -// NOTE: Organized by priority level -typedef enum { - LOG_ALL = 0, // Display all logs - LOG_TRACE, // Trace logging, intended for internal use only - LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds - LOG_INFO, // Info logging, used for program execution info - LOG_WARNING, // Warning logging, used on recoverable failures - LOG_ERROR, // Error logging, used on unrecoverable failures - LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) - LOG_NONE // Disable logging -} TraceLogLevel; -#endif - -// Music context type -// NOTE: Depends on data structure provided by the library -// in charge of reading the different file types -typedef enum { - MUSIC_AUDIO_NONE = 0, // No audio context loaded - MUSIC_AUDIO_WAV, // WAV audio context - MUSIC_AUDIO_OGG, // OGG audio context - MUSIC_AUDIO_FLAC, // FLAC audio context - MUSIC_AUDIO_MP3, // MP3 audio context - MUSIC_AUDIO_QOA, // QOA audio context - MUSIC_MODULE_XM, // XM module audio context - MUSIC_MODULE_MOD // MOD module audio context -} MusicContextType; - -// NOTE: Different logic is used when feeding data to the playback device -// depending on whether data is streamed (Music vs Sound) -typedef enum { - AUDIO_BUFFER_USAGE_STATIC = 0, - AUDIO_BUFFER_USAGE_STREAM -} AudioBufferUsage; - -// Audio buffer struct -struct rAudioBuffer { - ma_data_converter converter; // Audio data converter - - AudioCallback callback; // Audio buffer callback for buffer filling on audio threads - rAudioProcessor *processor; // Audio processor - - float volume; // Audio buffer volume - float pitch; // Audio buffer pitch - float pan; // Audio buffer pan (0.0f to 1.0f) - - bool playing; // Audio buffer state: AUDIO_PLAYING - bool paused; // Audio buffer state: AUDIO_PAUSED - bool looping; // Audio buffer looping, default to true for AudioStreams - int usage; // Audio buffer usage mode: STATIC or STREAM - - bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer) - unsigned int sizeInFrames; // Total buffer size in frames - unsigned int frameCursorPos; // Frame cursor position - unsigned int framesProcessed; // Total frames processed in this buffer (required for play timing) - - unsigned char *data; // Data buffer, on music stream keeps filling - - rAudioBuffer *next; // Next audio buffer on the list - rAudioBuffer *prev; // Previous audio buffer on the list -}; - -// Audio processor struct -// NOTE: Useful to apply effects to an AudioBuffer -struct rAudioProcessor { - AudioCallback process; // Processor callback function - rAudioProcessor *next; // Next audio processor on the list - rAudioProcessor *prev; // Previous audio processor on the list -}; - -#define AudioBuffer rAudioBuffer // HACK: To avoid CoreAudio (macOS) symbol collision - -// Audio data context -typedef struct AudioData { - struct { - ma_context context; // miniaudio context data - ma_device device; // miniaudio device - ma_mutex lock; // miniaudio mutex lock - bool isReady; // Check if audio device is ready - size_t pcmBufferSize; // Pre-allocated buffer size - void *pcmBuffer; // Pre-allocated buffer to read audio data from file/memory - } System; - struct { - AudioBuffer *first; // Pointer to first AudioBuffer in the list - AudioBuffer *last; // Pointer to last AudioBuffer in the list - int defaultSize; // Default audio buffer size for audio streams - } Buffer; - rAudioProcessor *mixedProcessor; -} AudioData; - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static AudioData AUDIO = { // Global AUDIO context - - // NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number - // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a - // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough - // In case of music-stalls, just increase this number - .Buffer.defaultSize = 0, - .mixedProcessor = NULL -}; - //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -2780,13 +2178,6 @@ void InitAudioDevice(void) return; } - TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend)); - TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); - TRACELOG(LOG_INFO, " > Channels: %d -> %d", AUDIO.System.device.playback.channels, AUDIO.System.device.playback.internalChannels); - TRACELOG(LOG_INFO, " > Sample rate: %d -> %d", AUDIO.System.device.sampleRate, AUDIO.System.device.playback.internalSampleRate); - TRACELOG(LOG_INFO, " > Periods size: %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods); - AUDIO.System.isReady = true; } @@ -5007,4 +4398,1132 @@ static bool SaveFileText(const char *fileName, char *text) #undef AudioBuffer -#endif // SUPPORT_MODULE_RAUDIO \ No newline at end of file +#endif // SUPPORT_MODULE_RAUDIO + +#include +#include +#include +#include +#include +#include + +#include "futex.h" +#include "../locking/rtmutex_common.h" + +/* + * The base of the bucket array and its size are always used together + * (after initialization only in futex_hash()), so ensure that they + * reside in the same cacheline. + */ +static struct { + struct futex_hash_bucket *queues; + unsigned long hashsize; +} __futex_data __read_mostly __aligned(2*sizeof(long)); +#define futex_queues (__futex_data.queues) +#define futex_hashsize (__futex_data.hashsize) + + +/* + * Fault injections for futexes. + */ +#ifdef CONFIG_FAIL_FUTEX + +static struct { + struct fault_attr attr; + + bool ignore_private; +} fail_futex = { + .attr = FAULT_ATTR_INITIALIZER, + .ignore_private = false, +}; + +static int __init setup_fail_futex(char *str) +{ + return setup_fault_attr(&fail_futex.attr, str); +} +__setup("fail_futex=", setup_fail_futex); + +bool should_fail_futex(bool fshared) +{ + if (fail_futex.ignore_private && !fshared) + return false; + + return should_fail(&fail_futex.attr, 1); +} + +#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS + +static int __init fail_futex_debugfs(void) +{ + umode_t mode = S_IFREG | S_IRUSR | S_IWUSR; + struct dentry *dir; + + dir = fault_create_debugfs_attr("fail_futex", NULL, + &fail_futex.attr); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + debugfs_create_bool("ignore-private", mode, dir, + &fail_futex.ignore_private); + return 0; +} + +late_initcall(fail_futex_debugfs); + +#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ + +#endif /* CONFIG_FAIL_FUTEX */ + +/** + * futex_hash - Return the hash bucket in the global hash + * @key: Pointer to the futex key for which the hash is calculated + * + * We hash on the keys returned from get_futex_key (see below) and return the + * corresponding hash bucket in the global hash. + */ +struct futex_hash_bucket *futex_hash(union futex_key *key) +{ + u32 hash = jhash2((u32 *)key, offsetof(typeof(*key), both.offset) / 4, + key->both.offset); + + return &futex_queues[hash & (futex_hashsize - 1)]; +} + + +/** + * futex_setup_timer - set up the sleeping hrtimer. + * @time: ptr to the given timeout value + * @timeout: the hrtimer_sleeper structure to be set up + * @flags: futex flags + * @range_ns: optional range in ns + * + * Return: Initialized hrtimer_sleeper structure or NULL if no timeout + * value given + */ +struct hrtimer_sleeper * +futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout, + int flags, u64 range_ns) +{ + if (!time) + return NULL; + + hrtimer_init_sleeper_on_stack(timeout, (flags & FLAGS_CLOCKRT) ? + CLOCK_REALTIME : CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); + /* + * If range_ns is 0, calling hrtimer_set_expires_range_ns() is + * effectively the same as calling hrtimer_set_expires(). + */ + hrtimer_set_expires_range_ns(&timeout->timer, *time, range_ns); + + return timeout; +} + +/* + * Generate a machine wide unique identifier for this inode. + * + * This relies on u64 not wrapping in the life-time of the machine; which with + * 1ns resolution means almost 585 years. + * + * This further relies on the fact that a well formed program will not unmap + * the file while it has a (shared) futex waiting on it. This mapping will have + * a file reference which pins the mount and inode. + * + * If for some reason an inode gets evicted and read back in again, it will get + * a new sequence number and will _NOT_ match, even though it is the exact same + * file. + * + * It is important that futex_match() will never have a false-positive, esp. + * for PI futexes that can mess up the state. The above argues that false-negatives + * are only possible for malformed programs. + */ +static u64 get_inode_sequence_number(struct inode *inode) +{ + static atomic64_t i_seq; + u64 old; + + /* Does the inode already have a sequence number? */ + old = atomic64_read(&inode->i_sequence); + if (likely(old)) + return old; + + for (;;) { + u64 new = atomic64_add_return(1, &i_seq); + if (WARN_ON_ONCE(!new)) + continue; + + old = atomic64_cmpxchg_relaxed(&inode->i_sequence, 0, new); + if (old) + return old; + return new; + } +} + +/** + * get_futex_key() - Get parameters which are the keys for a futex + * @uaddr: virtual address of the futex + * @flags: FLAGS_* + * @key: address where result is stored. + * @rw: mapping needs to be read/write (values: FUTEX_READ, + * FUTEX_WRITE) + * + * Return: a negative error code or 0 + * + * The key words are stored in @key on success. + * + * For shared mappings (when @fshared), the key is: + * + * ( inode->i_sequence, page->index, offset_within_page ) + * + * [ also see get_inode_sequence_number() ] + * + * For private mappings (or when !@fshared), the key is: + * + * ( current->mm, address, 0 ) + * + * This allows (cross process, where applicable) identification of the futex + * without keeping the page pinned for the duration of the FUTEX_WAIT. + * + * lock_page() might sleep, the caller should not hold a spinlock. + */ +int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key, + enum futex_access rw) +{ + unsigned long address = (unsigned long)uaddr; + struct mm_struct *mm = current->mm; + struct page *page; + struct folio *folio; + struct address_space *mapping; + int err, ro = 0; + bool fshared; + + fshared = flags & FLAGS_SHARED; + + /* + * The futex address must be "naturally" aligned. + */ + key->both.offset = address % PAGE_SIZE; + if (unlikely((address % sizeof(u32)) != 0)) + return -EINVAL; + address -= key->both.offset; + + if (unlikely(!access_ok(uaddr, sizeof(u32)))) + return -EFAULT; + + if (unlikely(should_fail_futex(fshared))) + return -EFAULT; + + /* + * PROCESS_PRIVATE futexes are fast. + * As the mm cannot disappear under us and the 'key' only needs + * virtual address, we dont even have to find the underlying vma. + * Note : We do have to check 'uaddr' is a valid user address, + * but access_ok() should be faster than find_vma() + */ + if (!fshared) { + /* + * On no-MMU, shared futexes are treated as private, therefore + * we must not include the current process in the key. Since + * there is only one address space, the address is a unique key + * on its own. + */ + if (IS_ENABLED(CONFIG_MMU)) + key->private.mm = mm; + else + key->private.mm = NULL; + + key->private.address = address; + return 0; + } + +again: + /* Ignore any VERIFY_READ mapping (futex common case) */ + if (unlikely(should_fail_futex(true))) + return -EFAULT; + + err = get_user_pages_fast(address, 1, FOLL_WRITE, &page); + /* + * If write access is not required (eg. FUTEX_WAIT), try + * and get read-only access. + */ + if (err == -EFAULT && rw == FUTEX_READ) { + err = get_user_pages_fast(address, 1, 0, &page); + ro = 1; + } + if (err < 0) + return err; + else + err = 0; + + /* + * The treatment of mapping from this point on is critical. The folio + * lock protects many things but in this context the folio lock + * stabilizes mapping, prevents inode freeing in the shared + * file-backed region case and guards against movement to swap cache. + * + * Strictly speaking the folio lock is not needed in all cases being + * considered here and folio lock forces unnecessarily serialization. + * From this point on, mapping will be re-verified if necessary and + * folio lock will be acquired only if it is unavoidable + * + * Mapping checks require the folio so it is looked up now. For + * anonymous pages, it does not matter if the folio is split + * in the future as the key is based on the address. For + * filesystem-backed pages, the precise page is required as the + * index of the page determines the key. + */ + folio = page_folio(page); + mapping = READ_ONCE(folio->mapping); + + /* + * If folio->mapping is NULL, then it cannot be an anonymous + * page; but it might be the ZERO_PAGE or in the gate area or + * in a special mapping (all cases which we are happy to fail); + * or it may have been a good file page when get_user_pages_fast + * found it, but truncated or holepunched or subjected to + * invalidate_complete_page2 before we got the folio lock (also + * cases which we are happy to fail). And we hold a reference, + * so refcount care in invalidate_inode_page's remove_mapping + * prevents drop_caches from setting mapping to NULL beneath us. + * + * The case we do have to guard against is when memory pressure made + * shmem_writepage move it from filecache to swapcache beneath us: + * an unlikely race, but we do need to retry for folio->mapping. + */ + if (unlikely(!mapping)) { + int shmem_swizzled; + + /* + * Folio lock is required to identify which special case above + * applies. If this is really a shmem page then the folio lock + * will prevent unexpected transitions. + */ + folio_lock(folio); + shmem_swizzled = folio_test_swapcache(folio) || folio->mapping; + folio_unlock(folio); + folio_put(folio); + + if (shmem_swizzled) + goto again; + + return -EFAULT; + } + + /* + * Private mappings are handled in a simple way. + * + * If the futex key is stored in anonymous memory, then the associated + * object is the mm which is implicitly pinned by the calling process. + * + * NOTE: When userspace waits on a MAP_SHARED mapping, even if + * it's a read-only handle, it's expected that futexes attach to + * the object not the particular process. + */ + if (folio_test_anon(folio)) { + /* + * A RO anonymous page will never change and thus doesn't make + * sense for futex operations. + */ + if (unlikely(should_fail_futex(true)) || ro) { + err = -EFAULT; + goto out; + } + + key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */ + key->private.mm = mm; + key->private.address = address; + + } else { + struct inode *inode; + + /* + * The associated futex object in this case is the inode and + * the folio->mapping must be traversed. Ordinarily this should + * be stabilised under folio lock but it's not strictly + * necessary in this case as we just want to pin the inode, not + * update i_pages or anything like that. + * + * The RCU read lock is taken as the inode is finally freed + * under RCU. If the mapping still matches expectations then the + * mapping->host can be safely accessed as being a valid inode. + */ + rcu_read_lock(); + + if (READ_ONCE(folio->mapping) != mapping) { + rcu_read_unlock(); + folio_put(folio); + + goto again; + } + + inode = READ_ONCE(mapping->host); + if (!inode) { + rcu_read_unlock(); + folio_put(folio); + + goto again; + } + + key->both.offset |= FUT_OFF_INODE; /* inode-based key */ + key->shared.i_seq = get_inode_sequence_number(inode); + key->shared.pgoff = folio->index + folio_page_idx(folio, page); + rcu_read_unlock(); + } + +out: + folio_put(folio); + return err; +} + +/** + * fault_in_user_writeable() - Fault in user address and verify RW access + * @uaddr: pointer to faulting user space address + * + * Slow path to fixup the fault we just took in the atomic write + * access to @uaddr. + * + * We have no generic implementation of a non-destructive write to the + * user address. We know that we faulted in the atomic pagefault + * disabled section so we can as well avoid the #PF overhead by + * calling get_user_pages() right away. + */ +int fault_in_user_writeable(u32 __user *uaddr) +{ + struct mm_struct *mm = current->mm; + int ret; + + mmap_read_lock(mm); + ret = fixup_user_fault(mm, (unsigned long)uaddr, + FAULT_FLAG_WRITE, NULL); + mmap_read_unlock(mm); + + return ret < 0 ? ret : 0; +} + +/** + * futex_top_waiter() - Return the highest priority waiter on a futex + * @hb: the hash bucket the futex_q's reside in + * @key: the futex key (to distinguish it from other futex futex_q's) + * + * Must be called with the hb lock held. + */ +struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key *key) +{ + struct futex_q *this; + + plist_for_each_entry(this, &hb->chain, list) { + if (futex_match(&this->key, key)) + return this; + } + return NULL; +} + +int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval) +{ + int ret; + + pagefault_disable(); + ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); + pagefault_enable(); + + return ret; +} + +int futex_get_value_locked(u32 *dest, u32 __user *from) +{ + int ret; + + pagefault_disable(); + ret = __get_user(*dest, from); + pagefault_enable(); + + return ret ? -EFAULT : 0; +} + +/** + * wait_for_owner_exiting - Block until the owner has exited + * @ret: owner's current futex lock status + * @exiting: Pointer to the exiting task + * + * Caller must hold a refcount on @exiting. + */ +void wait_for_owner_exiting(int ret, struct task_struct *exiting) +{ + if (ret != -EBUSY) { + WARN_ON_ONCE(exiting); + return; + } + + if (WARN_ON_ONCE(ret == -EBUSY && !exiting)) + return; + + mutex_lock(&exiting->futex_exit_mutex); + /* + * No point in doing state checking here. If the waiter got here + * while the task was in exec()->exec_futex_release() then it can + * have any FUTEX_STATE_* value when the waiter has acquired the + * mutex. OK, if running, EXITING or DEAD if it reached exit() + * already. Highly unlikely and not a problem. Just one more round + * through the futex maze. + */ + mutex_unlock(&exiting->futex_exit_mutex); + + put_task_struct(exiting); +} + +/** + * __futex_unqueue() - Remove the futex_q from its futex_hash_bucket + * @q: The futex_q to unqueue + * + * The q->lock_ptr must not be NULL and must be held by the caller. + */ +void __futex_unqueue(struct futex_q *q) +{ + struct futex_hash_bucket *hb; + + if (WARN_ON_SMP(!q->lock_ptr) || WARN_ON(plist_node_empty(&q->list))) + return; + lockdep_assert_held(q->lock_ptr); + + hb = container_of(q->lock_ptr, struct futex_hash_bucket, lock); + plist_del(&q->list, &hb->chain); + futex_hb_waiters_dec(hb); +} + +/* The key must be already stored in q->key. */ +struct futex_hash_bucket *futex_q_lock(struct futex_q *q) + __acquires(&hb->lock) +{ + struct futex_hash_bucket *hb; + + hb = futex_hash(&q->key); + + /* + * Increment the counter before taking the lock so that + * a potential waker won't miss a to-be-slept task that is + * waiting for the spinlock. This is safe as all futex_q_lock() + * users end up calling futex_queue(). Similarly, for housekeeping, + * decrement the counter at futex_q_unlock() when some error has + * occurred and we don't end up adding the task to the list. + */ + futex_hb_waiters_inc(hb); /* implies smp_mb(); (A) */ + + q->lock_ptr = &hb->lock; + + spin_lock(&hb->lock); + return hb; +} + +void futex_q_unlock(struct futex_hash_bucket *hb) + __releases(&hb->lock) +{ + spin_unlock(&hb->lock); + futex_hb_waiters_dec(hb); +} + +void __futex_queue(struct futex_q *q, struct futex_hash_bucket *hb) +{ + int prio; + + /* + * The priority used to register this element is + * - either the real thread-priority for the real-time threads + * (i.e. threads with a priority lower than MAX_RT_PRIO) + * - or MAX_RT_PRIO for non-RT threads. + * Thus, all RT-threads are woken first in priority order, and + * the others are woken last, in FIFO order. + */ + prio = min(current->normal_prio, MAX_RT_PRIO); + + plist_node_init(&q->list, prio); + plist_add(&q->list, &hb->chain); + q->task = current; +} + +/** + * futex_unqueue() - Remove the futex_q from its futex_hash_bucket + * @q: The futex_q to unqueue + * + * The q->lock_ptr must not be held by the caller. A call to futex_unqueue() must + * be paired with exactly one earlier call to futex_queue(). + * + * Return: + * - 1 - if the futex_q was still queued (and we removed unqueued it); + * - 0 - if the futex_q was already removed by the waking thread + */ +int futex_unqueue(struct futex_q *q) +{ + spinlock_t *lock_ptr; + int ret = 0; + + /* In the common case we don't take the spinlock, which is nice. */ +retry: + /* + * q->lock_ptr can change between this read and the following spin_lock. + * Use READ_ONCE to forbid the compiler from reloading q->lock_ptr and + * optimizing lock_ptr out of the logic below. + */ + lock_ptr = READ_ONCE(q->lock_ptr); + if (lock_ptr != NULL) { + spin_lock(lock_ptr); + /* + * q->lock_ptr can change between reading it and + * spin_lock(), causing us to take the wrong lock. This + * corrects the race condition. + * + * Reasoning goes like this: if we have the wrong lock, + * q->lock_ptr must have changed (maybe several times) + * between reading it and the spin_lock(). It can + * change again after the spin_lock() but only if it was + * already changed before the spin_lock(). It cannot, + * however, change back to the original value. Therefore + * we can detect whether we acquired the correct lock. + */ + if (unlikely(lock_ptr != q->lock_ptr)) { + spin_unlock(lock_ptr); + goto retry; + } + __futex_unqueue(q); + + BUG_ON(q->pi_state); + + spin_unlock(lock_ptr); + ret = 1; + } + + return ret; +} + +/* + * PI futexes can not be requeued and must remove themselves from the + * hash bucket. The hash bucket lock (i.e. lock_ptr) is held. + */ +void futex_unqueue_pi(struct futex_q *q) +{ + __futex_unqueue(q); + + BUG_ON(!q->pi_state); + put_pi_state(q->pi_state); + q->pi_state = NULL; +} + +/* Constants for the pending_op argument of handle_futex_death */ +#define HANDLE_DEATH_PENDING true +#define HANDLE_DEATH_LIST false + +/* + * Process a futex-list entry, check whether it's owned by the + * dying task, and do notification if so: + */ +static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, + bool pi, bool pending_op) +{ + u32 uval, nval, mval; + pid_t owner; + int err; + + /* Futex address must be 32bit aligned */ + if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0) + return -1; + +retry: + if (get_user(uval, uaddr)) + return -1; + + /* + * Special case for regular (non PI) futexes. The unlock path in + * user space has two race scenarios: + * + * 1. The unlock path releases the user space futex value and + * before it can execute the futex() syscall to wake up + * waiters it is killed. + * + * 2. A woken up waiter is killed before it can acquire the + * futex in user space. + * + * In the second case, the wake up notification could be generated + * by the unlock path in user space after setting the futex value + * to zero or by the kernel after setting the OWNER_DIED bit below. + * + * In both cases the TID validation below prevents a wakeup of + * potential waiters which can cause these waiters to block + * forever. + * + * In both cases the following conditions are met: + * + * 1) task->robust_list->list_op_pending != NULL + * @pending_op == true + * 2) The owner part of user space futex value == 0 + * 3) Regular futex: @pi == false + * + * If these conditions are met, it is safe to attempt waking up a + * potential waiter without touching the user space futex value and + * trying to set the OWNER_DIED bit. If the futex value is zero, + * the rest of the user space mutex state is consistent, so a woken + * waiter will just take over the uncontended futex. Setting the + * OWNER_DIED bit would create inconsistent state and malfunction + * of the user space owner died handling. Otherwise, the OWNER_DIED + * bit is already set, and the woken waiter is expected to deal with + * this. + */ + owner = uval & FUTEX_TID_MASK; + + if (pending_op && !pi && !owner) { + futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); + return 0; + } + + if (owner != task_pid_vnr(curr)) + return 0; + + /* + * Ok, this dying thread is truly holding a futex + * of interest. Set the OWNER_DIED bit atomically + * via cmpxchg, and if the value had FUTEX_WAITERS + * set, wake up a waiter (if any). (We have to do a + * futex_wake() even if OWNER_DIED is already set - + * to handle the rare but possible case of recursive + * thread-death.) The rest of the cleanup is done in + * userspace. + */ + mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; + + /* + * We are not holding a lock here, but we want to have + * the pagefault_disable/enable() protection because + * we want to handle the fault gracefully. If the + * access fails we try to fault in the futex with R/W + * verification via get_user_pages. get_user() above + * does not guarantee R/W access. If that fails we + * give up and leave the futex locked. + */ + if ((err = futex_cmpxchg_value_locked(&nval, uaddr, uval, mval))) { + switch (err) { + case -EFAULT: + if (fault_in_user_writeable(uaddr)) + return -1; + goto retry; + + case -EAGAIN: + cond_resched(); + goto retry; + + default: + WARN_ON_ONCE(1); + return err; + } + } + + if (nval != uval) + goto retry; + + /* + * Wake robust non-PI futexes here. The wakeup of + * PI futexes happens in exit_pi_state(): + */ + if (!pi && (uval & FUTEX_WAITERS)) + futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); + + return 0; +} + +/* + * Fetch a robust-list pointer. Bit 0 signals PI futexes: + */ +static inline int fetch_robust_entry(struct robust_list __user **entry, + struct robust_list __user * __user *head, + unsigned int *pi) +{ + unsigned long uentry; + + if (get_user(uentry, (unsigned long __user *)head)) + return -EFAULT; + + *entry = (void __user *)(uentry & ~1UL); + *pi = uentry & 1; + + return 0; +} + +/* + * Walk curr->robust_list (very carefully, it's a userspace list!) + * and mark any locks found there dead, and notify any waiters. + * + * We silently return on any sign of list-walking problem. + */ +static void exit_robust_list(struct task_struct *curr) +{ + struct robust_list_head __user *head = curr->robust_list; + struct robust_list __user *entry, *next_entry, *pending; + unsigned int limit = ROBUST_LIST_LIMIT, pi, pip; + unsigned int next_pi; + unsigned long futex_offset; + int rc; + + /* + * Fetch the list head (which was registered earlier, via + * sys_set_robust_list()): + */ + if (fetch_robust_entry(&entry, &head->list.next, &pi)) + return; + /* + * Fetch the relative futex offset: + */ + if (get_user(futex_offset, &head->futex_offset)) + return; + /* + * Fetch any possibly pending lock-add first, and handle it + * if it exists: + */ + if (fetch_robust_entry(&pending, &head->list_op_pending, &pip)) + return; + + next_entry = NULL; /* avoid warning with gcc */ + while (entry != &head->list) { + /* + * Fetch the next entry in the list before calling + * handle_futex_death: + */ + rc = fetch_robust_entry(&next_entry, &entry->next, &next_pi); + /* + * A pending lock might already be on the list, so + * don't process it twice: + */ + if (entry != pending) { + if (handle_futex_death((void __user *)entry + futex_offset, + curr, pi, HANDLE_DEATH_LIST)) + return; + } + if (rc) + return; + entry = next_entry; + pi = next_pi; + /* + * Avoid excessively long or circular lists: + */ + if (!--limit) + break; + + cond_resched(); + } + + if (pending) { + handle_futex_death((void __user *)pending + futex_offset, + curr, pip, HANDLE_DEATH_PENDING); + } +} + +#ifdef CONFIG_COMPAT +static void __user *futex_uaddr(struct robust_list __user *entry, + compat_long_t futex_offset) +{ + compat_uptr_t base = ptr_to_compat(entry); + void __user *uaddr = compat_ptr(base + futex_offset); + + return uaddr; +} + +/* + * Fetch a robust-list pointer. Bit 0 signals PI futexes: + */ +static inline int +compat_fetch_robust_entry(compat_uptr_t *uentry, struct robust_list __user **entry, + compat_uptr_t __user *head, unsigned int *pi) +{ + if (get_user(*uentry, head)) + return -EFAULT; + + *entry = compat_ptr((*uentry) & ~1); + *pi = (unsigned int)(*uentry) & 1; + + return 0; +} + +/* + * Walk curr->robust_list (very carefully, it's a userspace list!) + * and mark any locks found there dead, and notify any waiters. + * + * We silently return on any sign of list-walking problem. + */ +static void compat_exit_robust_list(struct task_struct *curr) +{ + struct compat_robust_list_head __user *head = curr->compat_robust_list; + struct robust_list __user *entry, *next_entry, *pending; + unsigned int limit = ROBUST_LIST_LIMIT, pi, pip; + unsigned int next_pi; + compat_uptr_t uentry, next_uentry, upending; + compat_long_t futex_offset; + int rc; + + /* + * Fetch the list head (which was registered earlier, via + * sys_set_robust_list()): + */ + if (compat_fetch_robust_entry(&uentry, &entry, &head->list.next, &pi)) + return; + /* + * Fetch the relative futex offset: + */ + if (get_user(futex_offset, &head->futex_offset)) + return; + /* + * Fetch any possibly pending lock-add first, and handle it + * if it exists: + */ + if (compat_fetch_robust_entry(&upending, &pending, + &head->list_op_pending, &pip)) + return; + + next_entry = NULL; /* avoid warning with gcc */ + while (entry != (struct robust_list __user *) &head->list) { + /* + * Fetch the next entry in the list before calling + * handle_futex_death: + */ + rc = compat_fetch_robust_entry(&next_uentry, &next_entry, + (compat_uptr_t __user *)&entry->next, &next_pi); + /* + * A pending lock might already be on the list, so + * dont process it twice: + */ + if (entry != pending) { + void __user *uaddr = futex_uaddr(entry, futex_offset); + + if (handle_futex_death(uaddr, curr, pi, + HANDLE_DEATH_LIST)) + return; + } + if (rc) + return; + uentry = next_uentry; + entry = next_entry; + pi = next_pi; + /* + * Avoid excessively long or circular lists: + */ + if (!--limit) + break; + + cond_resched(); + } + if (pending) { + void __user *uaddr = futex_uaddr(pending, futex_offset); + + handle_futex_death(uaddr, curr, pip, HANDLE_DEATH_PENDING); + } +} +#endif + +#ifdef CONFIG_FUTEX_PI + +/* + * This task is holding PI mutexes at exit time => bad. + * Kernel cleans up PI-state, but userspace is likely hosed. + * (Robust-futex cleanup is separate and might save the day for userspace.) + */ +static void exit_pi_state_list(struct task_struct *curr) +{ + struct list_head *next, *head = &curr->pi_state_list; + struct futex_pi_state *pi_state; + struct futex_hash_bucket *hb; + union futex_key key = FUTEX_KEY_INIT; + + /* + * We are a ZOMBIE and nobody can enqueue itself on + * pi_state_list anymore, but we have to be careful + * versus waiters unqueueing themselves: + */ + raw_spin_lock_irq(&curr->pi_lock); + while (!list_empty(head)) { + next = head->next; + pi_state = list_entry(next, struct futex_pi_state, list); + key = pi_state->key; + hb = futex_hash(&key); + + /* + * We can race against put_pi_state() removing itself from the + * list (a waiter going away). put_pi_state() will first + * decrement the reference count and then modify the list, so + * its possible to see the list entry but fail this reference + * acquire. + * + * In that case; drop the locks to let put_pi_state() make + * progress and retry the loop. + */ + if (!refcount_inc_not_zero(&pi_state->refcount)) { + raw_spin_unlock_irq(&curr->pi_lock); + cpu_relax(); + raw_spin_lock_irq(&curr->pi_lock); + continue; + } + raw_spin_unlock_irq(&curr->pi_lock); + + spin_lock(&hb->lock); + raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); + raw_spin_lock(&curr->pi_lock); + /* + * We dropped the pi-lock, so re-check whether this + * task still owns the PI-state: + */ + if (head->next != next) { + /* retain curr->pi_lock for the loop invariant */ + raw_spin_unlock(&pi_state->pi_mutex.wait_lock); + spin_unlock(&hb->lock); + put_pi_state(pi_state); + continue; + } + + WARN_ON(pi_state->owner != curr); + WARN_ON(list_empty(&pi_state->list)); + list_del_init(&pi_state->list); + pi_state->owner = NULL; + + raw_spin_unlock(&curr->pi_lock); + raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); + spin_unlock(&hb->lock); + + rt_mutex_futex_unlock(&pi_state->pi_mutex); + put_pi_state(pi_state); + + raw_spin_lock_irq(&curr->pi_lock); + } + raw_spin_unlock_irq(&curr->pi_lock); +} +#else +static inline void exit_pi_state_list(struct task_struct *curr) { } +#endif + +static void futex_cleanup(struct task_struct *tsk) +{ + if (unlikely(tsk->robust_list)) { + exit_robust_list(tsk); + tsk->robust_list = NULL; + } + +#ifdef CONFIG_COMPAT + if (unlikely(tsk->compat_robust_list)) { + compat_exit_robust_list(tsk); + tsk->compat_robust_list = NULL; + } +#endif + + if (unlikely(!list_empty(&tsk->pi_state_list))) + exit_pi_state_list(tsk); +} + +/** + * futex_exit_recursive - Set the tasks futex state to FUTEX_STATE_DEAD + * @tsk: task to set the state on + * + * Set the futex exit state of the task lockless. The futex waiter code + * observes that state when a task is exiting and loops until the task has + * actually finished the futex cleanup. The worst case for this is that the + * waiter runs through the wait loop until the state becomes visible. + * + * This is called from the recursive fault handling path in make_task_dead(). + * + * This is best effort. Either the futex exit code has run already or + * not. If the OWNER_DIED bit has been set on the futex then the waiter can + * take it over. If not, the problem is pushed back to user space. If the + * futex exit code did not run yet, then an already queued waiter might + * block forever, but there is nothing which can be done about that. + */ +void futex_exit_recursive(struct task_struct *tsk) +{ + /* If the state is FUTEX_STATE_EXITING then futex_exit_mutex is held */ + if (tsk->futex_state == FUTEX_STATE_EXITING) + mutex_unlock(&tsk->futex_exit_mutex); + tsk->futex_state = FUTEX_STATE_DEAD; +} + +static void futex_cleanup_begin(struct task_struct *tsk) +{ + /* + * Prevent various race issues against a concurrent incoming waiter + * including live locks by forcing the waiter to block on + * tsk->futex_exit_mutex when it observes FUTEX_STATE_EXITING in + * attach_to_pi_owner(). + */ + mutex_lock(&tsk->futex_exit_mutex); + + /* + * Switch the state to FUTEX_STATE_EXITING under tsk->pi_lock. + * + * This ensures that all subsequent checks of tsk->futex_state in + * attach_to_pi_owner() must observe FUTEX_STATE_EXITING with + * tsk->pi_lock held. + * + * It guarantees also that a pi_state which was queued right before + * the state change under tsk->pi_lock by a concurrent waiter must + * be observed in exit_pi_state_list(). + */ + raw_spin_lock_irq(&tsk->pi_lock); + tsk->futex_state = FUTEX_STATE_EXITING; + raw_spin_unlock_irq(&tsk->pi_lock); +} + +static void futex_cleanup_end(struct task_struct *tsk, int state) +{ + /* + * Lockless store. The only side effect is that an observer might + * take another loop until it becomes visible. + */ + tsk->futex_state = state; + /* + * Drop the exit protection. This unblocks waiters which observed + * FUTEX_STATE_EXITING to reevaluate the state. + */ + mutex_unlock(&tsk->futex_exit_mutex); +} + +void futex_exec_release(struct task_struct *tsk) +{ + /* + * The state handling is done for consistency, but in the case of + * exec() there is no way to prevent further damage as the PID stays + * the same. But for the unlikely and arguably buggy case that a + * futex is held on exec(), this provides at least as much state + * consistency protection which is possible. + */ + futex_cleanup_begin(tsk); + futex_cleanup(tsk); + /* + * Reset the state to FUTEX_STATE_OK. The task is alive and about + * exec a new binary. + */ + futex_cleanup_end(tsk, FUTEX_STATE_OK); +} + +void futex_exit_release(struct task_struct *tsk) +{ + futex_cleanup_begin(tsk); + futex_cleanup(tsk); + futex_cleanup_end(tsk, FUTEX_STATE_DEAD); +} + +static int __init futex_init(void) +{ + unsigned int futex_shift; + unsigned long i; + +#if CONFIG_BASE_SMALL + futex_hashsize = 16; +#else + futex_hashsize = roundup_pow_of_two(256 * num_possible_cpus()); +#endif + + futex_queues = alloc_large_system_hash("futex", sizeof(*futex_queues), + futex_hashsize, 0, 0, + &futex_shift, NULL, + futex_hashsize, futex_hashsize); + futex_hashsize = 1UL << futex_shift; + + for (i = 0; i < futex_hashsize; i++) { + atomic_set(&futex_queues[i].waiters, 0); + plist_head_init(&futex_queues[i].chain); + spin_lock_init(&futex_queues[i].lock); + } + + return 0; +} +core_initcall(futex_init); \ No newline at end of file