Quelle dm-log-userspace-transfer.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2006 - 2009 Red Hat , Inc .
*
* This file is released under the LGPL .
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <linux/workqueue.h>
#include <linux/connector.h>
#include <linux/device-mapper.h>
#include <linux/dm-log-userspace.h>
#include "dm-log-userspace-transfer.h"
static uint32_t dm_ulog_seq;
/*
* Netlink / Connector is an unreliable protocol . How long should
* we wait for a response before assuming it was lost and retrying ?
* ( If we do receive a response after this time , it will be discarded
* and the response to the resent request will be waited for .
*/
#define DM_ULOG_RETRY_TIMEOUT (15 * HZ)
/*
* Pre - allocated space for speed
*/
#define DM_ULOG_PREALLOCED_SIZE 512
static struct cn_msg *prealloced_cn_msg;
static struct dm_ulog_request *prealloced_ulog_tfr;
static struct cb_id ulog_cn_id = {
.idx = CN_IDX_DM,
.val = CN_VAL_DM_USERSPACE_LOG
};
static DEFINE_MUTEX(dm_ulog_lock);
struct receiving_pkg {
struct list_head list;
struct completion complete;
uint32_t seq;
int error;
size_t *data_size;
char *data;
};
static DEFINE_SPINLOCK(receiving_list_lock);
static struct list_head receiving_list;
static int dm_ulog_sendto_server(struct dm_ulog_request *tfr)
{
int r;
struct cn_msg *msg = prealloced_cn_msg;
memset(msg, 0 , sizeof (struct cn_msg));
msg->id.idx = ulog_cn_id.idx;
msg->id.val = ulog_cn_id.val;
msg->ack = 0 ;
msg->seq = tfr->seq;
msg->len = sizeof (struct dm_ulog_request) + tfr->data_size;
r = cn_netlink_send(msg, 0 , 0 , gfp_any());
return r;
}
/*
* Parameters for this function can be either msg or tfr , but not
* both . This function fills in the reply for a waiting request .
* If just msg is given , then the reply is simply an ACK from userspace
* that the request was received .
*
* Returns : 0 on success , - ENOENT on failure
*/
static int fill_pkg(struct cn_msg *msg, struct dm_ulog_request *tfr)
{
uint32_t rtn_seq = (msg) ? msg->seq : (tfr) ? tfr->seq : 0 ;
struct receiving_pkg *pkg;
/*
* The ' receiving_pkg ' entries in this list are statically
* allocated on the stack in ' dm_consult_userspace ' .
* Each process that is waiting for a reply from the user
* space server will have an entry in this list .
*
* We are safe to do it this way because the stack space
* is unique to each process , but still addressable by
* other processes .
*/
list_for_each_entry(pkg, &receiving_list, list) {
if (rtn_seq != pkg->seq)
continue ;
if (msg) {
pkg->error = -msg->ack;
/*
* If we are trying again , we will need to know our
* storage capacity . Otherwise , along with the
* error code , we make explicit that we have no data .
*/
if (pkg->error != -EAGAIN)
*(pkg->data_size) = 0 ;
} else if (tfr->data_size > *(pkg->data_size)) {
DMERR("Insufficient space to receive package [%u] (%u vs %zu)" ,
tfr->request_type, tfr->data_size, *(pkg->data_size));
*(pkg->data_size) = 0 ;
pkg->error = -ENOSPC;
} else {
pkg->error = tfr->error;
memcpy(pkg->data, tfr->data, tfr->data_size);
*(pkg->data_size) = tfr->data_size;
}
complete(&pkg->complete);
return 0 ;
}
return -ENOENT;
}
/*
* This is the connector callback that delivers data
* that was sent from userspace .
*/
static void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
{
struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1 );
if (!capable(CAP_SYS_ADMIN))
return ;
spin_lock(&receiving_list_lock);
if (msg->len == 0 )
fill_pkg(msg, NULL);
else if (msg->len < sizeof (*tfr))
DMERR("Incomplete message received (expected %u, got %u): [%u]" ,
(unsigned int )sizeof (*tfr), msg->len, msg->seq);
else
fill_pkg(NULL, tfr);
spin_unlock(&receiving_list_lock);
}
/**
* dm_consult_userspace
* @ uuid : log ' s universal unique identifier ( must be DM_UUID_LEN in size )
* @ luid : log ' s local unique identifier
* @ request_type : found in include / linux / dm - log - userspace . h
* @ data : data to tx to the server
* @ data_size : size of data in bytes
* @ rdata : place to put return data from server
* @ rdata_size : value - result ( amount of space given / amount of space used )
*
* rdata_size is undefined on failure .
*
* Memory used to communicate with userspace is zero ' ed
* before populating to ensure that no unwanted bits leak
* from kernel space to user - space . All userspace log communications
* between kernel and user space go through this function .
*
* Returns : 0 on success , - EXXX on failure
**/
int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type,
char *data, size_t data_size,
char *rdata, size_t *rdata_size)
{
int r = 0 ;
unsigned long tmo;
size_t dummy = 0 ;
int overhead_size = sizeof (struct dm_ulog_request) + sizeof (struct cn_msg);
struct dm_ulog_request *tfr = prealloced_ulog_tfr;
struct receiving_pkg pkg;
/*
* Given the space needed to hold the ' struct cn_msg ' and
* ' struct dm_ulog_request ' - do we have enough payload
* space remaining ?
*/
if (data_size > (DM_ULOG_PREALLOCED_SIZE - overhead_size)) {
DMINFO("Size of tfr exceeds preallocated size" );
return -EINVAL;
}
if (!rdata_size)
rdata_size = &dummy;
resend:
/*
* We serialize the sending of requests so we can
* use the preallocated space .
*/
mutex_lock(&dm_ulog_lock);
memset(tfr, 0 , DM_ULOG_PREALLOCED_SIZE - sizeof (struct cn_msg));
memcpy(tfr->uuid, uuid, DM_UUID_LEN);
tfr->version = DM_ULOG_REQUEST_VERSION;
tfr->luid = luid;
tfr->seq = dm_ulog_seq++;
/*
* Must be valid request type ( all other bits set to
* zero ) . This reserves other bits for possible future
* use .
*/
tfr->request_type = request_type & DM_ULOG_REQUEST_MASK;
tfr->data_size = data_size;
if (data && data_size)
memcpy(tfr->data, data, data_size);
memset(&pkg, 0 , sizeof (pkg));
init_completion(&pkg.complete);
pkg.seq = tfr->seq;
pkg.data_size = rdata_size;
pkg.data = rdata;
spin_lock(&receiving_list_lock);
list_add(&(pkg.list), &receiving_list);
spin_unlock(&receiving_list_lock);
r = dm_ulog_sendto_server(tfr);
mutex_unlock(&dm_ulog_lock);
if (r) {
DMERR("Unable to send log request [%u] to userspace: %d" ,
request_type, r);
spin_lock(&receiving_list_lock);
list_del_init(&(pkg.list));
spin_unlock(&receiving_list_lock);
goto out;
}
tmo = wait_for_completion_timeout(&(pkg.complete), DM_ULOG_RETRY_TIMEOUT);
spin_lock(&receiving_list_lock);
list_del_init(&(pkg.list));
spin_unlock(&receiving_list_lock);
if (!tmo) {
DMWARN("[%s] Request timed out: [%u/%u] - retrying" ,
(strlen(uuid) > 8 ) ?
(uuid + (strlen(uuid) - 8 )) : (uuid),
request_type, pkg.seq);
goto resend;
}
r = pkg.error;
if (r == -EAGAIN)
goto resend;
out:
return r;
}
int dm_ulog_tfr_init(void )
{
int r;
void *prealloced;
INIT_LIST_HEAD(&receiving_list);
prealloced = kmalloc(DM_ULOG_PREALLOCED_SIZE, GFP_KERNEL);
if (!prealloced)
return -ENOMEM;
prealloced_cn_msg = prealloced;
prealloced_ulog_tfr = prealloced + sizeof (struct cn_msg);
r = cn_add_callback(&ulog_cn_id, "dmlogusr" , cn_ulog_callback);
if (r) {
kfree(prealloced_cn_msg);
return r;
}
return 0 ;
}
void dm_ulog_tfr_exit(void )
{
cn_del_callback(&ulog_cn_id);
kfree(prealloced_cn_msg);
}
Messung V0.5 in Prozent C=96 H=94 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691