Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AIO #1

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5650f77
remove duplicated reference
adamsitnik Feb 11, 2020
51b6a36
add basic AIO methods
adamsitnik Feb 11, 2020
6cd0d74
setup & destroy aio ring
adamsitnik Feb 11, 2020
08181a9
make it compile after switching to actual Linux machine
adamsitnik Feb 11, 2020
656c4c7
implement a very simple batching
adamsitnik Feb 13, 2020
6d5bffc
remove unnecesary unsafe
adamsitnik Feb 14, 2020
f9d4de7
can I handle empty reads like this?
adamsitnik Feb 14, 2020
f6f219f
move TrySetRUnning to TryAsBatch to avoid memory unpinning issues
adamsitnik Feb 15, 2020
13a95bc
something works, but very slow
adamsitnik Feb 16, 2020
771a169
Merge remote-tracking branch 'upstream/master' into bufferSocketReads
adamsitnik Feb 17, 2020
f652825
move batching to context, make sure the lock is taken
adamsitnik Feb 17, 2020
f5a6acb
change the way io events are handled
adamsitnik Feb 18, 2020
bfad248
commit what I have to be able to see nice diff on GH
adamsitnik Feb 18, 2020
f16c0af
revert some changes to make reading the diff easier
adamsitnik Feb 18, 2020
a446329
all tests pass!
adamsitnik Feb 20, 2020
0ba4dec
native AIO API improvements
adamsitnik Feb 21, 2020
8e4ed97
aioContext must be initialized before sending to iosetup
adamsitnik Feb 22, 2020
7352277
11 is EAGAIN
adamsitnik Feb 22, 2020
fb60f59
IOCB_CMD_NOOP is not ignored by AIO as stated in the docs, we can't h…
adamsitnik Feb 23, 2020
9cf5405
executing callback on epoll thread was a very bad idea
adamsitnik Feb 23, 2020
98214a1
take advantage of data locality and initialize the fields in the orde…
adamsitnik Feb 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/libraries/Common/src/Interop/Unix/System.Native/Interop.Aio.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Sys
{
[StructLayout(LayoutKind.Sequential)]
internal struct IoEvent
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tmds you rather mean the C struct here I guess?

{
internal ulong Data;
internal ulong Obj;
internal long Res;
internal long Res2;
}

[StructLayout(LayoutKind.Sequential)]
internal struct IoControlBlock
{
internal ulong AioData;

// these fields swap for big endian
// https://github.com/torvalds/linux/blob/0a679e13ea30f85a1aef0669ee0c5a9fd7860b34/include/uapi/linux/aio_abi.h#L77-L83
private uint _swappedField_1;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a little bit tricky: Linux Kernel has different order of these two fields depending on the endianess...

I've encapsulated that my making them private and adding internal properties with some simple logic that handles the endianess

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't bother too much for fields that aren't used.

private uint _swappedField_2;

internal ushort AioLioOpcode;
internal short AioReqprio;
internal uint AioFildes;

internal ulong AioBuf;
internal ulong AioNbytes;
internal long AioOffset;

internal ulong AioReserved2;

internal uint AioFlags;
internal uint AioResfd;

internal uint AioKey
{
get => BitConverter.IsLittleEndian ? _swappedField_1 : _swappedField_2;
set
{
if (BitConverter.IsLittleEndian)
{
_swappedField_1 = value;
}
else
{
_swappedField_2 = value;
}
}
}
internal int AioRwFlags
{
get => BitConverter.IsLittleEndian ? (int)_swappedField_2 : (int)_swappedField_1;
set
{
if (BitConverter.IsLittleEndian)
{
_swappedField_2 = (uint)value;
}
else
{
_swappedField_1 = (uint)value;
}
}
}
}

[StructLayout(LayoutKind.Sequential)]
internal struct AioRing
{
internal uint Id;
internal uint Nr;
internal uint Head;
internal uint Tail;
internal uint Magic;
internal uint CompatFeatures;
internal uint IncompatFeatures;
internal uint HeaderLength;

internal static unsafe IoEvent* IoEvents(AioRing* ring, int idx)
{
IoEvent* ev = (IoEvent*)(ring + 1);
return ev + idx;
}
}

[StructLayout(LayoutKind.Sequential)]
internal struct AioContext
{
internal unsafe AioRing* Ring;
}

internal static class IoControlBlockFlags
{
internal const int IOCB_CMD_PREAD = 0;
internal const int IOCB_CMD_PWRITE = 1;
internal const int IOCB_CMD_FSYNC = 2;
internal const int IOCB_CMD_FDSYNC = 3;
// 4 was the experimental IOCB_CMD_PREADX
internal const int IOCB_CMD_POLL = 5;
internal const int IOCB_CMD_NOOP = 6;
internal const int IOCB_CMD_PREADV = 7;
internal const int IOCB_CMD_PWRITEV = 8;
}

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_IsAioSupported")]
internal static extern bool IsAioSupported();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be implicitly done based on IoSetup return value.


[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_IoSetup", SetLastError = true)]
internal static extern unsafe int IoSetup(uint eventsCount, AioContext* context);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_IoDestroy", SetLastError = true)]
internal static extern unsafe int IoDestroy(AioRing* ring);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_IoSubmit", SetLastError = true)]
internal static extern unsafe int IoSubmit(AioRing* ring, long controlBlocksCount, IoControlBlock** ioControlBlocks);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_IoGetEvents", SetLastError = true)]
internal static extern unsafe int IoGetEvents(AioRing* ring, long minimumEventsCount, long maximumEventsCount, IoEvent* ioEvents);
}
}
1 change: 1 addition & 0 deletions src/libraries/Native/Unix/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
#cmakedefine01 HAVE_TCP_H_TCP_KEEPALIVE
#cmakedefine01 HAVE_BUILTIN_MUL_OVERFLOW
#cmakedefine01 HAVE_DISCONNECTX
#cmakedefine01 HAVE_LINUX_AIO

