@@ -2,6 +2,7 @@
import os
import sys
+import time
from argparse import ArgumentParser
import lc_admin.dirnode as dirnode
@@ -29,13 +30,13 @@ interrupted = False
while True:
try:
- nr = device.nr_dirty_caches()
- while nr:
+ nr = lambda : device.nr_dirty_caches()
+ while nr():
if interrupted:
device.unlock()
sys.exit()
- print("could not detach the device. %d caches are still dirty remained." % (nr))
+ print("could not detach the device. %d caches are still dirty remained." % (nr()))
time.sleep(1)
print("no dirty cache remained in the device")
@@ -1295,7 +1295,7 @@ static void migrate_proc(struct work_struct *work)
/*
* (Locking)
* Only line of code changes
- * last_migrate_segment_id in runtime.
+ * last_migrate_segment_id during runtime.
*/
cache->last_migrated_segment_id += nr_mig;
@@ -1639,7 +1639,9 @@ static size_t calc_nr_segments(struct dm_dev *dev)
sector_t devsize = dm_devsize(dev);
/*
- * Disk format:
+ * Disk format
+ *
+ * Overall:
* superblock(1MB) [segment(1MB)]+
* We reserve the first segment (1MB) as the superblock.
*
@@ -1946,6 +1948,21 @@ static int lc_map(struct dm_target *ti, struct bio *bio)
k = ht_hash(cache, &key);
head = arr_at(cache->htable, k);
+ /*
+ * (Locking)
+ * Why mutex?
+ *
+ * The reason we use mutex instead of rw_semaphore
+ * that can allow truely concurrent read access
+ * is that mutex is even lighter than rw_semaphore.
+ * Since dm-lc is a real performance centric software
+ * the overhead of rw_semaphore is crucial.
+ * All in all,
+ * since exclusive region in read path is enough small
+ * and cheap, using rw_semaphore and let the reads
+ * execute concurrently won't improve the performance
+ * as much as one expects.
+ */
mutex_lock(&cache->io_lock);
mb = ht_lookup(cache, head, &key);
if (mb) {
@@ -1979,7 +1996,20 @@ static int lc_map(struct dm_target *ti, struct bio *bio)
migrate_buffered_mb(cache, mb, dirty_bits);
/*
- * Dirtiness of a live cache:
+ * Cache class
+ * Live and Stable
+ *
+ * Live:
+ * The cache is on the RAM buffer.
+ *
+ * Stable:
+ * The cache is not on the RAM buffer
+ * but at least queued in flush_queue.
+ */
+
+ /*
+ * (Locking)
+ * Dirtiness of a live cache
*
* We can assume dirtiness of a cache only increase
* when it is on the buffer, we call this cache is live.
@@ -2003,7 +2033,8 @@ static int lc_map(struct dm_target *ti, struct bio *bio)
} else {
/*
- * Dirtiness of a stable cache:
+ * (Locking)
+ * Dirtiness of a stable cache
*
* Unlike the live caches that don't
* fluctuate the dirtiness,
@@ -2305,13 +2336,43 @@ static struct kobj_type device_ktype = {
.release = device_release,
};
+static int parse_cache_id(char *s, u8 *cache_id)
+{
+ unsigned id;
+ if (sscanf(s, "%u", &id) != 1) {
+ LCERR();
+ return -EINVAL;
+ }
+ if (id >= LC_NR_SLOTS) {
+ LCERR();
+ return -EINVAL;
+ }
+ *cache_id = id;
+ return 0;
+}
+
/*
* <device-id> <path> <cache-id>
+ *
+ * By replacing it with cache_id_ptr
+ * cache-id can be removed from this constructor
+ * that will result in code dedup
+ * in this constructor and switch_to message.
+ *
+ * The reason why this constructor takes cache-id
+ * is to cooperate with the output of `dmsetup table`,
+ * that is, dmsetup table | dmsetup load should clone
+ * the logical device.
+ * This is an implicit rule in device-mapper
+ * and dm-lc follows it.
+ * Other non-essential tunable parameters
+ * will not be cloned because that is too much.
*/
static int lc_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct lc_device *lc;
- unsigned device_id, cache_id;
+ unsigned device_id;
+ u8 cache_id;
struct dm_dev *dev;
int r = dm_set_target_max_io_len(ti, (1 << 3));
@@ -2339,9 +2400,15 @@ static int lc_ctr(struct dm_target *ti, unsigned int argc, char **argv)
r = -EINVAL;
goto bad_device_id;
}
+ if (device_id >= LC_NR_SLOTS) {
+ LCERR();
+ r = -EINVAL;
+ goto bad_device_id;
+ }
lc->id = device_id;
- if (dm_get_device(ti, argv[1], dm_table_get_mode(ti->table), &dev)) {
+ if (dm_get_device(ti, argv[1], dm_table_get_mode(ti->table),
+ &dev)) {
LCERR();
r = -EINVAL;
goto bad_get_device;
@@ -2349,16 +2416,16 @@ static int lc_ctr(struct dm_target *ti, unsigned int argc, char **argv)
lc->device = dev;
lc->cache = NULL;
- if (sscanf(argv[2], "%u", &cache_id) != 1) {
+ cache_id = 0;
+ r = parse_cache_id(argv[2], &cache_id);
+ if (r) {
LCERR();
- r = -EINVAL;
goto bad_cache_id;
}
if (cache_id) {
struct lc_cache *cache = lc_caches[cache_id];
if (!cache) {
- LCERR("cache is not set for id(%u)",
- cache_id);
+ LCERR("cache is not set for id(%u)", cache_id);
goto bad_no_cache;
}
lc->cache = lc_caches[cache_id];
@@ -2381,7 +2448,6 @@ static int lc_ctr(struct dm_target *ti, unsigned int argc, char **argv)
*/
/*
- * Note:
* Reference to the mapped_device
* is used to show device name (major:minor).
* major:minor is used in admin scripts
@@ -2420,8 +2486,7 @@ static int lc_message(struct dm_target *ti, unsigned argc, char **argv)
char *cmd = argv[0];
/*
- * We must separate
- * these add/remove sysfs code from .ctr
+ * We must separate these add/remove sysfs code from .ctr
* for a very complex reason.
*/
if (!strcasecmp(cmd, "add_sysfs")) {
@@ -2941,12 +3006,12 @@ static int lc_mgr_message(struct dm_target *ti, unsigned int argc, char **argv)
* cache device to operate.
*/
if (!strcasecmp(cmd, "switch_to")) {
- unsigned id;
- if (sscanf(argv[1], "%u", &id) != 1) {
+ u8 id;
+ int r = parse_cache_id(argv[1], &id);
+ if (r) {
LCERR();
- return -EINVAL;
+ return r;
}
-
cache_id_ptr = id;
return 0;
}