Account for the offset of the page run when allocating the

dmar_map_entry.  Non-zero offset both increases the required mapping
size, which is handled in dmar_bus_dmamap_load_something1(), and makes
it possible that allocated range crosses boundary, which needs a check
in dmar_gas_match_one().

Reported and tested by:	jimharris
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2015-04-08 01:55:22 +00:00
parent 6554d5b421
commit 5f01ba81ae
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=281254
3 changed files with 32 additions and 21 deletions

View File

@ -462,6 +462,7 @@ dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
bus_size_t buflen1;
int error, idx, gas_flags, seg;
KASSERT(offset < DMAR_PAGE_SIZE, ("offset %d", offset));
if (segs == NULL)
segs = tag->segments;
ctx = tag->ctx;
@ -476,7 +477,6 @@ dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
}
buflen1 = buflen > tag->common.maxsegsz ?
tag->common.maxsegsz : buflen;
buflen -= buflen1;
size = round_page(offset + buflen1);
/*
@ -487,7 +487,7 @@ dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
if (seg + 1 < tag->common.nsegments)
gas_flags |= DMAR_GM_CANSPLIT;
error = dmar_gas_map(ctx, &tag->common, size,
error = dmar_gas_map(ctx, &tag->common, size, offset,
DMAR_MAP_ENTRY_READ | DMAR_MAP_ENTRY_WRITE,
gas_flags, ma + idx, &entry);
if (error != 0)
@ -506,6 +506,10 @@ dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
(uintmax_t)size, (uintmax_t)entry->start,
(uintmax_t)entry->end));
}
if (offset + buflen1 > size)
buflen1 = size - offset;
if (buflen1 > tag->common.maxsegsz)
buflen1 = tag->common.maxsegsz;
KASSERT(((entry->start + offset) & (tag->common.alignment - 1))
== 0,
@ -519,15 +523,16 @@ dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
(uintmax_t)entry->start, (uintmax_t)entry->end,
(uintmax_t)tag->common.lowaddr,
(uintmax_t)tag->common.highaddr));
KASSERT(dmar_test_boundary(entry->start, entry->end -
entry->start, tag->common.boundary),
KASSERT(dmar_test_boundary(entry->start + offset, buflen1,
tag->common.boundary),
("boundary failed: ctx %p start 0x%jx end 0x%jx "
"boundary 0x%jx", ctx, (uintmax_t)entry->start,
(uintmax_t)entry->end, (uintmax_t)tag->common.boundary));
KASSERT(buflen1 <= tag->common.maxsegsz,
("segment too large: ctx %p start 0x%jx end 0x%jx "
"maxsegsz 0x%jx", ctx, (uintmax_t)entry->start,
(uintmax_t)entry->end, (uintmax_t)tag->common.maxsegsz));
"buflen1 0x%jx maxsegsz 0x%jx", ctx,
(uintmax_t)entry->start, (uintmax_t)entry->end,
(uintmax_t)buflen1, (uintmax_t)tag->common.maxsegsz));
DMAR_CTX_LOCK(ctx);
TAILQ_INSERT_TAIL(&map->map_entries, entry, dmamap_link);
@ -541,6 +546,7 @@ dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
idx += OFF_TO_IDX(trunc_page(offset + buflen1));
offset += buflen1;
offset &= DMAR_PAGE_MASK;
buflen -= buflen1;
}
if (error == 0)
*segp = seg;

View File

@ -308,7 +308,7 @@ struct dmar_map_entry *dmar_gas_alloc_entry(struct dmar_ctx *ctx, u_int flags);
void dmar_gas_free_entry(struct dmar_ctx *ctx, struct dmar_map_entry *entry);
void dmar_gas_free_space(struct dmar_ctx *ctx, struct dmar_map_entry *entry);
int dmar_gas_map(struct dmar_ctx *ctx, const struct bus_dma_tag_common *common,
dmar_gaddr_t size, u_int eflags, u_int flags, vm_page_t *ma,
dmar_gaddr_t size, int offset, u_int eflags, u_int flags, vm_page_t *ma,
struct dmar_map_entry **res);
void dmar_gas_free_region(struct dmar_ctx *ctx, struct dmar_map_entry *entry);
int dmar_gas_map_region(struct dmar_ctx *ctx, struct dmar_map_entry *entry,

View File

@ -294,6 +294,7 @@ dmar_gas_fini_ctx(struct dmar_ctx *ctx)
struct dmar_gas_match_args {
struct dmar_ctx *ctx;
dmar_gaddr_t size;
int offset;
const struct bus_dma_tag_common *common;
u_int gas_flags;
struct dmar_map_entry *entry;
@ -310,25 +311,28 @@ dmar_gas_match_one(struct dmar_gas_match_args *a, struct dmar_map_entry *prev,
/* DMAR_PAGE_SIZE to create gap after new entry. */
if (a->entry->start < prev->end + DMAR_PAGE_SIZE ||
a->entry->start + a->size + DMAR_PAGE_SIZE > prev->end +
prev->free_after)
a->entry->start + a->size + a->offset + DMAR_PAGE_SIZE >
prev->end + prev->free_after)
return (false);
/* No boundary crossing. */
if (dmar_test_boundary(a->entry->start, a->size, a->common->boundary))
if (dmar_test_boundary(a->entry->start + a->offset, a->size,
a->common->boundary))
return (true);
/*
* The start to start + size region crosses the boundary.
* Check if there is enough space after the next boundary
* after the prev->end.
* The start + offset to start + offset + size region crosses
* the boundary. Check if there is enough space after the
* next boundary after the prev->end.
*/
bs = (a->entry->start + a->common->boundary) & ~(a->common->boundary
- 1);
bs = (a->entry->start + a->offset + a->common->boundary) &
~(a->common->boundary - 1);
start = roundup2(bs, a->common->alignment);
/* DMAR_PAGE_SIZE to create gap after new entry. */
if (start + a->size + DMAR_PAGE_SIZE <= prev->end + prev->free_after &&
start + a->size <= end && dmar_test_boundary(start, a->size,
if (start + a->offset + a->size + DMAR_PAGE_SIZE <=
prev->end + prev->free_after &&
start + a->offset + a->size <= end &&
dmar_test_boundary(start + a->offset, a->size,
a->common->boundary)) {
a->entry->start = start;
return (true);
@ -410,7 +414,7 @@ dmar_gas_lowermatch(struct dmar_gas_match_args *a, struct dmar_map_entry *prev)
return (0);
}
}
if (prev->free_down < a->size + DMAR_PAGE_SIZE)
if (prev->free_down < a->size + a->offset + DMAR_PAGE_SIZE)
return (ENOMEM);
l = RB_LEFT(prev, rb_entry);
if (l != NULL) {
@ -466,7 +470,7 @@ dmar_gas_uppermatch(struct dmar_gas_match_args *a)
static int
dmar_gas_find_space(struct dmar_ctx *ctx,
const struct bus_dma_tag_common *common, dmar_gaddr_t size,
u_int flags, struct dmar_map_entry *entry)
int offset, u_int flags, struct dmar_map_entry *entry)
{
struct dmar_gas_match_args a;
int error;
@ -477,6 +481,7 @@ dmar_gas_find_space(struct dmar_ctx *ctx,
a.ctx = ctx;
a.size = size;
a.offset = offset;
a.common = common;
a.gas_flags = flags;
a.entry = entry;
@ -618,7 +623,7 @@ dmar_gas_free_region(struct dmar_ctx *ctx, struct dmar_map_entry *entry)
int
dmar_gas_map(struct dmar_ctx *ctx, const struct bus_dma_tag_common *common,
dmar_gaddr_t size, u_int eflags, u_int flags, vm_page_t *ma,
dmar_gaddr_t size, int offset, u_int eflags, u_int flags, vm_page_t *ma,
struct dmar_map_entry **res)
{
struct dmar_map_entry *entry;
@ -632,7 +637,7 @@ dmar_gas_map(struct dmar_ctx *ctx, const struct bus_dma_tag_common *common,
if (entry == NULL)
return (ENOMEM);
DMAR_CTX_LOCK(ctx);
error = dmar_gas_find_space(ctx, common, size, flags, entry);
error = dmar_gas_find_space(ctx, common, size, offset, flags, entry);
if (error == ENOMEM) {
DMAR_CTX_UNLOCK(ctx);
dmar_gas_free_entry(ctx, entry);