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
101static int kttcp_send(struct thread *p, struct kttcp_io_args *);
102static int kttcp_recv(struct thread *p, struct kttcp_io_args *);
103
104static d_open_t		kttcpopen;
105static d_ioctl_t	kttcpioctl;
106
107static 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
115static int
116kttcpopen(struct cdev *dev, int flag, int mode, struct thread *td)
117{
118	/* Always succeeds. */
119	return (0);
120}
121
122static int
123kttcpioctl(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
146static int nbyte = 65536;
147
148static int
149kttcp_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
196static int
197kttcp_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
246static struct cdev *kttcp_dev;
247
248/*
249 * Initialization code, both for static and dynamic loading.
250 */
251static int
252kttcpdev_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
268static moduledata_t kttcpdev_mod = {
269	"kttcpdev",
270	kttcpdev_modevent,
271	0
272};
273MODULE_VERSION(kttcpdev, 1);
274DECLARE_MODULE(kttcpdev, kttcpdev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
275