From james@jrv.org  Wed Aug 26 22:59:47 2009
Return-Path: <james@jrv.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 62EC7106568B
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 26 Aug 2009 22:59:47 +0000 (UTC)
	(envelope-from james@jrv.org)
Received: from mail.jrv.org (rrcs-24-73-246-106.sw.biz.rr.com [24.73.246.106])
	by mx1.freebsd.org (Postfix) with ESMTP id 0B7D48FC21
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 26 Aug 2009 22:59:46 +0000 (UTC)
Received: from bigtex.housenet.jrv (localhost [127.0.0.1])
	by mail.jrv.org (8.14.3/8.14.3) with ESMTP id n7QMdeOB045329
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 26 Aug 2009 17:39:40 -0500 (CDT)
	(envelope-from james@bigtex.housenet.jrv)
Received: (from root@localhost)
	by bigtex.housenet.jrv (8.14.3/8.14.3/Submit) id n7QMdeHV045328;
	Wed, 26 Aug 2009 17:39:40 -0500 (CDT)
	(envelope-from james)
Message-Id: <200908262239.n7QMdeHV045328@bigtex.housenet.jrv>
Date: Wed, 26 Aug 2009 17:39:40 -0500 (CDT)
From: "James R. Van Artsdalen" <james-freebsd-current@jrv.org>
Reply-To: "James R. Van Artsdalen" <james-freebsd-current@jrv.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [patch] FreeBSD/amd64 can't see all system memory
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         138220
>Category:       amd64
>Synopsis:       [amd64] [patch] FreeBSD/amd64 can't see all system memory
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-amd64
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Aug 26 23:00:08 UTC 2009
>Closed-Date:    Fri Sep 25 15:10:08 UTC 2009
>Last-Modified:  Fri Sep 25 15:10:08 UTC 2009
>Originator:     James R. Van Artsdalen
>Release:        FreeBSD 9.0-CURRENT amd64
>Organization:
>Environment:
System: FreeBSD pygmy.housenet.jrv 9.0-CURRENT FreeBSD 9.0-CURRENT #1 r196500M: Wed Aug 26 11:28:43 CDT 2009 james@pygmy.housenet.jrv:/usr/src/sys/amd64/compile/GENERIC amd64


>Description:

Two related bugs:

1. FreeBSD erroneously assumes that the BIOS E820 system memory map data
is non-descending.  The Zotac GF9300-D-E is an example of a system where
this is not true.

2. There is a typo in code that detects overlaps in regions reported by
E820.  No action is in fact taken right now on amd64.

i386 may have bug #1 but not #2.

With this patch "available memory" goes from 2689 MB to 7605 MB on the
Zotac GF9300-D-E.



>How-To-Repeat:
Boot amd64 on Zotac GF9300-D-E motherboard with 8GB of RAM.  Less than
3GB is reported.

>Fix:

No user fix.

The patch sorts smap enteries on the base address and ignores overlapping
regions.

--- smap.pat begins here ---
Index: sys/amd64/amd64/machdep.c
===================================================================
--- sys/amd64/amd64/machdep.c	(revision 196500)
+++ sys/amd64/amd64/machdep.c	(working copy)
@@ -1236,6 +1236,19 @@
 	smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize);
 
 	for (smap = smapbase; smap < smapend; smap++) {
+		struct bios_smap *sp, *low = smap;
+
+		for (sp = smap + 1; sp < smapend; sp++)
+			if (low->base > sp->base)
+				low = sp;
+		if (low != smap) {
+			struct bios_smap ts;
+
+			ts = *smap;
+			*smap = *low;
+			*low = ts;
+		}
+
 		if (boothowto & RB_VERBOSE)
 			printf("SMAP type=%02x base=%016lx len=%016lx\n",
 			    smap->type, smap->base, smap->length);
@@ -1250,10 +1263,12 @@
 			if (smap->base < physmap[i + 1]) {
 				if (boothowto & RB_VERBOSE)
 					printf(
-	"Overlapping or non-monotonic memory region, ignoring second region\n");
-				continue;
+	"Overlapping memory region, ignoring second region\n");
+				break;
 			}
 		}
