932 lines · 22.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2006-2009 Red Hat, Inc. * * This file is released under the LGPL. */ #include <linux/bio.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/dm-dirty-log.h>#include <linux/device-mapper.h>#include <linux/dm-log-userspace.h>#include <linux/module.h>#include <linux/workqueue.h> #include "dm-log-userspace-transfer.h" #define DM_LOG_USERSPACE_VSN "1.3.0" #define FLUSH_ENTRY_POOL_SIZE 16 struct dm_dirty_log_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; 	char *usr_argv_str;	uint32_t usr_argc; 	uint32_t region_size;	region_t region_count;	uint64_t luid;	char uuid[DM_UUID_LEN]; 	/*	 * 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; 	/*	 * 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; 	/*	 * Workqueue for flush of clear region requests.	 */	struct workqueue_struct *dmlog_wq;	struct delayed_work flush_log_work;	atomic_t sched_flush; 	/*	 * Combine userspace flush and mark requests for efficiency.	 */	uint32_t integrated_flush; 	mempool_t flush_entry_pool;}; static struct kmem_cache *_flush_entry_cache; 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 int argc, char **argv,				    char **ctr_str){	int i, str_size;	char *str = NULL; 	*ctr_str = NULL; 	/*	 * Determine overall size of the string.	 */	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_size++; /* For NUL-terminator */ 	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;} static void do_flush(struct work_struct *work){	int r;	struct log_c *lc = container_of(work, struct log_c, flush_log_work.work); 	atomic_set(&lc->sched_flush, 0); 	r = userspace_do_request(lc, lc->uuid, DM_ULOG_FLUSH, NULL, 0, NULL, NULL); 	if (r)		dm_table_event(lc->ti->table);} /* * userspace_ctr * * argv contains: *	<UUID> [integrated_flush] <other args> * Where 'other args' are the userspace implementation-specific log * arguments. * * Example: *	<UUID> [integrated_flush] clustered-disk <arg count> <log dev> *	<region_size> [[no]sync] * * This module strips off the <UUID> and uses it for identification * purposes when communicating with userspace about a log. * * If integrated_flush is defined, the kernel combines flush * and mark requests. * * The rest of the line, beginning with 'clustered-disk', is passed * to the userspace ctr function. */static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,			 unsigned int 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_obj(*lc);	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;	} 	lc->usr_argc = argc; 	strscpy(lc->uuid, argv[0], sizeof(lc->uuid));	argc--;	argv++;	spin_lock_init(&lc->flush_lock);	INIT_LIST_HEAD(&lc->mark_list);	INIT_LIST_HEAD(&lc->clear_list); 	if (!strcasecmp(argv[0], "integrated_flush")) {		lc->integrated_flush = 1;		argc--;		argv++;	} 	str_size = build_constructor_string(ti, argc, argv, &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;	} 	r = mempool_init_slab_pool(&lc->flush_entry_pool, FLUSH_ENTRY_POOL_SIZE,				   _flush_entry_cache);	if (r) {		DMERR("Failed to create flush_entry_pool");		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);	} 	if (lc->integrated_flush) {		lc->dmlog_wq = alloc_workqueue("dmlogd",					       WQ_MEM_RECLAIM | WQ_PERCPU, 0);		if (!lc->dmlog_wq) {			DMERR("couldn't start dmlogd");			r = -ENOMEM;			goto out;		} 		INIT_DELAYED_WORK(&lc->flush_log_work, do_flush);		atomic_set(&lc->sched_flush, 0);	} out:	kfree(devices_rdata);	if (r) {		mempool_exit(&lc->flush_entry_pool);		kfree(lc);		kfree(ctr_str);	} else {		lc->usr_argv_str = ctr_str;		log->context = lc;	} 	return r;} static void userspace_dtr(struct dm_dirty_log *log){	struct log_c *lc = log->context; 	if (lc->integrated_flush) {		/* flush workqueue */		if (atomic_read(&lc->sched_flush))			flush_delayed_work(&lc->flush_log_work); 		destroy_workqueue(lc->dmlog_wq);	} 	(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); 	mempool_exit(&lc->flush_entry_pool); 	kfree(lc->usr_argv_str);	kfree(lc);} 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; 	/*	 * Run planned flush earlier.	 */	if (lc->integrated_flush && atomic_read(&lc->sched_flush))		flush_delayed_work(&lc->flush_log_work); 	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 *)&region64, 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 *)&region64, 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 dm_dirty_log_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 flush_with_payload){	int r = 0;	int count;	uint32_t type = 0;	struct dm_dirty_log_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) {

Lines 1–500 of 932.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t