#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/resourcevar.h>
#include <sys/proc.h>
#include <sys/module.h>
#include "kttcpio.h"
#ifndef timersub
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
#endif
static int kttcp_send(struct thread *p, struct kttcp_io_args *);
static int kttcp_recv(struct thread *p, struct kttcp_io_args *);
static d_open_t kttcpopen;
static d_ioctl_t kttcpioctl;
static struct cdevsw kttcp_cdevsw = {
.d_open = kttcpopen,
.d_ioctl = kttcpioctl,
.d_name = "kttcp",
.d_maj = MAJOR_AUTO,
.d_version = D_VERSION,
};
static int
kttcpopen(struct cdev *dev, int flag, int mode, struct thread *td)
{
return (0);
}
static int
kttcpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
{
int error;
if ((flag & FWRITE) == 0)
return EPERM;
switch (cmd) {
case KTTCP_IO_SEND:
error = kttcp_send(td, (struct kttcp_io_args *) data);
break;
case KTTCP_IO_RECV:
error = kttcp_recv(td, (struct kttcp_io_args *) data);
break;
default:
return EINVAL;
}
return error;
}
static int nbyte = 65536;
static int
kttcp_send(struct thread *td, struct kttcp_io_args *kio)
{
struct file *fp;
int error;
struct timeval t0, t1;
unsigned long long len = 0;
struct uio auio;
struct iovec aiov;
bzero(&aiov, sizeof(aiov));
bzero(&auio, sizeof(auio));
auio.uio_iov = &aiov;
auio.uio_segflg = UIO_NOCOPY;
error = fget(td, kio->kio_socket, &fp);
if (error != 0)
return error;
if ((fp->f_flag & FWRITE) == 0) {
fdrop(fp, td);
return EBADF;
}
if (fp->f_type == DTYPE_SOCKET) {
len = kio->kio_totalsize;
microtime(&t0);
do {
nbyte = MIN(len, (unsigned long long)nbyte);
aiov.iov_len = nbyte;
auio.uio_resid = nbyte;
auio.uio_offset = 0;
error = sosend((struct socket *)fp->f_data, NULL,
&auio, NULL, NULL, 0, td);
len -= auio.uio_offset;
} while (error == 0 && len != 0);
microtime(&t1);
} else
error = EFTYPE;
fdrop(fp, td);
if (error != 0)
return error;
timersub(&t1, &t0, &kio->kio_elapsed);
kio->kio_bytesdone = kio->kio_totalsize - len;
return 0;
}
static int
kttcp_recv(struct thread *td, struct kttcp_io_args *kio)
{
struct file *fp;
int error;
struct timeval t0, t1;
unsigned long long len = 0;
struct uio auio;
struct iovec aiov;
bzero(&aiov, sizeof(aiov));
bzero(&auio, sizeof(auio));
auio.uio_iov = &aiov;
auio.uio_segflg = UIO_NOCOPY;
error = fget(td, kio->kio_socket, &fp);
if (error != 0)
return error;
if ((fp->f_flag & FWRITE) == 0) {
fdrop(fp, td);
return EBADF;
}
if (fp->f_type == DTYPE_SOCKET) {
len = kio->kio_totalsize;
microtime(&t0);
do {
nbyte = MIN(len, (unsigned long long)nbyte);
aiov.iov_len = nbyte;
auio.uio_resid = nbyte;
auio.uio_offset = 0;
error = soreceive((struct socket *)fp->f_data,
NULL, &auio, NULL, NULL, NULL);
len -= auio.uio_offset;
} while (error == 0 && len > 0 && auio.uio_offset != 0);
microtime(&t1);
if (error == EPIPE)
error = 0;
} else
error = EFTYPE;
fdrop(fp, td);
if (error != 0)
return error;
timersub(&t1, &t0, &kio->kio_elapsed);
kio->kio_bytesdone = kio->kio_totalsize - len;
return 0;
}
static struct cdev *kttcp_dev;
static int
kttcpdev_modevent(module_t mod, int type, void *unused)
{
switch (type) {
case MOD_LOAD:
kttcp_dev = make_dev(&kttcp_cdevsw, 0,
UID_ROOT, GID_WHEEL, 0666,
"kttcp");
return 0;
case MOD_UNLOAD:
destroy_dev(kttcp_dev);
return 0;
}
return EINVAL;
}
static moduledata_t kttcpdev_mod = {
"kttcpdev",
kttcpdev_modevent,
0
};
MODULE_VERSION(kttcpdev, 1);
DECLARE_MODULE(kttcpdev, kttcpdev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);