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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/ksynch.h> 28 #include <sys/errno.h> 29 #include <sys/file.h> 30 #include <sys/open.h> 31 #include <sys/cred.h> 32 #include <sys/kmem.h> 33 #include <sys/uio.h> 34 #include <sys/ddi.h> 35 #include <sys/sdt.h> 36 37 #define __NSC_GEN__ 38 #include "nsc_dev.h" 39 #include "nsc_disk.h" 40 #include "../nsctl.h" 41 42 43 #define _I(x) (((long)(&((nsc_io_t *)0)->x))/sizeof (long)) 44 45 nsc_def_t _nsc_disk_def[] = { 46 { "UserRead", (uintptr_t)nsc_ioerr, _I(uread) }, 47 { "UserWrite", (uintptr_t)nsc_ioerr, _I(uwrite) }, 48 { "PartSize", (uintptr_t)nsc_null, _I(partsize) }, 49 { "MaxFbas", (uintptr_t)nsc_null, _I(maxfbas) }, 50 { "Control", (uintptr_t)nsc_ioerr, _I(control) }, 51 { NULL, (uintptr_t)NULL, 0 } 52 }; 53 54 55 extern nsc_mem_t *_nsc_local_mem; 56 57 static int _nsc_uread(dev_t, uio_t *, cred_t *, nsc_fd_t *); 58 static int _nsc_uwrite(dev_t, uio_t *, cred_t *, nsc_fd_t *); 59 static int _nsc_rw_uio(nsc_fd_t *, uio_t *, uio_rw_t); 60 61 static int _nsc_free_dhandle(nsc_dbuf_t *); 62 static int _nsc_alloc_dbuf(blind_t, nsc_off_t, nsc_size_t, int, nsc_dbuf_t **); 63 static int _nsc_free_dbuf(nsc_dbuf_t *); 64 static void _nsc_wait_dbuf(nsc_dbuf_t *); 65 static int _nsc_read_dbuf(nsc_dbuf_t *, nsc_off_t, nsc_size_t, int); 66 static int _nsc_write_dbuf(nsc_dbuf_t *, nsc_off_t, nsc_size_t, int); 67 static int _nsc_zero_dbuf(nsc_dbuf_t *, nsc_off_t, nsc_size_t, int); 68 static int _nsc_dbuf_io(int (*)(), nsc_dbuf_t *, nsc_off_t, nsc_size_t, int); 69 70 static nsc_dbuf_t *_nsc_alloc_dhandle(void (*)(), void (*)(), void (*)()); 71 72 73 /* 74 * void 75 * _nsc_add_disk (nsc_io_t *io) 76 * Add disk interface functions. 77 * 78 * Calling/Exit State: 79 * Updates the I/O module with the appropriate 80 * interface routines. 81 * 82 * Description: 83 * Add functions to the I/O module to provide a disk 84 * or cache interface as appropriate. 85 */ 86 void 87 _nsc_add_disk(nsc_io_t *io) 88 { 89 if ((io->alloc_buf != nsc_ioerr && io->free_buf != nsc_fatal) || 90 (io->flag & NSC_FILTER)) { 91 if (io->uread == nsc_ioerr) 92 io->uread = _nsc_uread; 93 94 if (io->uwrite == nsc_ioerr && 95 (io->write != nsc_fatal || (io->flag & NSC_FILTER))) 96 io->uwrite = _nsc_uwrite; 97 98 return; 99 } 100 101 if (io->alloc_h != (nsc_buf_t *(*)())nsc_null || 102 io->free_h != nsc_fatal || io->alloc_buf != nsc_ioerr || 103 io->free_buf != nsc_fatal || io->read != nsc_fatal || 104 io->write != nsc_fatal || io->zero != nsc_fatal) 105 return; 106 107 if (io->uread == nsc_ioerr && io->uwrite == nsc_ioerr) 108 return; 109 110 /* 111 * Layer the generic nsc_buf_t provider onto a uio_t provider. 112 */ 113 114 io->alloc_h = (nsc_buf_t *(*)())_nsc_alloc_dhandle; 115 io->free_h = _nsc_free_dhandle; 116 io->alloc_buf = _nsc_alloc_dbuf; 117 io->free_buf = _nsc_free_dbuf; 118 119 io->read = _nsc_read_dbuf; 120 io->write = _nsc_write_dbuf; 121 io->zero = _nsc_zero_dbuf; 122 123 io->provide |= NSC_ANON; 124 } 125 126 127 int 128 nsc_uread(nsc_fd_t *fd, void *uiop, void *crp) 129 { 130 return (*fd->sf_aio->uread)(fd->sf_cd, uiop, crp, fd); 131 } 132 133 134 int 135 nsc_uwrite(nsc_fd_t *fd, void *uiop, void *crp) 136 { 137 if ((fd->sf_avail & NSC_WRITE) == 0) 138 return (EIO); 139 140 return (*fd->sf_aio->uwrite)(fd->sf_cd, uiop, crp, fd); 141 } 142 143 144 int 145 nsc_partsize(nsc_fd_t *fd, nsc_size_t *valp) 146 { 147 *valp = 0; 148 return (*fd->sf_aio->partsize)(fd->sf_cd, valp); 149 } 150 151 152 int 153 nsc_maxfbas(nsc_fd_t *fd, int flag, nsc_size_t *valp) 154 { 155 *valp = 0; 156 return (*fd->sf_aio->maxfbas)(fd->sf_cd, flag, valp); 157 } 158 159 int 160 nsc_control(nsc_fd_t *fd, int command, void *argp, int argl) 161 { 162 return (*fd->sf_aio->control)(fd->sf_cd, command, argp, argl); 163 } 164 165 166 /* ARGSUSED */ 167 168 static int 169 _nsc_uread(dev_t dev, uio_t *uiop, cred_t *crp, nsc_fd_t *fd) 170 { 171 return (_nsc_rw_uio(fd, uiop, UIO_READ)); 172 } 173 174 175 /* ARGSUSED */ 176 177 static int 178 _nsc_uwrite(dev_t dev, uio_t *uiop, cred_t *crp, nsc_fd_t *fd) 179 { 180 return (_nsc_rw_uio(fd, uiop, UIO_WRITE)); 181 } 182 183 184 static int 185 _nsc_rw_uio(nsc_fd_t *fd, uio_t *uiop, uio_rw_t rw) 186 { 187 nsc_size_t buflen, len, limit, chunk; 188 nsc_off_t pos, off; 189 nsc_buf_t *buf; 190 nsc_vec_t *vec; 191 size_t n; 192 int rc; 193 194 pos = FPOS_TO_FBA(uiop); 195 off = FPOS_TO_OFF(uiop); 196 len = FBA_LEN(uiop->uio_resid + off); 197 198 DTRACE_PROBE3(_nsc_rw_uio_io, 199 uint64_t, pos, 200 uint64_t, off, 201 uint64_t, len); 202 203 /* prevent non-FBA bounded I/O - this is a disk driver! */ 204 if (off != 0 || FBA_OFF(uiop->uio_resid) != 0) 205 return (EINVAL); 206 207 if ((rc = nsc_partsize(fd, &limit)) != 0) 208 return (rc); 209 210 if ((rc = nsc_maxfbas(fd, 0, &chunk)) != 0) 211 return (rc); 212 213 DTRACE_PROBE2(_nsc_rw_uio_limit, 214 uint64_t, limit, 215 uint64_t, chunk); 216 217 if (limit && pos >= limit) { 218 if (pos > limit || rw == UIO_WRITE) 219 return (ENXIO); 220 return (0); 221 } 222 223 if (limit && pos + len > limit) 224 len = limit - pos; 225 226 while (len > 0) { 227 buflen = min(len, chunk); 228 229 buf = NULL; /* always use a temporary buffer */ 230 if ((rc = nsc_alloc_buf(fd, pos, buflen, 231 (rw == UIO_READ) ? NSC_RDBUF : NSC_WRBUF, &buf)) > 0) 232 return (rc); 233 234 vec = buf->sb_vec; 235 236 for (rc = 0; 237 !rc && uiop->uio_resid && vec->sv_addr; 238 vec++, off = 0) { 239 n = min(vec->sv_len - off, uiop->uio_resid); 240 rc = uiomove((char *)vec->sv_addr + off, 241 n, rw, uiop); 242 } 243 244 if (rw == UIO_WRITE) { 245 if (rc) { 246 (void) nsc_uncommit(buf, pos, buflen, 0); 247 } else if ((rc = nsc_write(buf, pos, buflen, 0)) < 0) { 248 rc = 0; 249 } 250 } 251 252 (void) nsc_free_buf(buf); 253 254 len -= buflen; 255 pos += buflen; 256 } 257 258 return (rc); 259 } 260 261 262 /* ARGSUSED */ 263 264 static nsc_dbuf_t * 265 _nsc_alloc_dhandle(void (*d_cb)(), void (*r_cb)(), void (*w_cb)()) 266 { 267 nsc_dbuf_t *h; 268 269 if ((h = nsc_kmem_zalloc(sizeof (nsc_dbuf_t), 270 KM_SLEEP, _nsc_local_mem)) == NULL) 271 return (NULL); 272 273 h->db_disc = d_cb; 274 h->db_flag = NSC_HALLOCATED; 275 276 return (h); 277 } 278 279 280 static int 281 _nsc_free_dhandle(nsc_dbuf_t *h) 282 { 283 nsc_kmem_free(h, sizeof (*h)); 284 return (0); 285 } 286 287 288 static int 289 _nsc_alloc_dbuf(blind_t cd, nsc_off_t pos, nsc_size_t len, 290 int flag, nsc_dbuf_t **hp) 291 { 292 nsc_dbuf_t *h = *hp; 293 int rc; 294 295 if (cd == NSC_ANON_CD) { 296 flag &= ~(NSC_READ | NSC_WRITE | NSC_RDAHEAD); 297 } else { 298 if (h->db_maxfbas == 0) { 299 rc = nsc_maxfbas(h->db_fd, 0, &h->db_maxfbas); 300 if (rc != 0) 301 return (rc); 302 else if (h->db_maxfbas == 0) 303 return (EIO); 304 } 305 306 if (len > h->db_maxfbas) 307 return (ENOSPC); 308 } 309 310 if (flag & NSC_NODATA) { 311 ASSERT(!(flag & NSC_RDBUF)); 312 h->db_addr = NULL; 313 } else { 314 if (h->db_disc) 315 (*h->db_disc)(h); 316 317 if (!(h->db_addr = nsc_kmem_alloc(FBA_SIZE(len), KM_SLEEP, 0))) 318 return (ENOMEM); 319 } 320 321 h->db_pos = pos; 322 h->db_len = len; 323 h->db_error = 0; 324 h->db_flag |= flag; 325 326 if (flag & NSC_NODATA) { 327 h->db_vec = NULL; 328 } else { 329 h->db_vec = &h->db_bvec[0]; 330 h->db_bvec[0].sv_len = FBA_SIZE(len); 331 h->db_bvec[0].sv_addr = (void *)h->db_addr; 332 h->db_bvec[0].sv_vme = 0; 333 334 h->db_bvec[1].sv_len = 0; 335 h->db_bvec[1].sv_addr = 0; 336 h->db_bvec[1].sv_vme = 0; 337 } 338 339 if ((flag & NSC_RDAHEAD) || (cd == NSC_ANON_CD)) 340 return (NSC_DONE); 341 342 _nsc_wait_dbuf(h); 343 344 if (flag & NSC_RDBUF) { 345 if ((rc = _nsc_dbuf_io(nsc_uread, h, pos, len, flag)) != 0) { 346 (void) _nsc_free_dbuf(h); 347 return (rc); 348 } 349 } 350 351 return (NSC_DONE); 352 } 353 354 355 static void 356 _nsc_wait_dbuf(nsc_dbuf_t *h) 357 { 358 nsc_iodev_t *iodev = h->db_fd->sf_iodev; 359 void (*fn)() = h->db_disc; 360 nsc_dbuf_t *hp; 361 362 mutex_enter(&iodev->si_lock); 363 364 h->db_next = iodev->si_active; 365 iodev->si_active = h; 366 367 /* CONSTCOND */ 368 369 while (1) { 370 for (hp = h->db_next; hp; hp = hp->db_next) 371 if (h->db_pos + h->db_len > hp->db_pos && 372 h->db_pos < hp->db_pos + hp->db_len) break; 373 374 if (!hp) 375 break; 376 377 if (fn) 378 (*fn)(h), fn = NULL; 379 380 cv_wait(&iodev->si_cv, &iodev->si_lock); 381 } 382 383 mutex_exit(&iodev->si_lock); 384 } 385 386 387 static int 388 _nsc_free_dbuf(nsc_dbuf_t *h) 389 { 390 nsc_dbuf_t **hpp, *hp; 391 nsc_iodev_t *iodev; 392 int wake = 0; 393 394 if (h->db_fd && !(h->db_flag & NSC_ABUF)) { 395 iodev = h->db_fd->sf_iodev; 396 397 mutex_enter(&iodev->si_lock); 398 399 hpp = (nsc_dbuf_t **)&iodev->si_active; 400 401 for (; *hpp; hpp = &hp->db_next) { 402 if ((hp = *hpp) == h) { 403 *hpp = h->db_next; 404 break; 405 } 406 407 if (h->db_pos + h->db_len > hp->db_pos && 408 h->db_pos < hp->db_pos + hp->db_len) wake = 1; 409 410 } 411 if (wake) 412 cv_broadcast(&iodev->si_cv); 413 414 mutex_exit(&iodev->si_lock); 415 } 416 417 if (!(h->db_flag & NSC_NODATA) && h->db_addr) 418 nsc_kmem_free(h->db_addr, FBA_SIZE(h->db_len)); 419 420 h->db_addr = NULL; 421 h->db_flag &= NSC_HALLOCATED; /* clear flags, preserve NSC_HALLOCATED */ 422 423 if ((h->db_flag & NSC_HALLOCATED) == 0) 424 (void) _nsc_free_dhandle(h); 425 426 427 return (0); 428 } 429 430 431 static int 432 _nsc_read_dbuf(nsc_dbuf_t *h, nsc_off_t pos, nsc_size_t len, int flag) 433 { 434 return (_nsc_dbuf_io(nsc_uread, h, pos, len, flag)); 435 } 436 437 438 static int 439 _nsc_write_dbuf(nsc_dbuf_t *h, nsc_off_t pos, nsc_size_t len, int flag) 440 { 441 return (_nsc_dbuf_io(nsc_uwrite, h, pos, len, flag)); 442 } 443 444 445 static int 446 _nsc_zero_dbuf(nsc_dbuf_t *h, nsc_off_t pos, nsc_size_t len, int flag) 447 { 448 return (_nsc_dbuf_io(NULL, h, pos, len, flag)); 449 } 450 451 452 static int 453 _nsc_dbuf_io(int (*fn)(), nsc_dbuf_t *h, nsc_off_t pos, 454 nsc_size_t len, int flag) 455 { 456 nsc_vec_t *vp = NULL; 457 cred_t *crp = NULL; 458 iovec_t *iovp; 459 nsc_size_t thisio; /* bytes in this io */ 460 nsc_size_t todo; /* anticipated bytes to go */ 461 nsc_size_t truedo; /* actual bytes to go */ 462 nsc_off_t xpos; /* offset of this io */ 463 int destidx; 464 nsc_size_t firstentryfix; /* value used for first entry */ 465 466 int (*iofn)(); 467 int rc = 0; 468 469 if (!h->db_vec || (h->db_flag & NSC_ABUF)) 470 return (EIO); 471 472 if (pos < h->db_pos || pos + len > h->db_pos + h->db_len) 473 return (EINVAL); 474 475 if (!len) 476 return (0); 477 if (fn == nsc_uread && (flag & NSC_RDAHEAD)) 478 return (0); 479 480 if (h->db_disc) 481 (*h->db_disc)(h); 482 483 crp = ddi_get_cred(); 484 bzero(&h->db_uio, sizeof (uio_t)); 485 bzero(&h->db_iov[0], (_NSC_DBUF_NVEC * sizeof (iovec_t))); 486 487 todo = FBA_SIZE(len); 488 489 /* 490 * determine where in the vector array we should start. 491 */ 492 vp = h->db_vec; 493 xpos = pos - h->db_pos; 494 for (; xpos >= FBA_NUM(vp->sv_len); vp++) 495 xpos -= FBA_NUM(vp->sv_len); 496 497 firstentryfix = FBA_SIZE(xpos); 498 499 xpos = pos; 500 501 /* 502 * Loop performing i/o to the underlying driver. 503 */ 504 while (todo) { 505 destidx = 0; 506 thisio = 0; 507 iofn = fn; 508 509 /* 510 * Copy up to _NSC_DBUF_NVEC vector entries from the 511 * nsc_vec_t into the iovec_t so that the number of 512 * i/o operations is minimised. 513 */ 514 while (destidx < _NSC_DBUF_NVEC && todo) { 515 iovp = &h->db_iov[destidx]; 516 517 ASSERT(FBA_LEN(vp->sv_len) == FBA_NUM(vp->sv_len)); 518 ASSERT((vp->sv_len - firstentryfix) && vp->sv_addr); 519 520 truedo = min(vp->sv_len - firstentryfix, todo); 521 iovp->iov_base = (caddr_t)vp->sv_addr + firstentryfix; 522 firstentryfix = 0; 523 iovp->iov_len = (size_t)truedo; 524 if (!iofn) { 525 bzero(iovp->iov_base, iovp->iov_len); 526 } 527 thisio += truedo; 528 todo -= truedo; 529 destidx++; 530 vp++; 531 } 532 533 h->db_uio.uio_iovcnt = destidx; 534 h->db_uio.uio_iov = &h->db_iov[0]; 535 h->db_uio.uio_segflg = UIO_SYSSPACE; 536 h->db_uio.uio_resid = (size_t)thisio; 537 538 SET_FPOS(&h->db_uio, xpos); 539 540 if (!iofn) { 541 iofn = nsc_uwrite; 542 } 543 544 rc = (*iofn)(h->db_fd, &h->db_uio, crp); 545 if (rc != 0) { 546 break; 547 } 548 549 ASSERT(FBA_LEN(thisio) == FBA_NUM(thisio)); 550 xpos += FBA_LEN(thisio); 551 } 552 553 return (rc); 554 }