From ffad9d8377408e269f7451723fe89e69a9e653ce Mon Sep 17 00:00:00 2001 From: nimelehin Date: Mon, 16 Nov 2020 19:03:08 +0300 Subject: [PATCH] Add statistics on blocked tasks Add functionality to see which tasks are blocked. Add statistics variables: alive_task_count and blocked_task_count. --- fs/sock.c | 44 +++++++++++++++++++++----------- kernel/fs.c | 68 ++++++++++++++++++++++++++++++++++--------------- kernel/poll.c | 10 ++++++-- kernel/signal.c | 6 +++-- kernel/task.c | 20 +++++++++++++++ kernel/task.h | 23 +++++++++++++++++ kernel/time.c | 6 ++++- 7 files changed, 136 insertions(+), 41 deletions(-) diff --git a/fs/sock.c b/fs/sock.c index f1c0d66c47..6f2acf9b84 100644 --- a/fs/sock.c +++ b/fs/sock.c @@ -5,6 +5,7 @@ #include #include #include "kernel/calls.h" +#include "kernel/task.h" #include "fs/fd.h" #include "fs/inode.h" #include "fs/path.h" @@ -409,14 +410,16 @@ int_t sys_accept(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr) { char sockaddr[sockaddr_len]; int client; - do { - sockrestart_begin_listen_wait(sock); - errno = 0; - client = accept(sock->real_fd, - sockaddr_addr != 0 ? (void *) sockaddr : NULL, - sockaddr_addr != 0 ? &sockaddr_len : NULL); - sockrestart_end_listen_wait(sock); - } while (sockrestart_should_restart_listen_wait() && errno == EINTR); + TASK_MAY_BLOCK { + do { + sockrestart_begin_listen_wait(sock); + errno = 0; + client = accept(sock->real_fd, + sockaddr_addr != 0 ? (void *) sockaddr : NULL, + sockaddr_addr != 0 ? &sockaddr_len : NULL); + sockrestart_end_listen_wait(sock); + } while (sockrestart_should_restart_listen_wait() && errno == EINTR); + } if (client < 0) return errno_map(); @@ -589,8 +592,11 @@ int_t sys_sendto(fd_t sock_fd, addr_t buffer_addr, dword_t len, dword_t flags, a goto error; } - ssize_t res = sendto(sock->real_fd, buffer, len, real_flags, - sockaddr_addr ? (void *) &sockaddr : NULL, sockaddr_len); + ssize_t res = 0; + TASK_MAY_BLOCK { + res = sendto(sock->real_fd, buffer, len, real_flags, + sockaddr_addr ? (void *) &sockaddr : NULL, sockaddr_len); + } free(buffer); if (res < 0) return errno_map(); @@ -616,9 +622,12 @@ int_t sys_recvfrom(fd_t sock_fd, addr_t buffer_addr, dword_t len, dword_t flags, char *buffer = malloc(len); char sockaddr[sockaddr_len]; - ssize_t res = recvfrom(sock->real_fd, buffer, len, real_flags, - sockaddr_addr != 0 ? (void *) sockaddr : NULL, - sockaddr_len_addr != 0 ? &sockaddr_len : NULL); + ssize_t res = 0; + TASK_MAY_BLOCK { + res = recvfrom(sock->real_fd, buffer, len, real_flags, + sockaddr_addr != 0 ? (void *) sockaddr : NULL, + sockaddr_len_addr != 0 ? &sockaddr_len : NULL); + } if (res < 0) { free(buffer); return errno_map(); @@ -946,7 +955,9 @@ int_t sys_sendmsg(fd_t sock_fd, addr_t msghdr_addr, int_t flags) { if (real_flags < 0) goto out_free_scm; - err = sendmsg(sock->real_fd, &msg, real_flags); + TASK_MAY_BLOCK { + err = sendmsg(sock->real_fd, &msg, real_flags); + } if (err < 0) { err = errno_map(); goto out_free_scm; @@ -1018,7 +1029,10 @@ int_t sys_recvmsg(fd_t sock_fd, addr_t msghdr_addr, int_t flags) { msg_iov[i].iov_base = malloc(msg_iov_fake[i].len); } - ssize_t res = recvmsg(sock->real_fd, &msg, real_flags); + ssize_t res = 0; + TASK_MAY_BLOCK { + res = recvmsg(sock->real_fd, &msg, real_flags); + } int err = 0; if (res < 0) err = errno_map(); diff --git a/kernel/fs.c b/kernel/fs.c index df1d2aaaa2..ffbd219910 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -76,7 +76,10 @@ fd_t sys_openat(fd_t at_f, addr_t path_addr, dword_t flags, mode_t_ mode) { struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; - struct fd *fd = generic_openat(at, path, flags, mode); + struct fd *fd; + TASK_MAY_BLOCK { + fd = generic_openat(at, path, flags, mode); + } if (IS_ERR(fd)) return PTR_ERR(fd); return f_install(fd, flags); @@ -241,7 +244,11 @@ dword_t sys_read(fd_t fd_no, addr_t buf_addr, dword_t size) { char *buf = (char *) malloc(size); if (buf == NULL) return _ENOMEM; - int_t res = sys_read_buf(fd_no, buf, size); + + int_t res = 0; + TASK_MAY_BLOCK { + res = sys_read_buf(fd_no, buf, size); + } if (res >= 0) { if (user_write(buf_addr, buf, res)) res = _EFAULT; @@ -282,7 +289,9 @@ dword_t sys_write(fd_t fd_no, addr_t buf_addr, dword_t size) { if (print_size > 100) print_size = 100; STRACE("write(%d, \"%.*s\", %d)", fd_no, print_size, buf, size); - res = sys_write_buf(fd_no, buf, size); + TASK_MAY_BLOCK { + res = sys_write_buf(fd_no, buf, size); + } out: free(buf); return res; @@ -325,7 +334,10 @@ dword_t sys_readv(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) { free(iovec); return _ENOMEM; } - ssize_t res = sys_read_buf(fd_no, buf, io_size); + ssize_t res = 0; + TASK_MAY_BLOCK { + res = sys_read_buf(fd_no, buf, io_size); + } if (res < 0) goto error; @@ -373,8 +385,9 @@ dword_t sys_writev(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) { STRACE(" {\"%.*s\", %u}", print_size, buf + offset, iovec[i].len); offset += iovec[i].len; } - res = sys_write_buf(fd_no, buf, io_size); - + TASK_MAY_BLOCK { + res = sys_write_buf(fd_no, buf, io_size); + } error: free(buf); free(iovec); @@ -422,6 +435,7 @@ dword_t sys_pread(fd_t f, addr_t buf_addr, dword_t size, off_t_ off) { char *buf = malloc(size+1); if (buf == NULL) return _ENOMEM; + task_may_block_start(); lock(&fd->lock); ssize_t res; if (fd->ops->pread) { @@ -447,6 +461,7 @@ dword_t sys_pread(fd_t f, addr_t buf_addr, dword_t size, off_t_ off) { } out: unlock(&fd->lock); + task_may_block_end(); free(buf); return res; } @@ -461,20 +476,24 @@ dword_t sys_pwrite(fd_t f, addr_t buf_addr, dword_t size, off_t_ off) { return _ENOMEM; if (user_read(buf_addr, buf, size)) return _EFAULT; + lock(&fd->lock); ssize_t res; - if (fd->ops->pwrite) { - res = fd->ops->pwrite(fd, buf, size, off); - } else { - off_t_ saved_off = fd->ops->lseek(fd, 0, LSEEK_CUR); - if ((res = fd->ops->lseek(fd, off, LSEEK_SET)) >= 0) { - res = fd->ops->write(fd, buf, size); - // This really shouldn't fail. The lseek man page lists these reasons: - // EBADF, ESPIPE: can't happen because the last lseek wouldn't have succeeded. - // EOVERFLOW: can't happen for LSEEK_SET. - // EINVAL: can't happen other than typoing LSEEK_SET, because we know saved_off is not negative. - off_t_ lseek_res = fd->ops->lseek(fd, saved_off, LSEEK_SET); - assert(lseek_res >= 0); + + TASK_MAY_BLOCK { + if (fd->ops->pwrite) { + res = fd->ops->pwrite(fd, buf, size, off); + } else { + off_t_ saved_off = fd->ops->lseek(fd, 0, LSEEK_CUR); + if ((res = fd->ops->lseek(fd, off, LSEEK_SET)) >= 0) { + res = fd->ops->write(fd, buf, size); + // This really shouldn't fail. The lseek man page lists these reasons: + // EBADF, ESPIPE: can't happen because the last lseek wouldn't have succeeded. + // EOVERFLOW: can't happen for LSEEK_SET. + // EINVAL: can't happen other than typoing LSEEK_SET, because we know saved_off is not negative. + off_t_ lseek_res = fd->ops->lseek(fd, saved_off, LSEEK_SET); + assert(lseek_res >= 0); + } } } unlock(&fd->lock); @@ -531,7 +550,12 @@ dword_t sys_ioctl(fd_t f, dword_t cmd, dword_t arg) { bit_clear(f, current->files->cloexec); return 0; } - return fd_ioctl(fd, cmd, arg); + + dword_t res = 0; + TASK_MAY_BLOCK { + res = fd_ioctl(fd, cmd, arg); + } + return res; } dword_t sys_getcwd(addr_t buf_addr, dword_t size) { @@ -953,8 +977,10 @@ dword_t sys_fsync(fd_t f) { if (fd == NULL) return _EBADF; int err = 0; - if (fd->ops->fsync) - err = fd->ops->fsync(fd); + TASK_MAY_BLOCK { + if (fd->ops->fsync) + err = fd->ops->fsync(fd); + } return err; } diff --git a/kernel/poll.c b/kernel/poll.c index e5a7add3a4..db223bc697 100644 --- a/kernel/poll.c +++ b/kernel/poll.c @@ -91,7 +91,10 @@ dword_t sys_select(fd_t nfds, addr_t readfds_addr, addr_t writefds_addr, addr_t memset(writefds, 0, fdset_size); memset(exceptfds, 0, fdset_size); struct select_context context = {readfds, writefds, exceptfds}; - int err = poll_wait(poll, select_event_callback, &context, timeout_addr == 0 ? NULL : &timeout_ts); + int err = 0; + TASK_MAY_BLOCK { + err = poll_wait(poll, select_event_callback, &context, timeout_addr == 0 ? NULL : &timeout_ts); + } STRACE("%d end select ", current->pid); for (fd_t i = 0; i < nfds; i++) { if (bit_test(i, readfds) || bit_test(i, writefds) || bit_test(i, exceptfds)) { @@ -191,7 +194,10 @@ dword_t sys_poll(addr_t fds, dword_t nfds, int_t timeout) { timeout_ts.tv_sec = timeout / 1000; timeout_ts.tv_nsec = (timeout % 1000) * 1000000; } - int res = poll_wait(poll, poll_event_callback, &context, timeout < 0 ? NULL : &timeout_ts); + int res = 0; + TASK_MAY_BLOCK { + res = poll_wait(poll, poll_event_callback, &context, timeout < 0 ? NULL : &timeout_ts); + } poll_destroy(poll); for (unsigned i = 0; i < nfds; i++) { if (files[i] != NULL) diff --git a/kernel/signal.c b/kernel/signal.c index 5349d05f26..b20a84691b 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -636,8 +636,10 @@ int_t sys_rt_sigsuspend(addr_t mask_addr, uint_t size) { int_t sys_pause() { lock(¤t->sighand->lock); - while (wait_for(¤t->pause, ¤t->sighand->lock, NULL) != _EINTR) - continue; + TASK_MAY_BLOCK { + while (wait_for(¤t->pause, ¤t->sighand->lock, NULL) != _EINTR) + continue; + } unlock(¤t->sighand->lock); return _EINTR; } diff --git a/kernel/task.c b/kernel/task.c index 65e3ed1d8e..c9fedf042b 100644 --- a/kernel/task.c +++ b/kernel/task.c @@ -48,6 +48,26 @@ struct pid *pid_get_last_allocated() { return pid_get(last_allocated_pid); } +dword_t get_count_of_blocked_tasks() { + dword_t res = 0; + for (int pid = 0; pid <= MAX_PID; pid++) { + if (pid_get_task(pid) && pid_get_task(pid)->io_block) { + res++; + } + } + return res; +} + +dword_t get_count_of_alive_tasks() { + dword_t res = 0; + for (int pid = 0; pid <= MAX_PID; pid++) { + if (pid_get_task(pid)) { + res++; + } + } + return res; +} + struct task *task_create_(struct task *parent) { lock(&pids_lock); do { diff --git a/kernel/task.h b/kernel/task.h index bb1c510651..712d937273 100644 --- a/kernel/task.h +++ b/kernel/task.h @@ -71,6 +71,7 @@ struct task { dword_t exit_code; bool zombie; bool exiting; + bool io_block; // this structure is allocated on the stack of the parent's clone() call struct vfork_info { @@ -174,6 +175,8 @@ struct pid *pid_get(dword_t pid); struct pid *pid_get_last_allocated(void); struct task *pid_get_task(dword_t pid); struct task *pid_get_task_zombie(dword_t id); // don't return null if the task exists as a zombie +dword_t get_count_of_blocked_tasks(void); +dword_t get_count_of_alive_tasks(void); #define MAX_PID (1 << 15) // oughta be enough @@ -189,4 +192,24 @@ extern void (*exit_hook)(struct task *task, int code); // Will ensure that the -pid part always fits, then will fit as much of comm as possible. void update_thread_name(void); +// To collect statics on which tasks are blocked we need to proccess areas +// of code which could block our task (e.g reads or writes). Before executing +// of functions which can block the task, we mark our task as blocked and +// unblock it after the function is executed. +__attribute__((always_inline)) inline int task_may_block_start(void) { + lock(&pids_lock); + current->io_block = 1; + unlock(&pids_lock); + return 0; +} + +__attribute__((always_inline)) inline int task_may_block_end(void) { + lock(&pids_lock); + current->io_block = 0; + unlock(&pids_lock); + return 0; +} + +#define TASK_MAY_BLOCK for (int i = task_may_block_start(); i < 1; task_may_block_end(), i++) + #endif diff --git a/kernel/time.c b/kernel/time.c index cf4c2872ae..c69932cddf 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -190,7 +190,11 @@ dword_t sys_nanosleep(addr_t req_addr, addr_t rem_addr) { req.tv_sec = req_ts.sec; req.tv_nsec = req_ts.nsec; struct timespec rem; - if (nanosleep(&req, &rem) < 0) + int res = 0; + TASK_MAY_BLOCK { + res = nanosleep(&req, &rem); + } + if (res < 0) return errno_map(); if (rem_addr != 0) { struct timespec_ rem_ts;