[PATCH] libata: fix oops with sparsemem

STALE7205d

7 messages, 4 authors, 2006-12-11 · open the first message on its own page

[PATCH] libata: fix oops with sparsemem

From: Arnd Bergmann <hidden>
Date: 2006-12-08 18:14:50

libata incorrectly passes NULL arguments to sg_set_buf, which
crashes on powerpc64 when looking for the corresponding mem_section.

This introduces a new ata_exec_nodma() wrapper that takes no buffer
arguments and does not call sg_set_buf either. In order to make it
easier to detect this sort of problem, it also adds a WARN_ON(!buf)
to sg_set_buf() so we get a log message even platforms without
sparsemem.

Signed-off-by: Arnd Bergmann <redacted>

Index: linux-2.6/drivers/ata/libata-core.c
===================================================================
--- linux-2.6.orig/drivers/ata/libata-core.c
+++ linux-2.6/drivers/ata/libata-core.c
@@ -1332,7 +1332,7 @@ unsigned ata_exec_internal_sg(struct ata
 }
 
 /**
- *	ata_exec_internal_sg - execute libata internal command
+ *	ata_exec_internal - execute libata internal command
  *	@dev: Device to which the command is sent
  *	@tf: Taskfile registers for the command and the result
  *	@cdb: CDB for packet command
@@ -1361,6 +1361,25 @@ unsigned ata_exec_internal(struct ata_de
 }
 
 /**
+ *	ata_exec_nodma - execute libata internal command
+ *	@dev: Device to which the command is sent
+ *	@tf: Taskfile registers for the command and the result
+ *
+ *	Wrapper around ata_exec_internal_sg() which takes no
+ *	data buffer.
+ *
+ *	LOCKING:
+ *	None.  Should be called with kernel context, might sleep.
+ *
+ *	RETURNS:
+ *	Zero on success, AC_ERR_* mask on failure
+ */
+static unsigned ata_exec_nodma(struct ata_device *dev, struct ata_taskfile *tf)
+{
+	return ata_exec_internal_sg(dev, tf, NULL, DMA_NONE, NULL, 0);
+}
+
+/**
  *	ata_do_simple_cmd - execute simple internal command
  *	@dev: Device to which the command is sent
  *	@cmd: Opcode to execute
@@ -1384,7 +1403,7 @@ unsigned int ata_do_simple_cmd(struct at
 	tf.flags |= ATA_TFLAG_DEVICE;
 	tf.protocol = ATA_PROT_NODATA;
 
-	return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	return ata_exec_nodma(dev, &tf);
 }
 
 /**
@@ -3475,7 +3494,7 @@ static unsigned int ata_dev_set_xfermode
 	tf.protocol = ATA_PROT_NODATA;
 	tf.nsect = dev->xfer_mode;
 
-	err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	err_mask = ata_exec_nodma(dev, &tf);
 
 	DPRINTK("EXIT, err_mask=%x\n", err_mask);
 	return err_mask;
@@ -3513,7 +3532,7 @@ static unsigned int ata_dev_init_params(
 	tf.nsect = sectors;
 	tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
 
-	err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+	err_mask = ata_exec_nodma(dev, &tf);
 
 	DPRINTK("EXIT, err_mask=%x\n", err_mask);
 	return err_mask;
Index: linux-2.6/include/linux/scatterlist.h
===================================================================
--- linux-2.6.orig/include/linux/scatterlist.h
+++ linux-2.6/include/linux/scatterlist.h
@@ -8,6 +8,8 @@
 static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
 			      unsigned int buflen)
 {
+	WARN_ON(!buf); /* virt_to_page(NULL) crashes with sparsemem */
+
 	sg->page = virt_to_page(buf);
 	sg->offset = offset_in_page(buf);
 	sg->length = buflen;

[PATCH] libata: don't initialize sg in ata_exec_internal() if DMA_NONE

From: Tejun Heo <hidden>
Date: 2006-12-11 14:03:07

Calling sg_init_one() with NULL buf causes oops on certain
configurations.  Don't initialize sg in ata_exec_internal() if
DMA_NONE and make the function complain if @buf is NULL when dma_dir
isn't DMA_NONE.  While at it, fix comment.

The problem is discovered and initial patch was submitted by Arnd
Bergmann.

Signed-off-by: Tejun Heo <redacted>
Cc: Arnd Bergmann <redacted>
---

