1 1.46 christos /* $NetBSD: dmover_io.c,v 1.46 2019/02/10 17:13:33 christos Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.12 thorpej * Copyright (c) 2002, 2003 Wasabi Systems, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed for the NetBSD Project by 20 1.1 thorpej * Wasabi Systems, Inc. 21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 1.1 thorpej * or promote products derived from this software without specific prior 23 1.1 thorpej * written permission. 24 1.1 thorpej * 25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej */ 37 1.1 thorpej 38 1.1 thorpej /* 39 1.1 thorpej * dmover_io.c: Support for user-space access to dmover-api 40 1.1 thorpej * 41 1.1 thorpej * This interface is quite simple: 42 1.1 thorpej * 43 1.1 thorpej * 1. The user opens /dev/dmover, which is a cloning device. This 44 1.1 thorpej * allocates internal state for the session. 45 1.1 thorpej * 46 1.1 thorpej * 2. The user does a DMIO_SETFUNC to select the data movement 47 1.1 thorpej * function. This actually creates the dmover session. 48 1.1 thorpej * 49 1.1 thorpej * 3. The user writes request messages to its dmover handle. 50 1.1 thorpej * 51 1.1 thorpej * 4. The user reads request responses from its dmover handle. 52 1.1 thorpej * 53 1.1 thorpej * 5. The user closes the file descriptor and the session is 54 1.1 thorpej * torn down. 55 1.1 thorpej */ 56 1.1 thorpej 57 1.1 thorpej #include <sys/cdefs.h> 58 1.46 christos __KERNEL_RCSID(0, "$NetBSD: dmover_io.c,v 1.46 2019/02/10 17:13:33 christos Exp $"); 59 1.1 thorpej 60 1.1 thorpej #include <sys/param.h> 61 1.1 thorpej #include <sys/queue.h> 62 1.1 thorpej #include <sys/conf.h> 63 1.1 thorpej #include <sys/pool.h> 64 1.1 thorpej #include <sys/proc.h> 65 1.1 thorpej #include <sys/poll.h> 66 1.1 thorpej #include <sys/malloc.h> 67 1.1 thorpej #include <sys/file.h> 68 1.1 thorpej #include <sys/filedesc.h> 69 1.1 thorpej #include <sys/filio.h> 70 1.1 thorpej #include <sys/select.h> 71 1.1 thorpej #include <sys/systm.h> 72 1.21 yamt #include <sys/workqueue.h> 73 1.21 yamt #include <sys/once.h> 74 1.35 nonaka #include <sys/stat.h> 75 1.35 nonaka #include <sys/kauth.h> 76 1.40 jakllsch #include <sys/mutex.h> 77 1.40 jakllsch #include <sys/condvar.h> 78 1.21 yamt 79 1.39 uebayasi #include <uvm/uvm_extern.h> 80 1.39 uebayasi 81 1.1 thorpej #include <dev/dmover/dmovervar.h> 82 1.1 thorpej #include <dev/dmover/dmover_io.h> 83 1.1 thorpej 84 1.44 christos #include "ioconf.h" 85 1.44 christos 86 1.1 thorpej struct dmio_usrreq_state { 87 1.21 yamt union { 88 1.21 yamt struct work u_work; 89 1.21 yamt TAILQ_ENTRY(dmio_usrreq_state) u_q; 90 1.21 yamt } dus_u; 91 1.21 yamt #define dus_q dus_u.u_q 92 1.21 yamt #define dus_work dus_u.u_work 93 1.1 thorpej struct uio dus_uio_out; 94 1.1 thorpej struct uio *dus_uio_in; 95 1.1 thorpej struct dmover_request *dus_req; 96 1.1 thorpej uint32_t dus_id; 97 1.21 yamt struct vmspace *dus_vmspace; 98 1.1 thorpej }; 99 1.1 thorpej 100 1.1 thorpej struct dmio_state { 101 1.1 thorpej struct dmover_session *ds_session; 102 1.1 thorpej TAILQ_HEAD(, dmio_usrreq_state) ds_pending; 103 1.1 thorpej TAILQ_HEAD(, dmio_usrreq_state) ds_complete; 104 1.1 thorpej struct selinfo ds_selq; 105 1.19 perry volatile int ds_flags; 106 1.1 thorpej u_int ds_nreqs; 107 1.40 jakllsch kmutex_t ds_lock; 108 1.40 jakllsch kcondvar_t ds_complete_cv; 109 1.40 jakllsch kcondvar_t ds_nreqs_cv; 110 1.33 christos struct timespec ds_atime; 111 1.33 christos struct timespec ds_mtime; 112 1.33 christos struct timespec ds_btime; 113 1.1 thorpej }; 114 1.1 thorpej 115 1.21 yamt static ONCE_DECL(dmio_cleaner_control); 116 1.21 yamt static struct workqueue *dmio_cleaner; 117 1.21 yamt static int dmio_cleaner_init(void); 118 1.40 jakllsch static struct dmio_state *dmio_state_get(void); 119 1.40 jakllsch static void dmio_state_put(struct dmio_state *); 120 1.21 yamt static void dmio_usrreq_fini1(struct work *wk, void *); 121 1.21 yamt 122 1.1 thorpej #define DMIO_STATE_SEL 0x0001 123 1.1 thorpej #define DMIO_STATE_DEAD 0x0002 124 1.1 thorpej #define DMIO_STATE_LARVAL 0x0004 125 1.1 thorpej #define DMIO_STATE_READ_WAIT 0x0008 126 1.1 thorpej #define DMIO_STATE_WRITE_WAIT 0x0010 127 1.1 thorpej 128 1.1 thorpej #define DMIO_NREQS_MAX 64 /* XXX pulled out of a hat */ 129 1.1 thorpej 130 1.1 thorpej struct pool dmio_state_pool; 131 1.1 thorpej struct pool dmio_usrreq_state_pool; 132 1.1 thorpej 133 1.5 gehenna dev_type_open(dmoverioopen); 134 1.5 gehenna 135 1.5 gehenna const struct cdevsw dmoverio_cdevsw = { 136 1.42 dholland .d_open = dmoverioopen, 137 1.42 dholland .d_close = noclose, 138 1.42 dholland .d_read = noread, 139 1.42 dholland .d_write = nowrite, 140 1.42 dholland .d_ioctl = noioctl, 141 1.42 dholland .d_stop = nostop, 142 1.42 dholland .d_tty = notty, 143 1.42 dholland .d_poll = nopoll, 144 1.42 dholland .d_mmap = nommap, 145 1.42 dholland .d_kqfilter = nokqfilter, 146 1.43 dholland .d_discard = nodiscard, 147 1.42 dholland .d_flag = D_OTHER 148 1.5 gehenna }; 149 1.1 thorpej 150 1.1 thorpej /* 151 1.1 thorpej * dmoverioattach: 152 1.1 thorpej * 153 1.1 thorpej * Pseudo-device attach routine. 154 1.1 thorpej */ 155 1.1 thorpej void 156 1.1 thorpej dmoverioattach(int count) 157 1.1 thorpej { 158 1.1 thorpej 159 1.1 thorpej pool_init(&dmio_state_pool, sizeof(struct dmio_state), 160 1.26 ad 0, 0, 0, "dmiostate", NULL, IPL_SOFTCLOCK); 161 1.1 thorpej pool_init(&dmio_usrreq_state_pool, sizeof(struct dmio_usrreq_state), 162 1.26 ad 0, 0, 0, "dmiourstate", NULL, IPL_SOFTCLOCK); 163 1.1 thorpej } 164 1.1 thorpej 165 1.1 thorpej /* 166 1.21 yamt * dmio_cleaner_init: 167 1.21 yamt * 168 1.21 yamt * Create cleaner thread. 169 1.21 yamt */ 170 1.21 yamt static int 171 1.21 yamt dmio_cleaner_init(void) 172 1.21 yamt { 173 1.21 yamt 174 1.21 yamt return workqueue_create(&dmio_cleaner, "dmioclean", dmio_usrreq_fini1, 175 1.24 yamt NULL, PWAIT, IPL_SOFTCLOCK, 0); 176 1.21 yamt } 177 1.21 yamt 178 1.40 jakllsch static struct dmio_state * 179 1.40 jakllsch dmio_state_get(void) 180 1.40 jakllsch { 181 1.40 jakllsch struct dmio_state *ds; 182 1.40 jakllsch 183 1.46 christos ds = pool_get(&dmio_state_pool, PR_WAITOK | PR_ZERO); 184 1.40 jakllsch 185 1.40 jakllsch getnanotime(&ds->ds_btime); 186 1.40 jakllsch ds->ds_atime = ds->ds_mtime = ds->ds_btime; 187 1.40 jakllsch 188 1.40 jakllsch mutex_init(&ds->ds_lock, MUTEX_DEFAULT, IPL_SOFTCLOCK); 189 1.40 jakllsch cv_init(&ds->ds_complete_cv, "dmvrrd"); 190 1.40 jakllsch cv_init(&ds->ds_nreqs_cv, "dmiowr"); 191 1.40 jakllsch TAILQ_INIT(&ds->ds_pending); 192 1.40 jakllsch TAILQ_INIT(&ds->ds_complete); 193 1.40 jakllsch selinit(&ds->ds_selq); 194 1.40 jakllsch 195 1.40 jakllsch return ds; 196 1.40 jakllsch } 197 1.40 jakllsch 198 1.40 jakllsch static void 199 1.40 jakllsch dmio_state_put(struct dmio_state *ds) 200 1.40 jakllsch { 201 1.40 jakllsch 202 1.40 jakllsch seldestroy(&ds->ds_selq); 203 1.40 jakllsch cv_destroy(&ds->ds_nreqs_cv); 204 1.40 jakllsch cv_destroy(&ds->ds_complete_cv); 205 1.40 jakllsch mutex_destroy(&ds->ds_lock); 206 1.40 jakllsch 207 1.40 jakllsch pool_put(&dmio_state_pool, ds); 208 1.40 jakllsch } 209 1.40 jakllsch 210 1.21 yamt /* 211 1.1 thorpej * dmio_usrreq_init: 212 1.1 thorpej * 213 1.1 thorpej * Build a request structure. 214 1.1 thorpej */ 215 1.1 thorpej static int 216 1.1 thorpej dmio_usrreq_init(struct file *fp, struct dmio_usrreq_state *dus, 217 1.1 thorpej struct dmio_usrreq *req, struct dmover_request *dreq) 218 1.1 thorpej { 219 1.1 thorpej struct dmio_state *ds = (struct dmio_state *) fp->f_data; 220 1.1 thorpej struct dmover_session *dses = ds->ds_session; 221 1.1 thorpej struct uio *uio_out = &dus->dus_uio_out; 222 1.1 thorpej struct uio *uio_in; 223 1.1 thorpej dmio_buffer inbuf; 224 1.1 thorpej size_t len; 225 1.7 thorpej int i, error; 226 1.7 thorpej u_int j; 227 1.1 thorpej 228 1.1 thorpej /* XXX How should malloc interact w/ FNONBLOCK? */ 229 1.1 thorpej 230 1.21 yamt error = RUN_ONCE(&dmio_cleaner_control, dmio_cleaner_init); 231 1.21 yamt if (error) { 232 1.21 yamt return error; 233 1.21 yamt } 234 1.21 yamt 235 1.21 yamt error = proc_vmspace_getref(curproc, &dus->dus_vmspace); 236 1.21 yamt if (error) { 237 1.21 yamt return error; 238 1.21 yamt } 239 1.21 yamt 240 1.12 thorpej if (req->req_outbuf.dmbuf_iovcnt != 0) { 241 1.12 thorpej if (req->req_outbuf.dmbuf_iovcnt > IOV_MAX) 242 1.12 thorpej return (EINVAL); 243 1.12 thorpej len = sizeof(struct iovec) * req->req_outbuf.dmbuf_iovcnt; 244 1.12 thorpej uio_out->uio_iov = malloc(len, M_TEMP, M_WAITOK); 245 1.12 thorpej error = copyin(req->req_outbuf.dmbuf_iov, uio_out->uio_iov, 246 1.12 thorpej len); 247 1.12 thorpej if (error) { 248 1.12 thorpej free(uio_out->uio_iov, M_TEMP); 249 1.12 thorpej return (error); 250 1.12 thorpej } 251 1.1 thorpej 252 1.12 thorpej for (j = 0, len = 0; j < req->req_outbuf.dmbuf_iovcnt; j++) { 253 1.12 thorpej len += uio_out->uio_iov[j].iov_len; 254 1.12 thorpej if (len > SSIZE_MAX) { 255 1.12 thorpej free(uio_out->uio_iov, M_TEMP); 256 1.12 thorpej return (EINVAL); 257 1.12 thorpej } 258 1.12 thorpej } 259 1.1 thorpej 260 1.12 thorpej uio_out->uio_iovcnt = req->req_outbuf.dmbuf_iovcnt; 261 1.12 thorpej uio_out->uio_resid = len; 262 1.12 thorpej uio_out->uio_rw = UIO_READ; 263 1.21 yamt uio_out->uio_vmspace = dus->dus_vmspace; 264 1.21 yamt 265 1.12 thorpej dreq->dreq_outbuf_type = DMOVER_BUF_UIO; 266 1.12 thorpej dreq->dreq_outbuf.dmbuf_uio = uio_out; 267 1.12 thorpej } else { 268 1.12 thorpej uio_out->uio_iov = NULL; 269 1.12 thorpej uio_out = NULL; 270 1.12 thorpej dreq->dreq_outbuf_type = DMOVER_BUF_NONE; 271 1.1 thorpej } 272 1.1 thorpej 273 1.12 thorpej memcpy(dreq->dreq_immediate, req->req_immediate, 274 1.12 thorpej sizeof(dreq->dreq_immediate)); 275 1.1 thorpej 276 1.1 thorpej if (dses->dses_ninputs == 0) { 277 1.12 thorpej /* No inputs; all done. */ 278 1.1 thorpej return (0); 279 1.1 thorpej } 280 1.1 thorpej 281 1.1 thorpej dreq->dreq_inbuf_type = DMOVER_BUF_UIO; 282 1.1 thorpej 283 1.1 thorpej dus->dus_uio_in = malloc(sizeof(struct uio) * dses->dses_ninputs, 284 1.1 thorpej M_TEMP, M_WAITOK); 285 1.1 thorpej memset(dus->dus_uio_in, 0, sizeof(struct uio) * dses->dses_ninputs); 286 1.1 thorpej 287 1.1 thorpej for (i = 0; i < dses->dses_ninputs; i++) { 288 1.1 thorpej uio_in = &dus->dus_uio_in[i]; 289 1.1 thorpej 290 1.1 thorpej error = copyin(&req->req_inbuf[i], &inbuf, sizeof(inbuf)); 291 1.1 thorpej if (error) 292 1.1 thorpej goto bad; 293 1.1 thorpej 294 1.1 thorpej if (inbuf.dmbuf_iovcnt > IOV_MAX) { 295 1.1 thorpej error = EINVAL; 296 1.1 thorpej goto bad; 297 1.1 thorpej } 298 1.1 thorpej len = sizeof(struct iovec) * inbuf.dmbuf_iovcnt; 299 1.1 thorpej if (len == 0) { 300 1.1 thorpej error = EINVAL; 301 1.1 thorpej goto bad; 302 1.1 thorpej } 303 1.1 thorpej uio_in->uio_iov = malloc(len, M_TEMP, M_WAITOK); 304 1.1 thorpej 305 1.1 thorpej error = copyin(inbuf.dmbuf_iov, uio_in->uio_iov, len); 306 1.1 thorpej if (error) { 307 1.1 thorpej free(uio_in->uio_iov, M_TEMP); 308 1.1 thorpej goto bad; 309 1.1 thorpej } 310 1.1 thorpej 311 1.12 thorpej for (j = 0, len = 0; j < inbuf.dmbuf_iovcnt; j++) { 312 1.1 thorpej len += uio_in->uio_iov[j].iov_len; 313 1.1 thorpej if (len > SSIZE_MAX) { 314 1.1 thorpej free(uio_in->uio_iov, M_TEMP); 315 1.1 thorpej error = EINVAL; 316 1.1 thorpej goto bad; 317 1.1 thorpej } 318 1.1 thorpej } 319 1.1 thorpej 320 1.12 thorpej if (uio_out != NULL && len != uio_out->uio_resid) { 321 1.1 thorpej free(uio_in->uio_iov, M_TEMP); 322 1.1 thorpej error = EINVAL; 323 1.1 thorpej goto bad; 324 1.1 thorpej } 325 1.1 thorpej 326 1.1 thorpej uio_in->uio_iovcnt = inbuf.dmbuf_iovcnt; 327 1.1 thorpej uio_in->uio_resid = len; 328 1.1 thorpej uio_in->uio_rw = UIO_WRITE; 329 1.21 yamt uio_in->uio_vmspace = dus->dus_vmspace; 330 1.1 thorpej 331 1.1 thorpej dreq->dreq_inbuf[i].dmbuf_uio = uio_in; 332 1.1 thorpej } 333 1.1 thorpej 334 1.1 thorpej return (0); 335 1.1 thorpej 336 1.1 thorpej bad: 337 1.1 thorpej if (i > 0) { 338 1.1 thorpej for (--i; i >= 0; i--) { 339 1.1 thorpej uio_in = &dus->dus_uio_in[i]; 340 1.1 thorpej free(uio_in->uio_iov, M_TEMP); 341 1.1 thorpej } 342 1.1 thorpej } 343 1.1 thorpej free(dus->dus_uio_in, M_TEMP); 344 1.12 thorpej if (uio_out != NULL) 345 1.12 thorpej free(uio_out->uio_iov, M_TEMP); 346 1.21 yamt uvmspace_free(dus->dus_vmspace); 347 1.1 thorpej return (error); 348 1.1 thorpej } 349 1.1 thorpej 350 1.1 thorpej /* 351 1.1 thorpej * dmio_usrreq_fini: 352 1.1 thorpej * 353 1.1 thorpej * Tear down a request. Must be called at splsoftclock(). 354 1.1 thorpej */ 355 1.1 thorpej static void 356 1.1 thorpej dmio_usrreq_fini(struct dmio_state *ds, struct dmio_usrreq_state *dus) 357 1.1 thorpej { 358 1.1 thorpej struct dmover_session *dses = ds->ds_session; 359 1.1 thorpej struct uio *uio_out = &dus->dus_uio_out; 360 1.1 thorpej struct uio *uio_in; 361 1.1 thorpej int i; 362 1.1 thorpej 363 1.12 thorpej if (uio_out->uio_iov != NULL) 364 1.12 thorpej free(uio_out->uio_iov, M_TEMP); 365 1.1 thorpej 366 1.21 yamt if (dses->dses_ninputs) { 367 1.21 yamt for (i = 0; i < dses->dses_ninputs; i++) { 368 1.21 yamt uio_in = &dus->dus_uio_in[i]; 369 1.21 yamt free(uio_in->uio_iov, M_TEMP); 370 1.21 yamt } 371 1.21 yamt free(dus->dus_uio_in, M_TEMP); 372 1.1 thorpej } 373 1.1 thorpej 374 1.27 rmind workqueue_enqueue(dmio_cleaner, &dus->dus_work, NULL); 375 1.21 yamt } 376 1.21 yamt 377 1.21 yamt static void 378 1.21 yamt dmio_usrreq_fini1(struct work *wk, void *dummy) 379 1.21 yamt { 380 1.21 yamt struct dmio_usrreq_state *dus = (void *)wk; 381 1.1 thorpej 382 1.21 yamt KASSERT(wk == &dus->dus_work); 383 1.1 thorpej 384 1.21 yamt uvmspace_free(dus->dus_vmspace); 385 1.1 thorpej pool_put(&dmio_usrreq_state_pool, dus); 386 1.1 thorpej } 387 1.1 thorpej 388 1.1 thorpej /* 389 1.1 thorpej * dmio_read: 390 1.1 thorpej * 391 1.1 thorpej * Read file op. 392 1.1 thorpej */ 393 1.1 thorpej static int 394 1.1 thorpej dmio_read(struct file *fp, off_t *offp, struct uio *uio, 395 1.22 elad kauth_cred_t cred, int flags) 396 1.1 thorpej { 397 1.1 thorpej struct dmio_state *ds = (struct dmio_state *) fp->f_data; 398 1.1 thorpej struct dmio_usrreq_state *dus; 399 1.1 thorpej struct dmover_request *dreq; 400 1.1 thorpej struct dmio_usrresp resp; 401 1.40 jakllsch int error = 0, progress = 0; 402 1.1 thorpej 403 1.1 thorpej if ((uio->uio_resid % sizeof(resp)) != 0) 404 1.1 thorpej return (EINVAL); 405 1.1 thorpej 406 1.1 thorpej if (ds->ds_session == NULL) 407 1.1 thorpej return (ENXIO); 408 1.1 thorpej 409 1.33 christos getnanotime(&ds->ds_atime); 410 1.40 jakllsch mutex_enter(&ds->ds_lock); 411 1.1 thorpej 412 1.1 thorpej while (uio->uio_resid != 0) { 413 1.1 thorpej 414 1.1 thorpej for (;;) { 415 1.1 thorpej dus = TAILQ_FIRST(&ds->ds_complete); 416 1.1 thorpej if (dus == NULL) { 417 1.1 thorpej if (fp->f_flag & FNONBLOCK) { 418 1.1 thorpej error = progress ? 0 : EWOULDBLOCK; 419 1.1 thorpej goto out; 420 1.1 thorpej } 421 1.9 scw ds->ds_flags |= DMIO_STATE_READ_WAIT; 422 1.40 jakllsch error = cv_wait_sig(&ds->ds_complete_cv, &ds->ds_lock); 423 1.1 thorpej if (error) 424 1.1 thorpej goto out; 425 1.1 thorpej continue; 426 1.1 thorpej } 427 1.1 thorpej /* Have a completed request. */ 428 1.1 thorpej TAILQ_REMOVE(&ds->ds_complete, dus, dus_q); 429 1.1 thorpej ds->ds_nreqs--; 430 1.1 thorpej if (ds->ds_flags & DMIO_STATE_WRITE_WAIT) { 431 1.1 thorpej ds->ds_flags &= ~DMIO_STATE_WRITE_WAIT; 432 1.40 jakllsch cv_broadcast(&ds->ds_nreqs_cv); 433 1.1 thorpej } 434 1.1 thorpej if (ds->ds_flags & DMIO_STATE_SEL) { 435 1.1 thorpej ds->ds_flags &= ~DMIO_STATE_SEL; 436 1.30 rmind selnotify(&ds->ds_selq, POLLIN | POLLRDNORM, 0); 437 1.1 thorpej } 438 1.1 thorpej break; 439 1.1 thorpej } 440 1.1 thorpej 441 1.1 thorpej dreq = dus->dus_req; 442 1.1 thorpej resp.resp_id = dus->dus_id; 443 1.12 thorpej if (dreq->dreq_flags & DMOVER_REQ_ERROR) 444 1.12 thorpej resp.resp_error = dreq->dreq_error; 445 1.12 thorpej else { 446 1.12 thorpej resp.resp_error = 0; 447 1.12 thorpej memcpy(resp.resp_immediate, dreq->dreq_immediate, 448 1.12 thorpej sizeof(resp.resp_immediate)); 449 1.12 thorpej } 450 1.1 thorpej 451 1.1 thorpej dmio_usrreq_fini(ds, dus); 452 1.1 thorpej 453 1.40 jakllsch mutex_exit(&ds->ds_lock); 454 1.1 thorpej 455 1.1 thorpej progress = 1; 456 1.1 thorpej 457 1.1 thorpej dmover_request_free(dreq); 458 1.1 thorpej 459 1.1 thorpej error = uiomove(&resp, sizeof(resp), uio); 460 1.1 thorpej if (error) 461 1.1 thorpej return (error); 462 1.1 thorpej 463 1.40 jakllsch mutex_enter(&ds->ds_lock); 464 1.1 thorpej } 465 1.1 thorpej 466 1.1 thorpej out: 467 1.40 jakllsch mutex_exit(&ds->ds_lock); 468 1.1 thorpej 469 1.1 thorpej return (error); 470 1.1 thorpej } 471 1.1 thorpej 472 1.1 thorpej /* 473 1.1 thorpej * dmio_usrreq_done: 474 1.1 thorpej * 475 1.1 thorpej * Dmover completion callback. 476 1.1 thorpej */ 477 1.1 thorpej static void 478 1.1 thorpej dmio_usrreq_done(struct dmover_request *dreq) 479 1.1 thorpej { 480 1.1 thorpej struct dmio_usrreq_state *dus = dreq->dreq_cookie; 481 1.1 thorpej struct dmio_state *ds = dreq->dreq_session->dses_cookie; 482 1.1 thorpej 483 1.1 thorpej /* We're already at splsoftclock(). */ 484 1.1 thorpej 485 1.40 jakllsch mutex_enter(&ds->ds_lock); 486 1.1 thorpej TAILQ_REMOVE(&ds->ds_pending, dus, dus_q); 487 1.1 thorpej if (ds->ds_flags & DMIO_STATE_DEAD) { 488 1.40 jakllsch int nreqs = --ds->ds_nreqs; 489 1.40 jakllsch mutex_exit(&ds->ds_lock); 490 1.1 thorpej dmio_usrreq_fini(ds, dus); 491 1.1 thorpej dmover_request_free(dreq); 492 1.40 jakllsch if (nreqs == 0) { 493 1.40 jakllsch dmio_state_put(ds); 494 1.1 thorpej } 495 1.40 jakllsch return; 496 1.40 jakllsch } 497 1.40 jakllsch 498 1.40 jakllsch TAILQ_INSERT_TAIL(&ds->ds_complete, dus, dus_q); 499 1.40 jakllsch if (ds->ds_flags & DMIO_STATE_READ_WAIT) { 500 1.40 jakllsch ds->ds_flags &= ~DMIO_STATE_READ_WAIT; 501 1.40 jakllsch cv_broadcast(&ds->ds_complete_cv); 502 1.40 jakllsch } 503 1.40 jakllsch if (ds->ds_flags & DMIO_STATE_SEL) { 504 1.40 jakllsch ds->ds_flags &= ~DMIO_STATE_SEL; 505 1.40 jakllsch selnotify(&ds->ds_selq, POLLOUT | POLLWRNORM, 0); 506 1.1 thorpej } 507 1.40 jakllsch mutex_exit(&ds->ds_lock); 508 1.1 thorpej } 509 1.1 thorpej 510 1.1 thorpej /* 511 1.1 thorpej * dmio_write: 512 1.1 thorpej * 513 1.1 thorpej * Write file op. 514 1.1 thorpej */ 515 1.1 thorpej static int 516 1.1 thorpej dmio_write(struct file *fp, off_t *offp, struct uio *uio, 517 1.22 elad kauth_cred_t cred, int flags) 518 1.1 thorpej { 519 1.1 thorpej struct dmio_state *ds = (struct dmio_state *) fp->f_data; 520 1.1 thorpej struct dmio_usrreq_state *dus; 521 1.1 thorpej struct dmover_request *dreq; 522 1.1 thorpej struct dmio_usrreq req; 523 1.40 jakllsch int error = 0, progress = 0; 524 1.1 thorpej 525 1.1 thorpej if ((uio->uio_resid % sizeof(req)) != 0) 526 1.1 thorpej return (EINVAL); 527 1.1 thorpej 528 1.1 thorpej if (ds->ds_session == NULL) 529 1.1 thorpej return (ENXIO); 530 1.1 thorpej 531 1.33 christos getnanotime(&ds->ds_mtime); 532 1.40 jakllsch mutex_enter(&ds->ds_lock); 533 1.1 thorpej 534 1.1 thorpej while (uio->uio_resid != 0) { 535 1.1 thorpej 536 1.1 thorpej if (ds->ds_nreqs == DMIO_NREQS_MAX) { 537 1.1 thorpej if (fp->f_flag & FNONBLOCK) { 538 1.1 thorpej error = progress ? 0 : EWOULDBLOCK; 539 1.1 thorpej break; 540 1.1 thorpej } 541 1.1 thorpej ds->ds_flags |= DMIO_STATE_WRITE_WAIT; 542 1.40 jakllsch error = cv_wait_sig(&ds->ds_complete_cv, &ds->ds_lock); 543 1.1 thorpej if (error) 544 1.1 thorpej break; 545 1.1 thorpej continue; 546 1.1 thorpej } 547 1.1 thorpej 548 1.1 thorpej ds->ds_nreqs++; 549 1.1 thorpej 550 1.40 jakllsch mutex_exit(&ds->ds_lock); 551 1.1 thorpej 552 1.1 thorpej progress = 1; 553 1.1 thorpej 554 1.1 thorpej error = uiomove(&req, sizeof(req), uio); 555 1.1 thorpej if (error) { 556 1.40 jakllsch mutex_enter(&ds->ds_lock); 557 1.1 thorpej ds->ds_nreqs--; 558 1.1 thorpej break; 559 1.1 thorpej } 560 1.1 thorpej 561 1.1 thorpej /* XXX How should this interact with FNONBLOCK? */ 562 1.1 thorpej dreq = dmover_request_alloc(ds->ds_session, NULL); 563 1.1 thorpej if (dreq == NULL) { 564 1.1 thorpej /* XXX */ 565 1.1 thorpej ds->ds_nreqs--; 566 1.1 thorpej error = ENOMEM; 567 1.40 jakllsch return error; 568 1.1 thorpej } 569 1.1 thorpej dus = pool_get(&dmio_usrreq_state_pool, PR_WAITOK); 570 1.1 thorpej 571 1.1 thorpej error = dmio_usrreq_init(fp, dus, &req, dreq); 572 1.1 thorpej if (error) { 573 1.1 thorpej dmover_request_free(dreq); 574 1.1 thorpej pool_put(&dmio_usrreq_state_pool, dus); 575 1.40 jakllsch return error; 576 1.1 thorpej } 577 1.1 thorpej 578 1.1 thorpej dreq->dreq_callback = dmio_usrreq_done; 579 1.1 thorpej dreq->dreq_cookie = dus; 580 1.1 thorpej 581 1.1 thorpej dus->dus_req = dreq; 582 1.1 thorpej dus->dus_id = req.req_id; 583 1.1 thorpej 584 1.40 jakllsch mutex_enter(&ds->ds_lock); 585 1.1 thorpej 586 1.1 thorpej TAILQ_INSERT_TAIL(&ds->ds_pending, dus, dus_q); 587 1.1 thorpej 588 1.40 jakllsch mutex_exit(&ds->ds_lock); 589 1.1 thorpej 590 1.1 thorpej dmover_process(dreq); 591 1.1 thorpej 592 1.40 jakllsch mutex_enter(&ds->ds_lock); 593 1.1 thorpej } 594 1.1 thorpej 595 1.40 jakllsch mutex_exit(&ds->ds_lock); 596 1.1 thorpej 597 1.1 thorpej return (error); 598 1.1 thorpej } 599 1.1 thorpej 600 1.33 christos static int 601 1.33 christos dmio_stat(struct file *fp, struct stat *st) 602 1.33 christos { 603 1.33 christos struct dmio_state *ds = fp->f_data; 604 1.33 christos 605 1.41 msaitoh (void)memset(st, 0, sizeof(*st)); 606 1.33 christos KERNEL_LOCK(1, NULL); 607 1.33 christos st->st_dev = makedev(cdevsw_lookup_major(&dmoverio_cdevsw), 0); 608 1.35 nonaka st->st_atimespec = ds->ds_atime; 609 1.35 nonaka st->st_mtimespec = ds->ds_mtime; 610 1.35 nonaka st->st_ctimespec = st->st_birthtimespec = ds->ds_btime; 611 1.34 christos st->st_uid = kauth_cred_geteuid(fp->f_cred); 612 1.34 christos st->st_gid = kauth_cred_getegid(fp->f_cred); 613 1.35 nonaka KERNEL_UNLOCK_ONE(NULL); 614 1.33 christos return 0; 615 1.33 christos } 616 1.33 christos 617 1.1 thorpej /* 618 1.1 thorpej * dmio_ioctl: 619 1.1 thorpej * 620 1.1 thorpej * Ioctl file op. 621 1.1 thorpej */ 622 1.1 thorpej static int 623 1.31 ad dmio_ioctl(struct file *fp, u_long cmd, void *data) 624 1.1 thorpej { 625 1.1 thorpej struct dmio_state *ds = (struct dmio_state *) fp->f_data; 626 1.40 jakllsch int error; 627 1.1 thorpej 628 1.1 thorpej switch (cmd) { 629 1.1 thorpej case FIONBIO: 630 1.1 thorpej case FIOASYNC: 631 1.1 thorpej return (0); 632 1.1 thorpej 633 1.1 thorpej case DMIO_SETFUNC: 634 1.1 thorpej { 635 1.8 dsl struct dmio_setfunc *dsf = data; 636 1.1 thorpej struct dmover_session *dses; 637 1.1 thorpej 638 1.40 jakllsch mutex_enter(&ds->ds_lock); 639 1.1 thorpej 640 1.1 thorpej if (ds->ds_session != NULL || 641 1.1 thorpej (ds->ds_flags & DMIO_STATE_LARVAL) != 0) { 642 1.40 jakllsch mutex_exit(&ds->ds_lock); 643 1.1 thorpej return (EBUSY); 644 1.1 thorpej } 645 1.1 thorpej 646 1.1 thorpej ds->ds_flags |= DMIO_STATE_LARVAL; 647 1.1 thorpej 648 1.40 jakllsch mutex_exit(&ds->ds_lock); 649 1.1 thorpej 650 1.1 thorpej dsf->dsf_name[DMIO_MAX_FUNCNAME - 1] = '\0'; 651 1.1 thorpej error = dmover_session_create(dsf->dsf_name, &dses); 652 1.1 thorpej 653 1.40 jakllsch mutex_enter(&ds->ds_lock); 654 1.1 thorpej 655 1.1 thorpej if (error == 0) { 656 1.1 thorpej dses->dses_cookie = ds; 657 1.1 thorpej ds->ds_session = dses; 658 1.1 thorpej } 659 1.1 thorpej ds->ds_flags &= ~DMIO_STATE_LARVAL; 660 1.1 thorpej 661 1.40 jakllsch mutex_exit(&ds->ds_lock); 662 1.1 thorpej break; 663 1.1 thorpej } 664 1.1 thorpej 665 1.1 thorpej default: 666 1.1 thorpej error = ENOTTY; 667 1.1 thorpej } 668 1.1 thorpej 669 1.1 thorpej return (error); 670 1.1 thorpej } 671 1.1 thorpej 672 1.1 thorpej /* 673 1.1 thorpej * dmio_poll: 674 1.1 thorpej * 675 1.1 thorpej * Poll file op. 676 1.1 thorpej */ 677 1.1 thorpej static int 678 1.31 ad dmio_poll(struct file *fp, int events) 679 1.1 thorpej { 680 1.1 thorpej struct dmio_state *ds = (struct dmio_state *) fp->f_data; 681 1.40 jakllsch int revents = 0; 682 1.1 thorpej 683 1.1 thorpej if ((events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) == 0) 684 1.1 thorpej return (revents); 685 1.1 thorpej 686 1.40 jakllsch mutex_enter(&ds->ds_lock); 687 1.1 thorpej 688 1.1 thorpej if (ds->ds_flags & DMIO_STATE_DEAD) { 689 1.1 thorpej /* EOF */ 690 1.1 thorpej revents |= events & (POLLIN | POLLRDNORM | 691 1.1 thorpej POLLOUT | POLLWRNORM); 692 1.1 thorpej goto out; 693 1.1 thorpej } 694 1.1 thorpej 695 1.1 thorpej /* We can read if there are completed requests. */ 696 1.1 thorpej if (events & (POLLIN | POLLRDNORM)) 697 1.1 thorpej if (TAILQ_EMPTY(&ds->ds_complete) == 0) 698 1.1 thorpej revents |= events & (POLLIN | POLLRDNORM); 699 1.1 thorpej 700 1.1 thorpej /* 701 1.1 thorpej * We can write if there is there are fewer then DMIO_NREQS_MAX 702 1.1 thorpej * are already in the queue. 703 1.1 thorpej */ 704 1.1 thorpej if (events & (POLLOUT | POLLWRNORM)) 705 1.1 thorpej if (ds->ds_nreqs < DMIO_NREQS_MAX) 706 1.1 thorpej revents |= events & (POLLOUT | POLLWRNORM); 707 1.1 thorpej 708 1.1 thorpej if (revents == 0) { 709 1.31 ad selrecord(curlwp, &ds->ds_selq); 710 1.1 thorpej ds->ds_flags |= DMIO_STATE_SEL; 711 1.1 thorpej } 712 1.1 thorpej 713 1.1 thorpej out: 714 1.40 jakllsch mutex_exit(&ds->ds_lock); 715 1.1 thorpej 716 1.1 thorpej return (revents); 717 1.1 thorpej } 718 1.1 thorpej 719 1.1 thorpej /* 720 1.1 thorpej * dmio_close: 721 1.1 thorpej * 722 1.1 thorpej * Close file op. 723 1.1 thorpej */ 724 1.1 thorpej static int 725 1.31 ad dmio_close(struct file *fp) 726 1.1 thorpej { 727 1.1 thorpej struct dmio_state *ds = (struct dmio_state *) fp->f_data; 728 1.1 thorpej struct dmio_usrreq_state *dus; 729 1.1 thorpej struct dmover_session *dses; 730 1.1 thorpej 731 1.40 jakllsch mutex_enter(&ds->ds_lock); 732 1.1 thorpej 733 1.1 thorpej ds->ds_flags |= DMIO_STATE_DEAD; 734 1.1 thorpej 735 1.1 thorpej /* Garbage-collect all the responses on the queue. */ 736 1.1 thorpej while ((dus = TAILQ_FIRST(&ds->ds_complete)) != NULL) { 737 1.1 thorpej TAILQ_REMOVE(&ds->ds_complete, dus, dus_q); 738 1.1 thorpej ds->ds_nreqs--; 739 1.40 jakllsch mutex_exit(&ds->ds_lock); 740 1.1 thorpej dmover_request_free(dus->dus_req); 741 1.1 thorpej dmio_usrreq_fini(ds, dus); 742 1.40 jakllsch mutex_enter(&ds->ds_lock); 743 1.1 thorpej } 744 1.1 thorpej 745 1.1 thorpej /* 746 1.1 thorpej * If there are any requests pending, we have to wait for 747 1.1 thorpej * them. Don't free the dmio_state in this case. 748 1.1 thorpej */ 749 1.1 thorpej if (ds->ds_nreqs == 0) { 750 1.1 thorpej dses = ds->ds_session; 751 1.40 jakllsch mutex_exit(&ds->ds_lock); 752 1.40 jakllsch dmio_state_put(ds); 753 1.1 thorpej } else { 754 1.1 thorpej dses = NULL; 755 1.40 jakllsch mutex_exit(&ds->ds_lock); 756 1.1 thorpej } 757 1.1 thorpej 758 1.1 thorpej fp->f_data = NULL; 759 1.1 thorpej 760 1.1 thorpej if (dses != NULL) 761 1.1 thorpej dmover_session_destroy(dses); 762 1.1 thorpej 763 1.1 thorpej return (0); 764 1.1 thorpej } 765 1.1 thorpej 766 1.15 christos static const struct fileops dmio_fileops = { 767 1.45 christos .fo_name = "dmio", 768 1.32 ad .fo_read = dmio_read, 769 1.32 ad .fo_write = dmio_write, 770 1.32 ad .fo_ioctl = dmio_ioctl, 771 1.32 ad .fo_fcntl = fnullop_fcntl, 772 1.32 ad .fo_poll = dmio_poll, 773 1.33 christos .fo_stat = dmio_stat, 774 1.32 ad .fo_close = dmio_close, 775 1.32 ad .fo_kqfilter = fnullop_kqfilter, 776 1.37 dsl .fo_restart = fnullop_restart, 777 1.1 thorpej }; 778 1.1 thorpej 779 1.1 thorpej /* 780 1.1 thorpej * dmoverioopen: 781 1.1 thorpej * 782 1.1 thorpej * Device switch open routine. 783 1.1 thorpej */ 784 1.1 thorpej int 785 1.18 christos dmoverioopen(dev_t dev, int flag, int mode, struct lwp *l) 786 1.1 thorpej { 787 1.1 thorpej struct dmio_state *ds; 788 1.1 thorpej struct file *fp; 789 1.40 jakllsch int error, fd; 790 1.1 thorpej 791 1.31 ad if ((error = fd_allocfile(&fp, &fd)) != 0) 792 1.1 thorpej return (error); 793 1.1 thorpej 794 1.40 jakllsch ds = dmio_state_get(); 795 1.1 thorpej 796 1.31 ad return fd_clone(fp, fd, flag, &dmio_fileops, ds); 797 1.1 thorpej } 798