1 /* $NetBSD: kttcp.c,v 1.3 2002/07/03 19:36:52 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Frank van der Linden and Jason R. Thorpe for 8 * Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed for the NetBSD Project by 21 * Wasabi Systems, Inc. 22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 23 * or promote products derived from this software without specific prior 24 * written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * kttcp.c -- 41 * 42 * This module provides kernel support for testing network 43 * throughput from the perspective of the kernel. It is 44 * similar in spirit to the classic ttcp network benchmark 45 * program, the main difference being that with kttcp, the 46 * kernel is the source and sink of the data. 47 * 48 * Testing like this is useful for a few reasons: 49 * 50 * 1. This allows us to know what kind of performance we can 51 * expect from network applications that run in the kernel 52 * space, such as the NFS server or the NFS client. These 53 * applications don't have to move the data to/from userspace, 54 * and so benchmark programs which run in userspace don't 55 * give us an accurate model. 56 * 57 * 2. Since data received is just thrown away, the receiver 58 * is very fast. This can provide better exercise for the 59 * sender at the other end. 60 * 61 * 3. Since the NetBSD kernel currently uses a run-to-completion 62 * scheduling model, kttcp provides a benchmark model where 63 * preemption of the benchmark program is not an issue. 64 */ 65 66 #include <sys/param.h> 67 #include <sys/systm.h> 68 #include <sys/malloc.h> 69 #include <sys/mbuf.h> 70 #include <sys/sysctl.h> 71 #include <sys/file.h> 72 #include <sys/filedesc.h> 73 #include <sys/errno.h> 74 #include <sys/uio.h> 75 #include <sys/conf.h> 76 #include <sys/kernel.h> 77 #include <sys/fcntl.h> 78 #include <sys/protosw.h> 79 #include <sys/socketvar.h> 80 #include <sys/socket.h> 81 #include <sys/mbuf.h> 82 #include <sys/resourcevar.h> 83 #include <sys/proc.h> 84 #include <sys/module.h> 85 86 #include "kttcpio.h" 87 88 #ifndef timersub 89 #define timersub(tvp, uvp, vvp) \ 90 do { \ 91 (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ 92 (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ 93 if ((vvp)->tv_usec < 0) { \ 94 (vvp)->tv_sec--; \ 95 (vvp)->tv_usec += 1000000; \ 96 } \ 97 } while (0) 98 #endif 99 100 static int kttcp_send(struct thread *p, struct kttcp_io_args *); 101 static int kttcp_recv(struct thread *p, struct kttcp_io_args *); 102 103 static d_open_t kttcpopen; 104 static d_ioctl_t kttcpioctl; 105 106 static struct cdevsw kttcp_cdevsw = { 107 .d_open = kttcpopen, 108 .d_ioctl = kttcpioctl, 109 .d_name = "kttcp", 110 .d_maj = MAJOR_AUTO, 111 .d_version = D_VERSION, 112 }; 113 114 static int 115 kttcpopen(struct cdev *dev, int flag, int mode, struct thread *td) 116 { 117 /* Always succeeds. */ 118 return (0); 119 } 120 121 static int 122 kttcpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 123 { 124 int error; 125 126 if ((flag & FWRITE) == 0) 127 return EPERM; 128 129 switch (cmd) { 130 case KTTCP_IO_SEND: 131 error = kttcp_send(td, (struct kttcp_io_args *) data); 132 break; 133 134 case KTTCP_IO_RECV: 135 error = kttcp_recv(td, (struct kttcp_io_args *) data); 136 break; 137 138 default: 139 return EINVAL; 140 } 141 142 return error; 143 } 144 145 static int nbyte = 65536; 146 147 static int 148 kttcp_send(struct thread *td, struct kttcp_io_args *kio) 149 { 150 struct file *fp; 151 int error; 152 struct timeval t0, t1; 153 unsigned long long len = 0; 154 struct uio auio; 155 struct iovec aiov; 156 157 bzero(&aiov, sizeof(aiov)); 158 bzero(&auio, sizeof(auio)); 159 auio.uio_iov = &aiov; 160 auio.uio_segflg = UIO_NOCOPY; 161 162 error = fget(td, kio->kio_socket, &fp); 163 if (error != 0) 164 return error; 165 166 if ((fp->f_flag & FWRITE) == 0) { 167 fdrop(fp, td); 168 return EBADF; 169 } 170 if (fp->f_type == DTYPE_SOCKET) { 171 len = kio->kio_totalsize; 172 microtime(&t0); 173 do { 174 nbyte = MIN(len, (unsigned long long)nbyte); 175 aiov.iov_len = nbyte; 176 auio.uio_resid = nbyte; 177 auio.uio_offset = 0; 178 error = sosend((struct socket *)fp->f_data, NULL, 179 &auio, NULL, NULL, 0, td); 180 len -= auio.uio_offset; 181 } while (error == 0 && len != 0); 182 microtime(&t1); 183 } else 184 error = EFTYPE; 185 fdrop(fp, td); 186 if (error != 0) 187 return error; 188 timersub(&t1, &t0, &kio->kio_elapsed); 189 190 kio->kio_bytesdone = kio->kio_totalsize - len; 191 192 return 0; 193 } 194 195 static int 196 kttcp_recv(struct thread *td, struct kttcp_io_args *kio) 197 { 198 struct file *fp; 199 int error; 200 struct timeval t0, t1; 201 unsigned long long len = 0; 202 struct uio auio; 203 struct iovec aiov; 204 205 bzero(&aiov, sizeof(aiov)); 206 bzero(&auio, sizeof(auio)); 207 auio.uio_iov = &aiov; 208 auio.uio_segflg = UIO_NOCOPY; 209 210 error = fget(td, kio->kio_socket, &fp); 211 if (error != 0) 212 return error; 213 214 if ((fp->f_flag & FWRITE) == 0) { 215 fdrop(fp, td); 216 return EBADF; 217 } 218 if (fp->f_type == DTYPE_SOCKET) { 219 len = kio->kio_totalsize; 220 microtime(&t0); 221 do { 222 nbyte = MIN(len, (unsigned long long)nbyte); 223 aiov.iov_len = nbyte; 224 auio.uio_resid = nbyte; 225 auio.uio_offset = 0; 226 error = soreceive((struct socket *)fp->f_data, 227 NULL, &auio, NULL, NULL, NULL); 228 len -= auio.uio_offset; 229 } while (error == 0 && len > 0 && auio.uio_offset != 0); 230 microtime(&t1); 231 if (error == EPIPE) 232 error = 0; 233 } else 234 error = EFTYPE; 235 fdrop(fp, td); 236 if (error != 0) 237 return error; 238 timersub(&t1, &t0, &kio->kio_elapsed); 239 240 kio->kio_bytesdone = kio->kio_totalsize - len; 241 242 return 0; 243 } 244 245 static struct cdev *kttcp_dev; 246 247 /* 248 * Initialization code, both for static and dynamic loading. 249 */ 250 static int 251 kttcpdev_modevent(module_t mod, int type, void *unused) 252 { 253 switch (type) { 254 case MOD_LOAD: 255 kttcp_dev = make_dev(&kttcp_cdevsw, 0, 256 UID_ROOT, GID_WHEEL, 0666, 257 "kttcp"); 258 return 0; 259 case MOD_UNLOAD: 260 /*XXX disallow if active sessions */ 261 destroy_dev(kttcp_dev); 262 return 0; 263 } 264 return EINVAL; 265 } 266 267 static moduledata_t kttcpdev_mod = { 268 "kttcpdev", 269 kttcpdev_modevent, 270 0 271 }; 272 MODULE_VERSION(kttcpdev, 1); 273 DECLARE_MODULE(kttcpdev, kttcpdev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 274