1/*	$NetBSD: rumpuser_bio.c,v 1.9 2014/08/25 10:21:39 justin Exp $	*/
2
3/*-
4 * Copyright (c) 2013 Antti Kantee.  All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include "rumpuser_port.h"
29
30#if !defined(lint)
31__RCSID("$NetBSD: rumpuser_bio.c,v 1.9 2014/08/25 10:21:39 justin Exp $");
32#endif /* !lint */
33
34#include <sys/types.h>
35
36#include <assert.h>
37#include <errno.h>
38#include <pthread.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <rump/rumpuser.h>
45
46#include "rumpuser_int.h"
47
48struct rumpuser_bio {
49	int bio_fd;
50	int bio_op;
51	void *bio_data;
52	size_t bio_dlen;
53	off_t bio_off;
54
55	rump_biodone_fn bio_done;
56	void *bio_donearg;
57};
58
59#define N_BIOS 128
60static pthread_mutex_t biomtx = PTHREAD_MUTEX_INITIALIZER;
61static pthread_cond_t biocv = PTHREAD_COND_INITIALIZER;
62static int bio_head, bio_tail;
63static struct rumpuser_bio bios[N_BIOS];
64
65static void
66dobio(struct rumpuser_bio *biop)
67{
68	ssize_t rv;
69	int error, dummy;
70
71	assert(biop->bio_donearg != NULL);
72	if (biop->bio_op & RUMPUSER_BIO_READ) {
73		error = 0;
74		rv = pread(biop->bio_fd, biop->bio_data,
75		    biop->bio_dlen, biop->bio_off);
76		if (rv < 0) {
77			rv = 0;
78			error = rumpuser__errtrans(errno);
79		}
80	} else {
81		error = 0;
82		rv = pwrite(biop->bio_fd, biop->bio_data,
83		    biop->bio_dlen, biop->bio_off);
84		if (rv < 0) {
85			rv = 0;
86			error = rumpuser__errtrans(errno);
87		} else if (biop->bio_op & RUMPUSER_BIO_SYNC) {
88#ifdef HAVE_FSYNC_RANGE
89			fsync_range(biop->bio_fd, FDATASYNC,
90			    biop->bio_off, biop->bio_dlen);
91#else
92			fsync(biop->bio_fd);
93#endif
94		}
95	}
96	rumpkern_sched(0, NULL);
97	biop->bio_done(biop->bio_donearg, (size_t)rv, error);
98	rumpkern_unsched(&dummy, NULL);
99
100	/* paranoia */
101	biop->bio_donearg = NULL;
102}
103
104static void *
105biothread(void *arg)
106{
107	struct rumpuser_bio *biop;
108	int rv;
109
110	rumpuser__hyp.hyp_schedule();
111	rv = rumpuser__hyp.hyp_lwproc_newlwp(0);
112	assert(rv == 0);
113	rumpuser__hyp.hyp_unschedule();
114	NOFAIL_ERRNO(pthread_mutex_lock(&biomtx));
115	for (;;) {
116		while (bio_head == bio_tail)
117			NOFAIL_ERRNO(pthread_cond_wait(&biocv, &biomtx));
118
119		biop = &bios[bio_tail];
120		pthread_mutex_unlock(&biomtx);
121
122		dobio(biop);
123
124		NOFAIL_ERRNO(pthread_mutex_lock(&biomtx));
125		bio_tail = (bio_tail+1) % N_BIOS;
126		pthread_cond_signal(&biocv);
127	}
128
129	/* unreachable */
130	abort();
131}
132
133void
134rumpuser_bio(int fd, int op, void *data, size_t dlen, int64_t doff,
135	rump_biodone_fn biodone, void *bioarg)
136{
137	struct rumpuser_bio bio;
138	static int inited = 0;
139	static int usethread = 1;
140	int nlocks;
141
142	rumpkern_unsched(&nlocks, NULL);
143
144	if (!inited) {
145		pthread_mutex_lock(&biomtx);
146		if (!inited) {
147			char buf[16];
148			pthread_t pt;
149
150			/*
151			 * duplicates policy of rump kernel.  maybe a bit
152			 * questionable, but since the setting is not
153			 * used in normal circumstances, let's not care
154			 */
155			if (getenv_r("RUMP_THREADS", buf, sizeof(buf)) == 0)
156				usethread = *buf != '0';
157
158			if (usethread)
159				pthread_create(&pt, NULL, biothread, NULL);
160			inited = 1;
161		}
162		pthread_mutex_unlock(&biomtx);
163		assert(inited);
164	}
165
166	bio.bio_fd = fd;
167	bio.bio_op = op;
168	bio.bio_data = data;
169	bio.bio_dlen = dlen;
170	bio.bio_off = (off_t)doff;
171	bio.bio_done = biodone;
172	bio.bio_donearg = bioarg;
173
174	if (!usethread) {
175		dobio(&bio);
176	} else {
177		pthread_mutex_lock(&biomtx);
178		while ((bio_head+1) % N_BIOS == bio_tail)
179			pthread_cond_wait(&biocv, &biomtx);
180
181		bios[bio_head] = bio;
182		bio_head = (bio_head+1) % N_BIOS;
183
184		pthread_cond_signal(&biocv);
185		pthread_mutex_unlock(&biomtx);
186	}
187
188	rumpkern_sched(nlocks, NULL);
189}
190