Home | History | Annotate | Line # | Download | only in librumpuser
      1 /*	$NetBSD: rumpuser_bio.c,v 1.10 2014/11/04 19:05:17 pooka 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.10 2014/11/04 19:05:17 pooka 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 
     48 struct 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
     60 static pthread_mutex_t biomtx = PTHREAD_MUTEX_INITIALIZER;
     61 static pthread_cond_t biocv = PTHREAD_COND_INITIALIZER;
     62 static int bio_head, bio_tail;
     63 static struct rumpuser_bio bios[N_BIOS];
     64 
     65 static void
     66 dobio(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 
    104 static void *
    105 biothread(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 
    133 void
    134 rumpuser_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