aboutsummaryrefslogtreecommitdiffstats
path: root/hw/goldfish_nand.c
diff options
context:
space:
mode:
authorTim Baverstock <weasel@google.com>2010-10-20 14:30:05 +0100
committerTim Baverstock <weasel@google.com>2010-11-23 18:48:11 +0000
commita658bc8efde7e572172ea80d8e185ed0741d95d6 (patch)
treef3dcd9b06aa701efff8383112945ed134443f5f1 /hw/goldfish_nand.c
parent3a21de8e50f7c4a3dae578ee428f9aad363183c0 (diff)
downloadexternal_qemu-a658bc8efde7e572172ea80d8e185ed0741d95d6.zip
external_qemu-a658bc8efde7e572172ea80d8e185ed0741d95d6.tar.gz
external_qemu-a658bc8efde7e572172ea80d8e185ed0741d95d6.tar.bz2
Replacement of padding with ftruncate.
Also stored some other parameters, speculatively. Guard ftruncate and lseek against EINTR Change-Id: I4237640318564db6c375a90415097c00e4fcafaa
Diffstat (limited to 'hw/goldfish_nand.c')
-rw-r--r--hw/goldfish_nand.c86
1 files changed, 53 insertions, 33 deletions
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
index af921b7..2aff835 100644
--- a/hw/goldfish_nand.c
+++ b/hw/goldfish_nand.c
@@ -132,8 +132,9 @@ typedef struct {
/* update this everytime you change the nand_dev_controller_state structure
* 1: initial version, saving only nand_dev_controller_state fields
* 2: saving actual disk contents as well
+ * 3: use the correct data length and truncate to avoid padding.
*/
-#define NAND_DEV_STATE_SAVE_VERSION 2
+#define NAND_DEV_STATE_SAVE_VERSION 3
#define QFIELD_STRUCT nand_dev_controller_state
QFIELD_BEGIN(nand_dev_controller_state_fields)
@@ -146,6 +147,7 @@ QFIELD_BEGIN(nand_dev_controller_state_fields)
QFIELD_END
+/* EINTR-proof read - due to SIGALRM in use elsewhere */
static int do_read(int fd, void* buf, size_t size)
{
int ret;
@@ -156,6 +158,7 @@ static int do_read(int fd, void* buf, size_t size)
return ret;
}
+/* EINTR-proof write - due to SIGALRM in use elsewhere */
static int do_write(int fd, const void* buf, size_t size)
{
int ret;
@@ -166,6 +169,28 @@ static int do_write(int fd, const void* buf, size_t size)
return ret;
}
+/* EINTR-proof lseek - due to SIGALRM in use elsewhere */
+static int do_lseek(int fd, off_t offset, int whence)
+{
+ int ret;
+ do {
+ ret = lseek(fd, offset, whence);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+/* EINTR-proof ftruncate - due to SIGALRM in use elsewhere */
+static int do_ftruncate(int fd, size_t size)
+{
+ int ret;
+ do {
+ ret = ftruncate(fd, size);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
#define NAND_DEV_SAVE_DISK_BUF_SIZE 2048
@@ -182,14 +207,20 @@ static void nand_dev_save_disk_state(QEMUFile *f, nand_dev *dev)
int ret;
uint64_t total_copied = 0;
- /* Put the size up front, since otherwise we don't know how much to read
- * when restoring.
- */
- const uint64_t total_size = dev->max_size;
+ /* Size of file to restore, hence size of data block following.
+ * TODO Work out whether to use lseek64 here. */
+
+ ret = do_lseek(dev->fd, 0, SEEK_END);
+ if (ret < 0) {
+ XLOG("%s EOF seek failed: %s\n", __FUNCTION__, strerror(errno));
+ qemu_file_set_error(f);
+ return;
+ }
+ const uint64_t total_size = ret;
qemu_put_be64(f, total_size);
/* copy all data from the stream to the stored image */
- ret = lseek(dev->fd, 0, SEEK_SET);
+ ret = do_lseek(dev->fd, 0, SEEK_SET);
if (ret < 0) {
XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno));
qemu_file_set_error(f);
@@ -206,24 +237,9 @@ static void nand_dev_save_disk_state(QEMUFile *f, nand_dev *dev)
total_copied += ret;
}
- while (ret == buf_size && total_copied < total_size);
-
- /* The file may be smaller than the device size. Pad with 0xff (pattern for
- * erased data) until we have filled the snapshot buffer we declared earlier.
- * TODO avoid padding. Unfortunately, attempts to use the actual size of the
- * underlying image instead result in broken restores. This also happens
- * when limited padding is inserted so the image size is a multiple of
- * page_size or erase_size.
- */
- memset(buffer, 0xff, NAND_DEV_SAVE_DISK_BUF_SIZE);
- while (total_copied < total_size) {
- /* adjust buffer size for last part of the image */
- if (total_size - total_copied < buf_size) {
- buf_size = total_size - total_copied;
- }
- qemu_put_buffer(f, buffer, buf_size);
- total_copied += buf_size;
- }
+ while (ret == buf_size && total_copied < dev->max_size);
+
+ /* TODO Maybe check that we've written total_size bytes */
}
@@ -248,7 +264,7 @@ static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev)
uint8_t buffer[NAND_DEV_SAVE_DISK_BUF_SIZE] = {0};
int ret;
- /* number of bytes to restore */
+ /* File size for restore and truncate */
uint64_t total_size = qemu_get_be64(f);
if (total_size > dev->max_size) {
XLOG("%s, restore failed: size required (%lld) exceeds device limit (%lld)\n",
@@ -258,9 +274,7 @@ static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev)
/* overwrite disk contents with snapshot contents */
uint64_t next_offset = 0;
- do {
- ret = lseek(dev->fd, 0, SEEK_SET);
- } while (ret < 0 && errno == EINTR);
+ ret = do_lseek(dev->fd, 0, SEEK_SET);
if (ret < 0) {
XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno));
return -EIO;
@@ -287,6 +301,12 @@ static int nand_dev_load_disk_state(QEMUFile *f, nand_dev *dev)
next_offset += buf_size;
}
+ ret = do_ftruncate(dev->fd, total_size);
+ if (ret < 0) {
+ XLOG("%s ftruncate failed: %s\n", __FUNCTION__, strerror(errno));
+ return -EIO;
+ }
+
return 0;
}
@@ -342,7 +362,7 @@ static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr,
NAND_UPDATE_READ_THRESHOLD(total_len);
- lseek(dev->fd, addr, SEEK_SET);
+ do_lseek(dev->fd, addr, SEEK_SET);
while(len > 0) {
if(read_len < dev->erase_size) {
memset(dev->data, 0xff, dev->erase_size);
@@ -369,7 +389,7 @@ static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr,
NAND_UPDATE_WRITE_THRESHOLD(total_len);
- lseek(dev->fd, addr, SEEK_SET);
+ do_lseek(dev->fd, addr, SEEK_SET);
while(len > 0) {
if(len < write_len)
write_len = len;
@@ -391,7 +411,7 @@ static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total
size_t write_len = dev->erase_size;
int ret;
- lseek(dev->fd, addr, SEEK_SET);
+ do_lseek(dev->fd, addr, SEEK_SET);
memset(dev->data, 0xff, dev->erase_size);
while(len > 0) {
if(len < write_len)
@@ -737,8 +757,8 @@ void nand_add_dev(const char *arg)
exit(1);
}
if(dev_size == 0) {
- dev_size = lseek(initfd, 0, SEEK_END);
- lseek(initfd, 0, SEEK_SET);
+ dev_size = do_lseek(initfd, 0, SEEK_END);
+ do_lseek(initfd, 0, SEEK_SET);
}
}