xref: /freebsd/sys/kern/kern_physio.c (revision 869fd29a7b764a0d50cf25f17483122901d7c278)
19454b2d8SWarner Losh /*-
226f9a767SRodney W. Grimes  * Copyright (c) 1994 John S. Dyson
326f9a767SRodney W. Grimes  * All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
6df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
7df8bae1dSRodney W. Grimes  * are met:
8df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
926f9a767SRodney W. Grimes  *    notice immediately at the beginning of the file, without modification,
1026f9a767SRodney W. Grimes  *    this list of conditions, and the following disclaimer.
11df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
12df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
13df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
1426f9a767SRodney W. Grimes  * 3. Absolutely no warranty of function or purpose is made by the author
1526f9a767SRodney W. Grimes  *    John S. Dyson.
1626f9a767SRodney W. Grimes  * 4. Modifications may be freely made to this file if the above conditions
1726f9a767SRodney W. Grimes  *    are met.
18df8bae1dSRodney W. Grimes  */
19df8bae1dSRodney W. Grimes 
20677b542eSDavid E. O'Brien #include <sys/cdefs.h>
21677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
22677b542eSDavid E. O'Brien 
23df8bae1dSRodney W. Grimes #include <sys/param.h>
24df8bae1dSRodney W. Grimes #include <sys/systm.h>
259626b608SPoul-Henning Kamp #include <sys/bio.h>
26df8bae1dSRodney W. Grimes #include <sys/buf.h>
27df8bae1dSRodney W. Grimes #include <sys/conf.h>
28*869fd29aSAlexander Motin #include <sys/malloc.h>
29df8bae1dSRodney W. Grimes #include <sys/proc.h>
3008637435SBruce Evans #include <sys/uio.h>
31*869fd29aSAlexander Motin #include <geom/geom.h>
3208637435SBruce Evans 
3326f9a767SRodney W. Grimes #include <vm/vm.h>
34*869fd29aSAlexander Motin #include <vm/vm_page.h>
35efeaf95aSDavid Greenman #include <vm/vm_extern.h>
36*869fd29aSAlexander Motin #include <vm/vm_map.h>
37df8bae1dSRodney W. Grimes 
38c48d1775SPoul-Henning Kamp int
3989c9c53dSPoul-Henning Kamp physio(struct cdev *dev, struct uio *uio, int ioflag)
40df8bae1dSRodney W. Grimes {
41*869fd29aSAlexander Motin 	struct buf *pbuf;
42*869fd29aSAlexander Motin 	struct bio *bp;
43*869fd29aSAlexander Motin 	struct vm_page **pages;
4416f62314SDavid Greenman 	caddr_t sa;
45*869fd29aSAlexander Motin 	u_int iolen, poff;
46*869fd29aSAlexander Motin 	int error, i, npages, maxpages;
47*869fd29aSAlexander Motin 	vm_prot_t prot;
4826f9a767SRodney W. Grimes 
497179e74fSPoul-Henning Kamp 	/* XXX: sanity check */
507179e74fSPoul-Henning Kamp 	if(dev->si_iosize_max < PAGE_SIZE) {
517179e74fSPoul-Henning Kamp 		printf("WARNING: %s si_iosize_max=%d, using DFLTPHYS.\n",
527179e74fSPoul-Henning Kamp 		    devtoname(dev), dev->si_iosize_max);
537179e74fSPoul-Henning Kamp 		dev->si_iosize_max = DFLTPHYS;
547179e74fSPoul-Henning Kamp 	}
557179e74fSPoul-Henning Kamp 
5693729c17SKenneth D. Merry 	/*
5793729c17SKenneth D. Merry 	 * If the driver does not want I/O to be split, that means that we
5893729c17SKenneth D. Merry 	 * need to reject any requests that will not fit into one buffer.
5993729c17SKenneth D. Merry 	 */
60880e57b6SKenneth D. Merry 	if (dev->si_flags & SI_NOSPLIT &&
61880e57b6SKenneth D. Merry 	    (uio->uio_resid > dev->si_iosize_max || uio->uio_resid > MAXPHYS ||
62880e57b6SKenneth D. Merry 	    uio->uio_iovcnt > 1)) {
6393729c17SKenneth D. Merry 		/*
6493729c17SKenneth D. Merry 		 * Tell the user why his I/O was rejected.
6593729c17SKenneth D. Merry 		 */
6693729c17SKenneth D. Merry 		if (uio->uio_resid > dev->si_iosize_max)
67880e57b6SKenneth D. Merry 			uprintf("%s: request size=%zd > si_iosize_max=%d; "
6893729c17SKenneth D. Merry 			    "cannot split request\n", devtoname(dev),
6993729c17SKenneth D. Merry 			    uio->uio_resid, dev->si_iosize_max);
7093729c17SKenneth D. Merry 		if (uio->uio_resid > MAXPHYS)
71880e57b6SKenneth D. Merry 			uprintf("%s: request size=%zd > MAXPHYS=%d; "
7293729c17SKenneth D. Merry 			    "cannot split request\n", devtoname(dev),
7393729c17SKenneth D. Merry 			    uio->uio_resid, MAXPHYS);
7493729c17SKenneth D. Merry 		if (uio->uio_iovcnt > 1)
75880e57b6SKenneth D. Merry 			uprintf("%s: request vectors=%d > 1; "
7693729c17SKenneth D. Merry 			    "cannot split request\n", devtoname(dev),
7793729c17SKenneth D. Merry 			    uio->uio_iovcnt);
78*869fd29aSAlexander Motin 		return (EFBIG);
7993729c17SKenneth D. Merry 	}
8093729c17SKenneth D. Merry 
81*869fd29aSAlexander Motin 	/*
82*869fd29aSAlexander Motin 	 * Keep the process UPAGES from being swapped.  Processes swapped
83*869fd29aSAlexander Motin 	 * out while holding pbufs, used by swapper, may lead to deadlock.
84*869fd29aSAlexander Motin 	 */
85*869fd29aSAlexander Motin 	PHOLD(curproc);
86*869fd29aSAlexander Motin 
87*869fd29aSAlexander Motin 	bp = g_alloc_bio();
88*869fd29aSAlexander Motin 	if (uio->uio_segflg != UIO_USERSPACE) {
89*869fd29aSAlexander Motin 		pbuf = NULL;
90*869fd29aSAlexander Motin 		pages = NULL;
91*869fd29aSAlexander Motin 	} else if ((dev->si_flags & SI_UNMAPPED) && unmapped_buf_allowed) {
92*869fd29aSAlexander Motin 		pbuf = NULL;
93*869fd29aSAlexander Motin 		maxpages = btoc(MIN(uio->uio_resid, MAXPHYS)) + 1;
94*869fd29aSAlexander Motin 		pages = malloc(sizeof(*pages) * maxpages, M_DEVBUF, M_WAITOK);
95*869fd29aSAlexander Motin 	} else {
96*869fd29aSAlexander Motin 		pbuf = getpbuf(NULL);
97*869fd29aSAlexander Motin 		sa = pbuf->b_data;
98*869fd29aSAlexander Motin 		maxpages = btoc(MAXPHYS);
99*869fd29aSAlexander Motin 		pages = pbuf->b_pages;
100*869fd29aSAlexander Motin 	}
101*869fd29aSAlexander Motin 	prot = VM_PROT_READ;
102*869fd29aSAlexander Motin 	if (uio->uio_rw == UIO_READ)
103*869fd29aSAlexander Motin 		prot |= VM_PROT_WRITE;	/* Less backwards than it looks */
104*869fd29aSAlexander Motin 	error = 0;
10526f9a767SRodney W. Grimes 	for (i = 0; i < uio->uio_iovcnt; i++) {
10626f9a767SRodney W. Grimes 		while (uio->uio_iov[i].iov_len) {
107*869fd29aSAlexander Motin 			bzero(bp, sizeof(*bp));
108eea7f71cSKonstantin Belousov 			if (uio->uio_rw == UIO_READ) {
109*869fd29aSAlexander Motin 				bp->bio_cmd = BIO_READ;
110eea7f71cSKonstantin Belousov 				curthread->td_ru.ru_inblock++;
111eea7f71cSKonstantin Belousov 			} else {
112*869fd29aSAlexander Motin 				bp->bio_cmd = BIO_WRITE;
113eea7f71cSKonstantin Belousov 				curthread->td_ru.ru_oublock++;
114eea7f71cSKonstantin Belousov 			}
115*869fd29aSAlexander Motin 			bp->bio_offset = uio->uio_offset;
116*869fd29aSAlexander Motin 			bp->bio_data = uio->uio_iov[i].iov_base;
117*869fd29aSAlexander Motin 			bp->bio_length = uio->uio_iov[i].iov_len;
118*869fd29aSAlexander Motin 			if (bp->bio_length > dev->si_iosize_max)
119*869fd29aSAlexander Motin 				bp->bio_length = dev->si_iosize_max;
120*869fd29aSAlexander Motin 			if (bp->bio_length > MAXPHYS)
121*869fd29aSAlexander Motin 				bp->bio_length = MAXPHYS;
1227179e74fSPoul-Henning Kamp 
1237179e74fSPoul-Henning Kamp 			/*
124*869fd29aSAlexander Motin 			 * Make sure the pbuf can map the request.
125*869fd29aSAlexander Motin 			 * The pbuf has kvasize = MAXPHYS, so a request
126*869fd29aSAlexander Motin 			 * larger than MAXPHYS - PAGE_SIZE must be
127*869fd29aSAlexander Motin 			 * page aligned or it will be fragmented.
1287179e74fSPoul-Henning Kamp 			 */
129*869fd29aSAlexander Motin 			poff = (vm_offset_t)bp->bio_data & PAGE_MASK;
130*869fd29aSAlexander Motin 			if (pbuf && bp->bio_length + poff > pbuf->b_kvasize) {
13193729c17SKenneth D. Merry 				if (dev->si_flags & SI_NOSPLIT) {
132880e57b6SKenneth D. Merry 					uprintf("%s: request ptr %p is not "
133880e57b6SKenneth D. Merry 					    "on a page boundary; cannot split "
13493729c17SKenneth D. Merry 					    "request\n", devtoname(dev),
135*869fd29aSAlexander Motin 					    bp->bio_data);
13693729c17SKenneth D. Merry 					error = EFBIG;
13793729c17SKenneth D. Merry 					goto doerror;
13893729c17SKenneth D. Merry 				}
139*869fd29aSAlexander Motin 				bp->bio_length = pbuf->b_kvasize;
140*869fd29aSAlexander Motin 				if (poff != 0)
141*869fd29aSAlexander Motin 					bp->bio_length -= PAGE_SIZE;
1427179e74fSPoul-Henning Kamp 			}
1437179e74fSPoul-Henning Kamp 
144*869fd29aSAlexander Motin 			bp->bio_bcount = bp->bio_length;
145*869fd29aSAlexander Motin 			bp->bio_dev = dev;
14626f9a767SRodney W. Grimes 
147*869fd29aSAlexander Motin 			if (pages) {
148*869fd29aSAlexander Motin 				if ((npages = vm_fault_quick_hold_pages(
149*869fd29aSAlexander Motin 				    &curproc->p_vmspace->vm_map,
150*869fd29aSAlexander Motin 				    (vm_offset_t)bp->bio_data, bp->bio_length,
151*869fd29aSAlexander Motin 				    prot, pages, maxpages)) < 0) {
1522d5c7e45SMatthew Dillon 					error = EFAULT;
1532d5c7e45SMatthew Dillon 					goto doerror;
1542d5c7e45SMatthew Dillon 				}
155*869fd29aSAlexander Motin 				if (pbuf) {
156*869fd29aSAlexander Motin 					pmap_qenter((vm_offset_t)sa,
157*869fd29aSAlexander Motin 					    pages, npages);
158*869fd29aSAlexander Motin 					bp->bio_data = sa + poff;
159*869fd29aSAlexander Motin 				} else {
160*869fd29aSAlexander Motin 					bp->bio_ma = pages;
161*869fd29aSAlexander Motin 					bp->bio_ma_n = npages;
162*869fd29aSAlexander Motin 					bp->bio_ma_offset = poff;
163*869fd29aSAlexander Motin 					bp->bio_data = unmapped_buf;
164*869fd29aSAlexander Motin 					bp->bio_flags |= BIO_UNMAPPED;
165*869fd29aSAlexander Motin 				}
16631932faeSAlexander Kabaev 			}
16726f9a767SRodney W. Grimes 
168*869fd29aSAlexander Motin 			dev->si_devsw->d_strategy(bp);
169749ffa4eSJeff Roberson 			if (uio->uio_rw == UIO_READ)
170*869fd29aSAlexander Motin 				biowait(bp, "physrd");
171749ffa4eSJeff Roberson 			else
172*869fd29aSAlexander Motin 				biowait(bp, "physwr");
17326f9a767SRodney W. Grimes 
174*869fd29aSAlexander Motin 			if (pages) {
175*869fd29aSAlexander Motin 				if (pbuf)
176*869fd29aSAlexander Motin 					pmap_qremove((vm_offset_t)sa, npages);
177*869fd29aSAlexander Motin 				vm_page_unhold_pages(pages, npages);
178*869fd29aSAlexander Motin 			}
179*869fd29aSAlexander Motin 
180*869fd29aSAlexander Motin 			iolen = bp->bio_length - bp->bio_resid;
181*869fd29aSAlexander Motin 			if (iolen == 0 && !(bp->bio_flags & BIO_ERROR))
1827d7bb69dSDavid Greenman 				goto doerror;	/* EOF */
18326f9a767SRodney W. Grimes 			uio->uio_iov[i].iov_len -= iolen;
1842b7f24d2SMike Barcroft 			uio->uio_iov[i].iov_base =
1852b7f24d2SMike Barcroft 			    (char *)uio->uio_iov[i].iov_base + iolen;
18626f9a767SRodney W. Grimes 			uio->uio_resid -= iolen;
18726f9a767SRodney W. Grimes 			uio->uio_offset += iolen;
188*869fd29aSAlexander Motin 			if (bp->bio_flags & BIO_ERROR) {
189*869fd29aSAlexander Motin 				error = bp->bio_error;
19026f9a767SRodney W. Grimes 				goto doerror;
19126f9a767SRodney W. Grimes 			}
19226f9a767SRodney W. Grimes 		}
19326f9a767SRodney W. Grimes 	}
19426f9a767SRodney W. Grimes doerror:
195*869fd29aSAlexander Motin 	if (pbuf)
196*869fd29aSAlexander Motin 		relpbuf(pbuf, NULL);
197*869fd29aSAlexander Motin 	else if (pages)
198*869fd29aSAlexander Motin 		free(pages, M_DEVBUF);
199*869fd29aSAlexander Motin 	g_destroy_bio(bp);
20057dc5948SPeter Wemm 	PRELE(curproc);
20126f9a767SRodney W. Grimes 	return (error);
202df8bae1dSRodney W. Grimes }
203