Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion qkernel/src/syscalls/sys_poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ pub fn DoSelect(
if (events & SELECT_EXCEPT_EVENTS) != 0 {
bitSetCount += 1;
} else {
w[i] &= !m;
e[i] &= !m;
}
}
}
Expand Down
79 changes: 77 additions & 2 deletions qlib/kernel/socket/hostinet/uring_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,64 @@ impl UringSocketOperations {
return self.connErrNo.load(Ordering::Acquire);
}

pub fn SetRemoteAddrFromHost(&self) -> Result<()> {
let mut addr: Vec<u8, GuestHostSharedAllocator> =
Vec::with_capacity_in(SIZEOF_SOCKADDR, GUEST_HOST_SHARED_ALLOCATOR);
addr.resize(SIZEOF_SOCKADDR, 0);

let mut len = Box::new_in(SIZEOF_SOCKADDR as i32, GUEST_HOST_SHARED_ALLOCATOR);
let res = Kernel::HostSpace::GetPeerName(
self.fd,
&mut addr[0] as *mut _ as u64,
&mut *len as *mut _ as u64,
);
if res < 0 {
return Err(Error::SysError(-res as i32));
}

let addr = GetAddr(addr[0] as i16, &addr[..*len as usize])?;
*self.remoteAddr.lock() = Some(addr);
return Ok(());
}

pub fn CompletePendingConnect(&self) -> Result<i32> {
if self.ConnErrno() != -SysErr::EINPROGRESS {
return Ok(self.ConnErrno());
}

match self.SocketType() {
UringSocketType::TCPConnecting => (),
_ => return Ok(self.ConnErrno()),
}

let mut hostErrno = Box::new_in(0i32, GUEST_HOST_SHARED_ALLOCATOR);
let mut hostLen = Box::new_in(4i32, GUEST_HOST_SHARED_ALLOCATOR);
let res = Kernel::HostSpace::GetSockOpt(
self.fd,
LibcConst::SOL_SOCKET as i32,
LibcConst::SO_ERROR as i32,
&mut *hostErrno as *mut i32 as u64,
&mut *hostLen as *mut i32 as u64,
);
if res < 0 {
return Err(Error::SysError(-res as i32));
}

if *hostErrno == 0 {
self.SetRemoteAddrFromHost()?;
self.SetConnErrno(0);
self.PostConnect();
return Ok(0);
}

if *hostErrno != SysErr::EINPROGRESS {
self.SetConnErrno(-*hostErrno);
*self.socketType.lock() = UringSocketType::TCPInit;
}

return Ok(self.ConnErrno());
}

pub fn New(
family: i32,
fd: i32,
Expand Down Expand Up @@ -406,7 +464,15 @@ impl UringSocketOperations {
SocketBuffIntern::Init(MemoryDef::DEFAULT_BUF_PAGE_COUNT),
GUEST_HOST_SHARED_ALLOCATOR,
));
*self.socketType.lock() = UringSocketType::Uring(socketBuf.clone());

{
let mut socketType = self.socketType.lock();
match &*socketType {
UringSocketType::Uring(_) => return,
_ => *socketType = UringSocketType::Uring(socketBuf.clone()),
}
}

QUring::BufSockInit(self.fd, self.queue.clone(), socketBuf, true).unwrap();
}

