Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 7 Aug 2015 14:38:27 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r286415 - head/sys/dev/ata
Message-ID:  <201508071438.t77EcRN2086686@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Fri Aug  7 14:38:26 2015
New Revision: 286415
URL: https://svnweb.freebsd.org/changeset/base/286415

Log:
  Add unmapped I/O support to ata(4) driver.
  
  Main problem there was PIO mode support, that required KVA mapping.
  Handle that case using recently added pmap_quick_enter_page(9) KPI,
  mapping data pages to KVA one at a time.

Modified:
  head/sys/dev/ata/ata-all.c
  head/sys/dev/ata/ata-lowlevel.c

Modified: head/sys/dev/ata/ata-all.c
==============================================================================
--- head/sys/dev/ata/ata-all.c	Fri Aug  7 14:12:51 2015	(r286414)
+++ head/sys/dev/ata/ata-all.c	Fri Aug  7 14:38:26 2015	(r286415)
@@ -1074,7 +1074,7 @@ ataaction(struct cam_sim *sim, union ccb
 		cpi->version_num = 1; /* XXX??? */
 		cpi->hba_inquiry = PI_SDTR_ABLE;
 		cpi->target_sprt = 0;
-		cpi->hba_misc = PIM_SEQSCAN;
+		cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED;
 		cpi->hba_eng_cnt = 0;
 		if (ch->flags & ATA_NO_SLAVE)
 			cpi->max_target = 0;

Modified: head/sys/dev/ata/ata-lowlevel.c
==============================================================================
--- head/sys/dev/ata/ata-lowlevel.c	Fri Aug  7 14:12:51 2015	(r286414)
+++ head/sys/dev/ata/ata-lowlevel.c	Fri Aug  7 14:38:26 2015	(r286415)
@@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/endian.h>
 #include <sys/ata.h>
+#include <sys/bio.h>
 #include <sys/conf.h>
 #include <sys/ctype.h>
 #include <sys/bus.h>
@@ -44,6 +45,12 @@ __FBSDID("$FreeBSD$");
 #include <dev/ata/ata-pci.h>
 #include <ata_if.h>
 
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+
 /* prototypes */
 static int ata_generic_status(device_t dev);
 static int ata_wait(struct ata_channel *ch, int unit, u_int8_t);
@@ -811,86 +818,176 @@ ata_tf_write(struct ata_request *request
 static void
 ata_pio_read(struct ata_request *request, int length)
 {
-    struct ata_channel *ch = device_get_softc(request->parent);
-    uint8_t *addr;
-    int size = min(request->transfersize, length);
-    int resid;
-    uint8_t buf[2] __aligned(sizeof(int16_t));
-#ifndef __NO_STRICT_ALIGNMENT
-    int i;
-#endif
+	struct ata_channel *ch = device_get_softc(request->parent);
+	struct bio *bio;
+	uint8_t *addr;
+	vm_offset_t page;
+	int todo, done, off, moff, resid, size, i;
+	uint8_t buf[2] __aligned(2);
+
+	todo = min(request->transfersize, length);
+	page = done = resid = 0;
+	while (done < todo) {
+		size = todo - done;
+
+		/* Prepare data address and limit size (if not sequential). */
+		off = request->donecount + done;
+		if ((request->flags & ATA_R_DATA_IN_CCB) == 0 ||
+		    (request->ccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) {
+			addr = (uint8_t *)request->data + off;
+		} else if ((request->ccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_BIO) {
+			bio = (struct bio *)request->data;
+			if ((bio->bio_flags & BIO_UNMAPPED) == 0) {
+				addr = (uint8_t *)bio->bio_data + off;
+			} else {
+				moff = bio->bio_ma_offset + off;
+				page = pmap_quick_enter_page(
+				    bio->bio_ma[moff / PAGE_SIZE]);
+				moff %= PAGE_SIZE;
+				size = min(size, PAGE_SIZE - moff);
+				addr = (void *)(page + moff);
+			}
+		} else
+			panic("ata_pio_read: Unsupported CAM data type %x\n",
+			    (request->ccb->ccb_h.flags & CAM_DATA_MASK));
+
+		/* We may have extra byte already red but not stored. */
+		if (resid) {
+			addr[0] = buf[1];
+			addr++;
+			done++;
+			size--;
+		}
 
-    addr = (uint8_t *)request->data + request->donecount;
-    if (__predict_false(ch->flags & ATA_USE_16BIT ||
-      (size % sizeof(int32_t)) || ((uintptr_t)addr % sizeof(int32_t)))) {
+		/* Process main part of data. */
+		resid = size % 2;
+		if (__predict_false((ch->flags & ATA_USE_16BIT) ||
+		    (size % 4) != 0 || ((uintptr_t)addr % 4) != 0)) {
 #ifndef __NO_STRICT_ALIGNMENT
-	if (__predict_false((uintptr_t)addr % sizeof(int16_t))) {
-	    for (i = 0, resid = size & ~1; resid > 0; resid -=
-	      sizeof(int16_t)) {
-		*(uint16_t *)&buf = ATA_IDX_INW_STRM(ch, ATA_DATA);
-	        addr[i++] = buf[0];
-	        addr[i++] = buf[1];
-	    }
-	} else
+			if (__predict_false((uintptr_t)addr % 2)) {
+				for (i = 0; i + 1 < size; i += 2) {
+					*(uint16_t *)&buf =
+					    ATA_IDX_INW_STRM(ch, ATA_DATA);
+					addr[i] = buf[0];
+					addr[i + 1] = buf[1];
+				}
+			} else
 #endif
-	    ATA_IDX_INSW_STRM(ch, ATA_DATA, (void*)addr, size /
-	      sizeof(int16_t));
-	if (size & 1) {
-	    *(uint16_t *)&buf = ATA_IDX_INW_STRM(ch, ATA_DATA);
-	    (addr + (size & ~1))[0] = buf[0];
-	}
-    } else
-	ATA_IDX_INSL_STRM(ch, ATA_DATA, (void*)addr, size / sizeof(int32_t));
-
-    if (request->transfersize < length) {
-	device_printf(request->parent, "WARNING - %s read data overrun %d>%d\n",
-		   ata_cmd2str(request), length, request->transfersize);
-	for (resid = request->transfersize + (size & 1); resid < length;
-	     resid += sizeof(int16_t))
-	    ATA_IDX_INW(ch, ATA_DATA);
-    }
+				ATA_IDX_INSW_STRM(ch, ATA_DATA, (void*)addr,
+				    size / 2);
+
+			/* If we have extra byte of data, leave it for later. */
+			if (resid) {
+				*(uint16_t *)&buf =
+				    ATA_IDX_INW_STRM(ch, ATA_DATA);
+				addr[size - 1] = buf[0];
+			}
+		} else
+			ATA_IDX_INSL_STRM(ch, ATA_DATA, (void*)addr, size / 4);
+
+		if (page) {
+			pmap_quick_remove_page(page);
+			page = 0;
+		}
+		done += size;
+	}
+
+	if (length > done) {
+		device_printf(request->parent,
+		    "WARNING - %s read data overrun %d > %d\n",
+		    ata_cmd2str(request), length, done);
+		for (i = done + resid; i < length; i += 2)
+			ATA_IDX_INW(ch, ATA_DATA);
+	}
 }
 
 static void
 ata_pio_write(struct ata_request *request, int length)
 {
-    struct ata_channel *ch = device_get_softc(request->parent);
-    uint8_t *addr;
-    int size = min(request->transfersize, length);
-    int resid;
-    uint8_t buf[2] __aligned(sizeof(int16_t));
+	struct ata_channel *ch = device_get_softc(request->parent);
+	struct bio *bio;
+	uint8_t *addr;
+	vm_offset_t page;
+	int todo, done, off, moff, resid, size, i;
+	uint8_t buf[2] __aligned(2);
+
+	todo = min(request->transfersize, length);
+	page = done = resid = 0;
+	while (done < todo) {
+		size = todo - done;
+
+		/* Prepare data address and limit size (if not sequential). */
+		off = request->donecount + done;
+		if ((request->flags & ATA_R_DATA_IN_CCB) == 0 ||
+		    (request->ccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) {
+			addr = (uint8_t *)request->data + off;
+		} else if ((request->ccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_BIO) {
+			bio = (struct bio *)request->data;
+			if ((bio->bio_flags & BIO_UNMAPPED) == 0) {
+				addr = (uint8_t *)bio->bio_data + off;
+			} else {
+				moff = bio->bio_ma_offset + off;
+				page = pmap_quick_enter_page(
+				    bio->bio_ma[moff / PAGE_SIZE]);
+				moff %= PAGE_SIZE;
+				size = min(size, PAGE_SIZE - moff);
+				addr = (void *)(page + moff);
+			}
+		} else
+			panic("ata_pio_write: Unsupported CAM data type %x\n",
+			    (request->ccb->ccb_h.flags & CAM_DATA_MASK));
+
+		/* We may have extra byte to be written first. */
+		if (resid) {
+			buf[1] = addr[0];
+			ATA_IDX_OUTW_STRM(ch, ATA_DATA, *(uint16_t *)&buf);
+			addr++;
+			done++;
+			size--;
+		}
+
+		/* Process main part of data. */
+		resid = size % 2;
+		if (__predict_false((ch->flags & ATA_USE_16BIT) ||
+		    (size % 4) != 0 || ((uintptr_t)addr % 4) != 0)) {
 #ifndef __NO_STRICT_ALIGNMENT
-    int i;
+			if (__predict_false((uintptr_t)addr % 2)) {
+				for (i = 0; i + 1 < size; i += 2) {
+					buf[0] = addr[i];
+					buf[1] = addr[i + 1];
+					ATA_IDX_OUTW_STRM(ch, ATA_DATA,
+					    *(uint16_t *)&buf);
+				}
+			} else
 #endif
+				ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (void*)addr,
+				    size / 2);
 
-    size = min(request->transfersize, length);
-    addr = (uint8_t *)request->data + request->donecount;
-    if (__predict_false(ch->flags & ATA_USE_16BIT ||
-      (size % sizeof(int32_t)) || ((uintptr_t)addr % sizeof(int32_t)))) {
-#ifndef __NO_STRICT_ALIGNMENT
-	if (__predict_false((uintptr_t)addr % sizeof(int16_t))) {
-	    for (i = 0, resid = size & ~1; resid > 0; resid -=
-	      sizeof(int16_t)) {
-	        buf[0] = addr[i++];
-	        buf[1] = addr[i++];
+			/* If we have extra byte of data, save it for later. */
+			if (resid)
+				buf[0] = addr[size - 1];
+		} else
+			ATA_IDX_OUTSL_STRM(ch, ATA_DATA,
+			    (void*)addr, size / sizeof(int32_t));
+
+		if (page) {
+			pmap_quick_remove_page(page);
+			page = 0;
+		}
+		done += size;
+	}
+
+	/* We may have extra byte of data to be written. Pad it with zero. */
+	if (resid) {
+		buf[1] = 0;
 		ATA_IDX_OUTW_STRM(ch, ATA_DATA, *(uint16_t *)&buf);
-	    }
-	} else
-#endif
-	    ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (void*)addr, size /
-	      sizeof(int16_t));
-	if (size & 1) {
-	    buf[0] = (addr + (size & ~1))[0];
-	    ATA_IDX_OUTW_STRM(ch, ATA_DATA, *(uint16_t *)&buf);
-	}
-    } else
-	ATA_IDX_OUTSL_STRM(ch, ATA_DATA, (void*)addr, size / sizeof(int32_t));
-
-    if (request->transfersize < length) {
-	device_printf(request->parent, "WARNING - %s write data underrun %d>%d\n",
-		   ata_cmd2str(request), length, request->transfersize);
-	for (resid = request->transfersize + (size & 1); resid < length;
-	     resid += sizeof(int16_t))
-	    ATA_IDX_OUTW(ch, ATA_DATA, 0);
-    }
+	}
+
+	if (length > done) {
+		device_printf(request->parent,
+		    "WARNING - %s write data underrun %d > %d\n",
+		    ata_cmd2str(request), length, done);
+		for (i = done + resid; i < length; i += 2)
+			ATA_IDX_OUTW(ch, ATA_DATA, 0);
+	}
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201508071438.t77EcRN2086686>