@@ -3197,21 +3197,36 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec,
unsigned int vlen, unsigned int flags,
- int send)
+ abi_ulong timeout, int send)
{
struct target_mmsghdr *mmsgp;
+ struct timespec ts, end_time, curr_time;
abi_long ret = 0;
int i;
if (vlen > UIO_MAXIOV) {
vlen = UIO_MAXIOV;
}
-
mmsgp = lock_user(VERIFY_WRITE, target_msgvec, sizeof(*mmsgp) * vlen, 1);
if (!mmsgp) {
return -TARGET_EFAULT;
}
+ if (timeout) {
+ if (target_to_host_timespec(&ts, timeout)) {
+ return -TARGET_EFAULT;
+ }
+ if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec > 1000000000) {
+ return -TARGET_EINVAL;
+ }
+ clock_gettime(CLOCK_REALTIME, &curr_time);
+ end_time.tv_sec = curr_time.tv_sec + ts.tv_sec;
+ end_time.tv_nsec = curr_time.tv_nsec + ts.tv_nsec;
+ if (end_time.tv_nsec > 1000000000) {
+ end_time.tv_nsec -= 1000000000;
+ end_time.tv_sec++;
+ }
+ }
for (i = 0; i < vlen; i++) {
ret = do_sendrecvmsg_locked(fd, &mmsgp[i].msg_hdr, flags, send);
if (is_error(ret)) {
@@ -3222,6 +3237,20 @@ static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec,
if (flags & MSG_WAITFORONE) {
flags |= MSG_DONTWAIT;
}
+ /*
+ * For recvmmsg() the timeout is checked only after a datagram is
+ * received.This is an existing bug in the kernel for this syscall:
+ * https://man7.org/linux/man-pages/man2/recvmmsg.2.html#BUGS
+ */
+ if (timeout) {
+ clock_gettime(CLOCK_REALTIME, &curr_time);
+ if (curr_time.tv_sec > end_time.tv_sec ||
+ (curr_time.tv_sec == end_time.tv_sec &&
+ curr_time.tv_nsec >= end_time.tv_nsec)) {
+ i++;
+ break;
+ }
+ }
}
unlock_user(mmsgp, target_msgvec, sizeof(*mmsgp) * i);
@@ -3477,7 +3506,7 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
[TARGET_SYS_SENDMSG] = 3, /* fd, msg, flags */
[TARGET_SYS_RECVMSG] = 3, /* fd, msg, flags */
[TARGET_SYS_ACCEPT4] = 4, /* fd, addr, addrlen, flags */
- [TARGET_SYS_RECVMMSG] = 4, /* fd, msgvec, vlen, flags */
+ [TARGET_SYS_RECVMMSG] = 5, /* fd, msgvec, vlen, flags, timeout */
[TARGET_SYS_SENDMMSG] = 4, /* fd, msgvec, vlen, flags */
};
abi_long a[6]; /* max 6 args */
@@ -3536,10 +3565,10 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
return do_sendrecvmsg(a[0], a[1], a[2], 0);
case TARGET_SYS_ACCEPT4: /* sockfd, addr, addrlen, flags */
return do_accept4(a[0], a[1], a[2], a[3]);
- case TARGET_SYS_RECVMMSG: /* sockfd, msgvec, vlen, flags */
- return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 0);
+ case TARGET_SYS_RECVMMSG: /* sockfd, msgvec, vlen, flags, timeout */
+ return do_sendrecvmmsg(a[0], a[1], a[2], a[3], a[4], 0);
case TARGET_SYS_SENDMMSG: /* sockfd, msgvec, vlen, flags */
- return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 1);
+ return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 0, 1);
default:
qemu_log_mask(LOG_UNIMP, "Unsupported socketcall: %d\n", num);
return -TARGET_EINVAL;
@@ -9343,11 +9372,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
#endif
#ifdef TARGET_NR_sendmmsg
case TARGET_NR_sendmmsg:
- return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 1);
+ return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 0, 1);
#endif
#ifdef TARGET_NR_recvmmsg
case TARGET_NR_recvmmsg:
- return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 0);
+ return do_sendrecvmmsg(arg1, arg2, arg3, arg4, arg5, 0);
#endif
#ifdef TARGET_NR_sendto
case TARGET_NR_sendto:
Implementation of syscall 'recvmmsg()' in file 'syscall.c' uses a loop over 'recvmsg()' to receive each individual message from a socket. However, the implementation ignores the timeout argument. This patch changes that by introducing a timeout check after each received message. Implementation notes: Function 'clock_gettime()' is used to check the time before messages start getting received. After each message is received, 'clock_gettime()' is called and used to check whether the timeout is expired. Signed-off-by: Filip Bozuta <Filip.Bozuta@syrmia.com> --- linux-user/syscall.c | 45 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-)