+		if (i <= physmap_idx)
+		  continue;
 
 		if (smap->base == physmap[physmap_idx + 1]) {
 			physmap[physmap_idx + 1] += smap->length;
--- smap.pat ends here ---


>Release-Note:
>Audit-Trail:

From: John Baldwin <jhb@freebsd.org>
To: freebsd-amd64@freebsd.org,
 "James R. Van Artsdalen" <james-freebsd-current@jrv.org>
Cc: FreeBSD-gnats-submit@freebsd.org
Subject: Re: amd64/138220: [patch] FreeBSD/amd64 can't see all system memory
Date: Mon, 14 Sep 2009 17:35:04 -0400

 Can you try this alternate patch instead?  I am leery of manipulating the SMAP
 directly since it resides in the kernel's module metadata which in theory
 should be read-only.  This patch changes the SMAP code to do an insertion sort
 into the physmap[] array for each new entry that is added.  I also changed the
 amd64 code to use a separate static routine to add each entry similar to i386
 which fixes the bug with overlapping regions that you noted while keeping the
 SMAP code identical between the two archs.
 
 --- //depot/vendor/freebsd/src/sys/amd64/amd64/machdep.c	2009/08/20 23:00:20
 +++ //depot/user/jhb/numa/sys/amd64/amd64/machdep.c	2009/09/14 19:37:10
 @@ -1192,6 +1192,77 @@
  
  u_int basemem;
  
 +static int
 +add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp)
 +{
 +	int i, insert_idx, physmap_idx;
 +
 +	physmap_idx = *physmap_idxp;
 +
 +	if (boothowto & RB_VERBOSE)
 +		printf("SMAP type=%02x base=%016lx len=%016lx\n",
 +		    smap->type, smap->base, smap->length);
 +
 +	if (smap->type != SMAP_TYPE_MEMORY)
 +		return (1);
 +
 +	if (smap->length == 0)
 +		return (0);
 +
 +	/*
 +	 * Find insertion point while checking for overlap.  Start off by
 +	 * assuming the new entry will be added to the end.
 +	 */
 +	insert_idx = physmap_idx + 2;
 +	for (i = 0; i <= physmap_idx; i += 2) {
 +		if (smap->base < physmap[i + 1]) {
 +			if (smap->base + smap->length <= physmap[i]) {
 +				insert_idx = i;
 +				break;
 +			}
 +			if (boothowto & RB_VERBOSE)
 +				printf(
 +	"Overlapping or non-monotonic memory region, ignoring second region\n");
 +			return (1);
 +		}
 +	}
 +
 +	/* See if we can prepend to the next entry. */
 +	if (insert_idx <= physmap_idx &&
 +	    smap->base + smap->length == physmap[insert_idx]) {
 +		physmap[insert_idx] = smap->base;
 +		return (1);
 +	}
 +
 +	/* See if we can append to the previous entry. */
 +	if (insert_idx > 0 && smap->base == physmap[insert_idx - 1]) {
 +		physmap[insert_idx - 1] += smap->length;
 +		return (1);
 +	}
 +
 +	physmap_idx += 2;
 +	*physmap_idxp = physmap_idx;
 +	if (physmap_idx == PHYSMAP_SIZE) {
 +		printf(
 +		"Too many segments in the physical address map, giving up\n");
 +		return (0);
 +	}
 +
 +	/*
 +	 * Move the last 'N' entries down to make room for the new
 +	 * entry if needed.
 +	 */
 +	for (i = physmap_idx - 2; i > insert_idx; i -= 2) {
 +		physmap[i] = physmap[i - 2];
 +		physmap[i + 1] = physmap[i - 1];
 +	}
 +
 +	/* Insert the new entry. */
 +	physmap[insert_idx] = smap->base;
 +	physmap[insert_idx + 1] = smap->base + smap->length;
 +	return (1);
 +}
 +
  /*
   * Populate the (physmap) array with base/bound pairs describing the
   * available physical memory in the system, then test this memory and
 @@ -1235,40 +1306,9 @@
  	smapsize = *((u_int32_t *)smapbase - 1);
  	smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize);
  
 -	for (smap = smapbase; smap < smapend; smap++) {
 -		if (boothowto & RB_VERBOSE)
 -			printf("SMAP type=%02x base=%016lx len=%016lx\n",
 -			    smap->type, smap->base, smap->length);
 -
 -		if (smap->type != SMAP_TYPE_MEMORY)
 -			continue;
 -
 -		if (smap->length == 0)
 -			continue;
 -
 -		for (i = 0; i <= physmap_idx; i += 2) {
 -			if (smap->base < physmap[i + 1]) {
 -				if (boothowto & RB_VERBOSE)
 -					printf(
 -	"Overlapping or non-monotonic memory region, ignoring second region\n");
 -				continue;
 -			}
 -		}
 -
 -		if (smap->base == physmap[physmap_idx + 1]) {
 -			physmap[physmap_idx + 1] += smap->length;
 -			continue;
 -		}
 -
 -		physmap_idx += 2;
 -		if (physmap_idx == PHYSMAP_SIZE) {
 -			printf(
 -		"Too many segments in the physical address map, giving up\n");
 +	for (smap = smapbase; smap < smapend; smap++)
 +		if (!add_smap_entry(smap, physmap, &physmap_idx))
  			break;
 -		}
 -		physmap[physmap_idx] = smap->base;
 -		physmap[physmap_idx + 1] = smap->base + smap->length;
 -	}
  
  	/*
  	 * Find the 'base memory' segment for SMP
 --- //depot/vendor/freebsd/src/sys/i386/i386/machdep.c	2009/09/04 14:55:14
 +++ //depot/user/jhb/numa/sys/i386/i386/machdep.c	2009/09/14 19:37:10
 @@ -1946,7 +1946,7 @@
  static int
  add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp)
  {
 -	int i, physmap_idx;
 +	int i, insert_idx, physmap_idx;
  
  	physmap_idx = *physmap_idxp;
  	
 @@ -1968,8 +1968,17 @@
  	}
  #endif
  
 +	/*
 +	 * Find insertion point while checking for overlap.  Start off by
 +	 * assuming the new entry will be added to the end.
 +	 */
 +	insert_idx = physmap_idx + 2;
  	for (i = 0; i <= physmap_idx; i += 2) {
  		if (smap->base < physmap[i + 1]) {
 +			if (smap->base + smap->length <= physmap[i]) {
 +				insert_idx = i;
 +				break;
 +			}
  			if (boothowto & RB_VERBOSE)
  				printf(
  	"Overlapping or non-monotonic memory region, ignoring second region\n");
 @@ -1977,8 +1986,16 @@
  		}
  	}
  
 -	if (smap->base == physmap[physmap_idx + 1]) {
 -		physmap[physmap_idx + 1] += smap->length;
 +	/* See if we can prepend to the next entry. */
 +	if (insert_idx <= physmap_idx &&
 +	    smap->base + smap->length == physmap[insert_idx]) {
 +		physmap[insert_idx] = smap->base;
 +		return (1);
 +	}
 +
 +	/* See if we can append to the previous entry. */
 +	if (insert_idx > 0 && smap->base == physmap[insert_idx - 1]) {
 +		physmap[insert_idx - 1] += smap->length;
  		return (1);
  	}
  
 @@ -1989,8 +2006,19 @@
  		"Too many segments in the physical address map, giving up\n");
  		return (0);
  	}
 -	physmap[physmap_idx] = smap->base;
 -	physmap[physmap_idx + 1] = smap->base + smap->length;
 +
 +	/*
 +	 * Move the last 'N' entries down to make room for the new
 +	 * entry if needed.
 +	 */
 +	for (i = physmap_idx - 2; i > insert_idx; i -= 2) {
 +		physmap[i] = physmap[i - 2];
 +		physmap[i + 1] = physmap[i - 1];
 +	}
 +
 +	/* Insert the new entry. */
 +	physmap[insert_idx] = smap->base;
 +	physmap[insert_idx + 1] = smap->base + smap->length;
  	return (1);
  }
  
 
 
 -- 
 John Baldwin

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: amd64/138220: commit references a PR
Date: Tue, 22 Sep 2009 16:51:14 +0000 (UTC)

 Author: jhb
 Date: Tue Sep 22 16:51:00 2009
 New Revision: 197410
 URL: http://svn.freebsd.org/changeset/base/197410
 
 Log:
   - Split the logic to parse an SMAP entry out into a separate function on
     amd64 similar to i386.  This fixes a bug on amd64 where overlapping
     entries would not cause the SMAP parsing to stop.
   - Change the SMAP parsing code to do a sorted insertion into physmap[]
     instead of an append to support systems with out-of-order SMAP entries.
   
   PR:		amd64/138220
   Reported by:	James R. Van Artsdalen  james of jrv org
   MFC after:	3 days
 
 Modified:
   head/sys/amd64/amd64/machdep.c
   head/sys/i386/i386/machdep.c
 
 Modified: head/sys/amd64/amd64/machdep.c
 ==============================================================================
 --- head/sys/amd64/amd64/machdep.c	Tue Sep 22 16:28:07 2009	(r197409)
 +++ head/sys/amd64/amd64/machdep.c	Tue Sep 22 16:51:00 2009	(r197410)
 @@ -1192,6 +1192,77 @@ isa_irq_pending(void)
  
  u_int basemem;
  
 +static int
 +add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp)
 +{
 +	int i, insert_idx, physmap_idx;
 +
 +	physmap_idx = *physmap_idxp;
 +
 +	if (boothowto & RB_VERBOSE)
 +		printf("SMAP type=%02x base=%016lx len=%016lx\n",
 +		    smap->type, smap->base, smap->length);
 +
 +	if (smap->type != SMAP_TYPE_MEMORY)
 +		return (1);
 +
 +	if (smap->length == 0)
 +		return (0);
 +
 +	/*
 +	 * Find insertion point while checking for overlap.  Start off by
 +	 * assuming the new entry will be added to the end.
 +	 */
 +	insert_idx = physmap_idx + 2;
 +	for (i = 0; i <= physmap_idx; i += 2) {
 +		if (smap->base < physmap[i + 1]) {
 +			if (smap->base + smap->length <= physmap[i]) {
 +				insert_idx = i;
 +				break;
 +			}
 +			if (boothowto & RB_VERBOSE)
 +				printf(
 +		    "Overlapping memory regions, ignoring second region\n");
 +			return (1);
 +		}
 +	}
 +
 +	/* See if we can prepend to the next entry. */
 +	if (insert_idx <= physmap_idx &&
 +	    smap->base + smap->length == physmap[insert_idx]) {
 +		physmap[insert_idx] = smap->base;
 +		return (1);
 +	}
 +
 +	/* See if we can append to the previous entry. */
 +	if (insert_idx > 0 && smap->base == physmap[insert_idx - 1]) {
 +		physmap[insert_idx - 1] += smap->length;
 +		return (1);
 +	}
 +
 +	physmap_idx += 2;
 +	*physmap_idxp = physmap_idx;
 +	if (physmap_idx == PHYSMAP_SIZE) {
 +		printf(
 +		"Too many segments in the physical address map, giving up\n");
 +		return (0);
 +	}
 +
 +	/*
 +	 * Move the last 'N' entries down to make room for the new
 +	 * entry if needed.
 +	 */
 +	for (i = physmap_idx; i > insert_idx; i -= 2) {
 +		physmap[i] = physmap[i - 2];
 +		physmap[i + 1] = physmap[i - 1];
 +	}
 +
 +	/* Insert the new entry. */
 +	physmap[insert_idx] = smap->base;
 +	physmap[insert_idx + 1] = smap->base + smap->length;
 +	return (1);
 +}
 +
  /*
   * Populate the (physmap) array with base/bound pairs describing the
   * available physical memory in the system, then test this memory and
 @@ -1235,40 +1306,9 @@ getmemsize(caddr_t kmdp, u_int64_t first
  	smapsize = *((u_int32_t *)smapbase - 1);
  	smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize);
  
 -	for (smap = smapbase; smap < smapend; smap++) {
 -		if (boothowto & RB_VERBOSE)
 -			printf("SMAP type=%02x base=%016lx len=%016lx\n",
 -			    smap->type, smap->base, smap->length);
 -
 -		if (smap->type != SMAP_TYPE_MEMORY)
 -			continue;
 -
 -		if (smap->length == 0)
 -			continue;
 -
 -		for (i = 0; i <= physmap_idx; i += 2) {
 -			if (smap->base < physmap[i + 1]) {
 -				if (boothowto & RB_VERBOSE)
 -					printf(
 -	"Overlapping or non-monotonic memory region, ignoring second region\n");
 -				continue;
 -			}
 -		}
 -
 -		if (smap->base == physmap[physmap_idx + 1]) {
 -			physmap[physmap_idx + 1] += smap->length;
 -			continue;
 -		}
 -
 -		physmap_idx += 2;
 -		if (physmap_idx == PHYSMAP_SIZE) {
 -			printf(
 -		"Too many segments in the physical address map, giving up\n");
 +	for (smap = smapbase; smap < smapend; smap++)
 +		if (!add_smap_entry(smap, physmap, &physmap_idx))
  			break;
 -		}
 -		physmap[physmap_idx] = smap->base;
 -		physmap[physmap_idx + 1] = smap->base + smap->length;
 -	}
  
  	/*
  	 * Find the 'base memory' segment for SMP
 
 Modified: head/sys/i386/i386/machdep.c
 ==============================================================================
 --- head/sys/i386/i386/machdep.c	Tue Sep 22 16:28:07 2009	(r197409)
 +++ head/sys/i386/i386/machdep.c	Tue Sep 22 16:51:00 2009	(r197410)
 @@ -1946,7 +1946,7 @@ sdtossd(sd, ssd)
  static int
  add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp)
  {
 -	int i, physmap_idx;
 +	int i, insert_idx, physmap_idx;
  
  	physmap_idx = *physmap_idxp;
  	
 @@ -1968,17 +1968,34 @@ add_smap_entry(struct bios_smap *smap, v
  	}
  #endif
  
 +	/*
 +	 * Find insertion point while checking for overlap.  Start off by
 +	 * assuming the new entry will be added to the end.
 +	 */
 +	insert_idx = physmap_idx + 2;
  	for (i = 0; i <= physmap_idx; i += 2) {
  		if (smap->base < physmap[i + 1]) {
 +			if (smap->base + smap->length <= physmap[i]) {
 +				insert_idx = i;
 +				break;
 +			}
  			if (boothowto & RB_VERBOSE)
  				printf(
 -	"Overlapping or non-monotonic memory region, ignoring second region\n");
 +		    "Overlapping memory regions, ignoring second region\n");
  			return (1);
  		}
  	}
  
 -	if (smap->base == physmap[physmap_idx + 1]) {
 -		physmap[physmap_idx + 1] += smap->length;
 +	/* See if we can prepend to the next entry. */
 +	if (insert_idx <= physmap_idx &&
 +	    smap->base + smap->length == physmap[insert_idx]) {
 +		physmap[insert_idx] = smap->base;
 +		return (1);
 +	}
 +
 +	/* See if we can append to the previous entry. */
 +	if (insert_idx > 0 && smap->base == physmap[insert_idx - 1]) {
 +		physmap[insert_idx - 1] += smap->length;
  		return (1);
  	}
  
 @@ -1989,8 +2006,19 @@ add_smap_entry(struct bios_smap *smap, v
  		"Too many segments in the physical address map, giving up\n");
  		return (0);
  	}
 -	physmap[physmap_idx] = smap->base;
 -	physmap[physmap_idx + 1] = smap->base + smap->length;
 +
 +	/*
 +	 * Move the last 'N' entries down to make room for the new
 +	 * entry if needed.
 +	 */
 +	for (i = physmap_idx; i > insert_idx; i -= 2) {
 +		physmap[i] = physmap[i - 2];
 +		physmap[i + 1] = physmap[i - 1];
 +	}
 +
 +	/* Insert the new entry. */
 +	physmap[insert_idx] = smap->base;
 +	physmap[insert_idx + 1] = smap->base + smap->length;
  	return (1);
  }
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: jhb 
State-Changed-When: Fri Sep 25 15:09:08 UTC 2009 
State-Changed-Why:  
Fixed in HEAD, 8, and 7. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=138220 
>Unformatted:
