Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 8 Apr 2015 01:55:23 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r281254 - head/sys/x86/iommu
Message-ID:  <201504080155.t381tNJg092915@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Wed Apr  8 01:55:22 2015
New Revision: 281254
URL: https://svnweb.freebsd.org/changeset/base/281254

Log:
  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

Modified:
  head/sys/x86/iommu/busdma_dmar.c
  head/sys/x86/iommu/intel_dmar.h
  head/sys/x86/iommu/intel_gas.c

Modified: head/sys/x86/iommu/busdma_dmar.c
==============================================================================
--- head/sys/x86/iommu/busdma_dmar.c	Wed Apr  8 01:43:29 2015	(r281253)
+++ head/sys/x86/iommu/busdma_dmar.c	Wed Apr  8 01:55:22 2015	(r281254)
@@ -462,6 +462,7 @@ dmar_bus_dmamap_load_something1(struct b
 	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 b
 		}
 		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 b
 		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 b
 			    (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 b
 		    (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 b
 		idx += OFF_TO_IDX(trunc_page(offset + buflen1));
 		offset += buflen1;
 		offset &= DMAR_PAGE_MASK;
+		buflen -= buflen1;
 	}
 	if (error == 0)
 		*segp = seg;

Modified: head/sys/x86/iommu/intel_dmar.h
==============================================================================
--- head/sys/x86/iommu/intel_dmar.h	Wed Apr  8 01:43:29 2015	(r281253)
+++ head/sys/x86/iommu/intel_dmar.h	Wed Apr  8 01:55:22 2015	(r281254)
@@ -308,7 +308,7 @@ struct dmar_map_entry *dmar_gas_alloc_en
 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,

Modified: head/sys/x86/iommu/intel_gas.c
==============================================================================
--- head/sys/x86/iommu/intel_gas.c	Wed Apr  8 01:43:29 2015	(r281253)
+++ head/sys/x86/iommu/intel_gas.c	Wed Apr  8 01:55:22 2015	(r281254)
@@ -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
 
 	/* 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_matc
 			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_matc
 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 *ct
 
 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
 	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);



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