From 16029d56b5c46ac541a9e2186acee79f6455ee41 Mon Sep 17 00:00:00 2001 From: wrstuden Date: Sat, 6 Nov 2004 02:03:20 +0000 Subject: [PATCH] Add support for FIONWRITE and FIONSPACE ioctls. FIONWRITE reports the number of bytes in the send queue, and FIONSPACE reports the number of free bytes in the send queue. These ioctls permit applications to monitor file descriptor transmission dynamics. In examining prior art, FIONWRITE exists with the semantics given here. FIONSPACE is provided so that programs may easily determine how much space is left in the send queue; they do not need to know the send queue size. The fact that a write may block even if there is enough space in the send queue for it is noted in the documentation. FIONWRITE functionality may be used to implement TIOCOUTQ for Linux emulation - Linux extended this ioctl to sockets, even though they are not ttys. --- lib/libc/sys/ioctl.2 | 22 +++++++++++++++++++++- sys/kern/sys_pipe.c | 36 ++++++++++++++++++++++++++++++++++-- sys/kern/sys_socket.c | 22 ++++++++++++++++++++-- sys/kern/tty.c | 18 ++++++++++++++++-- sys/kern/vfs_vnops.c | 13 +++++++++++-- sys/sys/filio.h | 6 +++++- 6 files changed, 107 insertions(+), 10 deletions(-) diff --git a/lib/libc/sys/ioctl.2 b/lib/libc/sys/ioctl.2 index 5de5116cfaf8..3d2d76a22356 100644 --- a/lib/libc/sys/ioctl.2 +++ b/lib/libc/sys/ioctl.2 @@ -1,4 +1,4 @@ -.\" $NetBSD: ioctl.2,v 1.18 2004/05/13 10:20:58 wiz Exp $ +.\" $NetBSD: ioctl.2,v 1.19 2004/11/06 02:03:20 wrstuden Exp $ .\" .\" Copyright (c) 1980, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -89,6 +89,26 @@ These include: .Bl -tag -width "xxxxxx" .It Dv FIONREAD "int" Get the number of bytes that are immediately available for reading. +.It Dv FIONWRITE "int" +Get the number of bytes in the descriptor's send queue. +These bytes are data which has been written to the descriptor but +which are being held by the kernel for further processing. +The nature of the required processing depends on the underlying device. +For tty devices, these bytes are typically queued for delivery +to the tty hardware. +For TCP sockets, these bytes have not yet been acknolwedged by the +other side of the connection. +For files, this operation always returns zero as files do not have +send queues. +.It Dv FIONSPACE "int" +Get the free space in the descriptor's send queue. +This value is the size of the send queue minus the number of bytes +being held in the queue. +Note: while this value represents the number of bytes that may be +added to the queue, other resource limitations may cause a write +not larger than the send queue's space to be blocked. +One such limitation would be a lack of network buffers for a write +to a network connection. .It Dv FIONBIO "int" Set non-blocking I/O mode if the argument is non-zero. In non-blocking mode, diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index 69a4e7cbbc0b..2bbd3e47e339 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -1,4 +1,4 @@ -/* $NetBSD: sys_pipe.c,v 1.58 2004/07/17 20:50:08 mycroft Exp $ */ +/* $NetBSD: sys_pipe.c,v 1.59 2004/11/06 02:03:20 wrstuden Exp $ */ /*- * Copyright (c) 2003 The NetBSD Foundation, Inc. @@ -83,7 +83,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: sys_pipe.c,v 1.58 2004/07/17 20:50:08 mycroft Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_pipe.c,v 1.59 2004/11/06 02:03:20 wrstuden Exp $"); #include #include @@ -1141,6 +1141,38 @@ pipe_ioctl(fp, cmd, data, p) PIPE_UNLOCK(pipe); return (0); + case FIONWRITE: + /* Look at other side */ + pipe = pipe->pipe_peer; + PIPE_LOCK(pipe); +#ifndef PIPE_NODIRECT + if (pipe->pipe_state & PIPE_DIRECTW) + *(int *)data = pipe->pipe_map.cnt; + else +#endif + *(int *)data = pipe->pipe_buffer.cnt; + PIPE_UNLOCK(pipe); + return (0); + + case FIONSPACE: + /* Look at other side */ + pipe = pipe->pipe_peer; + PIPE_LOCK(pipe); +#ifndef PIPE_NODIRECT + /* + * If we're in direct-mode, we don't really have a + * send queue, and any other write will block. Thus + * zero seems like the best answer. + */ + if (pipe->pipe_state & PIPE_DIRECTW) + *(int *)data = 0; + else +#endif + *(int *)data = pipe->pipe_buffer.size - + pipe->pipe_buffer.cnt; + PIPE_UNLOCK(pipe); + return (0); + case TIOCSPGRP: case FIOSETOWN: return fsetown(p, &pipe->pipe_pgid, cmd, data); diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c index 099e0ef2bfef..fd063a216ab0 100644 --- a/sys/kern/sys_socket.c +++ b/sys/kern/sys_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: sys_socket.c,v 1.40 2004/05/22 22:52:13 jonathan Exp $ */ +/* $NetBSD: sys_socket.c,v 1.41 2004/11/06 02:03:20 wrstuden Exp $ */ /* * Copyright (c) 1982, 1986, 1990, 1993 @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: sys_socket.c,v 1.40 2004/05/22 22:52:13 jonathan Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_socket.c,v 1.41 2004/11/06 02:03:20 wrstuden Exp $"); #include #include @@ -116,6 +116,24 @@ soo_ioctl(fp, cmd, data, p) *(int *)data = so->so_rcv.sb_cc; return (0); + case FIONWRITE: + *(int *)data = so->so_snd.sb_cc; + return (0); + + case FIONSPACE: + /* + * See the comment around sbspace()'s definition + * in sys/socketvar.h in face of counts about maximum + * to understand the following test. We detect overflow + * and return zero. + */ + if ((so->snd.sb_hiwat < so->snd.sb_cc) + || (so->snd.sb_mbmax < so->snd.sb_mbcnt)) + *(int *)data = 0; + else + *(int *)data = sbspace(so->so_snd); + return (0); + case SIOCSPGRP: case FIOSETOWN: case TIOCSPGRP: diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 3984b5cc1376..8d76c031f47e 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1,4 +1,4 @@ -/* $NetBSD: tty.c,v 1.169 2004/10/15 07:15:39 thorpej Exp $ */ +/* $NetBSD: tty.c,v 1.170 2004/11/06 02:03:20 wrstuden Exp $ */ /*- * Copyright (c) 1982, 1986, 1990, 1991, 1993 @@ -37,7 +37,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: tty.c,v 1.169 2004/10/15 07:15:39 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tty.c,v 1.170 2004/11/06 02:03:20 wrstuden Exp $"); #include #include @@ -880,6 +880,20 @@ ttioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) TTY_UNLOCK(tp); splx(s); break; + case FIONWRITE: /* get # bytes to written & unsent */ + s = spltty(); + TTY_LOCK(tp); + *(int *)data = tp->t_outq.c_cc; + TTY_UNLOCK(tp); + splx(s); + break; + case FIONSPACE: /* get # bytes to written & unsent */ + s = spltty(); + TTY_LOCK(tp); + *(int *)data = tp->t_outq.c_cn - tp->t_outq.c_cc; + TTY_UNLOCK(tp); + splx(s); + break; case TIOCEXCL: /* set exclusive use of tty */ s = spltty(); TTY_LOCK(tp); diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 35678b1a7302..32365b9fd599 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_vnops.c,v 1.80 2004/05/31 09:02:51 yamt Exp $ */ +/* $NetBSD: vfs_vnops.c,v 1.81 2004/11/06 02:03:20 wrstuden Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1993 @@ -37,7 +37,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.80 2004/05/31 09:02:51 yamt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.81 2004/11/06 02:03:20 wrstuden Exp $"); #include "fs_union.h" @@ -692,6 +692,15 @@ vn_ioctl(fp, com, data, p) *(int *)data = vattr.va_size - fp->f_offset; return (0); } + if ((com == FIONWRITE) || (comm == FIONSPACE)) { + /* + * Files don't have send queues, so there never + * are any bytes in them, nor is there any + * open space in them. + */ + *(int *)data = 0; + return (0); + } if (com == FIOGETBMAP) { daddr_t *block; diff --git a/sys/sys/filio.h b/sys/sys/filio.h index e9f478fa9248..aa64f167ea4a 100644 --- a/sys/sys/filio.h +++ b/sys/sys/filio.h @@ -1,4 +1,4 @@ -/* $NetBSD: filio.h,v 1.8 2003/08/07 16:34:04 agc Exp $ */ +/* $NetBSD: filio.h,v 1.9 2004/11/06 02:03:20 wrstuden Exp $ */ /*- * Copyright (c) 1982, 1986, 1990, 1993, 1994 @@ -51,6 +51,10 @@ #define FIOGETOWN _IOR('f', 123, int) /* get owner */ #define OFIOGETBMAP _IOWR('f', 122, uint32_t) /* get underlying block no. */ #define FIOGETBMAP _IOWR('f', 122, daddr_t) /* get underlying block no. */ +#define FIONWRITE _IOR('f', 121, int) /* get # bytes outstanding + * in send queue. */ +#define FIONSPACE _IOR('f', 120, int) /* get space in send queue. */ + /* Ugly symbol for compatibility with other operating systems */ #define FIBMAP FIOGETBMAP