Added feature

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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);
}

[Index of Archives]     [Linux SCSI]     [Linux RAID]     [Linux Clusters]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]

  Powered by Linux