18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2006-2009 Red Hat, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This file is released under the LGPL.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <net/sock.h>
118c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
128c2ecf20Sopenharmony_ci#include <linux/connector.h>
138c2ecf20Sopenharmony_ci#include <linux/device-mapper.h>
148c2ecf20Sopenharmony_ci#include <linux/dm-log-userspace.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "dm-log-userspace-transfer.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic uint32_t dm_ulog_seq;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * Netlink/Connector is an unreliable protocol.  How long should
228c2ecf20Sopenharmony_ci * we wait for a response before assuming it was lost and retrying?
238c2ecf20Sopenharmony_ci * (If we do receive a response after this time, it will be discarded
248c2ecf20Sopenharmony_ci * and the response to the resent request will be waited for.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci#define DM_ULOG_RETRY_TIMEOUT (15 * HZ)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Pre-allocated space for speed
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci#define DM_ULOG_PREALLOCED_SIZE 512
328c2ecf20Sopenharmony_cistatic struct cn_msg *prealloced_cn_msg;
338c2ecf20Sopenharmony_cistatic struct dm_ulog_request *prealloced_ulog_tfr;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct cb_id ulog_cn_id = {
368c2ecf20Sopenharmony_ci	.idx = CN_IDX_DM,
378c2ecf20Sopenharmony_ci	.val = CN_VAL_DM_USERSPACE_LOG
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dm_ulog_lock);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct receiving_pkg {
438c2ecf20Sopenharmony_ci	struct list_head list;
448c2ecf20Sopenharmony_ci	struct completion complete;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	uint32_t seq;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	int error;
498c2ecf20Sopenharmony_ci	size_t *data_size;
508c2ecf20Sopenharmony_ci	char *data;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(receiving_list_lock);
548c2ecf20Sopenharmony_cistatic struct list_head receiving_list;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int dm_ulog_sendto_server(struct dm_ulog_request *tfr)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	int r;
598c2ecf20Sopenharmony_ci	struct cn_msg *msg = prealloced_cn_msg;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	memset(msg, 0, sizeof(struct cn_msg));
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	msg->id.idx = ulog_cn_id.idx;
648c2ecf20Sopenharmony_ci	msg->id.val = ulog_cn_id.val;
658c2ecf20Sopenharmony_ci	msg->ack = 0;
668c2ecf20Sopenharmony_ci	msg->seq = tfr->seq;
678c2ecf20Sopenharmony_ci	msg->len = sizeof(struct dm_ulog_request) + tfr->data_size;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	r = cn_netlink_send(msg, 0, 0, gfp_any());
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return r;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Parameters for this function can be either msg or tfr, but not
768c2ecf20Sopenharmony_ci * both.  This function fills in the reply for a waiting request.
778c2ecf20Sopenharmony_ci * If just msg is given, then the reply is simply an ACK from userspace
788c2ecf20Sopenharmony_ci * that the request was received.
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci * Returns: 0 on success, -ENOENT on failure
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic int fill_pkg(struct cn_msg *msg, struct dm_ulog_request *tfr)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	uint32_t rtn_seq = (msg) ? msg->seq : (tfr) ? tfr->seq : 0;
858c2ecf20Sopenharmony_ci	struct receiving_pkg *pkg;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * The 'receiving_pkg' entries in this list are statically
898c2ecf20Sopenharmony_ci	 * allocated on the stack in 'dm_consult_userspace'.
908c2ecf20Sopenharmony_ci	 * Each process that is waiting for a reply from the user
918c2ecf20Sopenharmony_ci	 * space server will have an entry in this list.
928c2ecf20Sopenharmony_ci	 *
938c2ecf20Sopenharmony_ci	 * We are safe to do it this way because the stack space
948c2ecf20Sopenharmony_ci	 * is unique to each process, but still addressable by
958c2ecf20Sopenharmony_ci	 * other processes.
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	list_for_each_entry(pkg, &receiving_list, list) {
988c2ecf20Sopenharmony_ci		if (rtn_seq != pkg->seq)
998c2ecf20Sopenharmony_ci			continue;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if (msg) {
1028c2ecf20Sopenharmony_ci			pkg->error = -msg->ack;
1038c2ecf20Sopenharmony_ci			/*
1048c2ecf20Sopenharmony_ci			 * If we are trying again, we will need to know our
1058c2ecf20Sopenharmony_ci			 * storage capacity.  Otherwise, along with the
1068c2ecf20Sopenharmony_ci			 * error code, we make explicit that we have no data.
1078c2ecf20Sopenharmony_ci			 */
1088c2ecf20Sopenharmony_ci			if (pkg->error != -EAGAIN)
1098c2ecf20Sopenharmony_ci				*(pkg->data_size) = 0;
1108c2ecf20Sopenharmony_ci		} else if (tfr->data_size > *(pkg->data_size)) {
1118c2ecf20Sopenharmony_ci			DMERR("Insufficient space to receive package [%u] "
1128c2ecf20Sopenharmony_ci			      "(%u vs %zu)", tfr->request_type,
1138c2ecf20Sopenharmony_ci			      tfr->data_size, *(pkg->data_size));
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci			*(pkg->data_size) = 0;
1168c2ecf20Sopenharmony_ci			pkg->error = -ENOSPC;
1178c2ecf20Sopenharmony_ci		} else {
1188c2ecf20Sopenharmony_ci			pkg->error = tfr->error;
1198c2ecf20Sopenharmony_ci			memcpy(pkg->data, tfr->data, tfr->data_size);
1208c2ecf20Sopenharmony_ci			*(pkg->data_size) = tfr->data_size;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci		complete(&pkg->complete);
1238c2ecf20Sopenharmony_ci		return 0;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return -ENOENT;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * This is the connector callback that delivers data
1318c2ecf20Sopenharmony_ci * that was sent from userspace.
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_cistatic void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
1388c2ecf20Sopenharmony_ci		return;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	spin_lock(&receiving_list_lock);
1418c2ecf20Sopenharmony_ci	if (msg->len == 0)
1428c2ecf20Sopenharmony_ci		fill_pkg(msg, NULL);
1438c2ecf20Sopenharmony_ci	else if (msg->len < sizeof(*tfr))
1448c2ecf20Sopenharmony_ci		DMERR("Incomplete message received (expected %u, got %u): [%u]",
1458c2ecf20Sopenharmony_ci		      (unsigned)sizeof(*tfr), msg->len, msg->seq);
1468c2ecf20Sopenharmony_ci	else
1478c2ecf20Sopenharmony_ci		fill_pkg(NULL, tfr);
1488c2ecf20Sopenharmony_ci	spin_unlock(&receiving_list_lock);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * dm_consult_userspace
1538c2ecf20Sopenharmony_ci * @uuid: log's universal unique identifier (must be DM_UUID_LEN in size)
1548c2ecf20Sopenharmony_ci * @luid: log's local unique identifier
1558c2ecf20Sopenharmony_ci * @request_type:  found in include/linux/dm-log-userspace.h
1568c2ecf20Sopenharmony_ci * @data: data to tx to the server
1578c2ecf20Sopenharmony_ci * @data_size: size of data in bytes
1588c2ecf20Sopenharmony_ci * @rdata: place to put return data from server
1598c2ecf20Sopenharmony_ci * @rdata_size: value-result (amount of space given/amount of space used)
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * rdata_size is undefined on failure.
1628c2ecf20Sopenharmony_ci *
1638c2ecf20Sopenharmony_ci * Memory used to communicate with userspace is zero'ed
1648c2ecf20Sopenharmony_ci * before populating to ensure that no unwanted bits leak
1658c2ecf20Sopenharmony_ci * from kernel space to user-space.  All userspace log communications
1668c2ecf20Sopenharmony_ci * between kernel and user space go through this function.
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * Returns: 0 on success, -EXXX on failure
1698c2ecf20Sopenharmony_ci **/
1708c2ecf20Sopenharmony_ciint dm_consult_userspace(const char *uuid, uint64_t luid, int request_type,
1718c2ecf20Sopenharmony_ci			 char *data, size_t data_size,
1728c2ecf20Sopenharmony_ci			 char *rdata, size_t *rdata_size)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	int r = 0;
1758c2ecf20Sopenharmony_ci	unsigned long tmo;
1768c2ecf20Sopenharmony_ci	size_t dummy = 0;
1778c2ecf20Sopenharmony_ci	int overhead_size = sizeof(struct dm_ulog_request) + sizeof(struct cn_msg);
1788c2ecf20Sopenharmony_ci	struct dm_ulog_request *tfr = prealloced_ulog_tfr;
1798c2ecf20Sopenharmony_ci	struct receiving_pkg pkg;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/*
1828c2ecf20Sopenharmony_ci	 * Given the space needed to hold the 'struct cn_msg' and
1838c2ecf20Sopenharmony_ci	 * 'struct dm_ulog_request' - do we have enough payload
1848c2ecf20Sopenharmony_ci	 * space remaining?
1858c2ecf20Sopenharmony_ci	 */
1868c2ecf20Sopenharmony_ci	if (data_size > (DM_ULOG_PREALLOCED_SIZE - overhead_size)) {
1878c2ecf20Sopenharmony_ci		DMINFO("Size of tfr exceeds preallocated size");
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (!rdata_size)
1928c2ecf20Sopenharmony_ci		rdata_size = &dummy;
1938c2ecf20Sopenharmony_ciresend:
1948c2ecf20Sopenharmony_ci	/*
1958c2ecf20Sopenharmony_ci	 * We serialize the sending of requests so we can
1968c2ecf20Sopenharmony_ci	 * use the preallocated space.
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	mutex_lock(&dm_ulog_lock);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - sizeof(struct cn_msg));
2018c2ecf20Sopenharmony_ci	memcpy(tfr->uuid, uuid, DM_UUID_LEN);
2028c2ecf20Sopenharmony_ci	tfr->version = DM_ULOG_REQUEST_VERSION;
2038c2ecf20Sopenharmony_ci	tfr->luid = luid;
2048c2ecf20Sopenharmony_ci	tfr->seq = dm_ulog_seq++;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/*
2078c2ecf20Sopenharmony_ci	 * Must be valid request type (all other bits set to
2088c2ecf20Sopenharmony_ci	 * zero).  This reserves other bits for possible future
2098c2ecf20Sopenharmony_ci	 * use.
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci	tfr->request_type = request_type & DM_ULOG_REQUEST_MASK;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	tfr->data_size = data_size;
2148c2ecf20Sopenharmony_ci	if (data && data_size)
2158c2ecf20Sopenharmony_ci		memcpy(tfr->data, data, data_size);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	memset(&pkg, 0, sizeof(pkg));
2188c2ecf20Sopenharmony_ci	init_completion(&pkg.complete);
2198c2ecf20Sopenharmony_ci	pkg.seq = tfr->seq;
2208c2ecf20Sopenharmony_ci	pkg.data_size = rdata_size;
2218c2ecf20Sopenharmony_ci	pkg.data = rdata;
2228c2ecf20Sopenharmony_ci	spin_lock(&receiving_list_lock);
2238c2ecf20Sopenharmony_ci	list_add(&(pkg.list), &receiving_list);
2248c2ecf20Sopenharmony_ci	spin_unlock(&receiving_list_lock);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	r = dm_ulog_sendto_server(tfr);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	mutex_unlock(&dm_ulog_lock);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (r) {
2318c2ecf20Sopenharmony_ci		DMERR("Unable to send log request [%u] to userspace: %d",
2328c2ecf20Sopenharmony_ci		      request_type, r);
2338c2ecf20Sopenharmony_ci		spin_lock(&receiving_list_lock);
2348c2ecf20Sopenharmony_ci		list_del_init(&(pkg.list));
2358c2ecf20Sopenharmony_ci		spin_unlock(&receiving_list_lock);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		goto out;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	tmo = wait_for_completion_timeout(&(pkg.complete), DM_ULOG_RETRY_TIMEOUT);
2418c2ecf20Sopenharmony_ci	spin_lock(&receiving_list_lock);
2428c2ecf20Sopenharmony_ci	list_del_init(&(pkg.list));
2438c2ecf20Sopenharmony_ci	spin_unlock(&receiving_list_lock);
2448c2ecf20Sopenharmony_ci	if (!tmo) {
2458c2ecf20Sopenharmony_ci		DMWARN("[%s] Request timed out: [%u/%u] - retrying",
2468c2ecf20Sopenharmony_ci		       (strlen(uuid) > 8) ?
2478c2ecf20Sopenharmony_ci		       (uuid + (strlen(uuid) - 8)) : (uuid),
2488c2ecf20Sopenharmony_ci		       request_type, pkg.seq);
2498c2ecf20Sopenharmony_ci		goto resend;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	r = pkg.error;
2538c2ecf20Sopenharmony_ci	if (r == -EAGAIN)
2548c2ecf20Sopenharmony_ci		goto resend;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ciout:
2578c2ecf20Sopenharmony_ci	return r;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ciint dm_ulog_tfr_init(void)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	int r;
2638c2ecf20Sopenharmony_ci	void *prealloced;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&receiving_list);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	prealloced = kmalloc(DM_ULOG_PREALLOCED_SIZE, GFP_KERNEL);
2688c2ecf20Sopenharmony_ci	if (!prealloced)
2698c2ecf20Sopenharmony_ci		return -ENOMEM;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	prealloced_cn_msg = prealloced;
2728c2ecf20Sopenharmony_ci	prealloced_ulog_tfr = prealloced + sizeof(struct cn_msg);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	r = cn_add_callback(&ulog_cn_id, "dmlogusr", cn_ulog_callback);
2758c2ecf20Sopenharmony_ci	if (r) {
2768c2ecf20Sopenharmony_ci		kfree(prealloced_cn_msg);
2778c2ecf20Sopenharmony_ci		return r;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_civoid dm_ulog_tfr_exit(void)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	cn_del_callback(&ulog_cn_id);
2868c2ecf20Sopenharmony_ci	kfree(prealloced_cn_msg);
2878c2ecf20Sopenharmony_ci}
288