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