/*
* Copyright (C) 2013 Shaohua Li <shli@kernel.org>
* Copyright (C) 2014 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/idr.h>
#include <linux/timer.h>
#include <linux/parser.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <linux/uio_driver.h>
#include <net/genetlink.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/target_core_backend.h>
#include <target/target_core_backend_configfs.h>
#include <linux/target_core_user.h>
/*
* Define a shared-memory interface for LIO to pass SCSI commands and
* data to userspace for processing. This is to allow backends that
* are too complex for in-kernel support to be possible.
*
* It uses the UIO framework to do a lot of the device-creation and
* introspection work for us.
*
* See the .h file for how the ring is laid out. Note that while the
* command ring is defined, the particulars of the data area are
* not. Offset values in the command entry point to other locations
* internal to the mmap()ed area. There is separate space outside the
* command ring for data buffers. This leaves maximum flexibility for
* moving buffer allocations, or even page flipping or other
* allocation techniques, without altering the command ring layout.
*
* SECURITY:
* The user process must be assumed to be malicious. There's no way to
* prevent it breaking the command ring protocol if it wants, but in
* order to prevent other issues we must only ever read *data* from
* the shared memory area, not offsets or sizes. This applies to
* command ring entries as well as the mailbox. Extra code needed for
* this may have a 'UAM' comment.
*/
#define TCMU_TIME_OUT (30 * MSEC_PER_SEC)
#define CMDR_SIZE (16 * 4096)
#define DATA_SIZE (257 * 4096)
#define TCMU_RING_SIZE (CMDR_SIZE + DATA_SIZE)
static struct device *tcmu_root_device;
struct tcmu_hba {
u32 host_id;
};
/* User wants all cmds or just some */
enum passthru_level {
TCMU_PASS_ALL = 0,
TCMU_PASS_IO,
TCMU_PASS_INVALID,
};
#define TCMU_CONFIG_LEN 256
struct tcmu_dev {
struct se_device se_dev;
char *name;
struct se_hba *hba;
#define TCMU_DEV_BIT_OPEN 0
#define TCMU_DEV_BIT_BROKEN 1
unsigned long flags;
enum passthru_level pass_level;
struct uio_info uio_info;
struct tcmu_mailbox *mb_addr;
size_t dev_size;
u32 cmdr_size;
u32 cmdr_last_cleaned;
/* Offset of data ring from start of mb */
size_t data_off;
size_t data_size;
/* Ring head + tail values. */
/* Must add data_off and mb_addr to get the address */
size_t data_head;
size_t data_tail;
wait_queue_head_t wait_cmdr;
/* TODO should this be a mutex? */
spinlock_t cmdr_lock;
struct idr commands;
spinlock_t commands_lock;
struct timer_list timeout;
char dev_config[TCMU_CONFIG_LEN];
};
#define TCMU_DEV(_se_dev) container_of(_se_dev, struct tcmu_dev, se_dev)
#define CMDR_OFF sizeof(struct tcmu_mailbox)
struct tcmu_cmd {
struct se_cmd *se_cmd;
struct tcmu_dev *tcmu_dev;
uint16_t cmd_id;
/* Can't use se_cmd->data_length when cleaning up expired cmds, because if
cmd has been completed then accessing se_cmd is off limits */
size_t data_length;
unsigned long deadline;
#define TCMU_CMD_BIT_EXPIRED 0
unsigned long flags;
};
static struct kmem_cache *tcmu_cmd_cache;
/* multicast group */
enum tcmu_multicast_groups {
TCMU_MCGRP_CONFIG,
};
static const struct genl_multicast_group tcmu_mcgrps[] = {
[TCMU_MCGRP_CONFIG] = { .name = "config", },
};
/* Our generic netlink family */
static struct genl_family tcmu_genl_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "TCM-USER",
.version = 1,
.maxattr = TCMU_ATTR_MAX,
.mcgrps =