// Mac OS X has stat64, but it is deprecated since plain stat now
// provides the same 64-bit aware struct when targeting OS X > 10.5
Expand Down
53 changes: 53 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
#if HAVE_LINUX_CAN_H
#include <linux/can.h>
#endif
#if HAVE_LINUX_AIO
#include <linux/aio_abi.h>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important: the AIO is available on Linux, but not on all Unixes (like MacOS), hence I needed this switch

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIO is Linux only. No other OS will have this. Using #if defined(__linux) is also an option.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we do not support kernels where linux/aio_abi.h is not available, right?

#include <sys/syscall.h>
#endif
#if HAVE_KQUEUE
#if KEVENT_HAS_VOID_UDATA
static void* GetKeventUdata(uintptr_t udata)
Expand Down Expand Up @@ -2879,3 +2883,52 @@ uint32_t SystemNative_InterfaceNameToIndex(char* interfaceName)
interfaceName++;
return if_nametoindex(interfaceName);
}

int32_t SystemNative_IsAioSupported(void)
{
#if HAVE_LINUX_AIO
return true;
#else
return false;
#endif
}

int32_t SystemNative_IoSetup(uint32_t eventsCount, AioContext* context)
{
#if HAVE_LINUX_AIO
return (int32_t)syscall(__NR_io_setup, eventsCount, context);
#else
errno = ENOTSUP;
return -1;
#endif
}

int32_t SystemNative_IoDestroy(AioRing* ring)
{
#if HAVE_LINUX_AIO
return (int32_t)syscall(__NR_io_destroy, ring);
#else
errno = ENOTSUP;
return -1;
#endif
}

int32_t SystemNative_IoSubmit(AioRing* ring, int64_t controlBlocksCount, IoControlBlock** ioControlBlocks)
{
#if HAVE_LINUX_AIO
return (int32_t)syscall(__NR_io_submit, ring, controlBlocksCount, ioControlBlocks);
#else
errno = ENOTSUP;
return -1;
#endif
}

int32_t SystemNative_IoGetEvents(AioRing* ring, int64_t minEventsCount, int64_t maxEventsCount, IoEvent* ioEvents)
{
#if HAVE_LINUX_AIO
return (int32_t)syscall(__NR_io_getevents, ring, minEventsCount, maxEventsCount, ioEvents, NULL); // NULL is the timeout
#else
errno = ENOTSUP;
return -1;
#endif
}
51 changes: 51 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,47 @@ typedef struct
uint32_t Padding; // Pad out to 8-byte alignment
} SocketEvent;

typedef struct
{
uint64_t Data;
uint64_t Obj;
int64_t Res;
int64_t Res2;
} IoEvent;

typedef struct
{
uint64_t AioData;
uint32_t AioKey;
int32_t AioRwFlags;
uint16_t AioLioOpcode;
int16_t AioReqprio;
uint32_t AioFildes;
uint64_t AioBuf;
uint64_t AioNbytes;
int64_t AioOffset;
uint64_t AioReserved2;
uint32_t AioFlags;
uint32_t AioResfd;
} IoControlBlock;

typedef struct
{
uint32_t Id;
uint32_t Nr;
uint32_t Head;
uint32_t Tail;
uint32_t Magic;
uint32_t CompatFeatures;
uint32_t IncompatFeatures;
uint32_t HeaderLength;
} AioRing;

typedef struct
{
AioRing* Ring;
} AioContext;

DLLEXPORT int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry);

DLLEXPORT void SystemNative_FreeHostEntry(HostEntry* entry);
Expand Down Expand Up @@ -424,3 +465,13 @@ DLLEXPORT int32_t SystemNative_SendFile(intptr_t out_fd, intptr_t in_fd, int64_t
DLLEXPORT int32_t SystemNative_Disconnect(intptr_t socket);

DLLEXPORT uint32_t SystemNative_InterfaceNameToIndex(char* interfaceName);

DLLEXPORT int32_t SystemNative_IsAioSupported(void);

DLLEXPORT int32_t SystemNative_IoSetup(uint32_t eventsCount, AioContext* context);

DLLEXPORT int32_t SystemNative_IoDestroy(AioRing* ring);

DLLEXPORT int32_t SystemNative_IoSubmit(AioRing* ring, int64_t controlBlocksCount, IoControlBlock** ioControlBlocks);

DLLEXPORT int32_t SystemNative_IoGetEvents(AioRing* ring, int64_t minEventsCount, int64_t maxEventsCount, IoEvent* ioEvents);
11 changes: 11 additions & 0 deletions src/libraries/Native/Unix/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,17 @@ check_c_source_compiles(
"
HAVE_BUILTIN_MUL_OVERFLOW)

check_c_source_compiles(
"
#include <linux/aio_abi.h>
int main(void)
{
int x = IOCB_CMD_PREAD;
return x;
}
"
HAVE_LINUX_AIO)

configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Common/pal_config.h.in
${CMAKE_CURRENT_BINARY_DIR}/Common/pal_config.h)
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Accept.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Accept.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Aio.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Aio.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Bind.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Bind.cs</Link>
</Compile>
Expand Down Expand Up @@ -403,7 +406,4 @@
<Reference Include="System.Threading.Overlapped" />
<Reference Include="System.Threading.ThreadPool" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Reference Include="System.Threading.ThreadPool" />
</ItemGroup>
</Project>
Loading