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