/*
* Xen SCSI frontend driver
*
* Copyright (c) 2008, FUJITSU Limited
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation; or, when distributed
* separately from the Linux kernel or incorporated into other
* software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/blkdev.h>
#include <linux/pfn.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
#include <xen/grant_table.h>
#include <xen/events.h>
#include <xen/page.h>
#include <xen/interface/grant_table.h>
#include <xen/interface/io/vscsiif.h>
#include <xen/interface/io/protocols.h>
#include <asm/xen/hypervisor.h>
#define VSCSIFRONT_OP_ADD_LUN 1
#define VSCSIFRONT_OP_DEL_LUN 2
#define VSCSIFRONT_OP_READD_LUN 3
/* Tuning point. */
#define VSCSIIF_DEFAULT_CMD_PER_LUN 10
#define VSCSIIF_MAX_TARGET 64
#define VSCSIIF_MAX_LUN 255
#define VSCSIIF_RING_SIZE __CONST_RING_SIZE(vscsiif, PAGE_SIZE)
#define VSCSIIF_MAX_REQS VSCSIIF_RING_SIZE
#define vscsiif_grants_sg(_sg) (PFN_UP((_sg) * \
sizeof(struct scsiif_request_segment)))
struct vscsifrnt_shadow {
/* command between backend and frontend */
unsigned char act;
uint8_t nr_segments;
uint16_t rqid;
uint16_t ref_rqid;
bool inflight;
unsigned int nr_grants; /* number of grants in gref[] */
struct scsiif_request_segment *sg; /* scatter/gather elements */
struct scsiif_request_segment seg[VSCSIIF_SG_TABLESIZE];
/* Do reset or abort function. */
wait_queue_head_t wq_reset; /* reset work queue */
int wait_reset; /* reset work queue condition */
int32_t rslt_reset; /* reset response status: */
/* SUCCESS or FAILED or: */
#define RSLT_RESET_WAITING 0
#define RSLT_RESET_ERR -1
/* Requested struct scsi_cmnd is stored from kernel. */
struct scsi_cmnd *sc;
int gref[vscsiif_grants_sg(SG_ALL) + SG_ALL];
};
struct vscsifrnt_info {
struct xenbus_device *dev;
struct Scsi_Host *host;
enum {
STATE_INACTIVE,
STATE_ACTIVE,
STATE_ERROR
} host_active;
unsigned int evtchn;
unsigned int irq;
grant_ref_t ring_ref;
struct vscsiif_front_ring ring;
struct vscsiif_response ring_rsp;
spinlock_t shadow_lock;
DECLARE_BITMAP(shadow_free_bitmap, VSCSIIF_MAX_REQS);
struct vscsifrnt_shadow *shadow[VSCSIIF_MAX_REQS];
/* Following items are protected by the host lock. */
wait_queue_head_t wq_sync;
wait_queue_head_t wq_pause;
unsigned int wait_ring_available:1;
unsigned int waiting_pause:1;
unsigned int pause:1;
unsigned callers;
char dev_state_path[64];
struct task_struct *curr;
};
static DEFINE_MUTEX(scsifront_mutex);
static void scsifront_wake_up(struct vscsifrnt_info *info)
{
info->wait_ring_available = 0;
wake_up(&info->wq_sync);
}
static int scsifront_get_rqid(struct vscsifrnt_info *info)
{
unsigned long flags;
int free;
spin_lock_irqsave(&info->shadow_lock, flags);
free = find_first_bit(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
__clear_bit(free, info->shadow_free_bitmap);
spin_unlock_irqrestore(&info->shadow_lock, flags);
return free;
}
static int _scsifront_put_rqid(struct vscsifrnt_info *info,