svn commit: r258387 - head/sys/dev/nand

Grzegorz Bernacki gber at FreeBSD.org
Wed Nov 20 11:10:24 UTC 2013


Author: gber
Date: Wed Nov 20 11:10:23 2013
New Revision: 258387
URL: http://svnweb.freebsd.org/changeset/base/258387

Log:
  Split raw reading/programming into smaller chunks to avoid allocating too
  big chunk of kernel memory. Validate size of data. Add error handling to
  avoid calling copyout() when data has not been read correctly.
  
  Reviewed by:    zbb
  Reported by:    x90c <geinblues at gmail.com>
  MFC after:      2 days

Modified:
  head/sys/dev/nand/nand_cdev.c
  head/sys/dev/nand/nand_geom.c

Modified: head/sys/dev/nand/nand_cdev.c
==============================================================================
--- head/sys/dev/nand/nand_cdev.c	Wed Nov 20 11:09:12 2013	(r258386)
+++ head/sys/dev/nand/nand_cdev.c	Wed Nov 20 11:10:23 2013	(r258387)
@@ -294,19 +294,39 @@ nand_ioctl(struct cdev *dev, u_long cmd,
     struct thread *td)
 {
 	struct nand_chip *chip;
+	struct chip_geom  *cg;
 	struct nand_oob_rw *oob_rw = NULL;
 	struct nand_raw_rw *raw_rw = NULL;
 	device_t nandbus;
+	size_t bufsize, len, raw_size;
+	off_t off;
 	uint8_t *buf = NULL;
 	int ret = 0;
 	uint8_t status;
 
 	chip = (struct nand_chip *)dev->si_drv1;
+	cg = &chip->chip_geom;
 	nandbus = device_get_parent(chip->dev);
 
 	if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
 		raw_rw = (struct nand_raw_rw *)data;
-		buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+		raw_size =  cg->pgs_per_blk * (cg->page_size + cg->oob_size);
+
+		/* Check if len is not bigger than chip size */
+		if (raw_rw->len > raw_size)
+			return (EFBIG);
+
+		/*
+		 * Do not ask for too much memory, in case of large transfers
+		 * read/write in 16-pages chunks
+		 */
+		bufsize = 16 * (cg->page_size + cg->oob_size);
+		if (raw_rw->len < bufsize)
+			bufsize = raw_rw->len;
+
+		buf = malloc(bufsize, M_NAND, M_WAITOK);
+		len = raw_rw->len;
+		off = 0;
 	}
 	switch(cmd) {
 	case NAND_IO_ERASE:
@@ -335,19 +355,37 @@ nand_ioctl(struct cdev *dev, u_long cmd,
 		break;
 
 	case NAND_IO_RAW_PROG:
-		ret = copyin(raw_rw->data, buf, raw_rw->len);
-		if (ret)
-			break;
-		ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
-		    raw_rw->len);
+		while (len > 0) {
+			if (len < bufsize)
+				bufsize = len;
+			ret = copyin(raw_rw->data + off, buf, bufsize);
+			if (ret)
+				break;
+			ret = nand_prog_pages_raw(chip, raw_rw->off + off, buf,
+			    bufsize);
+			if (ret)
+				break;
+			len -= bufsize;
+			off += bufsize;
+		}
 		break;
 
 	case NAND_IO_RAW_READ:
-		ret = nand_read_pages_raw(chip, raw_rw->off, buf,
-		    raw_rw->len);
-		if (ret)
-			break;
-		ret = copyout(buf, raw_rw->data, raw_rw->len);
+		while (len > 0) {
+			if (len < bufsize)
+				bufsize = len;
+
+			ret = nand_read_pages_raw(chip, raw_rw->off + off, buf,
+			    bufsize);
+			if (ret)
+				break;
+
+			ret = copyout(buf, raw_rw->data + off, bufsize);
+			if (ret)
+				break;
+			len -= bufsize;
+			off += bufsize;
+		}
 		break;
 
 	case NAND_IO_PAGE_STAT:

Modified: head/sys/dev/nand/nand_geom.c
==============================================================================
--- head/sys/dev/nand/nand_geom.c	Wed Nov 20 11:09:12 2013	(r258386)
+++ head/sys/dev/nand/nand_geom.c	Wed Nov 20 11:10:23 2013	(r258387)
@@ -193,20 +193,41 @@ nand_ioctl(struct disk *ndisk, u_long cm
     struct thread *td)
 {
 	struct nand_chip *chip;
+	struct chip_geom  *cg;
 	struct nand_oob_rw *oob_rw = NULL;
 	struct nand_raw_rw *raw_rw = NULL;
 	device_t nandbus;
+	size_t bufsize, len, raw_size;
+	off_t off;
 	uint8_t *buf = NULL;
 	int ret = 0;
 	uint8_t status;
 
 	chip = (struct nand_chip *)ndisk->d_drv1;
+	cg = &chip->chip_geom;
 	nandbus = device_get_parent(chip->dev);
 
 	if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
 		raw_rw = (struct nand_raw_rw *)data;
-		buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+		raw_size =  cg->pgs_per_blk * (cg->page_size + cg->oob_size);
+
+		/* Check if len is not bigger than chip size */
+		if (raw_rw->len > raw_size)
+			return (EFBIG);
+
+		/*
+		 * Do not ask for too much memory, in case of large transfers
+		 * read/write in 16-pages chunks
+		 */
+		bufsize = 16 * (cg->page_size + cg->oob_size);
+		if (raw_rw->len < bufsize)
+			bufsize = raw_rw->len;
+
+		buf = malloc(bufsize, M_NAND, M_WAITOK);
+		len = raw_rw->len;
+		off = 0;
 	}
+
 	switch (cmd) {
 	case NAND_IO_ERASE:
 		ret = nand_erase_blocks(chip, ((off_t *)data)[0],
@@ -234,15 +255,38 @@ nand_ioctl(struct disk *ndisk, u_long cm
 		break;
 
 	case NAND_IO_RAW_PROG:
-		copyin(raw_rw->data, buf, raw_rw->len);
-		ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
-		    raw_rw->len);
+		while (len > 0) {
+			if (len < bufsize)
+				bufsize = len;
+
+			ret = copyin(raw_rw->data + off, buf, bufsize);
+			if (ret)
+				break;
+			ret = nand_prog_pages_raw(chip, raw_rw->off + off, buf,
+			    bufsize);
+			if (ret)
+				break;
+			len -= bufsize;
+			off += bufsize;
+		}
 		break;
 
 	case NAND_IO_RAW_READ:
-		ret = nand_read_pages_raw(chip, raw_rw->off, buf,
-		    raw_rw->len);
-		copyout(buf, raw_rw->data, raw_rw->len);
+		while (len > 0) {
+			if (len < bufsize)
+				bufsize = len;
+
+			ret = nand_read_pages_raw(chip, raw_rw->off + off, buf,
+			    bufsize);
+			if (ret)
+				break;
+
+			ret = copyout(buf, raw_rw->data + off, bufsize);
+			if (ret)
+				break;
+			len -= bufsize;
+			off += bufsize;
+		}
 		break;
 
 	case NAND_IO_GET_CHIP_PARAM:


More information about the svn-src-head mailing list