[Phi] fixing device memory creation, implemented AVX copy and fill for
Mirror Gitea refs to GitHub / mirror (push) Successful in 18s
Build / build (push) Failing after 58s
Test / build_and_test (push) Failing after 1m5s

vkCmdCopyBuffers and vkCmdFillBuffer
This commit is contained in:
2026-08-18 02:44:24 +02:00
parent 67314a71ae
commit d5a794aa64
25 changed files with 569 additions and 452 deletions
+1 -27
View File
@@ -235,33 +235,7 @@ To bring forth the driver:
zig build phi --release=[fast|safe|small] zig build phi --release=[fast|safe|small]
``` ```
Shouldst thou endeavor to forge Phi in the absence of thy Xeon Phi card, thou must permit the emulation of the host: In this manner, the daemon is cross-compiled by way of `k1om-mpss-linux-gcc`, delivered unto its destination via SSH, and speaketh through the SCIF.
```
zig build phi -Dphi-host-emulation=true --release=[fast|safe|small]
```
Take heed, for this path is forged solely for development; thou shalt find that even the execution of shaders may fail to manifest when thou requirest them most.
Behold how this creation is wrought: In this state, the daemon is compiled natively by the power of Zig's `std.Build`, and set within `zig-out/bin/phi_device-host`.
The driver revealeth but one synthetic Phi device, its voice reaching the daemon through an IPv4 TCP socket tethered only to `127.0.0.1`.
For the client socket, thou shalt find it fashioned directly from the `std.Io.net` API; it escheweth the use of C transport functions and requireth no summoning of `libmicmgmt` or `libscif`.
The driver first endeavoreth to bind itself to a daemon already manifest. If the void remaineth silent, it shall launch its embedded daemon from within, retrying the bond.
This transient essence shall vanish from the filesystem once its task is begun. Yet, thou art not bound to this way; thou mayest also start the daemon by thine own hand:
```
./zig-out/bin/phi_device-host
```
The loopback port resideth by default at `43616`, yet thou mayest decree another during the hour of forging, for both driver and daemon alike:
```
zig build phi -Dphi-host-emulation=true -Dphi-emulation-port=43617
```
Shouldst thou forgo the `-Dphi-host-emulation=true` decree, the true path of the Xeon Phi remaineth.
In this manner, the daemon is cross-compiled by way of `k1om-mpss-linux-gcc`, delivered unto its destination via SSH/SCP, and speaketh through the SCIF.\
For those who seek a different method of creation: For those who seek a different method of creation:
+35 -74
View File
@@ -516,29 +516,8 @@ fn customPhi(
options.addOption([]const u8, "phi_daemon_remote_path", daemon_remote_path); options.addOption([]const u8, "phi_daemon_remote_path", daemon_remote_path);
options.addOption([]const u8, "phi_daemon_host_prefix", daemon_host_prefix); options.addOption([]const u8, "phi_daemon_host_prefix", daemon_host_prefix);
const host_emulation = b.option(
bool,
"phi-host-emulation",
"Run the Phi device daemon on the host over a loopback TCP socket (/!\\ Intended for development use only /!\\)",
) orelse false;
const emulation_port = b.option(
u16,
"phi-emulation-port",
"Loopback TCP port used by Phi host emulation",
) orelse 43616;
options.addOption(bool, "phi_host_emulation", host_emulation);
options.addOption(u16, "phi_emulation_port", emulation_port);
lib_mod.addImport("phi_c", base_c_mod); lib_mod.addImport("phi_c", base_c_mod);
if (host_emulation) {
lib_mod.addImport("miclib", b.createModule(.{
.root_source_file = b.path("src/phi/mic_stub.zig"),
.target = target,
.optimize = optimize,
}));
} else {
const miclib = b.lazyDependency("miclib", .{ const miclib = b.lazyDependency("miclib", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
@@ -546,7 +525,6 @@ fn customPhi(
}) orelse return error.UnresolvedDependency; }) orelse return error.UnresolvedDependency;
lib_mod.addImport("miclib", miclib.module("miclib")); lib_mod.addImport("miclib", miclib.module("miclib"));
}
const phi_protocol_c = b.addTranslateC(.{ const phi_protocol_c = b.addTranslateC(.{
.root_source_file = b.path("src/phi/shared/Protocol.h"), .root_source_file = b.path("src/phi/shared/Protocol.h"),
@@ -578,11 +556,8 @@ fn customPhi(
"MPSS sysroot path", "MPSS sysroot path",
); );
const daemon = try addPhiDaemon(b, optimize, host_emulation, emulation_port, cc, sysroot); const daemon = try addPhiDaemon(b, optimize, cc, sysroot);
const install_daemon = b.addInstallFile( const install_daemon = b.addInstallFile(daemon, "lib/phi_device.mic");
daemon,
if (host_emulation) "bin/phi_device-host" else "lib/phi_device.mic",
);
lib.step.dependOn(&install_daemon.step); lib.step.dependOn(&install_daemon.step);
const embedded_daemon = addEmbeddedPhiDaemon(b, daemon); const embedded_daemon = addEmbeddedPhiDaemon(b, daemon);
@@ -591,19 +566,12 @@ fn customPhi(
}); });
} }
fn addPhiDaemon( fn addPhiDaemonCompilerArgs(
cmd: *Step.Run,
b: *std.Build, b: *std.Build,
optimize: std.builtin.OptimizeMode, optimize: std.builtin.OptimizeMode,
host_emulation: bool,
emulation_port: u16,
cc: []const u8,
sysroot: ?[]const u8, sysroot: ?[]const u8,
) !std.Build.LazyPath { ) void {
if (host_emulation)
return addPhiHostDaemon(b, optimize, emulation_port);
const cmd = b.addSystemCommand(&.{cc});
cmd.addArgs(&.{ cmd.addArgs(&.{
"-std=c11", "-std=c11",
"-Wall", "-Wall",
@@ -628,6 +596,11 @@ fn addPhiDaemon(
.ReleaseFast => cmd.addArgs(&.{ "-O3", "-DNDEBUG" }), .ReleaseFast => cmd.addArgs(&.{ "-O3", "-DNDEBUG" }),
.ReleaseSmall => cmd.addArgs(&.{ "-Os", "-DNDEBUG" }), .ReleaseSmall => cmd.addArgs(&.{ "-Os", "-DNDEBUG" }),
} }
}
fn addPhiDaemon(b: *std.Build, optimize: std.builtin.OptimizeMode, cc: []const u8, sysroot: ?[]const u8) !std.Build.LazyPath {
const cmd = b.addSystemCommand(&.{cc});
addPhiDaemonCompilerArgs(cmd, b, optimize, sysroot);
const sources = [_][]const u8{ const sources = [_][]const u8{
"src/phi/mic/main.c", "src/phi/mic/main.c",
@@ -637,51 +610,39 @@ fn addPhiDaemon(
"src/phi/mic/Logger.c", "src/phi/mic/Logger.c",
"src/phi/mic/Memory.c", "src/phi/mic/Memory.c",
"src/phi/mic/Transport.c", "src/phi/mic/Transport.c",
// Add new files here // Add non-AVX files here
}; };
for (sources) |source| { for (sources) |source| {
cmd.addFileArg(b.path(source)); cmd.addFileArg(b.path(source));
} }
cmd.addArgs(&.{ "-lscif", "-o" }); // Keep KNC AVX-512/IMCI code in separate translation units. This GCC
return cmd.addOutputFileArg("phi_device.mic"); // port must not compile the daemon's scalar/control code with -mavx512f.
const avx_sources = [_][]const u8{
"src/phi/mic/avx/Copy.c",
"src/phi/mic/avx/Fill.c",
// Add AVX files here
};
for (avx_sources, 0..) |source, index| {
const avx_cmd = b.addSystemCommand(&.{cc});
addPhiDaemonCompilerArgs(avx_cmd, b, optimize, sysroot);
avx_cmd.addArg("-mavx512f");
avx_cmd.addArg("-c");
avx_cmd.addFileArg(b.path(source));
avx_cmd.addArg("-o");
const avx_object = avx_cmd.addOutputFileArg(
b.fmt("phi_avx_{d}.o", .{index}),
);
cmd.addFileArg(avx_object);
} }
fn addPhiHostDaemon(b: *std.Build, optimize: std.builtin.OptimizeMode, emulation_port: u16) std.Build.LazyPath { cmd.addArgs(&.{ "-lscif", "-o" });
const daemon_mod = b.createModule(.{ return cmd.addOutputFileArg("phi_device.mic");
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
daemon_mod.addIncludePath(b.path("src/phi/mic"));
daemon_mod.addIncludePath(b.path("src/phi/shared"));
daemon_mod.addCMacro("PHI_HOST_EMULATION", "1");
daemon_mod.addCMacro("PHI_TRANSPORT_PORT", b.fmt("{d}", .{emulation_port}));
daemon_mod.addCSourceFiles(.{
.files = &.{
"src/phi/mic/main.c",
"src/phi/mic/Buffer.c",
"src/phi/mic/CommandBuffer.c",
"src/phi/mic/Daemon.c",
"src/phi/mic/Logger.c",
"src/phi/mic/Memory.c",
"src/phi/mic/Transport.c",
},
.flags = &.{
"-std=c11",
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
},
});
daemon_mod.linkSystemLibrary("pthread", .{});
const daemon = b.addExecutable(.{
.name = "phi_device-host",
.root_module = daemon_mod,
});
return daemon.getEmittedBin();
} }
fn addEmbeddedPhiDaemon(b: *std.Build, daemon: std.Build.LazyPath) std.Build.LazyPath { fn addEmbeddedPhiDaemon(b: *std.Build, daemon: std.Build.LazyPath) std.Build.LazyPath {
+3 -3
View File
@@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const proto = @import("lib.zig").proto;
const VkError = base.VkError; const VkError = base.VkError;
@@ -31,7 +32,6 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
allocator.destroy(self); allocator.destroy(self);
} }
pub fn getMemoryRequirements(interface: *Interface, requirements: *vk.MemoryRequirements) void { pub fn getMemoryRequirements(_: *Interface, requirements: *vk.MemoryRequirements) void {
_ = interface; requirements.alignment = proto.PHI_MEMORY_ALIGNMENT;
_ = requirements;
} }
-3
View File
@@ -120,9 +120,6 @@ fn appendCommand(self: *Self, comptime T: type, command_type: c_int, payload: T)
fn remoteMemory(buffer: *base.Buffer) VkError!*PhiDeviceMemory { fn remoteMemory(buffer: *base.Buffer) VkError!*PhiDeviceMemory {
const memory = buffer.memory orelse return VkError.ValidationFailed; const memory = buffer.memory orelse return VkError.ValidationFailed;
const phi_memory: *PhiDeviceMemory = @alignCast(@fieldParentPtr("interface", memory)); const phi_memory: *PhiDeviceMemory = @alignCast(@fieldParentPtr("interface", memory));
if (phi_memory.remote_handle == 0) {
return VkError.ValidationFailed;
}
return phi_memory; return phi_memory;
} }
-36
View File
@@ -82,9 +82,6 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a
const transport = PhiTransport.init(instance, phi_physical_device.scif_node_id) catch blk: { const transport = PhiTransport.init(instance, phi_physical_device.scif_node_id) catch blk: {
// If the first connection failed, launch the daemon on the selected device. // If the first connection failed, launch the daemon on the selected device.
if (comptime config.phi_host_emulation)
try launchHostDaemon(instance, allocator)
else
try uploadAndLaunchDaemon(instance, allocator, phi_physical_device.mic_device_num); try uploadAndLaunchDaemon(instance, allocator, phi_physical_device.mic_device_num);
const max_connect_attempts = 3; const max_connect_attempts = 3;
@@ -245,39 +242,6 @@ pub fn getDeviceGroupSurfacePresentModesKHR(_: *Interface, _: *base.SurfaceKHR)
return .{ .local_bit_khr = true }; return .{ .local_bit_khr = true };
} }
fn launchHostDaemon(instance: *base.Instance, allocator: std.mem.Allocator) VkError!void {
const io = instance.io();
const process_id = std.os.linux.getpid();
const thread_id = std.Thread.getCurrentId();
const local_path = std.fmt.allocPrint(allocator, "/tmp/ape_phi_device_{d}_{d}.host", .{ process_id, thread_id }) catch return VkError.OutOfHostMemory;
defer allocator.free(local_path);
errdefer std.Io.Dir.deleteFileAbsolute(io, local_path) catch |err| {
std.log.scoped(.PhiDevice).warn("Failed to remove Phi host daemon after launch error: {s}", .{@errorName(err)});
};
std.Io.Dir.writeFile(.cwd(), io, .{
.sub_path = local_path,
.data = daemon_binary,
}) catch |err| {
std.log.scoped(.PhiDevice).err("Failed to write embedded Phi host daemon: {s}", .{@errorName(err)});
return VkError.InitializationFailed;
};
const launch_command = std.fmt.allocPrint(
allocator,
"chmod +x {s} && (nohup {s} --unlink-on-start >/tmp/phi_device_host.log 2>&1 </dev/null &)",
.{ local_path, local_path },
) catch return VkError.OutOfHostMemory;
defer allocator.free(launch_command);
std.log.scoped(.PhiDevice).debug(
"Launching Phi host daemon on 127.0.0.1:{d}",
.{config.phi_emulation_port},
);
try runHostCommand(instance, allocator, &.{ "sh", "-c", launch_command });
}
fn uploadAndLaunchDaemon(instance: *base.Instance, allocator: std.mem.Allocator, mic_device_num: u32) VkError!void { fn uploadAndLaunchDaemon(instance: *base.Instance, allocator: std.mem.Allocator, mic_device_num: u32) VkError!void {
const io = instance.io(); const io = instance.io();
const process_id = std.os.linux.getpid(); const process_id = std.os.linux.getpid();
+110 -36
View File
@@ -13,14 +13,42 @@ const Self = @This();
pub const Interface = base.DeviceMemory; pub const Interface = base.DeviceMemory;
interface: Interface, interface: Interface,
remote_handle: u64, remote_handle: u64,
scif_offset: ?u64,
/// Size of the region registered with SCIF.
/// This is allocation size rounded up to page size.
registered_size: usize,
/// Bytes exposed through vkMapMemory.
/// For HOST_VISIBLE memory this is a slice of host_backing.
data: ?[]u8, data: ?[]u8,
/// Full page-aligned/page-rounded allocation registered with SCIF.
host_backing: ?[]u8,
pub fn create(device: *PhiDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self { pub fn create(device: *PhiDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self {
if (memory_type_index >= device.interface.physical_device.mem_props.memory_type_count) {
return VkError.ValidationFailed;
}
const allocation_size =
std.math.cast(usize, size) orelse return VkError.OutOfDeviceMemory;
const memory_type =
device.interface.physical_device.mem_props.memory_types[memory_type_index];
const host_visible = memory_type.property_flags.host_visible_bit;
const self = allocator.create(Self) catch return VkError.OutOfHostMemory; const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
var interface = try Interface.init(&device.interface, size, memory_type_index); var interface = try Interface.init(
&device.interface,
size,
memory_type_index,
);
interface.vtable = &.{ interface.vtable = &.{
.destroy = destroy, .destroy = destroy,
@@ -30,68 +58,109 @@ pub fn create(device: *PhiDevice, allocator: std.mem.Allocator, size: vk.DeviceS
.invalidateRange = invalidateRange, .invalidateRange = invalidateRange,
}; };
if (memory_type_index >= device.interface.physical_device.mem_props.memory_type_count) { if (host_visible) {
return VkError.ValidationFailed; const page_size = std.heap.pageSize();
} const registered_size = std.mem.alignForward(usize, allocation_size, page_size);
const memory_type = device.interface.physical_device.mem_props.memory_types[memory_type_index]; // This needs to be page aligned
const host_visible = memory_type.property_flags.host_visible_bit; const backing = device.interface.device_allocator.allocator().alignedAlloc(u8, .fromByteUnits(std.heap.page_size_max), registered_size) catch return VkError.OutOfHostMemory;
const device_local = memory_type.property_flags.device_local_bit; errdefer device.interface.device_allocator.allocator().free(backing);
const allocation_size = std.math.cast(usize, size) orelse return VkError.OutOfDeviceMemory;
const remote_handle = if (device_local) blk: { const offset = device.transport.registerHostMemory(backing) catch return VkError.OutOfHostMemory;
const alloc_request: proto.PhiAllocMemoryRequest = .{ errdefer device.transport.unregisterHostMemory(offset, backing.len) catch {};
.size = size,
.memory_type_index = memory_type_index, const request: proto.PhiMapHostMemoryRequest = .{
.flags = 0, .scif_offset = offset,
.scif_size = backing.len,
.size = allocation_size,
}; };
var reply = std.mem.zeroes(proto.PhiAllocMemoryReply); var reply = std.mem.zeroes(proto.PhiNewMemoryReply);
try device.transport.request(proto.PHI_PACKET_ALLOC_MEMORY, std.mem.asBytes(&alloc_request), std.mem.asBytes(&reply));
try device.transport.request(
proto.PHI_PACKET_MAP_HOST_MEMORY,
std.mem.asBytes(&request),
std.mem.asBytes(&reply),
);
if (reply.result.status != proto.PHI_STATUS_OK) { if (reply.result.status != proto.PHI_STATUS_OK) {
return PhiTransport.statusToErr(reply.result.status); return PhiTransport.statusToErr(reply.result.status);
} }
std.log.scoped(.PhiDeviceMemory).info("Recieved remote handle 0x{X}", .{reply.remote_handle}); self.* = .{
.interface = interface,
.remote_handle = reply.remote_handle,
.scif_offset = offset,
.registered_size = registered_size,
.data = backing[0..allocation_size],
.host_backing = backing,
};
} else {
const request: proto.PhiAllocMemoryRequest = .{
.size = size,
.memory_type_index = memory_type_index,
.flags = 0,
};
break :blk reply.remote_handle; var reply = std.mem.zeroes(proto.PhiNewMemoryReply);
} else 0;
errdefer if (remote_handle != 0) self.interface.destroy(allocator);
const data = if (host_visible) try device.transport.request(
device.interface.device_allocator.allocator().alloc(u8, allocation_size) catch return VkError.OutOfDeviceMemory proto.PHI_PACKET_ALLOC_MEMORY,
else std.mem.asBytes(&request),
null; std.mem.asBytes(&reply),
);
if (reply.result.status != proto.PHI_STATUS_OK) {
return PhiTransport.statusToErr(reply.result.status);
}
self.* = .{ self.* = .{
.interface = interface, .interface = interface,
.remote_handle = remote_handle, .remote_handle = reply.remote_handle,
.data = data, .scif_offset = null,
.registered_size = 0,
.data = null,
.host_backing = null,
}; };
}
return self; return self;
} }
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self =
const device: *PhiDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); @alignCast(@fieldParentPtr("interface", interface));
if (self.data) |data| {
interface.owner.device_allocator.allocator().free(data); const device: *PhiDevice =
} @alignCast(@fieldParentPtr("interface", interface.owner));
if (self.remote_handle != 0) { if (self.remote_handle != 0) {
const request_payload: proto.PhiFreeMemoryRequest = .{ const request_payload: proto.PhiDestroyMemoryRequest = .{
.remote_handle = self.remote_handle, .remote_handle = self.remote_handle,
}; };
var reply: proto.PhiFreeMemoryReply = undefined;
device.transport.request(proto.PHI_PACKET_FREE_MEMORY, std.mem.asBytes(&request_payload), std.mem.asBytes(&reply)) catch |err| { var reply = std.mem.zeroes(proto.PhiResultReply);
std.log.scoped(.PhiTransport).err("Remote free failed: {s}", .{@errorName(err)});
device.transport.request(proto.PHI_PACKET_DESTROY_MEMORY, std.mem.asBytes(&request_payload), std.mem.asBytes(&reply)) catch |err| {
std.log.scoped(.PhiDeviceMemory).err("Remote free/unmap failed for handle 0x{X}: {s}", .{ self.remote_handle, @errorName(err) });
return; return;
}; };
if (reply.result.status != proto.PHI_STATUS_OK) { if (reply.result.status != proto.PHI_STATUS_OK) {
std.log.scoped(.PhiTransport).err("Remote free returned status {d}", .{reply.result.status}); std.log.scoped(.PhiDeviceMemory).err("Remote free/unmap for handle 0x{X} returned status {d}", .{ self.remote_handle, reply.result.status });
} }
} }
if (self.scif_offset) |scif_offset| {
device.transport.unregisterHostMemory(scif_offset, self.interface.size) catch |err| {
std.log.scoped(.PhiDeviceMemory).err("SCIF unregister failed: {s}", .{@errorName(err)});
};
}
if (self.host_backing) |host_backing| {
interface.owner.device_allocator.allocator().free(host_backing);
}
allocator.destroy(self); allocator.destroy(self);
} }
@@ -109,19 +178,24 @@ pub fn invalidateRange(interface: *Interface, offset: vk.DeviceSize, size: vk.De
pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const data = self.data orelse return VkError.MemoryMapFailed; const data = self.data orelse return VkError.MemoryMapFailed;
const map_offset = std.math.cast(usize, offset) orelse return VkError.MemoryMapFailed; const map_offset = std.math.cast(usize, offset) orelse return VkError.MemoryMapFailed;
if (map_offset >= data.len) { if (map_offset >= data.len) {
return VkError.MemoryMapFailed; return VkError.MemoryMapFailed;
} }
const map_size = if (size == vk.WHOLE_SIZE) const map_size = if (size == vk.WHOLE_SIZE)
data.len - map_offset data.len - map_offset
else else
std.math.cast(usize, size) orelse return VkError.MemoryMapFailed; std.math.cast(usize, size) orelse return VkError.MemoryMapFailed;
if (map_size > data.len - map_offset) { if (map_size > data.len - map_offset) {
return VkError.MemoryMapFailed; return VkError.MemoryMapFailed;
} }
return data[map_offset..(map_offset + map_size)];
return data[map_offset .. map_offset + map_size];
} }
pub fn unmap(_: *Interface) void {} pub fn unmap(_: *Interface) void {}
-12
View File
@@ -61,7 +61,6 @@ fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void {
self.threaded.deinit(); self.threaded.deinit();
allocator.destroy(self); allocator.destroy(self);
if (comptime !lib.config.phi_host_emulation)
mic.unload(); mic.unload();
} }
@@ -70,17 +69,6 @@ fn requestPhysicalDevices(interface: *Interface, allocator: std.mem.Allocator, _
return; return;
} }
if (comptime lib.config.phi_host_emulation) {
const physical_device = try PhiPhysicalDevice.createEmulated(allocator, interface);
errdefer physical_device.interface.release(allocator) catch @panic("Caught an error while handling an error");
const dispatchable = try Dispatchable(base.PhysicalDevice).wrap(allocator, &physical_device.interface);
errdefer dispatchable.destroy(allocator);
interface.physical_devices.append(allocator, dispatchable) catch return VkError.OutOfHostMemory;
return;
}
mic.load() catch |err| { mic.load() catch |err| {
std.log.scoped(.MIC).err("Failed to load libmicmgmt: {s}", .{@errorName(err)}); std.log.scoped(.MIC).err("Failed to load libmicmgmt: {s}", .{@errorName(err)});
return VkError.InitializationFailed; return VkError.InitializationFailed;
+1 -31
View File
@@ -33,22 +33,7 @@ interface: Interface,
scif_node_id: u16, scif_node_id: u16,
mic_device_num: u32, mic_device_num: u32,
pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, mic_device: mic.Device, mic_device_num: u32) VkError!*Self { pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, mic_device: ?mic.Device, mic_device_num: u32) VkError!*Self {
if (comptime lib.config.phi_host_emulation) {
return VkError.InitializationFailed;
} else {
return createInternal(allocator, instance, mic_device, mic_device_num);
}
}
pub fn createEmulated(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*Self {
if (comptime lib.config.phi_host_emulation)
return createInternal(allocator, instance, null, 0)
else
return VkError.InitializationFailed;
}
fn createInternal(allocator: std.mem.Allocator, instance: *base.Instance, mic_device: ?mic.Device, mic_device_num: u32) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory; const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@@ -76,12 +61,6 @@ fn createInternal(allocator: std.mem.Allocator, instance: *base.Instance, mic_de
@memset(interface.props.device_name[0..], 0); @memset(interface.props.device_name[0..], 0);
if (comptime lib.config.phi_host_emulation) {
interface.props.vendor_id = 0x8086;
interface.props.device_id = 0x2250;
const name = "Intel(R) Xeon Phi(TM) Coprocessor Host Emulation [Phi ApeDriver]";
@memcpy(interface.props.device_name[0..name.len], name);
} else {
const device = mic_device.?; const device = mic_device.?;
if (device.pciConfig()) |pci_value| { if (device.pciConfig()) |pci_value| {
var pci = pci_value; var pci = pci_value;
@@ -107,7 +86,6 @@ fn createInternal(allocator: std.mem.Allocator, instance: *base.Instance, mic_de
std.log.scoped(.MIC).err("Failed to fetch device PCI config: {s}", .{@errorName(err)}); std.log.scoped(.MIC).err("Failed to fetch device PCI config: {s}", .{@errorName(err)});
return VkError.InitializationFailed; return VkError.InitializationFailed;
} }
}
interface.props.pipeline_cache_uuid = @splat(0); interface.props.pipeline_cache_uuid = @splat(0);
interface.props.limits = .{ interface.props.limits = .{
@@ -248,13 +226,6 @@ fn createInternal(allocator: std.mem.Allocator, instance: *base.Instance, mic_de
} }
interface.mem_props.memory_heap_count = 2; interface.mem_props.memory_heap_count = 2;
if (comptime lib.config.phi_host_emulation) {
interface.mem_props.memory_heaps[0] = .{
.size = std.process.totalSystemMemory() catch 0,
.flags = .{ .device_local_bit = true },
};
} else {
const device = mic_device.?;
if (device.memoryInfo()) |memory_value| { if (device.memoryInfo()) |memory_value| {
var memory = memory_value; var memory = memory_value;
defer memory.deinit(); defer memory.deinit();
@@ -267,7 +238,6 @@ fn createInternal(allocator: std.mem.Allocator, instance: *base.Instance, mic_de
std.log.scoped(.MIC).err("Failed to fetch device memory infos: {s}", .{@errorName(err)}); std.log.scoped(.MIC).err("Failed to fetch device memory infos: {s}", .{@errorName(err)});
return VkError.InitializationFailed; return VkError.InitializationFailed;
} }
}
interface.mem_props.memory_heaps[1] = .{ interface.mem_props.memory_heaps[1] = .{
.size = std.process.totalSystemMemory() catch 0, .size = std.process.totalSystemMemory() catch 0,
.flags = .{}, .flags = .{},
+1 -1
View File
@@ -66,7 +66,7 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, fence: ?*bas
@memcpy(payload[@sizeOf(proto.PhiWorkExecutionRequest)..], phi_command_buffer.commands.items); @memcpy(payload[@sizeOf(proto.PhiWorkExecutionRequest)..], phi_command_buffer.commands.items);
// Synchronous queues for now // Synchronous queues for now
var reply = std.mem.zeroes(proto.PhiWorkExecutionReply); var reply = std.mem.zeroes(proto.PhiResultReply);
try device.transport.request(proto.PHI_PACKET_WORK_EXECUTION, payload, std.mem.asBytes(&reply)); try device.transport.request(proto.PHI_PACKET_WORK_EXECUTION, payload, std.mem.asBytes(&reply));
if (reply.result.status != proto.PHI_STATUS_OK) { if (reply.result.status != proto.PHI_STATUS_OK) {
+27 -42
View File
@@ -5,7 +5,7 @@ const scif = @import("scif.zig");
const VkError = base.VkError; const VkError = base.VkError;
const proto = lib.proto; const proto = lib.proto;
const Endpoint = if (lib.config.phi_host_emulation) std.Io.net.Stream else scif.epd_t; const Endpoint = scif.epd_t;
const Self = @This(); const Self = @This();
@@ -15,19 +15,7 @@ mutex: std.Io.Mutex = .init,
instance: *base.Instance, instance: *base.Instance,
pub fn init(instance: *base.Instance, node_id: u16) VkError!Self { pub fn init(instance: *base.Instance, node_id: u16) VkError!Self {
const epd = if (comptime lib.config.phi_host_emulation) blk: { const epd = blk: {
const address: std.Io.net.IpAddress = .{
.ip4 = .loopback(lib.config.phi_emulation_port),
};
const stream = address.connect(instance.io(), .{ .mode = .stream }) catch |err| {
std.log.scoped(.PhiTransport).err(
"TCP connection to 127.0.0.1:{d} failed: {s}",
.{ lib.config.phi_emulation_port, @errorName(err) },
);
return VkError.InitializationFailed;
};
break :blk stream;
} else blk: {
try scif.load(); try scif.load();
errdefer scif.unload(); errdefer scif.unload();
@@ -50,8 +38,7 @@ pub fn init(instance: *base.Instance, node_id: u16) VkError!Self {
break :blk endpoint; break :blk endpoint;
}; };
errdefer { errdefer {
closeEndpoint(epd, instance.io()); closeEndpoint(epd);
if (comptime !lib.config.phi_host_emulation)
scif.unload(); scif.unload();
} }
@@ -71,8 +58,7 @@ pub fn deinit(self: *Self) void {
std.log.scoped(.PhiTransport).warn("Failed to shut down remote session: {s}", .{@errorName(err)}); std.log.scoped(.PhiTransport).warn("Failed to shut down remote session: {s}", .{@errorName(err)});
}; };
closeEndpoint(self.epd, self.instance.io()); closeEndpoint(self.epd);
if (comptime !lib.config.phi_host_emulation)
scif.unload(); scif.unload();
std.log.scoped(.PhiTransport).info("Closed connection", .{}); std.log.scoped(.PhiTransport).info("Closed connection", .{});
} }
@@ -116,21 +102,12 @@ pub fn statusToErr(status: c_int) VkError {
return switch (status) { return switch (status) {
proto.PHI_STATUS_OUT_OF_MEMORY => VkError.OutOfDeviceMemory, proto.PHI_STATUS_OUT_OF_MEMORY => VkError.OutOfDeviceMemory,
proto.PHI_STATUS_UNSUPPORTED_VERSION => VkError.InitializationFailed, proto.PHI_STATUS_UNSUPPORTED_VERSION => VkError.InitializationFailed,
proto.PHI_STATUS_INVALID_ARGUMENT => VkError.ValidationFailed,
else => VkError.Unknown, else => VkError.Unknown,
}; };
} }
fn writeAll(self: *Self, bytes: []const u8) VkError!void { fn writeAll(self: *Self, bytes: []const u8) VkError!void {
if (comptime lib.config.phi_host_emulation) {
var buffer: [0]u8 = .{};
var writer = self.epd.writer(self.instance.io(), &buffer);
writer.interface.writeAll(bytes) catch |err| {
std.log.scoped(.PhiTransport).err("TCP send failed: {s}", .{@errorName(err)});
return VkError.InitializationFailed;
};
return;
}
var offset: usize = 0; var offset: usize = 0;
while (offset < bytes.len) { while (offset < bytes.len) {
const written = scif.send(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.send_block); const written = scif.send(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.send_block);
@@ -142,16 +119,6 @@ fn writeAll(self: *Self, bytes: []const u8) VkError!void {
} }
fn readAll(self: *Self, bytes: []u8) VkError!void { fn readAll(self: *Self, bytes: []u8) VkError!void {
if (comptime lib.config.phi_host_emulation) {
var buffer: [0]u8 = .{};
var reader = self.epd.reader(self.instance.io(), &buffer);
reader.interface.readSliceAll(bytes) catch |err| {
std.log.scoped(.PhiTransport).err("TCP receive failed: {s}", .{@errorName(err)});
return VkError.InitializationFailed;
};
return;
}
var offset: usize = 0; var offset: usize = 0;
while (offset < bytes.len) { while (offset < bytes.len) {
const read = scif.recv(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.recv_block); const read = scif.recv(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.recv_block);
@@ -162,10 +129,7 @@ fn readAll(self: *Self, bytes: []u8) VkError!void {
} }
} }
fn closeEndpoint(endpoint: Endpoint, io: std.Io) void { fn closeEndpoint(endpoint: Endpoint) void {
if (comptime lib.config.phi_host_emulation)
endpoint.close(io)
else
_ = scif.close(endpoint); _ = scif.close(endpoint);
} }
@@ -186,3 +150,24 @@ fn handshake(self: *Self) VkError!void {
return VkError.InitializationFailed; return VkError.InitializationFailed;
} }
} }
pub fn registerHostMemory(self: *Self, memory: []u8) VkError!u64 {
const offset = scif.register(
self.epd,
memory.ptr,
memory.len,
0,
@intFromEnum(scif.Prot.read) | @intFromEnum(scif.Prot.write),
0,
);
if (offset < 0) {
return VkError.Unknown;
}
return @intCast(offset);
}
pub fn unregisterHostMemory(self: *Self, offset: u64, size: usize) VkError!void {
if (scif.unregister(self.epd, @intCast(offset), size) != 0) {
return VkError.Unknown;
}
}
+51 -7
View File
@@ -1,6 +1,7 @@
#include <Buffer.h> #include <Buffer.h>
#include <Memory.h>
#include <string.h> #include <avx/Avx.h>
int PhiIsBufferCommand(const PhiCmdHeader* header) int PhiIsBufferCommand(const PhiCmdHeader* header)
{ {
@@ -25,10 +26,13 @@ static PhiStatus CopyBuffer(PhiCommandReader* reader)
if(command.src_memory == 0 || command.dst_memory == 0) if(command.src_memory == 0 || command.dst_memory == 0)
return PHI_STATUS_INVALID_HANDLE; return PHI_STATUS_INVALID_HANDLE;
void* dst = (void*)((uintptr_t)command.dst_memory + (uintptr_t)command.dst_offset); Memory* dst_memory = (Memory*)command.dst_memory;
const void* src = (const void*)((uintptr_t)command.src_memory + (uintptr_t)command.src_offset); const Memory* src_memory = (const Memory*)command.src_memory;
memcpy(dst, src, (size_t)command.size); uint8_t* dst = (uint8_t*)dst_memory->ptr + (size_t)command.dst_offset;
const uint8_t* src = (const uint8_t*)src_memory->ptr + (size_t)command.src_offset;
AvxCopy(dst, src, (size_t)command.size);
return PHI_STATUS_OK; return PHI_STATUS_OK;
} }
@@ -36,17 +40,57 @@ static PhiStatus CopyBuffer(PhiCommandReader* reader)
static PhiStatus FillBuffer(PhiCommandReader* reader) static PhiStatus FillBuffer(PhiCommandReader* reader)
{ {
PhiCmdFillBuffer command; PhiCmdFillBuffer command;
PhiStatus status = PhiReadCommandData(reader, &command, sizeof(command)); PhiStatus status = PhiReadCommandData(reader, &command, sizeof(command));
if(status != PHI_STATUS_OK) if(status != PHI_STATUS_OK)
return status; return status;
if(command.memory == 0) if(command.memory == 0)
return PHI_STATUS_INVALID_HANDLE; return PHI_STATUS_INVALID_HANDLE;
uint32_t* dst = (uint32_t*)((uintptr_t)command.memory + (uintptr_t)command.offset); Memory* memory = (Memory*)command.memory;
for(; command.size >= 4; command.size -= 4, dst++) uint8_t* dst = (uint8_t*)memory->ptr + (size_t)command.offset;
*dst = command.data;
size_t size = (size_t)command.size;
const uint32_t value = command.data;
// Check if dst and size are 4-byte aligned
if((((uintptr_t)dst | size) & 3) != 0)
return PHI_STATUS_INVALID_ARGUMENT;
// Bring dst to a 64-byte cache-line boundary.
while(size >= 4 && ((uintptr_t)dst & 63) != 0)
{
*(uint32_t*)dst = value;
dst += 4;
size -= 4;
}
while(size >= 256)
{
AvxFill256(dst, value);
dst += 256;
size -= 256;
}
while(size >= 64)
{
AvxFill64(dst, value);
dst += 64;
size -= 64;
}
uint32_t* tail = (uint32_t*)dst;
while(size >= 4)
{
*tail++ = value;
size -= 4;
}
return PHI_STATUS_OK; return PHI_STATUS_OK;
} }
+1 -1
View File
@@ -47,7 +47,7 @@ static PhiStatus ExecuteCommand(PhiCommandReader* reader, const PhiCmdHeader* co
int HandleWorkExecution(PhiEndpoint endpoint, const PhiMessageHeader* header) int HandleWorkExecution(PhiEndpoint endpoint, const PhiMessageHeader* header)
{ {
PhiWorkExecutionRequest request; PhiWorkExecutionRequest request;
PhiWorkExecutionReply reply = { PhiResultReply reply = {
.result = { .result = {
.status = PHI_STATUS_OK, .status = PHI_STATUS_OK,
.reserved = 0, .reserved = 0,
+6 -4
View File
@@ -1,3 +1,4 @@
#include "Protocol.h"
#include <CommandBuffer.h> #include <CommandBuffer.h>
#include <Daemon.h> #include <Daemon.h>
#include <Logger.h> #include <Logger.h>
@@ -75,13 +76,14 @@ int HandlePacket(PhiEndpoint endpoint)
return -1; return -1;
break; break;
case PHI_PACKET_MAP_HOST_MEMORY:
case PHI_PACKET_ALLOC_MEMORY: case PHI_PACKET_ALLOC_MEMORY:
if(HandleAllocMemory(endpoint, &header) < 0) if(HandleNewMemory(endpoint, &header) < 0)
return -1; return -1;
break; break;
case PHI_PACKET_FREE_MEMORY: case PHI_PACKET_DESTROY_MEMORY:
if(HandleFreeMemory(endpoint, &header) < 0) if(HandleDestroyMemory(endpoint, &header) < 0)
return -1; return -1;
break; break;
@@ -157,7 +159,7 @@ int SendReply(PhiEndpoint endpoint, const PhiMessageHeader* request, const void*
int SendStatus(PhiEndpoint endpoint, const PhiMessageHeader* request, PhiStatus status) int SendStatus(PhiEndpoint endpoint, const PhiMessageHeader* request, PhiStatus status)
{ {
PhiFreeMemoryReply reply = { PhiResultReply reply = {
.result = { .result = {
.status = status, .status = status,
.reserved = 0, .reserved = 0,
+91 -18
View File
@@ -1,12 +1,60 @@
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <Logger.h> #include <Logger.h>
#include <Memory.h> #include <Memory.h>
int HandleAllocMemory(PhiEndpoint endpoint, const PhiMessageHeader* header) static size_t AlignUp(size_t value, size_t alignment)
{ {
PhiAllocMemoryRequest request; return (value + alignment - 1) & ~(alignment - 1);
PhiAllocMemoryReply reply = { }
static Memory* MapHostMemory(PhiEndpoint epd, const PhiMapHostMemoryRequest* request)
{
void* ptr = scif_mmap(NULL, request->scif_size, PROT_READ | PROT_WRITE, 0, epd, request->scif_offset);
if(ptr == MAP_FAILED)
{
PhiLogErrorFmt("Failed to map host memory: %s", strerror(errno));
return NULL;
}
Memory* memory = malloc(sizeof(*memory));
if(!memory)
{
scif_munmap(ptr, request->scif_size);
PhiLogError("Failed to allocate memory");
return NULL;
}
memory->type = PHI_MEMORY_HOST_MAPPED;
memory->ptr = ptr;
memory->size = request->size;
memory->scif_size = request->scif_size;
return memory;
}
static Memory* AllocMemory(PhiEndpoint epd, const PhiAllocMemoryRequest* request)
{
Memory* memory = (Memory*)malloc(sizeof(Memory) + PHI_MEMORY_ALIGNMENT + request->size);
if(!memory)
return NULL;
memory->type = PHI_MEMORY_LOCAL;
memory->ptr = (void*)AlignUp((uintptr_t)memory + sizeof(Memory), PHI_MEMORY_ALIGNMENT);
memory->size = request->size;
memory->scif_size = 0;
return memory;
}
int HandleNewMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
{
PhiNewMemoryReply reply = {
.result = { .result = {
.status = PHI_STATUS_OK, .status = PHI_STATUS_OK,
.reserved = 0, .reserved = 0,
@@ -15,37 +63,54 @@ int HandleAllocMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
.size = 0, .size = 0,
}; };
if(header->payload_size != sizeof(request)) if(header->payload_size != sizeof(PhiAllocMemoryRequest) && header->payload_size != sizeof(PhiMapHostMemoryRequest))
{ {
if(DrainPayload(endpoint, header->payload_size) < 0) if(DrainPayload(endpoint, header->payload_size) < 0)
return -1; return -1;
reply.result.status = PHI_STATUS_BAD_MESSAGE; reply.result.status = PHI_STATUS_BAD_MESSAGE;
return SendReply(endpoint, header, &reply, sizeof(reply)); return SendReply(endpoint, header, &reply, sizeof(reply));
} }
Memory* memory;
if(header->type == PHI_PACKET_ALLOC_MEMORY)
{
PhiAllocMemoryRequest request;
if(ReadAll(endpoint, &request, sizeof(request)) < 0) if(ReadAll(endpoint, &request, sizeof(request)) < 0)
return -1; return -1;
memory = AllocMemory(endpoint, &request);
const void* memory = malloc((size_t)request.size);
if(memory == NULL) if(memory == NULL)
{ PhiLogErrorFmt("Failed to allocate %zu bytes", (size_t)request.size);
reply.result.status = PHI_STATUS_OUT_OF_MEMORY;
PhiLogInfoFmt("Failed to allocate %zu bytes", (size_t)request.size);
}
else else
PhiLogInfoFmt("Allocated %llu bytes to handle 0x%X", request.size, (uintptr_t)memory);
}
else if(header->type == PHI_PACKET_MAP_HOST_MEMORY)
{
PhiMapHostMemoryRequest request;
if(ReadAll(endpoint, &request, sizeof(request)) < 0)
return -1;
memory = MapHostMemory(endpoint, &request);
if(memory == NULL)
reply.result.status = PHI_STATUS_MAP_HOST_MEMORY_FAILED;
else
PhiLogInfoFmt("Mapped host memory to handle 0x%X", (uint64_t)(uintptr_t)memory);
}
if(memory != NULL)
{ {
reply.remote_handle = (uint64_t)(uintptr_t)memory; reply.remote_handle = (uint64_t)(uintptr_t)memory;
reply.size = request.size; reply.size = memory->size;
PhiLogInfoFmt("Allocated %llu bytes to handle 0x%X", reply.size, reply.remote_handle);
} }
else if(reply.result.status == PHI_STATUS_OK)
reply.result.status = PHI_STATUS_OUT_OF_MEMORY;
return SendReply(endpoint, header, &reply, sizeof(reply)); return SendReply(endpoint, header, &reply, sizeof(reply));
} }
int HandleFreeMemory(PhiEndpoint endpoint, const PhiMessageHeader* header) int HandleDestroyMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
{ {
PhiFreeMemoryRequest request; PhiDestroyMemoryRequest request;
PhiFreeMemoryReply reply = { PhiResultReply reply = {
.result = { .result = {
.status = PHI_STATUS_OK, .status = PHI_STATUS_OK,
.reserved = 0, .reserved = 0,
@@ -70,8 +135,16 @@ int HandleFreeMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
} }
else else
{ {
free((void*)(uintptr_t)request.remote_handle); const Memory* memory = (const Memory*)(uintptr_t)request.remote_handle;
PhiLogInfoFmt("Freed memory handle 0x%X", request.remote_handle);
if(memory->type == PHI_MEMORY_LOCAL)
free((void*)memory);
else if(memory->type == PHI_MEMORY_HOST_MAPPED)
scif_munmap((void*)memory->ptr, memory->size);
PhiLogInfoFmt("Destroyed %s memory handle 0x%X",
memory->type == PHI_MEMORY_LOCAL ? "local" : "host-mapped",
request.remote_handle);
} }
return SendReply(endpoint, header, &reply, sizeof(reply)); return SendReply(endpoint, header, &reply, sizeof(reply));
+20 -2
View File
@@ -3,7 +3,25 @@
#include <Daemon.h> #include <Daemon.h>
int HandleAllocMemory(PhiEndpoint endpoint, const PhiMessageHeader* header); typedef enum MemoryType
int HandleFreeMemory(PhiEndpoint endpoint, const PhiMessageHeader* header); {
PHI_MEMORY_LOCAL,
PHI_MEMORY_HOST_MAPPED,
} MemoryType;
typedef struct Memory
{
MemoryType type;
void* ptr;
uint64_t size;
uint64_t scif_size;
off_t scif_offset;
} Memory;
int HandleNewMemory(PhiEndpoint endpoint, const PhiMessageHeader* header);
int HandleDestroyMemory(PhiEndpoint endpoint, const PhiMessageHeader* header);
#endif #endif
-70
View File
@@ -1,73 +1,5 @@
#include <Transport.h> #include <Transport.h>
#ifdef PHI_HOST_EMULATION
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
static struct sockaddr_in PhiLoopbackAddress(uint16_t port)
{
struct sockaddr_in address = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr = {
.s_addr = htonl(INADDR_LOOPBACK),
},
};
return address;
}
PhiEndpoint PhiTransportAccept(PhiEndpoint endpoint)
{
return accept(endpoint, NULL, NULL);
}
int PhiTransportClose(PhiEndpoint endpoint)
{
return close(endpoint);
}
PhiEndpoint PhiTransportListen(uint16_t port)
{
PhiEndpoint endpoint = socket(AF_INET, SOCK_STREAM, 0);
if(endpoint == PHI_ENDPOINT_INVALID)
return PHI_ENDPOINT_INVALID;
const int reuse_address = 1;
if(setsockopt(endpoint, SOL_SOCKET, SO_REUSEADDR, &reuse_address, sizeof(reuse_address)) < 0)
{
PhiTransportClose(endpoint);
return PHI_ENDPOINT_INVALID;
}
const struct sockaddr_in address = PhiLoopbackAddress(port);
if(bind(endpoint, (const struct sockaddr*)&address, sizeof(address)) < 0 || listen(endpoint, 16) < 0)
{
PhiTransportClose(endpoint);
return PHI_ENDPOINT_INVALID;
}
return endpoint;
}
ssize_t PhiTransportReceive(PhiEndpoint endpoint, void* data, size_t size)
{
return recv(endpoint, data, size, 0);
}
ssize_t PhiTransportSend(PhiEndpoint endpoint, const void* data, size_t size)
{
#ifdef MSG_NOSIGNAL
return send(endpoint, data, size, MSG_NOSIGNAL);
#else
return send(endpoint, data, size, 0);
#endif
}
#else
PhiEndpoint PhiTransportAccept(PhiEndpoint endpoint) PhiEndpoint PhiTransportAccept(PhiEndpoint endpoint)
{ {
struct scif_portID peer; struct scif_portID peer;
@@ -105,5 +37,3 @@ ssize_t PhiTransportSend(PhiEndpoint endpoint, const void* data, size_t size)
{ {
return scif_send(endpoint, (void*)data, size, SCIF_SEND_BLOCK); return scif_send(endpoint, (void*)data, size, SCIF_SEND_BLOCK);
} }
#endif
-4
View File
@@ -5,12 +5,8 @@
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef PHI_HOST_EMULATION
typedef int PhiEndpoint;
#else
#include <scif.h> #include <scif.h>
typedef scif_epd_t PhiEndpoint; typedef scif_epd_t PhiEndpoint;
#endif
#define PHI_ENDPOINT_INVALID ((PhiEndpoint) - 1) #define PHI_ENDPOINT_INVALID ((PhiEndpoint) - 1)
+12
View File
@@ -0,0 +1,12 @@
#ifndef APE_PHI_AVX_H
#define APE_PHI_AVX_H
#include <stddef.h>
#include <stdint.h>
void AvxCopy(uint8_t* dst, const uint8_t* src, size_t size);
void AvxFill64(void* dst, uint32_t value);
void AvxFill256(void* dst, uint32_t value);
#endif
+107
View File
@@ -0,0 +1,107 @@
#include <immintrin.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define PHI_CACHE_LINE_SIZE 64
static inline __m512i Load512KNC(const uint8_t* src)
{
return _mm512_load_epi32((const void*)src);
}
void AvxCopy(uint8_t* dst, const uint8_t* src, size_t size)
{
if(size == 0)
return;
// Generic fallback if not 4-byte aligned
if((((uintptr_t)dst | (uintptr_t)src | size) & 3) != 0)
{
memcpy(dst, src, size);
return;
}
// Align the destination to 64 bytes.
size_t prefix = (-(uintptr_t)dst) & (PHI_CACHE_LINE_SIZE - 1);
if(prefix > size)
prefix = size;
if(prefix != 0)
{
memcpy(dst, src, prefix);
dst += prefix;
src += prefix;
size -= prefix;
}
if(((uintptr_t)src & (PHI_CACHE_LINE_SIZE - 1)) == 0)
{
// Unroll four cache lines at a time
while(size >= 256)
{
const __m512i v0 = _mm512_load_epi32((const void*)(src + 0));
const __m512i v1 = _mm512_load_epi32((const void*)(src + 64));
const __m512i v2 = _mm512_load_epi32((const void*)(src + 128));
const __m512i v3 = _mm512_load_epi32((const void*)(src + 192));
_mm512_store_epi32((void*)(dst + 0), v0);
_mm512_store_epi32((void*)(dst + 64), v1);
_mm512_store_epi32((void*)(dst + 128), v2);
_mm512_store_epi32((void*)(dst + 192), v3);
src += 256;
dst += 256;
size -= 256;
}
while(size >= 64)
{
const __m512i value = _mm512_load_epi32((const void*)src);
_mm512_store_epi32((void*)dst, value);
src += 64;
dst += 64;
size -= 64;
}
}
else
{
// Source is only 4-byte aligned.
// KNC's loadunpack pair implements the conceptual unaligned 64-byte load.
while(size >= 256)
{
const __m512i v0 = Load512KNC(src + 0);
const __m512i v1 = Load512KNC(src + 64);
const __m512i v2 = Load512KNC(src + 128);
const __m512i v3 = Load512KNC(src + 192);
_mm512_store_epi32((void*)(dst + 0), v0);
_mm512_store_epi32((void*)(dst + 64), v1);
_mm512_store_epi32((void*)(dst + 128), v2);
_mm512_store_epi32((void*)(dst + 192), v3);
src += 256;
dst += 256;
size -= 256;
}
while(size >= 64)
{
const __m512i value = Load512KNC(src);
_mm512_store_epi32((void*)dst, value);
src += 64;
dst += 64;
size -= 64;
}
}
// At most 63 bytes remain
if(size != 0)
memcpy(dst, src, size);
}
+23
View File
@@ -0,0 +1,23 @@
#include <immintrin.h>
#include <stdint.h>
// Dst must be 64-byte aligned
void PhiFill256KNC(void* dst, uint32_t value)
{
__m512i v = _mm512_set1_epi32((int)value);
uint8_t* d = (uint8_t*)dst;
_mm512_store_epi32((void*)(d + 0), v);
_mm512_store_epi32((void*)(d + 64), v);
_mm512_store_epi32((void*)(d + 128), v);
_mm512_store_epi32((void*)(d + 192), v);
}
// Dst must be 64-byte aligned
void PhiFill64KNC(void* dst, uint32_t value)
{
__m512i v = _mm512_set1_epi32((int)value);
_mm512_store_epi32(dst, v);
}
-10
View File
@@ -2,11 +2,6 @@
#include <pthread.h> #include <pthread.h>
#include <stdint.h> #include <stdint.h>
#ifdef PHI_HOST_EMULATION
#include <string.h>
#include <unistd.h>
#endif
#include <Daemon.h> #include <Daemon.h>
#include <Logger.h> #include <Logger.h>
@@ -21,13 +16,8 @@ static void* HandleClient(void* const argument)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
#ifdef PHI_HOST_EMULATION
if(argc == 2 && strcmp(argv[1], "--unlink-on-start") == 0)
(void)unlink(argv[0]);
#else
(void)argc; (void)argc;
(void)argv; (void)argv;
#endif
PhiEndpoint endpoint = StartDaemon(); PhiEndpoint endpoint = StartDaemon();
pthread_attr_t client_thread_attributes; pthread_attr_t client_thread_attributes;
-3
View File
@@ -1,3 +0,0 @@
pub const Device = struct {};
pub fn unload() void {}
-14
View File
@@ -6,22 +6,8 @@ const PciInfo = struct {
/// Not a hashmap as they need runtime allocations /// Not a hashmap as they need runtime allocations
pub const map = [_]PciInfo{ pub const map = [_]PciInfo{
.{ .id = 0x2250, .name = "Intel(R) Xeon Phi(TM) Coprocessor 5100 Series" }, .{ .id = 0x2250, .name = "Intel(R) Xeon Phi(TM) Coprocessor 5100 Series" },
.{ .id = 0x2251, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2252, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2253, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2254, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2255, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2256, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2257, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2258, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x2259, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x225a, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x225b, .name = "Intel(R) Xeon Phi(TM) Coprocessor x100 Series" },
.{ .id = 0x225c, .name = "Intel(R) Xeon Phi(TM) Coprocessor SE10/7120 Series" }, .{ .id = 0x225c, .name = "Intel(R) Xeon Phi(TM) Coprocessor SE10/7120 Series" },
.{ .id = 0x225d, .name = "Intel(R) Xeon Phi(TM) Coprocessor 3120 Series" }, .{ .id = 0x225d, .name = "Intel(R) Xeon Phi(TM) Coprocessor 3120 Series" },
.{ .id = 0x225e, .name = "Intel(R) Xeon Phi(TM) Coprocessor 31S1" }, .{ .id = 0x225e, .name = "Intel(R) Xeon Phi(TM) Coprocessor 31S1" },
.{ .id = 0x2262, .name = "Intel(R) Xeon Phi(TM) Coprocessor 7220" }, .{ .id = 0x2262, .name = "Intel(R) Xeon Phi(TM) Coprocessor 7220" },
}; };
+19
View File
@@ -10,6 +10,11 @@ pub const PortId = extern struct {
port: u16, port: u16,
}; };
pub const Prot = enum(c_int) {
read = 1 << 0,
write = 1 << 1,
};
pub const send_block = 1; pub const send_block = 1;
pub const recv_block = 1; pub const recv_block = 1;
@@ -23,6 +28,10 @@ var scif_connect: *const fn (epd: epd_t, dst: *const PortId) callconv(.c) c_int
var scif_send: *const fn (epd: epd_t, msg: ?*const anyopaque, len: usize, flags: c_int) callconv(.c) isize = undefined; var scif_send: *const fn (epd: epd_t, msg: ?*const anyopaque, len: usize, flags: c_int) callconv(.c) isize = undefined;
// SAFETY: load assigns every function pointer before the public wrappers can be used. // SAFETY: load assigns every function pointer before the public wrappers can be used.
var scif_recv: *const fn (epd: epd_t, msg: ?*anyopaque, len: usize, flags: c_int) callconv(.c) isize = undefined; var scif_recv: *const fn (epd: epd_t, msg: ?*anyopaque, len: usize, flags: c_int) callconv(.c) isize = undefined;
// SAFETY: load assigns every function pointer before the public wrappers can be used.
var scif_register: *const fn (epd: epd_t, addr: ?*anyopaque, len: usize, offset: i64, prot_flags: c_int, map_flags: c_int) callconv(.c) i64 = undefined;
// SAFETY: load assigns every function pointer before the public wrappers can be used.
var scif_unregister: *const fn (epd: epd_t, offset: i64, len: usize) callconv(.c) c_int = undefined;
// SAFETY: load initializes the module before it can be closed or queried. // SAFETY: load initializes the module before it can be closed or queried.
var module: std.DynLib = undefined; var module: std.DynLib = undefined;
@@ -49,6 +58,8 @@ pub fn load() VkError!void {
scif_connect = module.lookup(@TypeOf(scif_connect), "scif_connect") orelse return VkError.InitializationFailed; scif_connect = module.lookup(@TypeOf(scif_connect), "scif_connect") orelse return VkError.InitializationFailed;
scif_send = module.lookup(@TypeOf(scif_send), "scif_send") orelse return VkError.InitializationFailed; scif_send = module.lookup(@TypeOf(scif_send), "scif_send") orelse return VkError.InitializationFailed;
scif_recv = module.lookup(@TypeOf(scif_recv), "scif_recv") orelse return VkError.InitializationFailed; scif_recv = module.lookup(@TypeOf(scif_recv), "scif_recv") orelse return VkError.InitializationFailed;
scif_register = module.lookup(@TypeOf(scif_register), "scif_register") orelse return VkError.InitializationFailed;
scif_unregister = module.lookup(@TypeOf(scif_unregister), "scif_unregister") orelse return VkError.InitializationFailed;
_ = ref_count.fetchAdd(1, .monotonic); _ = ref_count.fetchAdd(1, .monotonic);
} }
@@ -81,3 +92,11 @@ pub inline fn send(epd: epd_t, msg: ?*const anyopaque, len: usize, flags: c_int)
pub inline fn recv(epd: epd_t, msg: ?*anyopaque, len: usize, flags: c_int) isize { pub inline fn recv(epd: epd_t, msg: ?*anyopaque, len: usize, flags: c_int) isize {
return scif_recv(epd, msg, len, flags); return scif_recv(epd, msg, len, flags);
} }
pub inline fn register(epd: epd_t, addr: ?*anyopaque, len: usize, offset: i64, prot_flags: c_int, map_flags: c_int) i64 {
return scif_register(epd, addr, len, offset, prot_flags, map_flags);
}
pub inline fn unregister(epd: epd_t, offset: i64, len: usize) c_int {
return scif_unregister(epd, offset, len);
}
+22 -15
View File
@@ -4,6 +4,8 @@
#include "Commands.h" // IWYU pragma: keep #include "Commands.h" // IWYU pragma: keep
#include <stdint.h> #include <stdint.h>
#define PHI_MEMORY_ALIGNMENT 64
#define PHI_PROTOCOL_MAGIC 0x50484941u #define PHI_PROTOCOL_MAGIC 0x50484941u
#define PHI_PROTOCOL_VERSION 1u #define PHI_PROTOCOL_VERSION 1u
#define PHI_SCIF_PORT 43616u #define PHI_SCIF_PORT 43616u
@@ -16,11 +18,12 @@ typedef enum PhiPacketType
{ {
PHI_PACKET_HELLO = 1, PHI_PACKET_HELLO = 1,
PHI_PACKET_ALLOC_MEMORY = 2, PHI_PACKET_ALLOC_MEMORY = 2,
PHI_PACKET_FREE_MEMORY = 3, PHI_PACKET_DESTROY_MEMORY = 3,
PHI_PACKET_UPLOAD = 4, PHI_PACKET_UPLOAD = 4,
PHI_PACKET_DOWNLOAD = 5, PHI_PACKET_DOWNLOAD = 5,
PHI_PACKET_WORK_EXECUTION = 6, PHI_PACKET_WORK_EXECUTION = 6,
PHI_PACKET_SHUTDOWN = 7, PHI_PACKET_SHUTDOWN = 7,
PHI_PACKET_MAP_HOST_MEMORY = 8,
} PhiPacketType; } PhiPacketType;
typedef enum PhiStatus typedef enum PhiStatus
@@ -31,6 +34,8 @@ typedef enum PhiStatus
PHI_STATUS_UNSUPPORTED_PACKET = 3, PHI_STATUS_UNSUPPORTED_PACKET = 3,
PHI_STATUS_OUT_OF_MEMORY = 4, PHI_STATUS_OUT_OF_MEMORY = 4,
PHI_STATUS_INVALID_HANDLE = 5, PHI_STATUS_INVALID_HANDLE = 5,
PHI_STATUS_MAP_HOST_MEMORY_FAILED = 6,
PHI_STATUS_INVALID_ARGUMENT = 7,
} PhiStatus; } PhiStatus;
typedef struct PhiMessageHeader typedef struct PhiMessageHeader
@@ -48,6 +53,11 @@ typedef struct PhiResult
uint32_t reserved; uint32_t reserved;
} PhiResult; } PhiResult;
typedef struct PhiResultReply
{
PhiResult result;
} PhiResultReply;
typedef struct PhiHelloRequest typedef struct PhiHelloRequest
{ {
uint32_t host_protocol_version; uint32_t host_protocol_version;
@@ -68,22 +78,24 @@ typedef struct PhiAllocMemoryRequest
uint32_t flags; uint32_t flags;
} PhiAllocMemoryRequest; } PhiAllocMemoryRequest;
typedef struct PhiAllocMemoryReply typedef struct PhiNewMemoryReply
{ {
PhiResult result; PhiResult result;
uint64_t remote_handle; uint64_t remote_handle;
uint64_t size; uint64_t size;
} PhiAllocMemoryReply; } PhiNewMemoryReply;
typedef struct PhiFreeMemoryRequest typedef struct PhiMapHostMemoryRequest
{
uint64_t scif_offset;
uint64_t scif_size;
uint64_t size;
} PhiMapHostMemoryRequest;
typedef struct PhiDestroyMemoryRequest
{ {
uint64_t remote_handle; uint64_t remote_handle;
} PhiFreeMemoryRequest; } PhiDestroyMemoryRequest;
typedef struct PhiFreeMemoryReply
{
PhiResult result;
} PhiFreeMemoryReply;
typedef struct PhiWorkExecutionRequest typedef struct PhiWorkExecutionRequest
{ {
@@ -91,9 +103,4 @@ typedef struct PhiWorkExecutionRequest
uint64_t command_buffer_size; uint64_t command_buffer_size;
} PhiWorkExecutionRequest; } PhiWorkExecutionRequest;
typedef struct PhiWorkExecutionReply
{
PhiResult result;
} PhiWorkExecutionReply;
#endif #endif