[dm-devel] [PATCH 2/2] dm-userspace: userspace code
FUJITA Tomonori
fujita.tomonori at lab.ntt.co.jp
Wed Oct 25 10:44:13 UTC 2006
This userspace program works with both interfaces. Compile it with
USE_RB defined for the ring buffer interface.
---
/*
* Copyright (C) International Business Machines Corp., 2006
* Author: Dan Smith <danms at us.ibm.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License. See the file COPYING in the main directory
* of this archive for more details.
*
*/
/*
* This example program demonstrates a trivial use of the dmu library
* for userspace orchestration of a device-mapper pseudo-device.
* Here, we simply map all reads and writes to the device given as the
* first argument to the program. For example:
*
* # ./example /dev/ram0
*
* will create a device /dev/mapper/foo in which all accesses are
* redirected to /dev/ram0.
*/
#include <errno.h>
#include <fcntl.h>
#include <libdevmapper.h>
#include <stdlib.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/dm-userspace.h>
#include <sys/stat.h>
#include <poll.h>
#include <sys/mman.h>
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define QUEUE_SIZE (1024 << 10)
#define MKDEV(x,y) (((x << 8) & 0xFF00) | (y & 0xFF))
struct uring {
uint32_t idx;
char *buf;
int size;
};
static struct uring kuring, ukring;
static int major, minor;
int dmu_ctl_open(char *ctl_dev, int flags)
{
int ctl_fd;
if (!ctl_dev)
return -EEXIST;
ctl_fd = open(ctl_dev, O_RDWR | flags);
return ctl_fd;
}
static void get_dm_control_dev(char *dm_device, unsigned *maj, unsigned *min)
{
struct dm_task *task;
int ret;
void *next = NULL;
uint64_t start, length;
char *ttype = NULL, *params = NULL;
task = dm_task_create(DM_DEVICE_STATUS);
ret = dm_task_set_name(task, dm_device);
if (!ret) {
printf("Failed to set name\n");
dm_task_destroy(task);
return;
}
ret = dm_task_run(task);
if (!ret) {
printf("Failed to run task\n");
dm_task_destroy(task);
return;
}
ret = 0;
do {
next = dm_get_next_target(task, next, &start, &length,
&ttype, ¶ms);
if (strcmp(ttype, "userspace") == 0) {
ret = sscanf(params, "%x:%x", maj, min);
if (ret == 2)
break;
}
} while (next);
}
static int make_device_node(unsigned major, unsigned minor)
{
char path[256];
sprintf(path, "/dev/dmu%i", minor);
return mknod(path, S_IFCHR, MKDEV(major, minor));
}
char *get_dmu_ctl_device(char *dm_device)
{
unsigned ctl_major, ctl_minor;
static char path[256];
get_dm_control_dev(dm_device, &ctl_major, &ctl_minor);
if (ctl_major == 0) {
fprintf(stderr, "Unable to get device number\n");
return NULL;
}
sprintf(path, "/dev/dmu%i", ctl_minor);
if (access(path, R_OK | W_OK)) {
if (make_device_node(ctl_major, ctl_minor))
return NULL;
}
return path;
}
static void __map_rsp(struct dmu_msg *req, struct dmu_msg *rsp)
{
rsp->hdr.msg_type = DM_USERSPACE_MAP_BLOCK_RESP;
rsp->payload.map_rsp.new_block = req->payload.map_req.org_block;
rsp->payload.map_rsp.offset = 0;
rsp->payload.map_rsp.id_of_req = req->hdr.id;
rsp->payload.map_rsp.flags = 0;
rsp->payload.map_rsp.dst_maj = major;
rsp->payload.map_rsp.dst_min = minor;
}
#ifdef USE_RB
static inline void ring_index_inc(struct uring *ring)
{
ring->idx = (ring->idx == DMU_MAX_EVENTS - 1) ? 0 : ring->idx + 1;
}
static inline struct dmu_msg *head_ring_hdr(struct uring *ring)
{
uint32_t pidx, off, pos;
pidx = ring->idx / DMU_EVENT_PER_PAGE;
off = ring->idx % DMU_EVENT_PER_PAGE;
pos = pidx * PAGE_SIZE + off * sizeof(struct dmu_msg);
return (struct dmu_msg *) (ring->buf + pos);
}
static int map_rsp_send(struct dmu_msg *req)
{
struct dmu_msg *msg;
msg = head_ring_hdr(&ukring);
if (msg->hdr.status)
return -ENOMEM;
__map_rsp(req, msg);
ring_index_inc(&ukring);
msg->hdr.status = 1;
return 0;
}
static void event_handler(int fd)
{
struct dmu_msg *msg;
int err;
retry:
msg = head_ring_hdr(&kuring);
if (!msg->hdr.status) {
write(fd, &err, 1);
return;
}
switch (msg->hdr.msg_type) {
case DM_USERSPACE_MAP_BLOCK_REQ:
err = map_rsp_send(msg);
break;
default:
printf("unknown event %u\n", msg->hdr.msg_type);
}
if (err)
write(fd, &err, 1);
else {
msg->hdr.status = 0;
ring_index_inc(&kuring);
goto retry;
}
}
#else
static void event_handler(int fd)
{
int ret, offset;
struct dmu_msg *req, *rsp;
ret = read(fd, kuring.buf, QUEUE_SIZE);
if (ret < 0)
return;
kuring.size = ret;
offset = 0;
ukring.size = 0;
while (offset < kuring.size) {
req = (struct dmu_msg *) (kuring.buf + offset);
offset += sizeof(*req);
switch (req->hdr.msg_type) {
case DM_USERSPACE_MAP_BLOCK_REQ:
if (ukring.size + sizeof(*rsp) > QUEUE_SIZE) {
write(fd, ukring.buf, ukring.size);
ukring.size = 0;
}
rsp = (struct dmu_msg *) (ukring.buf + ukring.size);
ukring.size += sizeof(*rsp);
__map_rsp(req, rsp);
break;
default:
printf("unknown event %u\n", req->hdr.msg_type);
}
}
if (ukring.size)
write(fd, ukring.buf, ukring.size);
}
#endif
int main(int argc, char **argv)
{
int fd, err, size;
char *buf;
struct stat s;
char path[1024];
struct pollfd pfd[1];
struct sched_param sp;
if (argc != 2) {
printf("Usage: %s <device>\n", argv[0]);
exit(1);
}
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
printf("failed to mlockall, exiting...");
exit(1);
}
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
err = sched_setscheduler(0, SCHED_FIFO, &sp);
if (err < 0) {
printf("fail to setschedular, %m\n");
exit(1);
}
printf("I'm creating a device-mapper device called 'foo'. \n"
"Be sure to remove it when you're done with this example\n"
"program! (run 'dmsetup remove foo')\n");
if (stat(argv[1], &s)) {
printf("fail to stat, %m\n");
exit(1);
}
major = (s.st_rdev & 0xFF00) >> 8;
minor = (s.st_rdev & 0x00FF);
memset(path, 0, sizeof(path));
/* Create a very simple device-mapper device with a small
section of sectors mapped to dm-userspace, at 512-byte
blocks */
snprintf(path, sizeof(path),
"echo 0 `blockdev --getsize %s` userspace foo 4096 %d:%d |"
"dmsetup create foo",
argv[1], major, minor);
system(path);
/* Open the control device for the device-mapper device 'foo' */
fd = dmu_ctl_open(get_dmu_ctl_device("foo"), 0);
if (fd < 0) {
printf("Failed to get control device\n");
exit(1);
}
#ifdef USE_RB
size = DMU_RING_SIZE;
buf = mmap(NULL, size * 2, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
printf("fail to mmap, %m\n");
return -EINVAL;
}
printf("Using the mmap interface.\n");
#else
size = QUEUE_SIZE;
buf = malloc(size * 2);
if (!buf) {
printf("fail to malloc, %m\n");
return -ENOMEM;
}
printf("Using the system call interface.\n");
#endif
kuring.idx = ukring.idx = 0;
kuring.buf = buf;
ukring.buf = buf + size;
pfd[0].fd = fd;
pfd[0].events = POLLIN | POLLOUT;
while (1) {
poll(pfd, 1, -1);
event_handler(fd);
}
return 0;
}
More information about the dm-devel
mailing list