diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/md/dm-log-userspace-base.c')
-rw-r--r-- | ANDROID_3.4.5/drivers/md/dm-log-userspace-base.c | 818 |
1 files changed, 0 insertions, 818 deletions
diff --git a/ANDROID_3.4.5/drivers/md/dm-log-userspace-base.c b/ANDROID_3.4.5/drivers/md/dm-log-userspace-base.c deleted file mode 100644 index 9429159d..00000000 --- a/ANDROID_3.4.5/drivers/md/dm-log-userspace-base.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright (C) 2006-2009 Red Hat, Inc. - * - * This file is released under the LGPL. - */ - -#include <linux/bio.h> -#include <linux/slab.h> -#include <linux/dm-dirty-log.h> -#include <linux/device-mapper.h> -#include <linux/dm-log-userspace.h> -#include <linux/module.h> - -#include "dm-log-userspace-transfer.h" - -#define DM_LOG_USERSPACE_VSN "1.1.0" - -struct flush_entry { - int type; - region_t region; - struct list_head list; -}; - -/* - * This limit on the number of mark and clear request is, to a degree, - * arbitrary. However, there is some basis for the choice in the limits - * imposed on the size of data payload by dm-log-userspace-transfer.c: - * dm_consult_userspace(). - */ -#define MAX_FLUSH_GROUP_COUNT 32 - -struct log_c { - struct dm_target *ti; - struct dm_dev *log_dev; - uint32_t region_size; - region_t region_count; - uint64_t luid; - char uuid[DM_UUID_LEN]; - - char *usr_argv_str; - uint32_t usr_argc; - - /* - * in_sync_hint gets set when doing is_remote_recovering. It - * represents the first region that needs recovery. IOW, the - * first zero bit of sync_bits. This can be useful for to limit - * traffic for calls like is_remote_recovering and get_resync_work, - * but be take care in its use for anything else. - */ - uint64_t in_sync_hint; - - /* - * Mark and clear requests are held until a flush is issued - * so that we can group, and thereby limit, the amount of - * network traffic between kernel and userspace. The 'flush_lock' - * is used to protect these lists. - */ - spinlock_t flush_lock; - struct list_head mark_list; - struct list_head clear_list; -}; - -static mempool_t *flush_entry_pool; - -static void *flush_entry_alloc(gfp_t gfp_mask, void *pool_data) -{ - return kmalloc(sizeof(struct flush_entry), gfp_mask); -} - -static void flush_entry_free(void *element, void *pool_data) -{ - kfree(element); -} - -static int userspace_do_request(struct log_c *lc, const char *uuid, - int request_type, char *data, size_t data_size, - char *rdata, size_t *rdata_size) -{ - int r; - - /* - * If the server isn't there, -ESRCH is returned, - * and we must keep trying until the server is - * restored. - */ -retry: - r = dm_consult_userspace(uuid, lc->luid, request_type, data, - data_size, rdata, rdata_size); - - if (r != -ESRCH) - return r; - - DMERR(" Userspace log server not found."); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(2*HZ); - DMWARN("Attempting to contact userspace log server..."); - r = dm_consult_userspace(uuid, lc->luid, DM_ULOG_CTR, - lc->usr_argv_str, - strlen(lc->usr_argv_str) + 1, - NULL, NULL); - if (!r) - break; - } - DMINFO("Reconnected to userspace log server... DM_ULOG_CTR complete"); - r = dm_consult_userspace(uuid, lc->luid, DM_ULOG_RESUME, NULL, - 0, NULL, NULL); - if (!r) - goto retry; - - DMERR("Error trying to resume userspace log: %d", r); - - return -ESRCH; -} - -static int build_constructor_string(struct dm_target *ti, - unsigned argc, char **argv, - char **ctr_str) -{ - int i, str_size; - char *str = NULL; - - *ctr_str = NULL; - - for (i = 0, str_size = 0; i < argc; i++) - str_size += strlen(argv[i]) + 1; /* +1 for space between args */ - - str_size += 20; /* Max number of chars in a printed u64 number */ - - str = kzalloc(str_size, GFP_KERNEL); - if (!str) { - DMWARN("Unable to allocate memory for constructor string"); - return -ENOMEM; - } - - str_size = sprintf(str, "%llu", (unsigned long long)ti->len); - for (i = 0; i < argc; i++) - str_size += sprintf(str + str_size, " %s", argv[i]); - - *ctr_str = str; - return str_size; -} - -/* - * userspace_ctr - * - * argv contains: - * <UUID> <other args> - * Where 'other args' is the userspace implementation specific log - * arguments. An example might be: - * <UUID> clustered-disk <arg count> <log dev> <region_size> [[no]sync] - * - * So, this module will strip off the <UUID> for identification purposes - * when communicating with userspace about a log; but will pass on everything - * else. - */ -static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti, - unsigned argc, char **argv) -{ - int r = 0; - int str_size; - char *ctr_str = NULL; - struct log_c *lc = NULL; - uint64_t rdata; - size_t rdata_size = sizeof(rdata); - char *devices_rdata = NULL; - size_t devices_rdata_size = DM_NAME_LEN; - - if (argc < 3) { - DMWARN("Too few arguments to userspace dirty log"); - return -EINVAL; - } - - lc = kzalloc(sizeof(*lc), GFP_KERNEL); - if (!lc) { - DMWARN("Unable to allocate userspace log context."); - return -ENOMEM; - } - - /* The ptr value is sufficient for local unique id */ - lc->luid = (unsigned long)lc; - - lc->ti = ti; - - if (strlen(argv[0]) > (DM_UUID_LEN - 1)) { - DMWARN("UUID argument too long."); - kfree(lc); - return -EINVAL; - } - - strncpy(lc->uuid, argv[0], DM_UUID_LEN); - spin_lock_init(&lc->flush_lock); - INIT_LIST_HEAD(&lc->mark_list); - INIT_LIST_HEAD(&lc->clear_list); - - str_size = build_constructor_string(ti, argc - 1, argv + 1, &ctr_str); - if (str_size < 0) { - kfree(lc); - return str_size; - } - - devices_rdata = kzalloc(devices_rdata_size, GFP_KERNEL); - if (!devices_rdata) { - DMERR("Failed to allocate memory for device information"); - r = -ENOMEM; - goto out; - } - - /* - * Send table string and get back any opened device. - */ - r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR, - ctr_str, str_size, - devices_rdata, &devices_rdata_size); - - if (r < 0) { - if (r == -ESRCH) - DMERR("Userspace log server not found"); - else - DMERR("Userspace log server failed to create log"); - goto out; - } - - /* Since the region size does not change, get it now */ - rdata_size = sizeof(rdata); - r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_GET_REGION_SIZE, - NULL, 0, (char *)&rdata, &rdata_size); - - if (r) { - DMERR("Failed to get region size of dirty log"); - goto out; - } - - lc->region_size = (uint32_t)rdata; - lc->region_count = dm_sector_div_up(ti->len, lc->region_size); - - if (devices_rdata_size) { - if (devices_rdata[devices_rdata_size - 1] != '\0') { - DMERR("DM_ULOG_CTR device return string not properly terminated"); - r = -EINVAL; - goto out; - } - r = dm_get_device(ti, devices_rdata, - dm_table_get_mode(ti->table), &lc->log_dev); - if (r) - DMERR("Failed to register %s with device-mapper", - devices_rdata); - } -out: - kfree(devices_rdata); - if (r) { - kfree(lc); - kfree(ctr_str); - } else { - lc->usr_argv_str = ctr_str; - lc->usr_argc = argc; - log->context = lc; - } - - return r; -} - -static void userspace_dtr(struct dm_dirty_log *log) -{ - struct log_c *lc = log->context; - - (void) dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_DTR, - NULL, 0, - NULL, NULL); - - if (lc->log_dev) - dm_put_device(lc->ti, lc->log_dev); - - kfree(lc->usr_argv_str); - kfree(lc); - - return; -} - -static int userspace_presuspend(struct dm_dirty_log *log) -{ - int r; - struct log_c *lc = log->context; - - r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_PRESUSPEND, - NULL, 0, - NULL, NULL); - - return r; -} - -static int userspace_postsuspend(struct dm_dirty_log *log) -{ - int r; - struct log_c *lc = log->context; - - r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_POSTSUSPEND, - NULL, 0, - NULL, NULL); - - return r; -} - -static int userspace_resume(struct dm_dirty_log *log) -{ - int r; - struct log_c *lc = log->context; - - lc->in_sync_hint = 0; - r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_RESUME, - NULL, 0, - NULL, NULL); - - return r; -} - -static uint32_t userspace_get_region_size(struct dm_dirty_log *log) -{ - struct log_c *lc = log->context; - - return lc->region_size; -} - -/* - * userspace_is_clean - * - * Check whether a region is clean. If there is any sort of - * failure when consulting the server, we return not clean. - * - * Returns: 1 if clean, 0 otherwise - */ -static int userspace_is_clean(struct dm_dirty_log *log, region_t region) -{ - int r; - uint64_t region64 = (uint64_t)region; - int64_t is_clean; - size_t rdata_size; - struct log_c *lc = log->context; - - rdata_size = sizeof(is_clean); - r = userspace_do_request(lc, lc->uuid, DM_ULOG_IS_CLEAN, - (char *)®ion64, sizeof(region64), - (char *)&is_clean, &rdata_size); - - return (r) ? 0 : (int)is_clean; -} - -/* - * userspace_in_sync - * - * Check if the region is in-sync. If there is any sort - * of failure when consulting the server, we assume that - * the region is not in sync. - * - * If 'can_block' is set, return immediately - * - * Returns: 1 if in-sync, 0 if not-in-sync, -EWOULDBLOCK - */ -static int userspace_in_sync(struct dm_dirty_log *log, region_t region, - int can_block) -{ - int r; - uint64_t region64 = region; - int64_t in_sync; - size_t rdata_size; - struct log_c *lc = log->context; - - /* - * We can never respond directly - even if in_sync_hint is - * set. This is because another machine could see a device - * failure and mark the region out-of-sync. If we don't go - * to userspace to ask, we might think the region is in-sync - * and allow a read to pick up data that is stale. (This is - * very unlikely if a device actually fails; but it is very - * likely if a connection to one device from one machine fails.) - * - * There still might be a problem if the mirror caches the region - * state as in-sync... but then this call would not be made. So, - * that is a mirror problem. - */ - if (!can_block) - return -EWOULDBLOCK; - - rdata_size = sizeof(in_sync); - r = userspace_do_request(lc, lc->uuid, DM_ULOG_IN_SYNC, - (char *)®ion64, sizeof(region64), - (char *)&in_sync, &rdata_size); - return (r) ? 0 : (int)in_sync; -} - -static int flush_one_by_one(struct log_c *lc, struct list_head *flush_list) -{ - int r = 0; - struct flush_entry *fe; - - list_for_each_entry(fe, flush_list, list) { - r = userspace_do_request(lc, lc->uuid, fe->type, - (char *)&fe->region, - sizeof(fe->region), - NULL, NULL); - if (r) - break; - } - - return r; -} - -static int flush_by_group(struct log_c *lc, struct list_head *flush_list) -{ - int r = 0; - int count; - uint32_t type = 0; - struct flush_entry *fe, *tmp_fe; - LIST_HEAD(tmp_list); - uint64_t group[MAX_FLUSH_GROUP_COUNT]; - - /* - * Group process the requests - */ - while (!list_empty(flush_list)) { - count = 0; - - list_for_each_entry_safe(fe, tmp_fe, flush_list, list) { - group[count] = fe->region; - count++; - - list_move(&fe->list, &tmp_list); - - type = fe->type; - if (count >= MAX_FLUSH_GROUP_COUNT) - break; - } - - r = userspace_do_request(lc, lc->uuid, type, - (char *)(group), - count * sizeof(uint64_t), - NULL, NULL); - if (r) { - /* Group send failed. Attempt one-by-one. */ - list_splice_init(&tmp_list, flush_list); - r = flush_one_by_one(lc, flush_list); - break; - } - } - - /* - * Must collect flush_entrys that were successfully processed - * as a group so that they will be free'd by the caller. - */ - list_splice_init(&tmp_list, flush_list); - - return r; -} - -/* - * userspace_flush - * - * This function is ok to block. - * The flush happens in two stages. First, it sends all - * clear/mark requests that are on the list. Then it - * tells the server to commit them. This gives the - * server a chance to optimise the commit, instead of - * doing it for every request. - * - * Additionally, we could implement another thread that - * sends the requests up to the server - reducing the - * load on flush. Then the flush would have less in - * the list and be responsible for the finishing commit. - * - * Returns: 0 on success, < 0 on failure - */ -static int userspace_flush(struct dm_dirty_log *log) -{ - int r = 0; - unsigned long flags; - struct log_c *lc = log->context; - LIST_HEAD(mark_list); - LIST_HEAD(clear_list); - struct flush_entry *fe, *tmp_fe; - - spin_lock_irqsave(&lc->flush_lock, flags); - list_splice_init(&lc->mark_list, &mark_list); - list_splice_init(&lc->clear_list, &clear_list); - spin_unlock_irqrestore(&lc->flush_lock, flags); - - if (list_empty(&mark_list) && list_empty(&clear_list)) - return 0; - - r = flush_by_group(lc, &mark_list); - if (r) - goto fail; - - r = flush_by_group(lc, &clear_list); - if (r) - goto fail; - - r = userspace_do_request(lc, lc->uuid, DM_ULOG_FLUSH, - NULL, 0, NULL, NULL); - -fail: - /* - * We can safely remove these entries, even if failure. - * Calling code will receive an error and will know that - * the log facility has failed. - */ - list_for_each_entry_safe(fe, tmp_fe, &mark_list, list) { - list_del(&fe->list); - mempool_free(fe, flush_entry_pool); - } - list_for_each_entry_safe(fe, tmp_fe, &clear_list, list) { - list_del(&fe->list); - mempool_free(fe, flush_entry_pool); - } - - if (r) - dm_table_event(lc->ti->table); - - return r; -} - -/* - * userspace_mark_region - * - * This function should avoid blocking unless absolutely required. - * (Memory allocation is valid for blocking.) - */ -static void userspace_mark_region(struct dm_dirty_log *log, region_t region) -{ - unsigned long flags; - struct log_c *lc = log->context; - struct flush_entry *fe; - - /* Wait for an allocation, but _never_ fail */ - fe = mempool_alloc(flush_entry_pool, GFP_NOIO); - BUG_ON(!fe); - - spin_lock_irqsave(&lc->flush_lock, flags); - fe->type = DM_ULOG_MARK_REGION; - fe->region = region; - list_add(&fe->list, &lc->mark_list); - spin_unlock_irqrestore(&lc->flush_lock, flags); - - return; -} - -/* - * userspace_clear_region - * - * This function must not block. - * So, the alloc can't block. In the worst case, it is ok to - * fail. It would simply mean we can't clear the region. - * Does nothing to current sync context, but does mean - * the region will be re-sync'ed on a reload of the mirror - * even though it is in-sync. - */ -static void userspace_clear_region(struct dm_dirty_log *log, region_t region) -{ - unsigned long flags; - struct log_c *lc = log->context; - struct flush_entry *fe; - - /* - * If we fail to allocate, we skip the clearing of - * the region. This doesn't hurt us in any way, except - * to cause the region to be resync'ed when the - * device is activated next time. - */ - fe = mempool_alloc(flush_entry_pool, GFP_ATOMIC); - if (!fe) { - DMERR("Failed to allocate memory to clear region."); - return; - } - - spin_lock_irqsave(&lc->flush_lock, flags); - fe->type = DM_ULOG_CLEAR_REGION; - fe->region = region; - list_add(&fe->list, &lc->clear_list); - spin_unlock_irqrestore(&lc->flush_lock, flags); - - return; -} - -/* - * userspace_get_resync_work - * - * Get a region that needs recovery. It is valid to return - * an error for this function. - * - * Returns: 1 if region filled, 0 if no work, <0 on error - */ -static int userspace_get_resync_work(struct dm_dirty_log *log, region_t *region) -{ - int r; - size_t rdata_size; - struct log_c *lc = log->context; - struct { - int64_t i; /* 64-bit for mix arch compatibility */ - region_t r; - } pkg; - - if (lc->in_sync_hint >= lc->region_count) - return 0; - - rdata_size = sizeof(pkg); - r = userspace_do_request(lc, lc->uuid, DM_ULOG_GET_RESYNC_WORK, - NULL, 0, - (char *)&pkg, &rdata_size); - - *region = pkg.r; - return (r) ? r : (int)pkg.i; -} - -/* - * userspace_set_region_sync - * - * Set the sync status of a given region. This function - * must not fail. - */ -static void userspace_set_region_sync(struct dm_dirty_log *log, - region_t region, int in_sync) -{ - int r; - struct log_c *lc = log->context; - struct { - region_t r; - int64_t i; - } pkg; - - pkg.r = region; - pkg.i = (int64_t)in_sync; - - r = userspace_do_request(lc, lc->uuid, DM_ULOG_SET_REGION_SYNC, - (char *)&pkg, sizeof(pkg), - NULL, NULL); - - /* - * It would be nice to be able to report failures. - * However, it is easy emough to detect and resolve. - */ - return; -} - -/* - * userspace_get_sync_count - * - * If there is any sort of failure when consulting the server, - * we assume that the sync count is zero. - * - * Returns: sync count on success, 0 on failure - */ -static region_t userspace_get_sync_count(struct dm_dirty_log *log) -{ - int r; - size_t rdata_size; - uint64_t sync_count; - struct log_c *lc = log->context; - - rdata_size = sizeof(sync_count); - r = userspace_do_request(lc, lc->uuid, DM_ULOG_GET_SYNC_COUNT, - NULL, 0, - (char *)&sync_count, &rdata_size); - - if (r) - return 0; - - if (sync_count >= lc->region_count) - lc->in_sync_hint = lc->region_count; - - return (region_t)sync_count; -} - -/* - * userspace_status - * - * Returns: amount of space consumed - */ -static int userspace_status(struct dm_dirty_log *log, status_type_t status_type, - char *result, unsigned maxlen) -{ - int r = 0; - char *table_args; - size_t sz = (size_t)maxlen; - struct log_c *lc = log->context; - - switch (status_type) { - case STATUSTYPE_INFO: - r = userspace_do_request(lc, lc->uuid, DM_ULOG_STATUS_INFO, - NULL, 0, - result, &sz); - - if (r) { - sz = 0; - DMEMIT("%s 1 COM_FAILURE", log->type->name); - } - break; - case STATUSTYPE_TABLE: - sz = 0; - table_args = strchr(lc->usr_argv_str, ' '); - BUG_ON(!table_args); /* There will always be a ' ' */ - table_args++; - - DMEMIT("%s %u %s %s ", log->type->name, lc->usr_argc, - lc->uuid, table_args); - break; - } - return (r) ? 0 : (int)sz; -} - -/* - * userspace_is_remote_recovering - * - * Returns: 1 if region recovering, 0 otherwise - */ -static int userspace_is_remote_recovering(struct dm_dirty_log *log, - region_t region) -{ - int r; - uint64_t region64 = region; - struct log_c *lc = log->context; - static unsigned long long limit; - struct { - int64_t is_recovering; - uint64_t in_sync_hint; - } pkg; - size_t rdata_size = sizeof(pkg); - - /* - * Once the mirror has been reported to be in-sync, - * it will never again ask for recovery work. So, - * we can safely say there is not a remote machine - * recovering if the device is in-sync. (in_sync_hint - * must be reset at resume time.) - */ - if (region < lc->in_sync_hint) - return 0; - else if (jiffies < limit) - return 1; - - limit = jiffies + (HZ / 4); - r = userspace_do_request(lc, lc->uuid, DM_ULOG_IS_REMOTE_RECOVERING, - (char *)®ion64, sizeof(region64), - (char *)&pkg, &rdata_size); - if (r) - return 1; - - lc->in_sync_hint = pkg.in_sync_hint; - - return (int)pkg.is_recovering; -} - -static struct dm_dirty_log_type _userspace_type = { - .name = "userspace", - .module = THIS_MODULE, - .ctr = userspace_ctr, - .dtr = userspace_dtr, - .presuspend = userspace_presuspend, - .postsuspend = userspace_postsuspend, - .resume = userspace_resume, - .get_region_size = userspace_get_region_size, - .is_clean = userspace_is_clean, - .in_sync = userspace_in_sync, - .flush = userspace_flush, - .mark_region = userspace_mark_region, - .clear_region = userspace_clear_region, - .get_resync_work = userspace_get_resync_work, - .set_region_sync = userspace_set_region_sync, - .get_sync_count = userspace_get_sync_count, - .status = userspace_status, - .is_remote_recovering = userspace_is_remote_recovering, -}; - -static int __init userspace_dirty_log_init(void) -{ - int r = 0; - - flush_entry_pool = mempool_create(100, flush_entry_alloc, - flush_entry_free, NULL); - - if (!flush_entry_pool) { - DMWARN("Unable to create flush_entry_pool: No memory."); - return -ENOMEM; - } - - r = dm_ulog_tfr_init(); - if (r) { - DMWARN("Unable to initialize userspace log communications"); - mempool_destroy(flush_entry_pool); - return r; - } - - r = dm_dirty_log_type_register(&_userspace_type); - if (r) { - DMWARN("Couldn't register userspace dirty log type"); - dm_ulog_tfr_exit(); - mempool_destroy(flush_entry_pool); - return r; - } - - DMINFO("version " DM_LOG_USERSPACE_VSN " loaded"); - return 0; -} - -static void __exit userspace_dirty_log_exit(void) -{ - dm_dirty_log_type_unregister(&_userspace_type); - dm_ulog_tfr_exit(); - mempool_destroy(flush_entry_pool); - - DMINFO("version " DM_LOG_USERSPACE_VSN " unloaded"); - return; -} - -module_init(userspace_dirty_log_init); -module_exit(userspace_dirty_log_exit); - -MODULE_DESCRIPTION(DM_NAME " userspace dirty log link"); -MODULE_AUTHOR("Jonathan Brassow <dm-devel@redhat.com>"); -MODULE_LICENSE("GPL"); |