xref: /freebsd/tools/tools/kttcp/sys/kttcp.c (revision c697fb7f)
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