Hello, Arnd Bergmann.

Thanks for spotting and fixing this but ata_exec_internal_nodma() is
almost identical to ata_do_simple_cmd() and ata_exec_internal() itself
needs fixing anyway.  This patch just fixes ata_exec_internal().  I'll
follow up with conversion to ata_do_simple_cmd().

Thanks.
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 011c0a8..70e02e9 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1332,7 +1332,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 }
 
 /**
- *	ata_exec_internal_sg - execute libata internal command
+ *	ata_exec_internal - execute libata internal command
  *	@dev: Device to which the command is sent
  *	@tf: Taskfile registers for the command and the result
  *	@cdb: CDB for packet command
@@ -1354,10 +1354,15 @@ unsigned ata_exec_internal(struct ata_device *dev,
 			   int dma_dir, void *buf, unsigned int buflen)
 {
 	struct scatterlist sg;
+	unsigned int n_elem = 0;
 
-	sg_init_one(&sg, buf, buflen);
+	if (dma_dir != DMA_NONE) {
+		WARN_ON(!buf);
+		sg_init_one(&sg, buf, buflen);
+		n_elem++;
+	}
 
-	return ata_exec_internal_sg(dev, tf, cdb, dma_dir, &sg, 1);
+	return ata_exec_internal_sg(dev, tf, cdb, dma_dir, &sg, n_elem);
 }
 
 /**

Re: [PATCH] libata: don't initialize sg in ata_exec_internal() if DMA_NONE

From: Arnd Bergmann <arnd@arndb.de>
Date: 2006-12-11 14:18:55

T24gTW9uZGF5IDExIERlY2VtYmVyIDIwMDYgMTU6MDIsIFRlanVuIEhlbyB3cm90ZToKPiCgewo+
IKCgoKCgoKCgc3RydWN0IHNjYXR0ZXJsaXN0IHNnOwo+ICugoKCgoKCgdW5zaWduZWQgaW50IG5f
ZWxlbSA9IDA7Cj4goAo+IC2goKCgoKCgc2dfaW5pdF9vbmUoJnNnLCBidWYsIGJ1Zmxlbik7Cj4g
K6CgoKCgoKBpZiAoZG1hX2RpciAhPSBETUFfTk9ORSkgewo+ICugoKCgoKCgoKCgoKCgoKBXQVJO
X09OKCFidWYpOwo+ICugoKCgoKCgoKCgoKCgoKBzZ19pbml0X29uZSgmc2csIGJ1ZiwgYnVmbGVu
KTsKPiAroKCgoKCgoKCgoKCgoKCgbl9lbGVtKys7Cj4gK6CgoKCgoKB9Cj4goApPaywgbG9va3Mg
Z29vZCBhcyB3ZWxsLiBJIHN0aWxsIHRoaW5rIHdlIHNob3VsZCBoYXZlIHRoZSBXQVJOX09OKCkK
aW4gc2dfc2V0X2J1ZigpLCBidXQgSSBjYW4gc2VuZCBhIHNlcGFyYXRlIHBhdGNoIGZvciB0aGF0
IHRvIGxpbnV4LW1tLgoKCUFybmQgPD48Cg==

Re: [PATCH] libata: don't initialize sg in ata_exec_internal() if DMA_NONE

From: Tejun Heo <hidden>
Date: 2006-12-11 14:23:46

Tejun Heo wrote:
I'll follow up with conversion to ata_do_simple_cmd().
The current situation is...

ata_exec_internal_sg()	: no user except for ata_exec_internal() yet
ata_exec_internal()	: one data transferring user. other are non-data
ata_do_simple_cmd()	: three users

So, adding another exec_internal variant doesn't look so hot.  It seems
we already have enough variants considering the small number of users.
I think it's best to leave it alone for now.

Thanks.

-- 
tejun

Re: [PATCH] libata: don't initialize sg in ata_exec_internal() if DMA_NONE

From: Jeff Garzik <hidden>
Date: 2006-12-11 14:34:06

Arnd Bergmann wrote:
On Monday 11 December 2006 15:02, Tejun Heo wrote:
quoted
 {
        struct scatterlist sg;
+       unsigned int n_elem = 0;
 
-       sg_init_one(&sg, buf, buflen);
+       if (dma_dir != DMA_NONE) {
+               WARN_ON(!buf);
+               sg_init_one(&sg, buf, buflen);
+               n_elem++;
+       }
 
Ok, looks good as well. I still think we should have the WARN_ON()
in sg_set_buf(), but I can send a separate patch for that to linux-mm.
Please CC me and linux-ide on all libata patches (certainly akpm as 
well).  Andrew picks up most of the libata changes automatically via git 
from my libata-dev.git#ALL.

	Jeff

Re: [PATCH] libata: don't initialize sg in ata_exec_internal() if DMA_NONE

From: Jeff Garzik <hidden>
Date: 2006-12-11 15:55:19

Tejun Heo wrote:
quoted hunk
Calling sg_init_one() with NULL buf causes oops on certain
configurations.  Don't initialize sg in ata_exec_internal() if
DMA_NONE and make the function complain if @buf is NULL when dma_dir
isn't DMA_NONE.  While at it, fix comment.

The problem is discovered and initial patch was submitted by Arnd
Bergmann.

Signed-off-by: Tejun Heo <redacted>
Cc: Arnd Bergmann <redacted>
---

Hello, Arnd Bergmann.

Thanks for spotting and fixing this but ata_exec_internal_nodma() is
almost identical to ata_do_simple_cmd() and ata_exec_internal() itself
needs fixing anyway.  This patch just fixes ata_exec_internal().  I'll
follow up with conversion to ata_do_simple_cmd().

Thanks.
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 011c0a8..70e02e9 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1332,7 +1332,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 }
 
 /**
- *	ata_exec_internal_sg - execute libata internal command
+ *	ata_exec_internal - execute libata internal command
  *	@dev: Device to which the command is sent
  *	@tf: Taskfile registers for the command and the result
  *	@cdb: CDB for packet command
@@ -1354,10 +1354,15 @@ unsigned ata_exec_internal(struct ata_device *dev,
 			   int dma_dir, void *buf, unsigned int buflen)
 {
 	struct scatterlist sg;
+	unsigned int n_elem = 0;
 
-	sg_init_one(&sg, buf, buflen);
+	if (dma_dir != DMA_NONE) {
+		WARN_ON(!buf);
+		sg_init_one(&sg, buf, buflen);
+		n_elem++;
+	}
 
-	return ata_exec_internal_sg(dev, tf, cdb, dma_dir, &sg, 1);
+	return ata_exec_internal_sg(dev, tf, cdb, dma_dir, &sg, n_elem);
ACK, if you conditionally replace "&sg" with NULL.  That's the safer 
choice, as it guarantees (via an oops) that the user will not be 
touching sg, if dma_dir==DMA_NONE.

	Jeff

[PATCH] libata: don't initialize sg in ata_exec_internal() if DMA_NONE (take #2)

From: Tejun Heo <hidden>
Date: 2006-12-11 17:15:41

Calling sg_init_one() with NULL buf causes oops on certain
configurations.  Don't initialize sg in ata_exec_internal() if
DMA_NONE and make the function complain if @buf is NULL when dma_dir
isn't DMA_NONE.  While at it, fix comment.

The problem is discovered and initial patch was submitted by Arnd
Bergmann.

Signed-off-by: Tejun Heo <redacted>
Cc: Arnd Bergmann <redacted>
---

Modified as suggested.

Thanks.
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 011c0a8..0d51d13 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1332,7 +1332,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 }
 
 /**
- *	ata_exec_internal_sg - execute libata internal command
+ *	ata_exec_internal - execute libata internal command
  *	@dev: Device to which the command is sent
  *	@tf: Taskfile registers for the command and the result
  *	@cdb: CDB for packet command
@@ -1353,11 +1353,17 @@ unsigned ata_exec_internal(struct ata_device *dev,
 			   struct ata_taskfile *tf, const u8 *cdb,
 			   int dma_dir, void *buf, unsigned int buflen)
 {
-	struct scatterlist sg;
+	struct scatterlist *psg = NULL, sg;
+	unsigned int n_elem = 0;
 
-	sg_init_one(&sg, buf, buflen);
+	if (dma_dir != DMA_NONE) {
+		WARN_ON(!buf);
+		sg_init_one(&sg, buf, buflen);
+		psg = &sg;
+		n_elem++;
+	}
 
-	return ata_exec_internal_sg(dev, tf, cdb, dma_dir, &sg, 1);
+	return ata_exec_internal_sg(dev, tf, cdb, dma_dir, psg, n_elem);
 }
 
 /**
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help