authorgravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2019-04-21 18:03:27+10:00
committergravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2019-04-30 16:47:09+10:00
logfd056752daf6b6841ce06061072f092d9c7f8513
treea46c2e44a64fd3116ec00e60b0303c62e434818f
parent217b95da31227fa346bb80568a9dc2971d1c7c1d
signature Commit is signed but in an unrecognized format.

std: add sendmmsg on linux


1 files changed, 51 insertions(+), 0 deletions(-)

std/os/linux.zig+51
...@@ -12,6 +12,7 @@ pub use switch (builtin.arch) {...@@ -12,6 +12,7 @@ pub use switch (builtin.arch) {
12pub use @import("linux/errno.zig");12pub use @import("linux/errno.zig");
1313
14pub const PATH_MAX = 4096;14pub const PATH_MAX = 4096;
15pub const IOV_MAX = 1024;
1516
16pub const STDIN_FILENO = 0;17pub const STDIN_FILENO = 0;
17pub const STDOUT_FILENO = 1;18pub const STDOUT_FILENO = 1;
...@@ -1193,6 +1194,16 @@ pub const iovec_const = extern struct {...@@ -1193,6 +1194,16 @@ pub const iovec_const = extern struct {
1193 iov_len: usize,1194 iov_len: usize,
1194};1195};
11951196
1197pub const mmsghdr = extern struct {
1198 msg_hdr: msghdr,
1199 msg_len: u32,
1200};
1201
1202pub const mmsghdr_const = extern struct {
1203 msg_hdr: msghdr_const,
1204 msg_len: u32,
1205};
1206
1196pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {1207pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
1197 return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));1208 return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));
1198}1209}
...@@ -1217,6 +1228,46 @@ pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {...@@ -1217,6 +1228,46 @@ pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
1217 return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);1228 return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);
1218}1229}
12191230
1231pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
1232 if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) {
1233 // workaround kernel brokenness:
1234 // if adding up all iov_len overflows a i32 then split into multiple calls
1235 // see https://www.openwall.com/lists/musl/2014/06/07/5
1236 const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel
1237 var next_unsent: usize = 0;
1238 for (msgvec[0..kvlen]) |*msg, i| {
1239 var size: i32 = 0;
1240 const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned
1241 for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| {
1242 if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) {
1243 // batch-send all messages up to the current message
1244 if (next_unsent < i) {
1245 const batch_size = i - next_unsent;
1246 const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
1247 if (getErrno(r) != 0) return next_unsent;
1248 if (r < batch_size) return next_unsent + r;
1249 }
1250 // send current message as own packet
1251 const r = sendmsg(fd, &msg.msg_hdr, flags);
1252 if (getErrno(r) != 0) return r;
1253 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
1254 msg.msg_len = @intCast(u32, r);
1255 next_unsent = i + 1;
1256 break;
1257 }
1258 }
1259 }
1260 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR)
1261 const batch_size = kvlen - next_unsent;
1262 const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
1263 if (getErrno(r) != 0) return r;
1264 return next_unsent + r;
1265 }
1266 return kvlen;
1267 }
1268 return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags);
1269}
1270
1220pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {1271pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
1221 return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len);1272 return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len);
1222}1273}