From owner-svn-src-head@FreeBSD.ORG Wed Apr 8 01:55:24 2015 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 62E0B10F; Wed, 8 Apr 2015 01:55:24 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 43882AD; Wed, 8 Apr 2015 01:55:24 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t381tOu8092920; Wed, 8 Apr 2015 01:55:24 GMT (envelope-from kib@FreeBSD.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t381tNJg092915; Wed, 8 Apr 2015 01:55:23 GMT (envelope-from kib@FreeBSD.org) Message-Id: <201504080155.t381tNJg092915@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: kib set sender to kib@FreeBSD.org using -f From: Konstantin Belousov Date: Wed, 8 Apr 2015 01:55:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r281254 - head/sys/x86/iommu X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Apr 2015 01:55:24 -0000 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);