Hi all,
I needed the ability to use the file backing store to create a LUN that
was actually stored on multiple files. I copied and modified the
existing file backing store to store the data across multiple files.
I am attaching the source file. If this is something you feel would be
useful to include with tgtd then I would be more than happy to
contribute it.
The way it works is that the
path that is passed in should contain a list of the files (and size of
each) that the lun should be created on.
Thanks,
Kevin
/*
* Synchronous I/O file backing store routine that supports multiple
* files.
*
* Copyright (C) 2006-2007 FUJITA Tomonori <tomof@xxxxxxx>
* Copyright (C) 2006-2007 Mike Christie <michaelc@xxxxxxxxxxx>
* Copyright (C) 20011-2012 Kevin Baughman <curb_pks@xxxxxxxxx>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#define _XOPEN_SOURCE 500
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <linux/fs.h>
#include <sys/epoll.h>
#include <pthread.h>
#include "list.h"
#include "util.h"
#include "tgtd.h"
#include "scsi.h"
#include "bs_thread.h"
struct multi_fd
{
char *path;
int fd;
uint64_t offset;
uint32_t length;
uint32_t refcnt;
int bsoflags;
struct multi_fd *next;
};
struct multi_fd *multi_fds[128];
int max_fd = 0;
pthread_mutex_t fd_lock = PTHREAD_MUTEX_INITIALIZER;
static void set_medium_error(int *result, uint8_t *key, uint16_t *asc)
{
*result = SAM_STAT_CHECK_CONDITION;
*key = MEDIUM_ERROR;
*asc = ASC_READ_ERROR;
}
static int bs_reserve_multi_fd(struct multi_fd *multi_fd)
{
int ret=0;
pthread_mutex_lock(&fd_lock);
if (multi_fd->fd == -1)
{
multi_fd->fd = open(multi_fd->path, O_RDWR|O_LARGEFILE|O_CREAT|multi_fd->bsoflags);
if (multi_fd->fd < 0)
{
eprintf("Could not open %s, %m\n", multi_fd->path);
pthread_mutex_unlock(&fd_lock);
return -1;
}
ret = ftruncate(multi_fd->fd, multi_fd->length);
if (ret < 0)
{
eprintf("Could not truncate %s, %m\n", multi_fd->path);
close(multi_fd->fd);
multi_fd->fd = -1;
pthread_mutex_unlock(&fd_lock);
return ret;
}
}
multi_fd->refcnt++;
pthread_mutex_unlock(&fd_lock);
return ret;
}
static void bs_release_multi_fd(struct multi_fd *multi_fd)
{
pthread_mutex_lock(&fd_lock);
multi_fd->refcnt--;
if (multi_fd->refcnt == 0)
{
int ret = close(multi_fd->fd);
if (ret < 0)
{
eprintf("Could not close %s, %m\n", multi_fd->path);
}
multi_fd->fd = -1;
}
pthread_mutex_unlock(&fd_lock);
}
static void bs_sync_sync_range(struct scsi_cmd *cmd, uint32_t length,
int *result, uint8_t *key, uint16_t *asc)
{
int ret;
struct multi_fd *multi_fd = multi_fds[cmd->dev->fd];
while (multi_fd != NULL)
{
bs_reserve_multi_fd(multi_fd);
ret = fdatasync(cmd->dev->fd);
if (ret)
set_medium_error(result, key, asc);
bs_release_multi_fd(multi_fd);
multi_fd = multi_fd->next;
}
}
static int bs_rwdr_fd_overlap(struct multi_fd *multi_fd, uint64_t offset, uint32_t length,
uint64_t *ret_offset, uint32_t *ret_length, const char *op)
{
uint32_t shorten_bytes=0;
*ret_offset = 0;
*ret_length = 0;
if (offset + length >= multi_fd->offset && offset < multi_fd->offset + multi_fd->length)
{
/* Determine the offset for this chunk */
if (offset < multi_fd->offset)
{
*ret_offset = 0;
shorten_bytes = multi_fd->offset - offset;
}
else if((offset + length) > (multi_fd->offset + multi_fd->length))
{
*ret_offset = offset - multi_fd->offset;
shorten_bytes = (offset + length) - (multi_fd->offset + multi_fd->length);
}
else
{
*ret_offset = offset - multi_fd->offset;
}
/*
eprintf("%s overlaps path %s offset %llu length %d fd offset %llu fd length %d shorten %d",
op, multi_fd->path, offset, length, multi_fd->offset, multi_fd->length, shorten_bytes);
*/
*ret_length = length - shorten_bytes;
return 1;
}
else
{
/*
eprintf("%s does not overlap path %s offset %llu length %d fd offset %llu fd length %d shorten %d",
op, multi_fd->path, offset, length, multi_fd->offset, multi_fd->length, shorten_bytes);
*/
}
return 0;
}
static void bs_rdwr_request(struct scsi_cmd *cmd)
{
int ret;
uint32_t length;
int result = SAM_STAT_GOOD;
uint8_t key;
uint16_t asc;
struct multi_fd *multi_fd;
uint64_t tmp_offset;
uint32_t tmp_length;
ret = length = 0;
key = asc = 0;
switch (cmd->scb[0])
{
case SYNCHRONIZE_CACHE:
case SYNCHRONIZE_CACHE_16:
/* TODO */
length = (cmd->scb[0] == SYNCHRONIZE_CACHE) ? 0 : 0;
if (cmd->scb[1] & 0x2) {
result = SAM_STAT_CHECK_CONDITION;
key = ILLEGAL_REQUEST;
asc = ASC_INVALID_FIELD_IN_CDB;
} else
bs_sync_sync_range(cmd, length, &result, &key, &asc);
break;
case WRITE_6:
case WRITE_10:
case WRITE_12:
case WRITE_16:
length = scsi_get_out_length(cmd);
multi_fd = multi_fds[cmd->dev->fd];
while(multi_fd != NULL)
{
if (bs_rwdr_fd_overlap(multi_fd, cmd->offset, length, &tmp_offset, &tmp_length, "Write"))
{
bs_reserve_multi_fd(multi_fd);
ret += pwrite64(multi_fd->fd, scsi_get_out_buffer(cmd)+ret, tmp_length, tmp_offset);
bs_release_multi_fd(multi_fd);
}
multi_fd = multi_fd->next;
}
if (ret == length) {
/*
* it would be better not to access to pg
* directy.
*/
struct mode_pg *pg = cmd->dev->mode_pgs[0x8];
if (((cmd->scb[0] != WRITE_6) && (cmd->scb[1] & 0x8)) ||
!(pg->mode_data[0] & 0x04))
bs_sync_sync_range(cmd, length, &result, &key,
&asc);
} else
set_medium_error(&result, &key, &asc);
break;
case READ_6:
case READ_10:
case READ_12:
case READ_16:
length = scsi_get_in_length(cmd);
multi_fd = multi_fds[cmd->dev->fd];
while(multi_fd != NULL)
{
if (bs_rwdr_fd_overlap(multi_fd, cmd->offset, length, &tmp_offset, &tmp_length, "Read"))
{
bs_reserve_multi_fd(multi_fd);
ret += pread64(multi_fd->fd, scsi_get_in_buffer(cmd)+ret, tmp_length, tmp_offset);
bs_release_multi_fd(multi_fd);
}
multi_fd = multi_fd->next;
}
if (ret != length)
set_medium_error(&result, &key, &asc);
break;
default:
break;
}
dprintf("io done %p %x %d %u\n", cmd, cmd->scb[0], ret, length);
scsi_set_result(cmd, result);
if (result != SAM_STAT_GOOD) {
eprintf("io error %p %x %d %d %" PRIu64 ", %m\n",
cmd, cmd->scb[0], ret, length, cmd->offset);
sense_data_build(cmd, key, asc);
}
}
static int bs_rdwr_insert_fd(struct multi_fd *multi_fd)
{
int ret_fd;
pthread_mutex_lock(&fd_lock);
if (max_fd >= (sizeof(multi_fds)/sizeof(struct multi_fd *)))
{
ret_fd = -1;
goto fail;
}
multi_fds[max_fd] = multi_fd;
ret_fd = max_fd;
max_fd++;
fail:
pthread_mutex_unlock(&fd_lock);
return ret_fd;
}
static int bs_rdwr_multi_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
struct multi_fd *multi_fd=NULL, *tmp_fd=NULL;
uint64_t offset = 0;
int chunksize;
*size = 0;
fp = fopen(path, "r");
if (fp == NULL)
return -1;
read = getline(&line, &len, fp);
if (read == -1)
{
eprintf("Could not read chunksize %s, %m\n", path);
return read;
}
chunksize = atoi(line);
eprintf("Starting with chunksize %d file %s\n", chunksize, path);
while ((read = getline(&line, &len, fp)) != -1)
{
if (strcmp(line, "\n") == 0)
continue;
if (!multi_fd)
{
multi_fd = calloc(1, sizeof(struct multi_fd));
if (!multi_fd)
return -1;
tmp_fd = multi_fd;
}
else
{
tmp_fd->next = calloc(1, sizeof(struct multi_fd));
if (!tmp_fd->next)
return -1;
tmp_fd = tmp_fd->next;
}
line[strlen(line)-1]='\0';
tmp_fd->length = chunksize;
tmp_fd->offset = offset;
tmp_fd->path = strdup(line);
tmp_fd->bsoflags = lu->bsoflags;
tmp_fd->fd = -1;
*size += chunksize;
offset = *size;
}
*fd = bs_rdwr_insert_fd(multi_fd);
free(line);
fclose(fp);
return 0;
}
static void bs_rdwr_close(struct scsi_lu *lu)
{
close(lu->fd);
}
static int bs_rdwr_init(struct scsi_lu *lu)
{
struct bs_thread_info *info = BS_THREAD_I(lu);
return bs_thread_open(info, bs_rdwr_request, NR_WORKER_THREADS);
}
static void bs_rdwr_exit(struct scsi_lu *lu)
{
struct bs_thread_info *info = BS_THREAD_I(lu);
bs_thread_close(info);
}
static struct backingstore_template rdwr_bst = {
.bs_name = "rdwr_multi",
.bs_datasize = sizeof(struct bs_thread_info),
.bs_open = bs_rdwr_multi_open,
.bs_close = bs_rdwr_close,
.bs_init = bs_rdwr_init,
.bs_exit = bs_rdwr_exit,
.bs_cmd_submit = bs_thread_cmd_submit,
.bs_oflags_supported = O_SYNC | O_DIRECT,
};
__attribute__((constructor)) static void bs_rdwr_constructor(void)
{
register_backingstore_template(&rdwr_bst);
}