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:
parent
6554d5b421
commit
5f01ba81ae
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user