Home | History | Annotate | Download | only in diskomizer
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 #pragma ident	"@(#)daio_async.c	1.8	09/05/26 SMI"
     23 
     24 /*
     25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     26  * Use is subject to license terms.
     27  */
     28 
     29 #include <diskomizer/daio.h>
     30 #include "async.h"
     31 #include "list.h"
     32 /*LINTED*/
     33 #include "daio_async.h"
     34 #include <pthread.h>
     35 #include <stdlib.h>
     36 #include <errno.h>
     37 #include <sys/time.h>
     38 #include <unistd.h>
     39 
     40 struct daio_async {
     41 	struct async_work work;
     42 	/* lock protects the timestamps and status */
     43 	pthread_mutex_t mlock;
     44 	daio_status_t status;
     45 	int fildes;
     46 	int bufs;
     47 	int is_read:1;
     48 	off_t offset;
     49 	uchar_t *bufp;
     50 	struct daio_id *id;
     51 	ssize_t (*rw)(int fildes, void  *buf,  size_t  nbyte,  off_t offset);
     52 	daio_result_t *resultp;
     53 	daio_result_t res;
     54 	struct daio_async *next;
     55 	struct daio_async *prev;
     56 };
     57 struct daio_async_list {
     58 	struct daio_async *head;
     59 	struct daio_async *tail;
     60 };
     61 
     62 static struct daio_async_list free_list;
     63 static struct daio_async_list work_list;
     64 static int saved_count;
     65 static struct daio_async *base;
     66 
     67 static int (*data_check)(uchar_t *buf, size_t bufs, struct daio_id *id);
     68 
     69 static int
     70 bounds_check(struct daio_async *x)
     71 {
     72 	int off = base - x;
     73 	if (off < 0 || off >= saved_count || &base[off] != x) {
     74 		return (0);
     75 	} else {
     76 		return (1);
     77 	}
     78 }
     79 
     80 static struct timespec
     81 timespec_plus_timeval(const struct timespec *ts, const struct timeval *tv)
     82 {
     83 	struct timespec res;
     84 
     85 	res.tv_nsec = ts->tv_nsec + (tv->tv_usec * 1000);
     86 	if (res.tv_nsec >= 1000000000) {
     87 		res.tv_sec = 1 + ts->tv_sec + tv->tv_sec;
     88 		res.tv_nsec %= 1000000000;
     89 	} else {
     90 		res.tv_sec = ts->tv_sec + tv->tv_sec;
     91 	}
     92 	return (res);
     93 }
     94 void
     95 free_daio_async(struct daio_async *x)
     96 {
     97 	LIST_REMOVE(&work_list, x);
     98 	LIST_ADD(&free_list, x);
     99 }
    100 void
    101 daio_async_fini(void *y)
    102 {
    103 	/* To be general purpose this should wait for the work_list to drain */
    104 	int i;
    105 	struct daio_async *x = (struct daio_async *)y;
    106 
    107 	for (i = 0; i < saved_count; i++) {
    108 		pthread_mutex_destroy(&x[i].mlock);
    109 	}
    110 	free(x);
    111 }
    112 
    113 void *
    114 daio_async_init(int count)
    115 {
    116 	int i;
    117 
    118 	if ((base = calloc(count, sizeof (struct daio_async))) == NULL) {
    119 		return (NULL);
    120 	}
    121 	saved_count = count;
    122 
    123 	for (i = 0; i < count; i++) {
    124 		pthread_mutex_init(&base[i].mlock, NULL);
    125 		LIST_ADD(&free_list, &base[i]);
    126 	}
    127 	return (base);
    128 }
    129 
    130 /*ARGSUSED*/
    131 void
    132 daio_async_init_checker(const char *checker, off64_t max_block_size)
    133 {
    134 	data_check = choose_data_checker(checker);
    135 }
    136 
    137 data_checker_t
    138 daio_async_get_checker(void)
    139 {
    140 	return (data_check);
    141 }
    142 
    143 static struct daio_async *
    144 get_daio_async(void)
    145 {
    146 	struct daio_async *x;
    147 
    148 	x = LIST_HEAD(&free_list);
    149 	if (x == NULL) {
    150 		errno = ENOMEM;
    151 		return (NULL);
    152 	}
    153 	LIST_REMOVE(&free_list, x);
    154 	LIST_ADD(&work_list, x);
    155 	return (x);
    156 }
    157 hrtime_t
    158 daio_async_end_time(daio_result_t *y)
    159 {
    160 	struct daio_async *x = (struct daio_async *)y->private_data;
    161 	hrtime_t res;
    162 
    163 	if (!bounds_check(x)) {
    164 		return (0);
    165 	}
    166 
    167 	pthread_mutex_lock(&x->mlock);
    168 	res = DAIO_GET_END_TIME(x->res);
    169 	pthread_mutex_unlock(&x->mlock);
    170 	return (res);
    171 }
    172 daio_status_t
    173 daio_async_status(daio_result_t *y)
    174 {
    175 	struct daio_async *x = (struct daio_async *)y->private_data;
    176 	daio_status_t res;
    177 
    178 	if (!bounds_check(x)) {
    179 		return (DAIO_ERROR);
    180 	}
    181 
    182 	pthread_mutex_lock(&x->mlock);
    183 	res = x->status;
    184 	pthread_mutex_unlock(&x->mlock);
    185 	return (res);
    186 }
    187 hrtime_t
    188 daio_async_start_time(daio_result_t *y)
    189 {
    190 	struct daio_async *x = (struct daio_async *)y->private_data;
    191 	hrtime_t res;
    192 
    193 	if (!bounds_check(x)) {
    194 		return (0);
    195 	}
    196 
    197 	pthread_mutex_lock(&x->mlock);
    198 	res = DAIO_GET_START_TIME(x->res);
    199 	pthread_mutex_unlock(&x->mlock);
    200 	return (res);
    201 }
    202 static void *
    203 do_daio_async_rw(void *y)
    204 {
    205 	struct daio_async *x = (struct daio_async *)y;
    206 	hrtime_t et;
    207 
    208 	pthread_mutex_lock(&x->mlock);
    209 	x->status = DAIO_INPROGRESS;
    210 	DAIO_SET_START_TIME(x->res, gethrtime());
    211 	pthread_mutex_unlock(&x->mlock);
    212 	DAIO_SET_RETURN(x->res, x->rw(x->fildes, x->bufp, x->bufs, x->offset));
    213 	DAIO_SET_ERROR(x->res, errno);
    214 	et =  gethrtime();
    215 	if (DAIO_RETURN(x->res) < 0) {
    216 		pthread_mutex_lock(&x->mlock);
    217 		x->status = DAIO_COMPLETE_ERROR;
    218 	} else {
    219 		if (x->is_read &&
    220 		    data_check(x->bufp, DAIO_RETURN(x->res), x->id) != 0) {
    221 			DAIO_SET_RETURN(x->res, DAIO_CORRUPT);
    222 		}
    223 		pthread_mutex_lock(&x->mlock);
    224 		x->status = DAIO_COMPLETE_OK;
    225 	}
    226 	DAIO_SET_END_TIME(x->res, et);
    227 	pthread_mutex_unlock(&x->mlock);
    228 	return (x);
    229 }
    230 
    231 int
    232 daio_async_cancel(daio_result_t *y)
    233 {
    234 	struct daio_async *x = (struct daio_async *)y->private_data;
    235 
    236 	if (bounds_check(x)) {
    237 		return (async_cancel_request(&x->work, 0));
    238 	} else {
    239 		errno = EINVAL;
    240 		return (-1);
    241 	}
    242 }
    243 
    244 static int
    245 /*ARGSUSED*/
    246 do_daio_async_cancel(void *x, int flags)
    247 {
    248 	errno = ENOTSUP;
    249 	return (-1);
    250 }
    251 ssize_t
    252 daio_async_pread(int fildes, uchar_t *bufp, size_t bufs, off_t off,
    253 		struct daio_id *id)
    254 {
    255 	ssize_t ret;
    256 
    257 	ret = pread(fildes, bufp, bufs, off);
    258 
    259 	if (id != NULL && ret >= 0) {
    260 		if (data_check(bufp, ret, id) != 0)
    261 			ret = DAIO_CORRUPT;
    262 	}
    263 	return (ret);
    264 }
    265 /*ARGSUSED4*/
    266 ssize_t
    267 daio_async_pwrite(int fildes, const uchar_t *bufp, size_t bufs, off_t off,
    268 		struct daio_id *id)
    269 {
    270 	return (pwrite(fildes, bufp, bufs, off));
    271 }
    272 int
    273 daio_async_rw(int fildes, uchar_t *bufp, int  bufs, off_t
    274 	offset, daio_result_t *resultp,  struct daio_id *id,
    275 	ssize_t (*rw)(int fildes, void  *buf,  size_t  nbyte,  off_t offset),
    276 	read_or_write_t is_read)
    277 {
    278 	struct daio_async *x;
    279 
    280 	if ((x = get_daio_async()) == NULL) {
    281 		errno = ENOMEM;
    282 		return (-1);
    283 	}
    284 
    285 	x->work.u.funcs.func = (void * (*)(void *))do_daio_async_rw;
    286 	x->work.u.funcs.cancel = (int (*)(void *, int))do_daio_async_cancel;
    287 	x->work.arg = x;
    288 	x->work.flags = 0;
    289 	x->fildes = fildes;
    290 	x->bufs = bufs;
    291 	x->offset = offset;
    292 	x->bufp = bufp;
    293 	x->id = id;
    294 	x->rw = rw;
    295 	x->is_read = is_read == IS_READ ? 1 : 0;
    296 	x->resultp = resultp;
    297 	DAIO_SET_START_TIME(x->res, DAIO_NOT_STARTED);
    298 	resultp->private_data = (void *)x;
    299 
    300 	return (async_queue_request(&x->work) == NULL ? -1 : 0);
    301 
    302 }
    303 daio_result_t *
    304 daio_async_wait(const struct timeval *timeout)
    305 {
    306 	struct async_work *work;
    307 	struct daio_async *x;
    308 	daio_result_t *res;
    309 
    310 	if (LIST_HEAD(&work_list) == NULL) {
    311 		errno = EINVAL;
    312 		return ((daio_result_t *)-1);
    313 	}
    314 
    315 	if (timeout == NULL) {
    316 		work = async_queue_wait(NULL, NULL, 0);
    317 	} else {
    318 		struct timespec abstime;
    319 		clock_gettime(CLOCK_REALTIME, &abstime);
    320 
    321 		abstime = timespec_plus_timeval(&abstime, timeout);
    322 		work = async_queue_wait(NULL, &abstime, 0);
    323 	}
    324 	if (work == NULL)
    325 		return ((daio_result_t *)NULL);
    326 
    327 	x = work->arg;
    328 
    329 	*x->resultp = x->res;
    330 	res = x->resultp;
    331 
    332 	free_daio_async(x);
    333 
    334 	return (res);
    335 }
    336