From 559dc87f50d062d881fed406efb76925aa1f30e7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 10 Jun 2009 04:38:55 +0000 Subject: ps3flash: Use ps3_system_bus_[gs]et_drvdata() instead of direct access Signed-off-by: Geert Uytterhoeven Signed-off-by: Benjamin Herrenschmidt --- drivers/char/ps3flash.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/char/ps3flash.c') diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index afbe45676d7..184e6ed2393 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -108,7 +108,7 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { struct ps3_storage_device *dev = ps3flash_dev; - struct ps3flash_private *priv = dev->sbd.core.driver_data; + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); u64 size, start_sector, end_sector, offset; ssize_t sectors_read; size_t remaining, n; @@ -173,7 +173,7 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { struct ps3_storage_device *dev = ps3flash_dev; - struct ps3flash_private *priv = dev->sbd.core.driver_data; + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); u64 size, chunk_sectors, start_write_sector, end_write_sector, end_read_sector, start_read_sector, head, tail, offset; ssize_t res; @@ -366,7 +366,7 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) goto fail; } - dev->sbd.core.driver_data = priv; + ps3_system_bus_set_drvdata(&dev->sbd, priv); mutex_init(&priv->mutex); dev->bounce_size = ps3flash_bounce_buffer.size; @@ -392,7 +392,7 @@ fail_teardown: ps3stor_teardown(dev); fail_free_priv: kfree(priv); - dev->sbd.core.driver_data = NULL; + ps3_system_bus_set_drvdata(&dev->sbd, NULL); fail: ps3flash_dev = NULL; return error; @@ -404,8 +404,8 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev) misc_deregister(&ps3flash_misc); ps3stor_teardown(dev); - kfree(dev->sbd.core.driver_data); - dev->sbd.core.driver_data = NULL; + kfree(ps3_system_bus_get_drvdata(&dev->sbd)); + ps3_system_bus_set_drvdata(&dev->sbd, NULL); ps3flash_dev = NULL; return 0; } -- cgit v1.2.3-70-g09d2 From a4e623fbc9b201930abcf78df6db5e49aa8e00cb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 10 Jun 2009 04:39:06 +0000 Subject: ps3: Replace direct file operations by callback Currently the FLASH database is updated by the kernel using file operations, meant for userspace only. While this works for us because copy_{from,to}_user() on powerpc can handle kernel pointers, this is unportable and a bad example. Replace the file operations by callbacks, registered by the ps3flash driver. Signed-off-by: Geert Uytterhoeven Cc: Geoff Levand Acked-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/ps3.h | 7 ++ arch/powerpc/platforms/ps3/os-area.c | 138 +++++++++++++++++++---------------- drivers/char/ps3flash.c | 94 ++++++++++++++++++------ 3 files changed, 152 insertions(+), 87 deletions(-) (limited to 'drivers/char/ps3flash.c') diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h index 7660694ab3c..7f065e178ec 100644 --- a/arch/powerpc/include/asm/ps3.h +++ b/arch/powerpc/include/asm/ps3.h @@ -53,6 +53,13 @@ enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void); extern u64 ps3_os_area_get_rtc_diff(void); extern void ps3_os_area_set_rtc_diff(u64 rtc_diff); +struct ps3_os_area_flash_ops { + ssize_t (*read)(void *buf, size_t count, loff_t pos); + ssize_t (*write)(const void *buf, size_t count, loff_t pos); +}; + +extern void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops); + /* dma routines */ enum ps3_dma_page_size { diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c index 6e4125d6150..d6487a9c801 100644 --- a/arch/powerpc/platforms/ps3/os-area.c +++ b/arch/powerpc/platforms/ps3/os-area.c @@ -226,6 +226,44 @@ static struct property property_av_multi_out = { .value = &saved_params.av_multi_out, }; + +static DEFINE_MUTEX(os_area_flash_mutex); + +static const struct ps3_os_area_flash_ops *os_area_flash_ops; + +void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops) +{ + mutex_lock(&os_area_flash_mutex); + os_area_flash_ops = ops; + mutex_unlock(&os_area_flash_mutex); +} +EXPORT_SYMBOL_GPL(ps3_os_area_flash_register); + +static ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos) +{ + ssize_t res = -ENODEV; + + mutex_lock(&os_area_flash_mutex); + if (os_area_flash_ops) + res = os_area_flash_ops->read(buf, count, pos); + mutex_unlock(&os_area_flash_mutex); + + return res; +} + +static ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos) +{ + ssize_t res = -ENODEV; + + mutex_lock(&os_area_flash_mutex); + if (os_area_flash_ops) + res = os_area_flash_ops->write(buf, count, pos); + mutex_unlock(&os_area_flash_mutex); + + return res; +} + + /** * os_area_set_property - Add or overwrite a saved_params value to the device tree. * @@ -352,12 +390,12 @@ static int db_verify(const struct os_area_db *db) if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM, sizeof(db->magic_num))) { pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); - return -1; + return -EINVAL; } if (db->version != 1) { pr_debug("%s:%d version failed\n", __func__, __LINE__); - return -1; + return -EINVAL; } return 0; @@ -578,59 +616,48 @@ static void os_area_db_init(struct os_area_db *db) * */ -static void __maybe_unused update_flash_db(void) +static int update_flash_db(void) { - int result; - int file; - off_t offset; + const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE; + struct os_area_header *header; ssize_t count; - static const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE; - const struct os_area_header *header; + int error; + loff_t pos; struct os_area_db* db; /* Read in header and db from flash. */ - file = sys_open("/dev/ps3flash", O_RDWR, 0); - - if (file < 0) { - pr_debug("%s:%d sys_open failed\n", __func__, __LINE__); - goto fail_open; - } - header = kmalloc(buf_len, GFP_KERNEL); - if (!header) { - pr_debug("%s:%d kmalloc failed\n", __func__, __LINE__); - goto fail_malloc; + pr_debug("%s: kmalloc failed\n", __func__); + return -ENOMEM; } - offset = sys_lseek(file, 0, SEEK_SET); - - if (offset != 0) { - pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__); - goto fail_header_seek; + count = os_area_flash_read(header, buf_len, 0); + if (count < 0) { + pr_debug("%s: os_area_flash_read failed %zd\n", __func__, + count); + error = count; + goto fail; } - count = sys_read(file, (char __user *)header, buf_len); - - result = count < OS_AREA_SEGMENT_SIZE || verify_header(header) - || count < header->db_area_offset * OS_AREA_SEGMENT_SIZE; - - if (result) { - pr_debug("%s:%d verify_header failed\n", __func__, __LINE__); + pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE; + if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) || + count < pos) { + pr_debug("%s: verify_header failed\n", __func__); dump_header(header); - goto fail_header; + error = -EINVAL; + goto fail; } /* Now got a good db offset and some maybe good db data. */ - db = (void*)header + header->db_area_offset * OS_AREA_SEGMENT_SIZE; + db = (void *)header + pos; - result = db_verify(db); - - if (result) { - printk(KERN_NOTICE "%s:%d: Verify of flash database failed, " - "formatting.\n", __func__, __LINE__); + error = db_verify(db); + if (error) { + pr_notice("%s: Verify of flash database failed, formatting.\n", + __func__); dump_db(db); os_area_db_init(db); } @@ -639,29 +666,16 @@ static void __maybe_unused update_flash_db(void) db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff); - offset = sys_lseek(file, header->db_area_offset * OS_AREA_SEGMENT_SIZE, - SEEK_SET); - - if (offset != header->db_area_offset * OS_AREA_SEGMENT_SIZE) { - pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__); - goto fail_db_seek; - } - - count = sys_write(file, (const char __user *)db, - sizeof(struct os_area_db)); - + count = os_area_flash_write(db, sizeof(struct os_area_db), pos); if (count < sizeof(struct os_area_db)) { - pr_debug("%s:%d sys_write failed\n", __func__, __LINE__); + pr_debug("%s: os_area_flash_write failed %zd\n", __func__, + count); + error = count < 0 ? count : -EIO; } -fail_db_seek: -fail_header: -fail_header_seek: +fail: kfree(header); -fail_malloc: - sys_close(file); -fail_open: - return; + return error; } /** @@ -674,11 +688,11 @@ fail_open: static void os_area_queue_work_handler(struct work_struct *work) { struct device_node *node; + int error; pr_debug(" -> %s:%d\n", __func__, __LINE__); node = of_find_node_by_path("/"); - if (node) { os_area_set_property(node, &property_rtc_diff); of_node_put(node); @@ -686,12 +700,10 @@ static void os_area_queue_work_handler(struct work_struct *work) pr_debug("%s:%d of_find_node_by_path failed\n", __func__, __LINE__); -#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) - update_flash_db(); -#else - printk(KERN_WARNING "%s:%d: No flash rom driver configured.\n", - __func__, __LINE__); -#endif + error = update_flash_db(); + if (error) + pr_warning("%s: Could not update FLASH ROM\n", __func__); + pr_debug(" <- %s:%d\n", __func__, __LINE__); } diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 184e6ed2393..f7f21f47ea0 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -104,18 +104,19 @@ out: return res; } -static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, - loff_t *pos) +static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, + size_t count, loff_t *pos) { struct ps3_storage_device *dev = ps3flash_dev; struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); u64 size, start_sector, end_sector, offset; ssize_t sectors_read; size_t remaining, n; + const void *src; dev_dbg(&dev->sbd.core, - "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n", - __func__, __LINE__, count, *pos, buf); + "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n", + __func__, __LINE__, count, *pos, userbuf, kernelbuf); size = dev->regions[dev->region_idx].size*dev->blk_size; if (*pos >= size || !count) @@ -145,19 +146,26 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, } n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); + src = dev->bounce_buf+offset; dev_dbg(&dev->sbd.core, - "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n", - __func__, __LINE__, n, dev->bounce_buf+offset, buf); - if (copy_to_user(buf, dev->bounce_buf+offset, n)) { - mutex_unlock(&priv->mutex); - sectors_read = -EFAULT; - goto fail; + "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", + __func__, __LINE__, n, src, userbuf, kernelbuf); + if (userbuf) { + if (copy_to_user(userbuf, src, n)) { + mutex_unlock(&priv->mutex); + sectors_read = -EFAULT; + goto fail; + } + userbuf += n; + } + if (kernelbuf) { + memcpy(kernelbuf, src, n); + kernelbuf += n; } mutex_unlock(&priv->mutex); *pos += n; - buf += n; remaining -= n; start_sector += sectors_read; offset = 0; @@ -169,8 +177,8 @@ fail: return sectors_read; } -static ssize_t ps3flash_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) +static ssize_t ps3flash_write(const char __user *userbuf, + const void *kernelbuf, size_t count, loff_t *pos) { struct ps3_storage_device *dev = ps3flash_dev; struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); @@ -179,10 +187,11 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, ssize_t res; size_t remaining, n; unsigned int sec_off; + void *dst; dev_dbg(&dev->sbd.core, - "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n", - __func__, __LINE__, count, *pos, buf); + "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n", + __func__, __LINE__, count, *pos, userbuf, kernelbuf); size = dev->regions[dev->region_idx].size*dev->blk_size; if (*pos >= size || !count) @@ -259,12 +268,20 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, } n = min_t(u64, remaining, dev->bounce_size-offset); + dst = dev->bounce_buf+offset; dev_dbg(&dev->sbd.core, - "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n", - __func__, __LINE__, n, buf, dev->bounce_buf+offset); - if (copy_from_user(dev->bounce_buf+offset, buf, n)) { - res = -EFAULT; - goto fail; + "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", + __func__, __LINE__, n, userbuf, kernelbuf, dst); + if (userbuf) { + if (copy_from_user(dst, userbuf, n)) { + res = -EFAULT; + goto fail; + } + userbuf += n; + } + if (kernelbuf) { + memcpy(dst, kernelbuf, n); + kernelbuf += n; } res = ps3flash_write_chunk(dev, start_write_sector); @@ -274,7 +291,6 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, mutex_unlock(&priv->mutex); *pos += n; - buf += n; remaining -= n; start_write_sector += chunk_sectors; head = 0; @@ -288,6 +304,29 @@ fail: return res; } +static ssize_t ps3flash_user_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + return ps3flash_read(buf, NULL, count, pos); +} + +static ssize_t ps3flash_user_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + return ps3flash_write(buf, NULL, count, pos); +} + +static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos) +{ + return ps3flash_read(NULL, buf, count, &pos); +} + +static ssize_t ps3flash_kernel_write(const void *buf, size_t count, + loff_t pos) +{ + return ps3flash_write(NULL, buf, count, &pos); +} + static irqreturn_t ps3flash_interrupt(int irq, void *data) { @@ -312,12 +351,16 @@ static irqreturn_t ps3flash_interrupt(int irq, void *data) return IRQ_HANDLED; } - static const struct file_operations ps3flash_fops = { .owner = THIS_MODULE, .llseek = ps3flash_llseek, - .read = ps3flash_read, - .write = ps3flash_write, + .read = ps3flash_user_read, + .write = ps3flash_user_write, +}; + +static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = { + .read = ps3flash_kernel_read, + .write = ps3flash_kernel_write, }; static struct miscdevice ps3flash_misc = { @@ -386,6 +429,8 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", __func__, __LINE__, ps3flash_misc.minor); + + ps3_os_area_flash_register(&ps3flash_kernel_ops); return 0; fail_teardown: @@ -402,6 +447,7 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev) { struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + ps3_os_area_flash_register(NULL); misc_deregister(&ps3flash_misc); ps3stor_teardown(dev); kfree(ps3_system_bus_get_drvdata(&dev->sbd)); -- cgit v1.2.3-70-g09d2 From 6bd57f2e5db408e0dfdb3bf052d58c4e7b18ed3c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 10 Jun 2009 04:39:07 +0000 Subject: ps3flash: Cache the last accessed FLASH chunk Add support for caching, to reduce FLASH wear when writing using small blocksizes. As we also don't care anymore about heads and tails in case of partial writes, this greatly simplifies the code for handling writes. Note: We don't bother caching reads smaller than the FLASH chunk size (256 KiB). Signed-off-by: Geert Uytterhoeven Signed-off-by: Benjamin Herrenschmidt --- drivers/char/ps3flash.c | 209 ++++++++++++++++++++++++------------------------ 1 file changed, 104 insertions(+), 105 deletions(-) (limited to 'drivers/char/ps3flash.c') diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index f7f21f47ea0..d884d31491d 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -33,13 +33,16 @@ struct ps3flash_private { struct mutex mutex; /* Bounce buffer mutex */ + u64 chunk_sectors; + int tag; /* Start sector of buffer, -1 if invalid */ + bool dirty; }; static struct ps3_storage_device *ps3flash_dev; -static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev, - u64 lpar, u64 start_sector, - u64 sectors, int write) +static int ps3flash_read_write_sectors(struct ps3_storage_device *dev, + u64 lpar, u64 start_sector, u64 sectors, + int write) { u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, write); @@ -48,33 +51,55 @@ static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev, __LINE__, write ? "write" : "read", res); return -EIO; } - return sectors; + return 0; } -static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev, - u64 start_sector, u64 sectors, - unsigned int sector_offset) +static int ps3flash_writeback(struct ps3_storage_device *dev) { - u64 max_sectors, lpar; + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + int res; - max_sectors = dev->bounce_size / dev->blk_size; - if (sectors > max_sectors) { - dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %llu\n", - __func__, __LINE__, max_sectors); - sectors = max_sectors; - } + if (!priv->dirty || priv->tag < 0) + return 0; + + res = ps3flash_read_write_sectors(dev, dev->bounce_lpar, priv->tag, + priv->chunk_sectors, 1); + if (res) + return res; - lpar = dev->bounce_lpar + sector_offset * dev->blk_size; - return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, - 0); + priv->dirty = false; + return 0; } -static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev, - u64 start_sector) +static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector, + u64 sectors) { - u64 sectors = dev->bounce_size / dev->blk_size; - return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector, - sectors, 1); + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + unsigned int tag, offset; + u64 lpar; + int res; + + offset = start_sector % priv->chunk_sectors; + tag = start_sector - offset; + if (tag == priv->tag) + return 0; + + res = ps3flash_writeback(dev); + if (res) + return res; + + priv->tag = -1; + + lpar = dev->bounce_lpar + offset * dev->blk_size; + res = ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, 0); + if (res) + return res; + + /* We don't bother caching reads smaller than the chunk size */ + if (sectors == priv->chunk_sectors) + priv->tag = tag; + + return 0; } static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) @@ -109,8 +134,8 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, { struct ps3_storage_device *dev = ps3flash_dev; struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); - u64 size, start_sector, end_sector, offset; - ssize_t sectors_read; + u64 size, start_sector, end_sector, offset, sectors; + int res; size_t remaining, n; const void *src; @@ -130,30 +155,29 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, } start_sector = *pos / dev->blk_size; - offset = *pos % dev->blk_size; + offset = *pos % dev->bounce_size; end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size); remaining = count; do { + sectors = min(end_sector - start_sector, + priv->chunk_sectors - + start_sector % priv->chunk_sectors); + mutex_lock(&priv->mutex); - sectors_read = ps3flash_read_sectors(dev, start_sector, - end_sector-start_sector, - 0); - if (sectors_read < 0) { - mutex_unlock(&priv->mutex); + res = ps3flash_fetch(dev, start_sector, sectors); + if (res) goto fail; - } - n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); - src = dev->bounce_buf+offset; + n = min_t(u64, remaining, dev->bounce_size - offset); + src = dev->bounce_buf + offset; dev_dbg(&dev->sbd.core, "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", __func__, __LINE__, n, src, userbuf, kernelbuf); if (userbuf) { if (copy_to_user(userbuf, src, n)) { - mutex_unlock(&priv->mutex); - sectors_read = -EFAULT; + res = -EFAULT; goto fail; } userbuf += n; @@ -167,14 +191,15 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, *pos += n; remaining -= n; - start_sector += sectors_read; + start_sector += sectors; offset = 0; } while (remaining > 0); return count; fail: - return sectors_read; + mutex_unlock(&priv->mutex); + return res; } static ssize_t ps3flash_write(const char __user *userbuf, @@ -182,11 +207,9 @@ static ssize_t ps3flash_write(const char __user *userbuf, { struct ps3_storage_device *dev = ps3flash_dev; struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); - u64 size, chunk_sectors, start_write_sector, end_write_sector, - end_read_sector, start_read_sector, head, tail, offset; - ssize_t res; + u64 size, sector, offset; + int res = 0; size_t remaining, n; - unsigned int sec_off; void *dst; dev_dbg(&dev->sbd.core, @@ -204,71 +227,23 @@ static ssize_t ps3flash_write(const char __user *userbuf, count = size - *pos; } - chunk_sectors = dev->bounce_size / dev->blk_size; - - start_write_sector = *pos / dev->bounce_size * chunk_sectors; + sector = *pos / dev->bounce_size * priv->chunk_sectors; offset = *pos % dev->bounce_size; - end_write_sector = DIV_ROUND_UP(*pos + count, dev->bounce_size) * - chunk_sectors; - - end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size); - start_read_sector = (*pos + count) / dev->blk_size; - - /* - * As we have to write in 256 KiB chunks, while we can read in blk_size - * (usually 512 bytes) chunks, we perform the following steps: - * 1. Read from start_write_sector to end_read_sector ("head") - * 2. Read from start_read_sector to end_write_sector ("tail") - * 3. Copy data to buffer - * 4. Write from start_write_sector to end_write_sector - * All of this is complicated by using only one 256 KiB bounce buffer. - */ - - head = end_read_sector - start_write_sector; - tail = end_write_sector - start_read_sector; remaining = count; do { + n = min_t(u64, remaining, dev->bounce_size - offset); + mutex_lock(&priv->mutex); - if (end_read_sector >= start_read_sector) { - /* Merge head and tail */ - dev_dbg(&dev->sbd.core, - "Merged head and tail: %llu sectors at %llu\n", - chunk_sectors, start_write_sector); - res = ps3flash_read_sectors(dev, start_write_sector, - chunk_sectors, 0); - if (res < 0) - goto fail; - } else { - if (head) { - /* Read head */ - dev_dbg(&dev->sbd.core, - "head: %llu sectors at %llu\n", head, - start_write_sector); - res = ps3flash_read_sectors(dev, - start_write_sector, - head, 0); - if (res < 0) - goto fail; - } - if (start_read_sector < - start_write_sector+chunk_sectors) { - /* Read tail */ - dev_dbg(&dev->sbd.core, - "tail: %llu sectors at %llu\n", tail, - start_read_sector); - sec_off = start_read_sector-start_write_sector; - res = ps3flash_read_sectors(dev, - start_read_sector, - tail, sec_off); - if (res < 0) - goto fail; - } - } + if (n != dev->bounce_size) + res = ps3flash_fetch(dev, sector, priv->chunk_sectors); + else if (sector != priv->tag) + res = ps3flash_writeback(dev); + if (res) + goto fail; - n = min_t(u64, remaining, dev->bounce_size-offset); - dst = dev->bounce_buf+offset; + dst = dev->bounce_buf + offset; dev_dbg(&dev->sbd.core, "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", __func__, __LINE__, n, userbuf, kernelbuf, dst); @@ -284,16 +259,14 @@ static ssize_t ps3flash_write(const char __user *userbuf, kernelbuf += n; } - res = ps3flash_write_chunk(dev, start_write_sector); - if (res < 0) - goto fail; + priv->tag = sector; + priv->dirty = true; mutex_unlock(&priv->mutex); *pos += n; remaining -= n; - start_write_sector += chunk_sectors; - head = 0; + sector += priv->chunk_sectors; offset = 0; } while (remaining > 0); @@ -324,9 +297,31 @@ static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos) static ssize_t ps3flash_kernel_write(const void *buf, size_t count, loff_t pos) { - return ps3flash_write(NULL, buf, count, &pos); + ssize_t res; + int wb; + + res = ps3flash_write(NULL, buf, count, &pos); + if (res < 0) + return res; + + /* Make kernel writes synchronous */ + wb = ps3flash_writeback(ps3flash_dev); + if (wb) + return wb; + + return res; } +static int ps3flash_flush(struct file *file, fl_owner_t id) +{ + return ps3flash_writeback(ps3flash_dev); +} + +static int ps3flash_fsync(struct file *file, struct dentry *dentry, + int datasync) +{ + return ps3flash_writeback(ps3flash_dev); +} static irqreturn_t ps3flash_interrupt(int irq, void *data) { @@ -356,6 +351,8 @@ static const struct file_operations ps3flash_fops = { .llseek = ps3flash_llseek, .read = ps3flash_user_read, .write = ps3flash_user_write, + .flush = ps3flash_flush, + .fsync = ps3flash_fsync, }; static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = { @@ -411,9 +408,11 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) ps3_system_bus_set_drvdata(&dev->sbd, priv); mutex_init(&priv->mutex); + priv->tag = -1; dev->bounce_size = ps3flash_bounce_buffer.size; dev->bounce_buf = ps3flash_bounce_buffer.address; + priv->chunk_sectors = dev->bounce_size / dev->blk_size; error = ps3stor_setup(dev, ps3flash_interrupt); if (error) -- cgit v1.2.3-70-g09d2 From 42e27bfc4bfa42bd905e53be93d862b8e3d80a00 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 10 Jun 2009 04:39:08 +0000 Subject: ps3flash: Always read chunks of 256 KiB, and cache them Signed-off-by: Geert Uytterhoeven Signed-off-by: Benjamin Herrenschmidt --- drivers/char/ps3flash.c | 47 +++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 30 deletions(-) (limited to 'drivers/char/ps3flash.c') diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index d884d31491d..f424d394a28 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -41,10 +41,11 @@ struct ps3flash_private { static struct ps3_storage_device *ps3flash_dev; static int ps3flash_read_write_sectors(struct ps3_storage_device *dev, - u64 lpar, u64 start_sector, u64 sectors, - int write) + u64 start_sector, int write) { - u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, + struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); + u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, + start_sector, priv->chunk_sectors, write); if (res) { dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, @@ -62,8 +63,7 @@ static int ps3flash_writeback(struct ps3_storage_device *dev) if (!priv->dirty || priv->tag < 0) return 0; - res = ps3flash_read_write_sectors(dev, dev->bounce_lpar, priv->tag, - priv->chunk_sectors, 1); + res = ps3flash_read_write_sectors(dev, priv->tag, 1); if (res) return res; @@ -71,17 +71,12 @@ static int ps3flash_writeback(struct ps3_storage_device *dev) return 0; } -static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector, - u64 sectors) +static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector) { struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); - unsigned int tag, offset; - u64 lpar; int res; - offset = start_sector % priv->chunk_sectors; - tag = start_sector - offset; - if (tag == priv->tag) + if (start_sector == priv->tag) return 0; res = ps3flash_writeback(dev); @@ -90,15 +85,11 @@ static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector, priv->tag = -1; - lpar = dev->bounce_lpar + offset * dev->blk_size; - res = ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, 0); + res = ps3flash_read_write_sectors(dev, start_sector, 0); if (res) return res; - /* We don't bother caching reads smaller than the chunk size */ - if (sectors == priv->chunk_sectors) - priv->tag = tag; - + priv->tag = start_sector; return 0; } @@ -134,7 +125,7 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, { struct ps3_storage_device *dev = ps3flash_dev; struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); - u64 size, start_sector, end_sector, offset, sectors; + u64 size, sector, offset; int res; size_t remaining, n; const void *src; @@ -154,24 +145,20 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, count = size - *pos; } - start_sector = *pos / dev->blk_size; + sector = *pos / dev->bounce_size * priv->chunk_sectors; offset = *pos % dev->bounce_size; - end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size); remaining = count; do { - sectors = min(end_sector - start_sector, - priv->chunk_sectors - - start_sector % priv->chunk_sectors); + n = min_t(u64, remaining, dev->bounce_size - offset); + src = dev->bounce_buf + offset; mutex_lock(&priv->mutex); - res = ps3flash_fetch(dev, start_sector, sectors); + res = ps3flash_fetch(dev, sector); if (res) goto fail; - n = min_t(u64, remaining, dev->bounce_size - offset); - src = dev->bounce_buf + offset; dev_dbg(&dev->sbd.core, "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", __func__, __LINE__, n, src, userbuf, kernelbuf); @@ -191,7 +178,7 @@ static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, *pos += n; remaining -= n; - start_sector += sectors; + sector += priv->chunk_sectors; offset = 0; } while (remaining > 0); @@ -233,17 +220,17 @@ static ssize_t ps3flash_write(const char __user *userbuf, remaining = count; do { n = min_t(u64, remaining, dev->bounce_size - offset); + dst = dev->bounce_buf + offset; mutex_lock(&priv->mutex); if (n != dev->bounce_size) - res = ps3flash_fetch(dev, sector, priv->chunk_sectors); + res = ps3flash_fetch(dev, sector); else if (sector != priv->tag) res = ps3flash_writeback(dev); if (res) goto fail; - dst = dev->bounce_buf + offset; dev_dbg(&dev->sbd.core, "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", __func__, __LINE__, n, userbuf, kernelbuf, dst); -- cgit v1.2.3-70-g09d2