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.
This commit is contained in:
Alexander Motin 2015-08-07 14:38:26 +00:00
parent 3301406331
commit ebef81ecea
2 changed files with 165 additions and 68 deletions

View File

@ -1074,7 +1074,7 @@ ataaction(struct cam_sim *sim, union ccb *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;

View File

@ -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);
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)))) {
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--;
}
/* 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];
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;
}
} 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);
}
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));
#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);
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)))) {
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
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++];
ATA_IDX_OUTW_STRM(ch, ATA_DATA, *(uint16_t *)&buf);
}
} else
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 /
sizeof(int16_t));
if (size & 1) {
buf[0] = (addr + (size & ~1))[0];
ATA_IDX_OUTW_STRM(ch, ATA_DATA, *(uint16_t *)&buf);
ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (void*)addr,
size / 2);
/* 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;
}
} 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);
}
/* 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);
}
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);
}
}