mirror of https://github.com/OpenIPC/firmware.git
377 lines
10 KiB
Diff
377 lines
10 KiB
Diff
diff -drupN a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
|
|
--- a/drivers/mmc/card/block.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/drivers/mmc/card/block.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -287,6 +287,250 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
|
|
+
|
|
+static int max_read_speed, max_write_speed, cache_size = 4;
|
|
+
|
|
+module_param(max_read_speed, int, S_IRUSR | S_IRGRP);
|
|
+MODULE_PARM_DESC(max_read_speed, "maximum KB/s read speed 0=off");
|
|
+module_param(max_write_speed, int, S_IRUSR | S_IRGRP);
|
|
+MODULE_PARM_DESC(max_write_speed, "maximum KB/s write speed 0=off");
|
|
+module_param(cache_size, int, S_IRUSR | S_IRGRP);
|
|
+MODULE_PARM_DESC(cache_size, "MB high speed memory or SLC cache");
|
|
+
|
|
+/*
|
|
+ * helper macros and expectations:
|
|
+ * size - unsigned long number of bytes
|
|
+ * jiffies - unsigned long HZ timestamp difference
|
|
+ * speed - unsigned KB/s transfer rate
|
|
+ */
|
|
+#define size_and_speed_to_jiffies(size, speed) \
|
|
+ ((size) * HZ / (speed) / 1024UL)
|
|
+#define jiffies_and_speed_to_size(jiffies, speed) \
|
|
+ (((speed) * (jiffies) * 1024UL) / HZ)
|
|
+#define jiffies_and_size_to_speed(jiffies, size) \
|
|
+ ((size) * HZ / (jiffies) / 1024UL)
|
|
+
|
|
+/* Limits to report warning */
|
|
+/* jiffies_and_size_to_speed(10*HZ, queue_max_hw_sectors(q) * 512UL) ~ 25 */
|
|
+#define MIN_SPEED(q) 250 /* 10 times faster than a floppy disk */
|
|
+#define MAX_SPEED(q) jiffies_and_size_to_speed(1, queue_max_sectors(q) * 512UL)
|
|
+
|
|
+#define speed_valid(speed) ((speed) > 0)
|
|
+
|
|
+static const char off[] = "off\n";
|
|
+
|
|
+static int max_speed_show(int speed, char *buf)
|
|
+{
|
|
+ if (speed)
|
|
+ return scnprintf(buf, PAGE_SIZE, "%uKB/s\n", speed);
|
|
+ else
|
|
+ return scnprintf(buf, PAGE_SIZE, off);
|
|
+}
|
|
+
|
|
+static int max_speed_store(const char *buf, struct request_queue *q)
|
|
+{
|
|
+ unsigned int limit, set = 0;
|
|
+
|
|
+ if (!strncasecmp(off, buf, sizeof(off) - 2))
|
|
+ return set;
|
|
+ if (kstrtouint(buf, 0, &set) || (set > INT_MAX))
|
|
+ return -EINVAL;
|
|
+ if (set == 0)
|
|
+ return set;
|
|
+ limit = MAX_SPEED(q);
|
|
+ if (set > limit)
|
|
+ pr_warn("max speed %u ineffective above %u\n", set, limit);
|
|
+ limit = MIN_SPEED(q);
|
|
+ if (set < limit)
|
|
+ pr_warn("max speed %u painful below %u\n", set, limit);
|
|
+ return set;
|
|
+}
|
|
+
|
|
+static ssize_t max_write_speed_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
|
+ int ret = max_speed_show(atomic_read(&md->queue.max_write_speed), buf);
|
|
+
|
|
+ mmc_blk_put(md);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t max_write_speed_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
|
+ int set = max_speed_store(buf, md->queue.queue);
|
|
+
|
|
+ if (set < 0) {
|
|
+ mmc_blk_put(md);
|
|
+ return set;
|
|
+ }
|
|
+
|
|
+ atomic_set(&md->queue.max_write_speed, set);
|
|
+ mmc_blk_put(md);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const DEVICE_ATTR(max_write_speed, S_IRUGO | S_IWUSR,
|
|
+ max_write_speed_show, max_write_speed_store);
|
|
+
|
|
+static ssize_t max_read_speed_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
|
+ int ret = max_speed_show(atomic_read(&md->queue.max_read_speed), buf);
|
|
+
|
|
+ mmc_blk_put(md);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t max_read_speed_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
|
+ int set = max_speed_store(buf, md->queue.queue);
|
|
+
|
|
+ if (set < 0) {
|
|
+ mmc_blk_put(md);
|
|
+ return set;
|
|
+ }
|
|
+
|
|
+ atomic_set(&md->queue.max_read_speed, set);
|
|
+ mmc_blk_put(md);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const DEVICE_ATTR(max_read_speed, S_IRUGO | S_IWUSR,
|
|
+ max_read_speed_show, max_read_speed_store);
|
|
+
|
|
+static ssize_t cache_size_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
|
+ struct mmc_queue *mq = &md->queue;
|
|
+ int cache_size = atomic_read(&mq->cache_size);
|
|
+ int ret;
|
|
+
|
|
+ if (!cache_size)
|
|
+ ret = scnprintf(buf, PAGE_SIZE, off);
|
|
+ else {
|
|
+ int speed = atomic_read(&mq->max_write_speed);
|
|
+
|
|
+ if (!speed_valid(speed))
|
|
+ ret = scnprintf(buf, PAGE_SIZE, "%uMB\n", cache_size);
|
|
+ else { /* We accept race between cache_jiffies and cache_used */
|
|
+ unsigned long size = jiffies_and_speed_to_size(
|
|
+ jiffies - mq->cache_jiffies, speed);
|
|
+ long used = atomic_long_read(&mq->cache_used);
|
|
+
|
|
+ if (size >= used)
|
|
+ size = 0;
|
|
+ else
|
|
+ size = (used - size) * 100 / cache_size
|
|
+ / 1024UL / 1024UL;
|
|
+
|
|
+ ret = scnprintf(buf, PAGE_SIZE, "%uMB %lu%% used\n",
|
|
+ cache_size, size);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mmc_blk_put(md);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t cache_size_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct mmc_blk_data *md;
|
|
+ unsigned int set = 0;
|
|
+
|
|
+ if (strncasecmp(off, buf, sizeof(off) - 2)
|
|
+ && (kstrtouint(buf, 0, &set) || (set > INT_MAX)))
|
|
+ return -EINVAL;
|
|
+
|
|
+ md = mmc_blk_get(dev_to_disk(dev));
|
|
+ atomic_set(&md->queue.cache_size, set);
|
|
+ mmc_blk_put(md);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const DEVICE_ATTR(cache_size, S_IRUGO | S_IWUSR,
|
|
+ cache_size_show, cache_size_store);
|
|
+
|
|
+/* correct for write-back */
|
|
+static long mmc_blk_cache_used(struct mmc_queue *mq, unsigned long waitfor)
|
|
+{
|
|
+ long used = 0;
|
|
+ int speed = atomic_read(&mq->max_write_speed);
|
|
+
|
|
+ if (speed_valid(speed)) {
|
|
+ unsigned long size = jiffies_and_speed_to_size(
|
|
+ waitfor - mq->cache_jiffies, speed);
|
|
+ used = atomic_long_read(&mq->cache_used);
|
|
+
|
|
+ if (size >= used)
|
|
+ used = 0;
|
|
+ else
|
|
+ used -= size;
|
|
+ }
|
|
+
|
|
+ atomic_long_set(&mq->cache_used, used);
|
|
+ mq->cache_jiffies = waitfor;
|
|
+
|
|
+ return used;
|
|
+}
|
|
+
|
|
+static void mmc_blk_simulate_delay(
|
|
+ struct mmc_queue *mq,
|
|
+ struct request *req,
|
|
+ unsigned long waitfor)
|
|
+{
|
|
+ int max_speed;
|
|
+
|
|
+ if (!req)
|
|
+ return;
|
|
+
|
|
+ max_speed = (rq_data_dir(req) == READ)
|
|
+ ? atomic_read(&mq->max_read_speed)
|
|
+ : atomic_read(&mq->max_write_speed);
|
|
+ if (speed_valid(max_speed)) {
|
|
+ unsigned long bytes = blk_rq_bytes(req);
|
|
+
|
|
+ if (rq_data_dir(req) != READ) {
|
|
+ int cache_size = atomic_read(&mq->cache_size);
|
|
+
|
|
+ if (cache_size) {
|
|
+ unsigned long size = cache_size * 1024L * 1024L;
|
|
+ long used = mmc_blk_cache_used(mq, waitfor);
|
|
+
|
|
+ used += bytes;
|
|
+ atomic_long_set(&mq->cache_used, used);
|
|
+ bytes = 0;
|
|
+ if (used > size)
|
|
+ bytes = used - size;
|
|
+ }
|
|
+ }
|
|
+ waitfor += size_and_speed_to_jiffies(bytes, max_speed);
|
|
+ if (time_is_after_jiffies(waitfor)) {
|
|
+ long msecs = jiffies_to_msecs(waitfor - jiffies);
|
|
+
|
|
+ if (likely(msecs > 0))
|
|
+ msleep(msecs);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#else
|
|
+
|
|
+#define mmc_blk_simulate_delay(mq, req, waitfor)
|
|
+
|
|
+#endif
|
|
+
|
|
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
|
|
{
|
|
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
|
|
@@ -1284,6 +1528,23 @@ static int mmc_blk_issue_flush(struct mm
|
|
if (ret)
|
|
ret = -EIO;
|
|
|
|
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
|
|
+ else if (atomic_read(&mq->cache_size)) {
|
|
+ long used = mmc_blk_cache_used(mq, jiffies);
|
|
+
|
|
+ if (used) {
|
|
+ int speed = atomic_read(&mq->max_write_speed);
|
|
+
|
|
+ if (speed_valid(speed)) {
|
|
+ unsigned long msecs = jiffies_to_msecs(
|
|
+ size_and_speed_to_jiffies(
|
|
+ used, speed));
|
|
+ if (msecs)
|
|
+ msleep(msecs);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
blk_end_request_all(req, ret);
|
|
|
|
return ret ? 0 : 1;
|
|
@@ -1613,7 +1874,10 @@ static void mmc_blk_rw_rq_prep(struct mm
|
|
mmc_set_data_timeout(&brq->data, card);
|
|
|
|
brq->data.sg = mqrq->sg;
|
|
- brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
|
|
+ if (mmc_card_mmc(card))
|
|
+ brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
|
|
+ else
|
|
+ brq->data.sg_len = sd_mmc_queue_map_sg(mq, mqrq);
|
|
|
|
/*
|
|
* Adjust the sg list so it is the same size as the
|
|
@@ -1850,7 +2114,10 @@ static void mmc_blk_packed_hdr_wrq_prep(
|
|
mmc_set_data_timeout(&brq->data, card);
|
|
|
|
brq->data.sg = mqrq->sg;
|
|
- brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
|
|
+ if (mmc_card_mmc(card))
|
|
+ brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
|
|
+ else
|
|
+ brq->data.sg_len = sd_mmc_queue_map_sg(mq, mqrq);
|
|
|
|
mqrq->mmc_active.mrq = &brq->mrq;
|
|
mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
|
|
@@ -1965,6 +2232,9 @@ static int mmc_blk_issue_rw_rq(struct mm
|
|
struct mmc_async_req *areq;
|
|
const u8 packed_nr = 2;
|
|
u8 reqs = 0;
|
|
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
|
|
+ unsigned long waitfor = jiffies;
|
|
+#endif
|
|
|
|
if (!rqc && !mq->mqrq_prev->req)
|
|
return 0;
|
|
@@ -2015,6 +2285,8 @@ static int mmc_blk_issue_rw_rq(struct mm
|
|
*/
|
|
mmc_blk_reset_success(md, type);
|
|
|
|
+ mmc_blk_simulate_delay(mq, rqc, waitfor);
|
|
+
|
|
if (mmc_packed_cmd(mq_rq->cmd_type)) {
|
|
ret = mmc_blk_end_packed_req(mq_rq);
|
|
break;
|
|
@@ -2437,6 +2709,14 @@ static void mmc_blk_remove_req(struct mm
|
|
card->ext_csd.boot_ro_lockable)
|
|
device_remove_file(disk_to_dev(md->disk),
|
|
&md->power_ro_lock);
|
|
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
|
|
+ device_remove_file(disk_to_dev(md->disk),
|
|
+ &dev_attr_max_write_speed);
|
|
+ device_remove_file(disk_to_dev(md->disk),
|
|
+ &dev_attr_max_read_speed);
|
|
+ device_remove_file(disk_to_dev(md->disk),
|
|
+ &dev_attr_cache_size);
|
|
+#endif
|
|
|
|
del_gendisk(md->disk);
|
|
}
|
|
@@ -2471,6 +2751,24 @@ static int mmc_add_disk(struct mmc_blk_d
|
|
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
|
|
if (ret)
|
|
goto force_ro_fail;
|
|
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
|
|
+ atomic_set(&md->queue.max_write_speed, max_write_speed);
|
|
+ ret = device_create_file(disk_to_dev(md->disk),
|
|
+ &dev_attr_max_write_speed);
|
|
+ if (ret)
|
|
+ goto max_write_speed_fail;
|
|
+ atomic_set(&md->queue.max_read_speed, max_read_speed);
|
|
+ ret = device_create_file(disk_to_dev(md->disk),
|
|
+ &dev_attr_max_read_speed);
|
|
+ if (ret)
|
|
+ goto max_read_speed_fail;
|
|
+ atomic_set(&md->queue.cache_size, cache_size);
|
|
+ atomic_long_set(&md->queue.cache_used, 0);
|
|
+ md->queue.cache_jiffies = jiffies;
|
|
+ ret = device_create_file(disk_to_dev(md->disk), &dev_attr_cache_size);
|
|
+ if (ret)
|
|
+ goto cache_size_fail;
|
|
+#endif
|
|
|
|
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
|
|
card->ext_csd.boot_ro_lockable) {
|
|
@@ -2495,6 +2793,14 @@ static int mmc_add_disk(struct mmc_blk_d
|
|
return ret;
|
|
|
|
power_ro_lock_fail:
|
|
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
|
|
+ device_remove_file(disk_to_dev(md->disk), &dev_attr_cache_size);
|
|
+cache_size_fail:
|
|
+ device_remove_file(disk_to_dev(md->disk), &dev_attr_max_read_speed);
|
|
+max_read_speed_fail:
|
|
+ device_remove_file(disk_to_dev(md->disk), &dev_attr_max_write_speed);
|
|
+max_write_speed_fail:
|
|
+#endif
|
|
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
|
|
force_ro_fail:
|
|
del_gendisk(md->disk);
|