Expand Down Expand Up @@ -500,7 +566,10 @@ impl Waitable for UringSocketOperations {
fn Readiness(&self, _task: &Task, mask: EventMask) -> EventMask {
match self.SocketType() {
UringSocketType::TCPConnecting => {
let errno = self.ConnErrno();
let errno = match self.CompletePendingConnect() {
Ok(errno) => errno,
Err(_) => self.ConnErrno(),
};
if errno != -SysErr::EINPROGRESS {
return EVENT_OUT & mask;
}
Expand Down Expand Up @@ -841,6 +910,10 @@ impl SockOperations for UringSocketOperations {
}

let errno = self.ConnErrno();
if errno == 0 {
return Ok(0);
}

return Err(Error::SysError(-errno));
}

Expand Down Expand Up @@ -1148,6 +1221,8 @@ impl SockOperations for UringSocketOperations {
return Err(Error::SysError(SysErr::EINVAL));
}

self.CompletePendingConnect()?;

if self.ConnErrno() != 0 {
let errno = self.ConnErrno();
self.SetConnErrno(0);
Expand Down
8 changes: 6 additions & 2 deletions test/c/makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
all: std server client server_conn client_conn unixcli unixsrv socketpair stat dev fork signal futex multithread epoll mkdir fifo timerfd eventfd seek gettimeofday server_benchmark client_benchmark epoll_client epoll_server multithread_client multithread_server multithread_pp_client multithread_pp_server poll udpcli udpsrv udpclidual udpsrvdual
all: std server client server_conn client_conn unixcli unixsrv socketpair stat dev fork signal futex multithread epoll mkdir fifo timerfd eventfd seek gettimeofday server_benchmark client_benchmark epoll_client epoll_server multithread_client multithread_server multithread_pp_client multithread_pp_server poll select_except_connect soerror_connect_loop udpcli udpsrv udpclidual udpsrvdual

std: std.c
gcc -o std std.c
dnstest: dnstest.c
gcc -o dnstest dnstest.c
poll: poll.c
gcc -o poll poll.c
select_except_connect: select_except_connect.c
gcc -Wall -Wextra -o select_except_connect select_except_connect.c
soerror_connect_loop: soerror_connect_loop.c
gcc -Wall -Wextra -o soerror_connect_loop soerror_connect_loop.c
server: server.c
gcc -o server server.c
client: client.c
Expand Down Expand Up @@ -79,4 +83,4 @@ udpclidual: udpclidual.c
udpsrvdual: udpsrvdual.c
gcc -o udpsrvdual udpsrvdual.c
clean:
rm std server client unixcli unixsrv socketpair stat dev fork signal futex multithread epoll mkdir fifo timerfd eventfd seek gettimeofday
rm -f std server client select_except_connect soerror_connect_loop unixcli unixsrv socketpair stat dev fork signal futex multithread epoll mkdir fifo timerfd eventfd seek gettimeofday
141 changes: 141 additions & 0 deletions test/c/select_except_connect.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (c) 2026 Quark Container Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>

static void fail(const char *msg)
{
perror(msg);
exit(1);
}

int main(void)
{
int listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0) {
fail("socket(listener)");
}

int one = 1;
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
fail("setsockopt(SO_REUSEADDR)");
}

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = 0;

if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fail("bind(listener)");
}
if (listen(listener, 1) < 0) {
fail("listen(listener)");
}

socklen_t addr_len = sizeof(addr);
if (getsockname(listener, (struct sockaddr *)&addr, &addr_len) < 0) {
fail("getsockname(listener)");
}

pid_t child = fork();
if (child < 0) {
fail("fork");
}
if (child == 0) {
int accepted = accept(listener, NULL, NULL);
if (accepted < 0) {
fail("accept");
}
char byte;
(void)read(accepted, &byte, sizeof(byte));
close(accepted);
close(listener);
return 0;
}

int client = socket(AF_INET, SOCK_STREAM, 0);
if (client < 0) {
fail("socket(client)");
}
int flags = fcntl(client, F_GETFL, 0);
if (flags < 0 || fcntl(client, F_SETFL, flags | O_NONBLOCK) < 0) {
fail("fcntl(O_NONBLOCK)");
}

int rc = connect(client, (struct sockaddr *)&addr, sizeof(addr));
if (rc < 0 && errno != EINPROGRESS) {
fail("connect(client)");
}

fd_set readfds;
fd_set writefds;
fd_set exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET(client, &readfds);
FD_SET(client, &writefds);
FD_SET(client, &exceptfds);

struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
rc = select(client + 1, &readfds, &writefds, &exceptfds, &timeout);
if (rc < 0) {
fail("select");
}
if (rc == 0) {
fprintf(stderr, "select timed out\n");
return 1;
}

int err = 0;
socklen_t err_len = sizeof(err);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, &err, &err_len) < 0) {
fail("getsockopt(SO_ERROR)");
}

printf("select rc=%d read=%d write=%d except=%d so_error=%d\n",
rc,
FD_ISSET(client, &readfds) != 0,
FD_ISSET(client, &writefds) != 0,
FD_ISSET(client, &exceptfds) != 0,
err);

if (err != 0) {
fprintf(stderr, "SO_ERROR=%d (%s)\n", err, strerror(err));
return 1;
}
if (!FD_ISSET(client, &writefds)) {
fprintf(stderr, "client socket was not in writefds\n");
return 1;
}
if (FD_ISSET(client, &exceptfds)) {
fprintf(stderr, "client socket was incorrectly left in exceptfds\n");
return 1;
}

(void)write(client, "x", 1);
close(client);
close(listener);

int status = 0;
if (waitpid(child, &status, 0) < 0) {
fail("waitpid");
}
return status == 0 ? 0 : 1;
}
Loading