18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Xen SCSI frontend driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2008, FUJITSU Limited
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2
88c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; or, when distributed
98c2ecf20Sopenharmony_ci * separately from the Linux kernel or incorporated into other
108c2ecf20Sopenharmony_ci * software packages, subject to the following license:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
138c2ecf20Sopenharmony_ci * of this source file (the "Software"), to deal in the Software without
148c2ecf20Sopenharmony_ci * restriction, including without limitation the rights to use, copy, modify,
158c2ecf20Sopenharmony_ci * merge, publish, distribute, sublicense, and/or sell copies of the Software,
168c2ecf20Sopenharmony_ci * and to permit persons to whom the Software is furnished to do so, subject to
178c2ecf20Sopenharmony_ci * the following conditions:
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
208c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
238c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
248c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
258c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
268c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
278c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
288c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/module.h>
328c2ecf20Sopenharmony_ci#include <linux/kernel.h>
338c2ecf20Sopenharmony_ci#include <linux/device.h>
348c2ecf20Sopenharmony_ci#include <linux/wait.h>
358c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
368c2ecf20Sopenharmony_ci#include <linux/mutex.h>
378c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
388c2ecf20Sopenharmony_ci#include <linux/sched.h>
398c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
408c2ecf20Sopenharmony_ci#include <linux/pfn.h>
418c2ecf20Sopenharmony_ci#include <linux/slab.h>
428c2ecf20Sopenharmony_ci#include <linux/bitops.h>
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
458c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
468c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
478c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include <xen/xen.h>
508c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
518c2ecf20Sopenharmony_ci#include <xen/grant_table.h>
528c2ecf20Sopenharmony_ci#include <xen/events.h>
538c2ecf20Sopenharmony_ci#include <xen/page.h>
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include <xen/interface/grant_table.h>
568c2ecf20Sopenharmony_ci#include <xen/interface/io/vscsiif.h>
578c2ecf20Sopenharmony_ci#include <xen/interface/io/protocols.h>
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h>
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define GRANT_INVALID_REF	0
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define VSCSIFRONT_OP_ADD_LUN	1
658c2ecf20Sopenharmony_ci#define VSCSIFRONT_OP_DEL_LUN	2
668c2ecf20Sopenharmony_ci#define VSCSIFRONT_OP_READD_LUN	3
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Tuning point. */
698c2ecf20Sopenharmony_ci#define VSCSIIF_DEFAULT_CMD_PER_LUN 10
708c2ecf20Sopenharmony_ci#define VSCSIIF_MAX_TARGET          64
718c2ecf20Sopenharmony_ci#define VSCSIIF_MAX_LUN             255
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define VSCSIIF_RING_SIZE	__CONST_RING_SIZE(vscsiif, PAGE_SIZE)
748c2ecf20Sopenharmony_ci#define VSCSIIF_MAX_REQS	VSCSIIF_RING_SIZE
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define vscsiif_grants_sg(_sg)	(PFN_UP((_sg) *		\
778c2ecf20Sopenharmony_ci				sizeof(struct scsiif_request_segment)))
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct vscsifrnt_shadow {
808c2ecf20Sopenharmony_ci	/* command between backend and frontend */
818c2ecf20Sopenharmony_ci	unsigned char act;
828c2ecf20Sopenharmony_ci	uint8_t nr_segments;
838c2ecf20Sopenharmony_ci	uint16_t rqid;
848c2ecf20Sopenharmony_ci	uint16_t ref_rqid;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	unsigned int nr_grants;		/* number of grants in gref[] */
878c2ecf20Sopenharmony_ci	struct scsiif_request_segment *sg;	/* scatter/gather elements */
888c2ecf20Sopenharmony_ci	struct scsiif_request_segment seg[VSCSIIF_SG_TABLESIZE];
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Do reset or abort function. */
918c2ecf20Sopenharmony_ci	wait_queue_head_t wq_reset;	/* reset work queue           */
928c2ecf20Sopenharmony_ci	int wait_reset;			/* reset work queue condition */
938c2ecf20Sopenharmony_ci	int32_t rslt_reset;		/* reset response status:     */
948c2ecf20Sopenharmony_ci					/* SUCCESS or FAILED or:      */
958c2ecf20Sopenharmony_ci#define RSLT_RESET_WAITING	0
968c2ecf20Sopenharmony_ci#define RSLT_RESET_ERR		-1
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* Requested struct scsi_cmnd is stored from kernel. */
998c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc;
1008c2ecf20Sopenharmony_ci	int gref[vscsiif_grants_sg(SG_ALL) + SG_ALL];
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistruct vscsifrnt_info {
1048c2ecf20Sopenharmony_ci	struct xenbus_device *dev;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
1078c2ecf20Sopenharmony_ci	int host_active;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	unsigned int evtchn;
1108c2ecf20Sopenharmony_ci	unsigned int irq;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	grant_ref_t ring_ref;
1138c2ecf20Sopenharmony_ci	struct vscsiif_front_ring ring;
1148c2ecf20Sopenharmony_ci	struct vscsiif_response	ring_rsp;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	spinlock_t shadow_lock;
1178c2ecf20Sopenharmony_ci	DECLARE_BITMAP(shadow_free_bitmap, VSCSIIF_MAX_REQS);
1188c2ecf20Sopenharmony_ci	struct vscsifrnt_shadow *shadow[VSCSIIF_MAX_REQS];
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Following items are protected by the host lock. */
1218c2ecf20Sopenharmony_ci	wait_queue_head_t wq_sync;
1228c2ecf20Sopenharmony_ci	wait_queue_head_t wq_pause;
1238c2ecf20Sopenharmony_ci	unsigned int wait_ring_available:1;
1248c2ecf20Sopenharmony_ci	unsigned int waiting_pause:1;
1258c2ecf20Sopenharmony_ci	unsigned int pause:1;
1268c2ecf20Sopenharmony_ci	unsigned callers;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	char dev_state_path[64];
1298c2ecf20Sopenharmony_ci	struct task_struct *curr;
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(scsifront_mutex);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void scsifront_wake_up(struct vscsifrnt_info *info)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	info->wait_ring_available = 0;
1378c2ecf20Sopenharmony_ci	wake_up(&info->wq_sync);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int scsifront_get_rqid(struct vscsifrnt_info *info)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	unsigned long flags;
1438c2ecf20Sopenharmony_ci	int free;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->shadow_lock, flags);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	free = find_first_bit(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
1488c2ecf20Sopenharmony_ci	__clear_bit(free, info->shadow_free_bitmap);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->shadow_lock, flags);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return free;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int _scsifront_put_rqid(struct vscsifrnt_info *info, uint32_t id)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int empty = bitmap_empty(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	__set_bit(id, info->shadow_free_bitmap);
1608c2ecf20Sopenharmony_ci	info->shadow[id] = NULL;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return empty || info->wait_ring_available;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void scsifront_put_rqid(struct vscsifrnt_info *info, uint32_t id)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	unsigned long flags;
1688c2ecf20Sopenharmony_ci	int kick;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->shadow_lock, flags);
1718c2ecf20Sopenharmony_ci	kick = _scsifront_put_rqid(info, id);
1728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->shadow_lock, flags);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (kick)
1758c2ecf20Sopenharmony_ci		scsifront_wake_up(info);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int scsifront_do_request(struct vscsifrnt_info *info,
1798c2ecf20Sopenharmony_ci				struct vscsifrnt_shadow *shadow)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct vscsiif_front_ring *ring = &(info->ring);
1828c2ecf20Sopenharmony_ci	struct vscsiif_request *ring_req;
1838c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc = shadow->sc;
1848c2ecf20Sopenharmony_ci	uint32_t id;
1858c2ecf20Sopenharmony_ci	int i, notify;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (RING_FULL(&info->ring))
1888c2ecf20Sopenharmony_ci		return -EBUSY;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	id = scsifront_get_rqid(info);	/* use id in response */
1918c2ecf20Sopenharmony_ci	if (id >= VSCSIIF_MAX_REQS)
1928c2ecf20Sopenharmony_ci		return -EBUSY;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	info->shadow[id] = shadow;
1958c2ecf20Sopenharmony_ci	shadow->rqid = id;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ring_req = RING_GET_REQUEST(&(info->ring), ring->req_prod_pvt);
1988c2ecf20Sopenharmony_ci	ring->req_prod_pvt++;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	ring_req->rqid        = id;
2018c2ecf20Sopenharmony_ci	ring_req->act         = shadow->act;
2028c2ecf20Sopenharmony_ci	ring_req->ref_rqid    = shadow->ref_rqid;
2038c2ecf20Sopenharmony_ci	ring_req->nr_segments = shadow->nr_segments;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ring_req->id      = sc->device->id;
2068c2ecf20Sopenharmony_ci	ring_req->lun     = sc->device->lun;
2078c2ecf20Sopenharmony_ci	ring_req->channel = sc->device->channel;
2088c2ecf20Sopenharmony_ci	ring_req->cmd_len = sc->cmd_len;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	BUG_ON(sc->cmd_len > VSCSIIF_MAX_COMMAND_SIZE);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	memcpy(ring_req->cmnd, sc->cmnd, sc->cmd_len);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ring_req->sc_data_direction   = (uint8_t)sc->sc_data_direction;
2158c2ecf20Sopenharmony_ci	ring_req->timeout_per_command = sc->request->timeout / HZ;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	for (i = 0; i < (shadow->nr_segments & ~VSCSIIF_SG_GRANT); i++)
2188c2ecf20Sopenharmony_ci		ring_req->seg[i] = shadow->seg[i];
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify);
2218c2ecf20Sopenharmony_ci	if (notify)
2228c2ecf20Sopenharmony_ci		notify_remote_via_irq(info->irq);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void scsifront_gnttab_done(struct vscsifrnt_info *info,
2288c2ecf20Sopenharmony_ci				  struct vscsifrnt_shadow *shadow)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	int i;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (shadow->sc->sc_data_direction == DMA_NONE)
2338c2ecf20Sopenharmony_ci		return;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	for (i = 0; i < shadow->nr_grants; i++) {
2368c2ecf20Sopenharmony_ci		if (unlikely(!gnttab_try_end_foreign_access(shadow->gref[i]))) {
2378c2ecf20Sopenharmony_ci			shost_printk(KERN_ALERT, info->host, KBUILD_MODNAME
2388c2ecf20Sopenharmony_ci				     "grant still in use by backend\n");
2398c2ecf20Sopenharmony_ci			BUG();
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	kfree(shadow->sg);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void scsifront_cdb_cmd_done(struct vscsifrnt_info *info,
2478c2ecf20Sopenharmony_ci				   struct vscsiif_response *ring_rsp)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct vscsifrnt_shadow *shadow;
2508c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc;
2518c2ecf20Sopenharmony_ci	uint32_t id;
2528c2ecf20Sopenharmony_ci	uint8_t sense_len;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	id = ring_rsp->rqid;
2558c2ecf20Sopenharmony_ci	shadow = info->shadow[id];
2568c2ecf20Sopenharmony_ci	sc = shadow->sc;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	BUG_ON(sc == NULL);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	scsifront_gnttab_done(info, shadow);
2618c2ecf20Sopenharmony_ci	scsifront_put_rqid(info, id);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	sc->result = ring_rsp->rslt;
2648c2ecf20Sopenharmony_ci	scsi_set_resid(sc, ring_rsp->residual_len);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	sense_len = min_t(uint8_t, VSCSIIF_SENSE_BUFFERSIZE,
2678c2ecf20Sopenharmony_ci			  ring_rsp->sense_len);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (sense_len)
2708c2ecf20Sopenharmony_ci		memcpy(sc->sense_buffer, ring_rsp->sense_buffer, sense_len);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	sc->scsi_done(sc);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic void scsifront_sync_cmd_done(struct vscsifrnt_info *info,
2768c2ecf20Sopenharmony_ci				    struct vscsiif_response *ring_rsp)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	uint16_t id = ring_rsp->rqid;
2798c2ecf20Sopenharmony_ci	unsigned long flags;
2808c2ecf20Sopenharmony_ci	struct vscsifrnt_shadow *shadow = info->shadow[id];
2818c2ecf20Sopenharmony_ci	int kick;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->shadow_lock, flags);
2848c2ecf20Sopenharmony_ci	shadow->wait_reset = 1;
2858c2ecf20Sopenharmony_ci	switch (shadow->rslt_reset) {
2868c2ecf20Sopenharmony_ci	case RSLT_RESET_WAITING:
2878c2ecf20Sopenharmony_ci		shadow->rslt_reset = ring_rsp->rslt;
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	case RSLT_RESET_ERR:
2908c2ecf20Sopenharmony_ci		kick = _scsifront_put_rqid(info, id);
2918c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->shadow_lock, flags);
2928c2ecf20Sopenharmony_ci		kfree(shadow);
2938c2ecf20Sopenharmony_ci		if (kick)
2948c2ecf20Sopenharmony_ci			scsifront_wake_up(info);
2958c2ecf20Sopenharmony_ci		return;
2968c2ecf20Sopenharmony_ci	default:
2978c2ecf20Sopenharmony_ci		shost_printk(KERN_ERR, info->host, KBUILD_MODNAME
2988c2ecf20Sopenharmony_ci			     "bad reset state %d, possibly leaking %u\n",
2998c2ecf20Sopenharmony_ci			     shadow->rslt_reset, id);
3008c2ecf20Sopenharmony_ci		break;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->shadow_lock, flags);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	wake_up(&shadow->wq_reset);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void scsifront_do_response(struct vscsifrnt_info *info,
3088c2ecf20Sopenharmony_ci				  struct vscsiif_response *ring_rsp)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	if (WARN(ring_rsp->rqid >= VSCSIIF_MAX_REQS ||
3118c2ecf20Sopenharmony_ci		 test_bit(ring_rsp->rqid, info->shadow_free_bitmap),
3128c2ecf20Sopenharmony_ci		 "illegal rqid %u returned by backend!\n", ring_rsp->rqid))
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (info->shadow[ring_rsp->rqid]->act == VSCSIIF_ACT_SCSI_CDB)
3168c2ecf20Sopenharmony_ci		scsifront_cdb_cmd_done(info, ring_rsp);
3178c2ecf20Sopenharmony_ci	else
3188c2ecf20Sopenharmony_ci		scsifront_sync_cmd_done(info, ring_rsp);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int scsifront_ring_drain(struct vscsifrnt_info *info)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct vscsiif_response *ring_rsp;
3248c2ecf20Sopenharmony_ci	RING_IDX i, rp;
3258c2ecf20Sopenharmony_ci	int more_to_do = 0;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	rp = info->ring.sring->rsp_prod;
3288c2ecf20Sopenharmony_ci	rmb();	/* ordering required respective to dom0 */
3298c2ecf20Sopenharmony_ci	for (i = info->ring.rsp_cons; i != rp; i++) {
3308c2ecf20Sopenharmony_ci		ring_rsp = RING_GET_RESPONSE(&info->ring, i);
3318c2ecf20Sopenharmony_ci		scsifront_do_response(info, ring_rsp);
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	info->ring.rsp_cons = i;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (i != info->ring.req_prod_pvt)
3378c2ecf20Sopenharmony_ci		RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do);
3388c2ecf20Sopenharmony_ci	else
3398c2ecf20Sopenharmony_ci		info->ring.sring->rsp_event = i + 1;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return more_to_do;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int scsifront_cmd_done(struct vscsifrnt_info *info)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	int more_to_do;
3478c2ecf20Sopenharmony_ci	unsigned long flags;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	spin_lock_irqsave(info->host->host_lock, flags);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	more_to_do = scsifront_ring_drain(info);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	info->wait_ring_available = 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(info->host->host_lock, flags);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	wake_up(&info->wq_sync);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return more_to_do;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic irqreturn_t scsifront_irq_fn(int irq, void *dev_id)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = dev_id;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	while (scsifront_cmd_done(info))
3678c2ecf20Sopenharmony_ci		/* Yield point for this unbounded loop. */
3688c2ecf20Sopenharmony_ci		cond_resched();
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void scsifront_finish_all(struct vscsifrnt_info *info)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	unsigned i;
3768c2ecf20Sopenharmony_ci	struct vscsiif_response resp;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	scsifront_ring_drain(info);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	for (i = 0; i < VSCSIIF_MAX_REQS; i++) {
3818c2ecf20Sopenharmony_ci		if (test_bit(i, info->shadow_free_bitmap))
3828c2ecf20Sopenharmony_ci			continue;
3838c2ecf20Sopenharmony_ci		resp.rqid = i;
3848c2ecf20Sopenharmony_ci		resp.sense_len = 0;
3858c2ecf20Sopenharmony_ci		resp.rslt = DID_RESET << 16;
3868c2ecf20Sopenharmony_ci		resp.residual_len = 0;
3878c2ecf20Sopenharmony_ci		scsifront_do_response(info, &resp);
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int map_data_for_request(struct vscsifrnt_info *info,
3928c2ecf20Sopenharmony_ci				struct scsi_cmnd *sc,
3938c2ecf20Sopenharmony_ci				struct vscsifrnt_shadow *shadow)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	grant_ref_t gref_head;
3968c2ecf20Sopenharmony_ci	struct page *page;
3978c2ecf20Sopenharmony_ci	int err, ref, ref_cnt = 0;
3988c2ecf20Sopenharmony_ci	int grant_ro = (sc->sc_data_direction == DMA_TO_DEVICE);
3998c2ecf20Sopenharmony_ci	unsigned int i, off, len, bytes;
4008c2ecf20Sopenharmony_ci	unsigned int data_len = scsi_bufflen(sc);
4018c2ecf20Sopenharmony_ci	unsigned int data_grants = 0, seg_grants = 0;
4028c2ecf20Sopenharmony_ci	struct scatterlist *sg;
4038c2ecf20Sopenharmony_ci	struct scsiif_request_segment *seg;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (sc->sc_data_direction == DMA_NONE || !data_len)
4068c2ecf20Sopenharmony_ci		return 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	scsi_for_each_sg(sc, sg, scsi_sg_count(sc), i)
4098c2ecf20Sopenharmony_ci		data_grants += PFN_UP(sg->offset + sg->length);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (data_grants > VSCSIIF_SG_TABLESIZE) {
4128c2ecf20Sopenharmony_ci		if (data_grants > info->host->sg_tablesize) {
4138c2ecf20Sopenharmony_ci			shost_printk(KERN_ERR, info->host, KBUILD_MODNAME
4148c2ecf20Sopenharmony_ci			     "Unable to map request_buffer for command!\n");
4158c2ecf20Sopenharmony_ci			return -E2BIG;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci		seg_grants = vscsiif_grants_sg(data_grants);
4188c2ecf20Sopenharmony_ci		shadow->sg = kcalloc(data_grants,
4198c2ecf20Sopenharmony_ci			sizeof(struct scsiif_request_segment), GFP_ATOMIC);
4208c2ecf20Sopenharmony_ci		if (!shadow->sg)
4218c2ecf20Sopenharmony_ci			return -ENOMEM;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci	seg = shadow->sg ? : shadow->seg;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	err = gnttab_alloc_grant_references(seg_grants + data_grants,
4268c2ecf20Sopenharmony_ci					    &gref_head);
4278c2ecf20Sopenharmony_ci	if (err) {
4288c2ecf20Sopenharmony_ci		kfree(shadow->sg);
4298c2ecf20Sopenharmony_ci		shost_printk(KERN_ERR, info->host, KBUILD_MODNAME
4308c2ecf20Sopenharmony_ci			     "gnttab_alloc_grant_references() error\n");
4318c2ecf20Sopenharmony_ci		return -ENOMEM;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (seg_grants) {
4358c2ecf20Sopenharmony_ci		page = virt_to_page(seg);
4368c2ecf20Sopenharmony_ci		off = offset_in_page(seg);
4378c2ecf20Sopenharmony_ci		len = sizeof(struct scsiif_request_segment) * data_grants;
4388c2ecf20Sopenharmony_ci		while (len > 0) {
4398c2ecf20Sopenharmony_ci			bytes = min_t(unsigned int, len, PAGE_SIZE - off);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci			ref = gnttab_claim_grant_reference(&gref_head);
4428c2ecf20Sopenharmony_ci			BUG_ON(ref == -ENOSPC);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci			gnttab_grant_foreign_access_ref(ref,
4458c2ecf20Sopenharmony_ci				info->dev->otherend_id,
4468c2ecf20Sopenharmony_ci				xen_page_to_gfn(page), 1);
4478c2ecf20Sopenharmony_ci			shadow->gref[ref_cnt] = ref;
4488c2ecf20Sopenharmony_ci			shadow->seg[ref_cnt].gref   = ref;
4498c2ecf20Sopenharmony_ci			shadow->seg[ref_cnt].offset = (uint16_t)off;
4508c2ecf20Sopenharmony_ci			shadow->seg[ref_cnt].length = (uint16_t)bytes;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci			page++;
4538c2ecf20Sopenharmony_ci			len -= bytes;
4548c2ecf20Sopenharmony_ci			off = 0;
4558c2ecf20Sopenharmony_ci			ref_cnt++;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci		BUG_ON(seg_grants < ref_cnt);
4588c2ecf20Sopenharmony_ci		seg_grants = ref_cnt;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	scsi_for_each_sg(sc, sg, scsi_sg_count(sc), i) {
4628c2ecf20Sopenharmony_ci		page = sg_page(sg);
4638c2ecf20Sopenharmony_ci		off = sg->offset;
4648c2ecf20Sopenharmony_ci		len = sg->length;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		while (len > 0 && data_len > 0) {
4678c2ecf20Sopenharmony_ci			/*
4688c2ecf20Sopenharmony_ci			 * sg sends a scatterlist that is larger than
4698c2ecf20Sopenharmony_ci			 * the data_len it wants transferred for certain
4708c2ecf20Sopenharmony_ci			 * IO sizes.
4718c2ecf20Sopenharmony_ci			 */
4728c2ecf20Sopenharmony_ci			bytes = min_t(unsigned int, len, PAGE_SIZE - off);
4738c2ecf20Sopenharmony_ci			bytes = min(bytes, data_len);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci			ref = gnttab_claim_grant_reference(&gref_head);
4768c2ecf20Sopenharmony_ci			BUG_ON(ref == -ENOSPC);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci			gnttab_grant_foreign_access_ref(ref,
4798c2ecf20Sopenharmony_ci				info->dev->otherend_id,
4808c2ecf20Sopenharmony_ci				xen_page_to_gfn(page),
4818c2ecf20Sopenharmony_ci				grant_ro);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci			shadow->gref[ref_cnt] = ref;
4848c2ecf20Sopenharmony_ci			seg->gref   = ref;
4858c2ecf20Sopenharmony_ci			seg->offset = (uint16_t)off;
4868c2ecf20Sopenharmony_ci			seg->length = (uint16_t)bytes;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci			page++;
4898c2ecf20Sopenharmony_ci			seg++;
4908c2ecf20Sopenharmony_ci			len -= bytes;
4918c2ecf20Sopenharmony_ci			data_len -= bytes;
4928c2ecf20Sopenharmony_ci			off = 0;
4938c2ecf20Sopenharmony_ci			ref_cnt++;
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (seg_grants)
4988c2ecf20Sopenharmony_ci		shadow->nr_segments = VSCSIIF_SG_GRANT | seg_grants;
4998c2ecf20Sopenharmony_ci	else
5008c2ecf20Sopenharmony_ci		shadow->nr_segments = (uint8_t)ref_cnt;
5018c2ecf20Sopenharmony_ci	shadow->nr_grants = ref_cnt;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return 0;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int scsifront_enter(struct vscsifrnt_info *info)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	if (info->pause)
5098c2ecf20Sopenharmony_ci		return 1;
5108c2ecf20Sopenharmony_ci	info->callers++;
5118c2ecf20Sopenharmony_ci	return 0;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void scsifront_return(struct vscsifrnt_info *info)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	info->callers--;
5178c2ecf20Sopenharmony_ci	if (info->callers)
5188c2ecf20Sopenharmony_ci		return;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	if (!info->waiting_pause)
5218c2ecf20Sopenharmony_ci		return;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	info->waiting_pause = 0;
5248c2ecf20Sopenharmony_ci	wake_up(&info->wq_pause);
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic int scsifront_queuecommand(struct Scsi_Host *shost,
5288c2ecf20Sopenharmony_ci				  struct scsi_cmnd *sc)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = shost_priv(shost);
5318c2ecf20Sopenharmony_ci	struct vscsifrnt_shadow *shadow = scsi_cmd_priv(sc);
5328c2ecf20Sopenharmony_ci	unsigned long flags;
5338c2ecf20Sopenharmony_ci	int err;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	sc->result = 0;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	shadow->sc  = sc;
5388c2ecf20Sopenharmony_ci	shadow->act = VSCSIIF_ACT_SCSI_CDB;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
5418c2ecf20Sopenharmony_ci	if (scsifront_enter(info)) {
5428c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
5438c2ecf20Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	err = map_data_for_request(info, sc, shadow);
5478c2ecf20Sopenharmony_ci	if (err < 0) {
5488c2ecf20Sopenharmony_ci		pr_debug("%s: err %d\n", __func__, err);
5498c2ecf20Sopenharmony_ci		scsifront_return(info);
5508c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
5518c2ecf20Sopenharmony_ci		if (err == -ENOMEM)
5528c2ecf20Sopenharmony_ci			return SCSI_MLQUEUE_HOST_BUSY;
5538c2ecf20Sopenharmony_ci		sc->result = DID_ERROR << 16;
5548c2ecf20Sopenharmony_ci		sc->scsi_done(sc);
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (scsifront_do_request(info, shadow)) {
5598c2ecf20Sopenharmony_ci		scsifront_gnttab_done(info, shadow);
5608c2ecf20Sopenharmony_ci		goto busy;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	scsifront_return(info);
5648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return 0;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cibusy:
5698c2ecf20Sopenharmony_ci	scsifront_return(info);
5708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
5718c2ecf20Sopenharmony_ci	pr_debug("%s: busy\n", __func__);
5728c2ecf20Sopenharmony_ci	return SCSI_MLQUEUE_HOST_BUSY;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci/*
5768c2ecf20Sopenharmony_ci * Any exception handling (reset or abort) must be forwarded to the backend.
5778c2ecf20Sopenharmony_ci * We have to wait until an answer is returned. This answer contains the
5788c2ecf20Sopenharmony_ci * result to be returned to the requestor.
5798c2ecf20Sopenharmony_ci */
5808c2ecf20Sopenharmony_cistatic int scsifront_action_handler(struct scsi_cmnd *sc, uint8_t act)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct Scsi_Host *host = sc->device->host;
5838c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = shost_priv(host);
5848c2ecf20Sopenharmony_ci	struct vscsifrnt_shadow *shadow, *s = scsi_cmd_priv(sc);
5858c2ecf20Sopenharmony_ci	int err = 0;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	shadow = kzalloc(sizeof(*shadow), GFP_NOIO);
5888c2ecf20Sopenharmony_ci	if (!shadow)
5898c2ecf20Sopenharmony_ci		return FAILED;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	shadow->act = act;
5928c2ecf20Sopenharmony_ci	shadow->rslt_reset = RSLT_RESET_WAITING;
5938c2ecf20Sopenharmony_ci	shadow->sc = sc;
5948c2ecf20Sopenharmony_ci	shadow->ref_rqid = s->rqid;
5958c2ecf20Sopenharmony_ci	init_waitqueue_head(&shadow->wq_reset);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	spin_lock_irq(host->host_lock);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	for (;;) {
6008c2ecf20Sopenharmony_ci		if (scsifront_enter(info))
6018c2ecf20Sopenharmony_ci			goto fail;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		if (!scsifront_do_request(info, shadow))
6048c2ecf20Sopenharmony_ci			break;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		scsifront_return(info);
6078c2ecf20Sopenharmony_ci		if (err)
6088c2ecf20Sopenharmony_ci			goto fail;
6098c2ecf20Sopenharmony_ci		info->wait_ring_available = 1;
6108c2ecf20Sopenharmony_ci		spin_unlock_irq(host->host_lock);
6118c2ecf20Sopenharmony_ci		err = wait_event_interruptible(info->wq_sync,
6128c2ecf20Sopenharmony_ci					       !info->wait_ring_available);
6138c2ecf20Sopenharmony_ci		spin_lock_irq(host->host_lock);
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	spin_unlock_irq(host->host_lock);
6178c2ecf20Sopenharmony_ci	err = wait_event_interruptible(shadow->wq_reset, shadow->wait_reset);
6188c2ecf20Sopenharmony_ci	spin_lock_irq(host->host_lock);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if (!err) {
6218c2ecf20Sopenharmony_ci		err = shadow->rslt_reset;
6228c2ecf20Sopenharmony_ci		scsifront_put_rqid(info, shadow->rqid);
6238c2ecf20Sopenharmony_ci		kfree(shadow);
6248c2ecf20Sopenharmony_ci	} else {
6258c2ecf20Sopenharmony_ci		spin_lock(&info->shadow_lock);
6268c2ecf20Sopenharmony_ci		shadow->rslt_reset = RSLT_RESET_ERR;
6278c2ecf20Sopenharmony_ci		spin_unlock(&info->shadow_lock);
6288c2ecf20Sopenharmony_ci		err = FAILED;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	scsifront_return(info);
6328c2ecf20Sopenharmony_ci	spin_unlock_irq(host->host_lock);
6338c2ecf20Sopenharmony_ci	return err;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cifail:
6368c2ecf20Sopenharmony_ci	spin_unlock_irq(host->host_lock);
6378c2ecf20Sopenharmony_ci	kfree(shadow);
6388c2ecf20Sopenharmony_ci	return FAILED;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic int scsifront_eh_abort_handler(struct scsi_cmnd *sc)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
6448c2ecf20Sopenharmony_ci	return scsifront_action_handler(sc, VSCSIIF_ACT_SCSI_ABORT);
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic int scsifront_dev_reset_handler(struct scsi_cmnd *sc)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
6508c2ecf20Sopenharmony_ci	return scsifront_action_handler(sc, VSCSIIF_ACT_SCSI_RESET);
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic int scsifront_sdev_configure(struct scsi_device *sdev)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = shost_priv(sdev->host);
6568c2ecf20Sopenharmony_ci	int err;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (info && current == info->curr) {
6598c2ecf20Sopenharmony_ci		err = xenbus_printf(XBT_NIL, info->dev->nodename,
6608c2ecf20Sopenharmony_ci			      info->dev_state_path, "%d", XenbusStateConnected);
6618c2ecf20Sopenharmony_ci		if (err) {
6628c2ecf20Sopenharmony_ci			xenbus_dev_error(info->dev, err,
6638c2ecf20Sopenharmony_ci				"%s: writing dev_state_path", __func__);
6648c2ecf20Sopenharmony_ci			return err;
6658c2ecf20Sopenharmony_ci		}
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	return 0;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic void scsifront_sdev_destroy(struct scsi_device *sdev)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = shost_priv(sdev->host);
6748c2ecf20Sopenharmony_ci	int err;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (info && current == info->curr) {
6778c2ecf20Sopenharmony_ci		err = xenbus_printf(XBT_NIL, info->dev->nodename,
6788c2ecf20Sopenharmony_ci			      info->dev_state_path, "%d", XenbusStateClosed);
6798c2ecf20Sopenharmony_ci		if (err)
6808c2ecf20Sopenharmony_ci			xenbus_dev_error(info->dev, err,
6818c2ecf20Sopenharmony_ci				"%s: writing dev_state_path", __func__);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic struct scsi_host_template scsifront_sht = {
6868c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
6878c2ecf20Sopenharmony_ci	.name			= "Xen SCSI frontend driver",
6888c2ecf20Sopenharmony_ci	.queuecommand		= scsifront_queuecommand,
6898c2ecf20Sopenharmony_ci	.eh_abort_handler	= scsifront_eh_abort_handler,
6908c2ecf20Sopenharmony_ci	.eh_device_reset_handler = scsifront_dev_reset_handler,
6918c2ecf20Sopenharmony_ci	.slave_configure	= scsifront_sdev_configure,
6928c2ecf20Sopenharmony_ci	.slave_destroy		= scsifront_sdev_destroy,
6938c2ecf20Sopenharmony_ci	.cmd_per_lun		= VSCSIIF_DEFAULT_CMD_PER_LUN,
6948c2ecf20Sopenharmony_ci	.can_queue		= VSCSIIF_MAX_REQS,
6958c2ecf20Sopenharmony_ci	.this_id		= -1,
6968c2ecf20Sopenharmony_ci	.cmd_size		= sizeof(struct vscsifrnt_shadow),
6978c2ecf20Sopenharmony_ci	.sg_tablesize		= VSCSIIF_SG_TABLESIZE,
6988c2ecf20Sopenharmony_ci	.proc_name		= "scsifront",
6998c2ecf20Sopenharmony_ci};
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int scsifront_alloc_ring(struct vscsifrnt_info *info)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct xenbus_device *dev = info->dev;
7048c2ecf20Sopenharmony_ci	struct vscsiif_sring *sring;
7058c2ecf20Sopenharmony_ci	grant_ref_t gref;
7068c2ecf20Sopenharmony_ci	int err = -ENOMEM;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/***** Frontend to Backend ring start *****/
7098c2ecf20Sopenharmony_ci	sring = (struct vscsiif_sring *)__get_free_page(GFP_KERNEL);
7108c2ecf20Sopenharmony_ci	if (!sring) {
7118c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err,
7128c2ecf20Sopenharmony_ci			"fail to allocate shared ring (Front to Back)");
7138c2ecf20Sopenharmony_ci		return err;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci	SHARED_RING_INIT(sring);
7168c2ecf20Sopenharmony_ci	FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	err = xenbus_grant_ring(dev, sring, 1, &gref);
7198c2ecf20Sopenharmony_ci	if (err < 0) {
7208c2ecf20Sopenharmony_ci		free_page((unsigned long)sring);
7218c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err,
7228c2ecf20Sopenharmony_ci			"fail to grant shared ring (Front to Back)");
7238c2ecf20Sopenharmony_ci		return err;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci	info->ring_ref = gref;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	err = xenbus_alloc_evtchn(dev, &info->evtchn);
7288c2ecf20Sopenharmony_ci	if (err) {
7298c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn");
7308c2ecf20Sopenharmony_ci		goto free_gnttab;
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	err = bind_evtchn_to_irq(info->evtchn);
7348c2ecf20Sopenharmony_ci	if (err <= 0) {
7358c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq");
7368c2ecf20Sopenharmony_ci		goto free_gnttab;
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	info->irq = err;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	err = request_threaded_irq(info->irq, NULL, scsifront_irq_fn,
7428c2ecf20Sopenharmony_ci				   IRQF_ONESHOT, "scsifront", info);
7438c2ecf20Sopenharmony_ci	if (err) {
7448c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "request_threaded_irq");
7458c2ecf20Sopenharmony_ci		goto free_irq;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return 0;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci/* free resource */
7518c2ecf20Sopenharmony_cifree_irq:
7528c2ecf20Sopenharmony_ci	unbind_from_irqhandler(info->irq, info);
7538c2ecf20Sopenharmony_cifree_gnttab:
7548c2ecf20Sopenharmony_ci	gnttab_end_foreign_access(info->ring_ref, 0,
7558c2ecf20Sopenharmony_ci				  (unsigned long)info->ring.sring);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	return err;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void scsifront_free_ring(struct vscsifrnt_info *info)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	unbind_from_irqhandler(info->irq, info);
7638c2ecf20Sopenharmony_ci	gnttab_end_foreign_access(info->ring_ref, 0,
7648c2ecf20Sopenharmony_ci				  (unsigned long)info->ring.sring);
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic int scsifront_init_ring(struct vscsifrnt_info *info)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct xenbus_device *dev = info->dev;
7708c2ecf20Sopenharmony_ci	struct xenbus_transaction xbt;
7718c2ecf20Sopenharmony_ci	int err;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	err = scsifront_alloc_ring(info);
7768c2ecf20Sopenharmony_ci	if (err)
7778c2ecf20Sopenharmony_ci		return err;
7788c2ecf20Sopenharmony_ci	pr_debug("%s: %u %u\n", __func__, info->ring_ref, info->evtchn);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ciagain:
7818c2ecf20Sopenharmony_ci	err = xenbus_transaction_start(&xbt);
7828c2ecf20Sopenharmony_ci	if (err)
7838c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "starting transaction");
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u",
7868c2ecf20Sopenharmony_ci			    info->ring_ref);
7878c2ecf20Sopenharmony_ci	if (err) {
7888c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "%s", "writing ring-ref");
7898c2ecf20Sopenharmony_ci		goto fail;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
7938c2ecf20Sopenharmony_ci			    info->evtchn);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (err) {
7968c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "%s", "writing event-channel");
7978c2ecf20Sopenharmony_ci		goto fail;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	err = xenbus_transaction_end(xbt, 0);
8018c2ecf20Sopenharmony_ci	if (err) {
8028c2ecf20Sopenharmony_ci		if (err == -EAGAIN)
8038c2ecf20Sopenharmony_ci			goto again;
8048c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "completing transaction");
8058c2ecf20Sopenharmony_ci		goto free_sring;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return 0;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cifail:
8118c2ecf20Sopenharmony_ci	xenbus_transaction_end(xbt, 1);
8128c2ecf20Sopenharmony_cifree_sring:
8138c2ecf20Sopenharmony_ci	scsifront_free_ring(info);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	return err;
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic int scsifront_probe(struct xenbus_device *dev,
8208c2ecf20Sopenharmony_ci			   const struct xenbus_device_id *id)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info;
8238c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
8248c2ecf20Sopenharmony_ci	int err = -ENOMEM;
8258c2ecf20Sopenharmony_ci	char name[TASK_COMM_LEN];
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	host = scsi_host_alloc(&scsifront_sht, sizeof(*info));
8288c2ecf20Sopenharmony_ci	if (!host) {
8298c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, err, "fail to allocate scsi host");
8308c2ecf20Sopenharmony_ci		return err;
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci	info = (struct vscsifrnt_info *)host->hostdata;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, info);
8358c2ecf20Sopenharmony_ci	info->dev = dev;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	bitmap_fill(info->shadow_free_bitmap, VSCSIIF_MAX_REQS);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	err = scsifront_init_ring(info);
8408c2ecf20Sopenharmony_ci	if (err) {
8418c2ecf20Sopenharmony_ci		scsi_host_put(host);
8428c2ecf20Sopenharmony_ci		return err;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	init_waitqueue_head(&info->wq_sync);
8468c2ecf20Sopenharmony_ci	init_waitqueue_head(&info->wq_pause);
8478c2ecf20Sopenharmony_ci	spin_lock_init(&info->shadow_lock);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	snprintf(name, TASK_COMM_LEN, "vscsiif.%d", host->host_no);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	host->max_id      = VSCSIIF_MAX_TARGET;
8528c2ecf20Sopenharmony_ci	host->max_channel = 0;
8538c2ecf20Sopenharmony_ci	host->max_lun     = VSCSIIF_MAX_LUN;
8548c2ecf20Sopenharmony_ci	host->max_sectors = (host->sg_tablesize - 1) * PAGE_SIZE / 512;
8558c2ecf20Sopenharmony_ci	host->max_cmd_len = VSCSIIF_MAX_COMMAND_SIZE;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	err = scsi_add_host(host, &dev->dev);
8588c2ecf20Sopenharmony_ci	if (err) {
8598c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "fail to add scsi host %d\n", err);
8608c2ecf20Sopenharmony_ci		goto free_sring;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci	info->host = host;
8638c2ecf20Sopenharmony_ci	info->host_active = 1;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateInitialised);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	return 0;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cifree_sring:
8708c2ecf20Sopenharmony_ci	scsifront_free_ring(info);
8718c2ecf20Sopenharmony_ci	scsi_host_put(host);
8728c2ecf20Sopenharmony_ci	return err;
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cistatic int scsifront_resume(struct xenbus_device *dev)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
8788c2ecf20Sopenharmony_ci	struct Scsi_Host *host = info->host;
8798c2ecf20Sopenharmony_ci	int err;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	spin_lock_irq(host->host_lock);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Finish all still pending commands. */
8848c2ecf20Sopenharmony_ci	scsifront_finish_all(info);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	spin_unlock_irq(host->host_lock);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/* Reconnect to dom0. */
8898c2ecf20Sopenharmony_ci	scsifront_free_ring(info);
8908c2ecf20Sopenharmony_ci	err = scsifront_init_ring(info);
8918c2ecf20Sopenharmony_ci	if (err) {
8928c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "fail to resume %d\n", err);
8938c2ecf20Sopenharmony_ci		scsi_host_put(host);
8948c2ecf20Sopenharmony_ci		return err;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateInitialised);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	return 0;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_cistatic int scsifront_suspend(struct xenbus_device *dev)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
9058c2ecf20Sopenharmony_ci	struct Scsi_Host *host = info->host;
9068c2ecf20Sopenharmony_ci	int err = 0;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	/* No new commands for the backend. */
9098c2ecf20Sopenharmony_ci	spin_lock_irq(host->host_lock);
9108c2ecf20Sopenharmony_ci	info->pause = 1;
9118c2ecf20Sopenharmony_ci	while (info->callers && !err) {
9128c2ecf20Sopenharmony_ci		info->waiting_pause = 1;
9138c2ecf20Sopenharmony_ci		info->wait_ring_available = 0;
9148c2ecf20Sopenharmony_ci		spin_unlock_irq(host->host_lock);
9158c2ecf20Sopenharmony_ci		wake_up(&info->wq_sync);
9168c2ecf20Sopenharmony_ci		err = wait_event_interruptible(info->wq_pause,
9178c2ecf20Sopenharmony_ci					       !info->waiting_pause);
9188c2ecf20Sopenharmony_ci		spin_lock_irq(host->host_lock);
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci	spin_unlock_irq(host->host_lock);
9218c2ecf20Sopenharmony_ci	return err;
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_cistatic int scsifront_remove(struct xenbus_device *dev)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	pr_debug("%s: %s removed\n", __func__, dev->nodename);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	mutex_lock(&scsifront_mutex);
9318c2ecf20Sopenharmony_ci	if (info->host_active) {
9328c2ecf20Sopenharmony_ci		/* Scsi_host not yet removed */
9338c2ecf20Sopenharmony_ci		scsi_remove_host(info->host);
9348c2ecf20Sopenharmony_ci		info->host_active = 0;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	mutex_unlock(&scsifront_mutex);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	scsifront_free_ring(info);
9398c2ecf20Sopenharmony_ci	scsi_host_put(info->host);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	return 0;
9428c2ecf20Sopenharmony_ci}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_cistatic void scsifront_disconnect(struct vscsifrnt_info *info)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	struct xenbus_device *dev = info->dev;
9478c2ecf20Sopenharmony_ci	struct Scsi_Host *host = info->host;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	pr_debug("%s: %s disconnect\n", __func__, dev->nodename);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	/*
9528c2ecf20Sopenharmony_ci	 * When this function is executed, all devices of
9538c2ecf20Sopenharmony_ci	 * Frontend have been deleted.
9548c2ecf20Sopenharmony_ci	 * Therefore, it need not block I/O before remove_host.
9558c2ecf20Sopenharmony_ci	 */
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	mutex_lock(&scsifront_mutex);
9588c2ecf20Sopenharmony_ci	if (info->host_active) {
9598c2ecf20Sopenharmony_ci		scsi_remove_host(host);
9608c2ecf20Sopenharmony_ci		info->host_active = 0;
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci	mutex_unlock(&scsifront_mutex);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	xenbus_frontend_closed(dev);
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_cistatic void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct xenbus_device *dev = info->dev;
9708c2ecf20Sopenharmony_ci	int i, err = 0;
9718c2ecf20Sopenharmony_ci	char str[64];
9728c2ecf20Sopenharmony_ci	char **dir;
9738c2ecf20Sopenharmony_ci	unsigned int dir_n = 0;
9748c2ecf20Sopenharmony_ci	unsigned int device_state;
9758c2ecf20Sopenharmony_ci	unsigned int hst, chn, tgt, lun;
9768c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	dir = xenbus_directory(XBT_NIL, dev->otherend, "vscsi-devs", &dir_n);
9798c2ecf20Sopenharmony_ci	if (IS_ERR(dir))
9808c2ecf20Sopenharmony_ci		return;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* mark current task as the one allowed to modify device states */
9838c2ecf20Sopenharmony_ci	BUG_ON(info->curr);
9848c2ecf20Sopenharmony_ci	info->curr = current;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	for (i = 0; i < dir_n; i++) {
9878c2ecf20Sopenharmony_ci		/* read status */
9888c2ecf20Sopenharmony_ci		snprintf(str, sizeof(str), "vscsi-devs/%s/state", dir[i]);
9898c2ecf20Sopenharmony_ci		err = xenbus_scanf(XBT_NIL, dev->otherend, str, "%u",
9908c2ecf20Sopenharmony_ci				   &device_state);
9918c2ecf20Sopenharmony_ci		if (XENBUS_EXIST_ERR(err))
9928c2ecf20Sopenharmony_ci			continue;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci		/* virtual SCSI device */
9958c2ecf20Sopenharmony_ci		snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", dir[i]);
9968c2ecf20Sopenharmony_ci		err = xenbus_scanf(XBT_NIL, dev->otherend, str,
9978c2ecf20Sopenharmony_ci				   "%u:%u:%u:%u", &hst, &chn, &tgt, &lun);
9988c2ecf20Sopenharmony_ci		if (XENBUS_EXIST_ERR(err))
9998c2ecf20Sopenharmony_ci			continue;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		/*
10028c2ecf20Sopenharmony_ci		 * Front device state path, used in slave_configure called
10038c2ecf20Sopenharmony_ci		 * on successfull scsi_add_device, and in slave_destroy called
10048c2ecf20Sopenharmony_ci		 * on remove of a device.
10058c2ecf20Sopenharmony_ci		 */
10068c2ecf20Sopenharmony_ci		snprintf(info->dev_state_path, sizeof(info->dev_state_path),
10078c2ecf20Sopenharmony_ci			 "vscsi-devs/%s/state", dir[i]);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci		switch (op) {
10108c2ecf20Sopenharmony_ci		case VSCSIFRONT_OP_ADD_LUN:
10118c2ecf20Sopenharmony_ci			if (device_state != XenbusStateInitialised)
10128c2ecf20Sopenharmony_ci				break;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci			if (scsi_add_device(info->host, chn, tgt, lun)) {
10158c2ecf20Sopenharmony_ci				dev_err(&dev->dev, "scsi_add_device\n");
10168c2ecf20Sopenharmony_ci				err = xenbus_printf(XBT_NIL, dev->nodename,
10178c2ecf20Sopenharmony_ci					      info->dev_state_path,
10188c2ecf20Sopenharmony_ci					      "%d", XenbusStateClosed);
10198c2ecf20Sopenharmony_ci				if (err)
10208c2ecf20Sopenharmony_ci					xenbus_dev_error(dev, err,
10218c2ecf20Sopenharmony_ci						"%s: writing dev_state_path", __func__);
10228c2ecf20Sopenharmony_ci			}
10238c2ecf20Sopenharmony_ci			break;
10248c2ecf20Sopenharmony_ci		case VSCSIFRONT_OP_DEL_LUN:
10258c2ecf20Sopenharmony_ci			if (device_state != XenbusStateClosing)
10268c2ecf20Sopenharmony_ci				break;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci			sdev = scsi_device_lookup(info->host, chn, tgt, lun);
10298c2ecf20Sopenharmony_ci			if (sdev) {
10308c2ecf20Sopenharmony_ci				scsi_remove_device(sdev);
10318c2ecf20Sopenharmony_ci				scsi_device_put(sdev);
10328c2ecf20Sopenharmony_ci			}
10338c2ecf20Sopenharmony_ci			break;
10348c2ecf20Sopenharmony_ci		case VSCSIFRONT_OP_READD_LUN:
10358c2ecf20Sopenharmony_ci			if (device_state == XenbusStateConnected) {
10368c2ecf20Sopenharmony_ci				err = xenbus_printf(XBT_NIL, dev->nodename,
10378c2ecf20Sopenharmony_ci					      info->dev_state_path,
10388c2ecf20Sopenharmony_ci					      "%d", XenbusStateConnected);
10398c2ecf20Sopenharmony_ci				if (err)
10408c2ecf20Sopenharmony_ci					xenbus_dev_error(dev, err,
10418c2ecf20Sopenharmony_ci						"%s: writing dev_state_path", __func__);
10428c2ecf20Sopenharmony_ci			}
10438c2ecf20Sopenharmony_ci			break;
10448c2ecf20Sopenharmony_ci		default:
10458c2ecf20Sopenharmony_ci			break;
10468c2ecf20Sopenharmony_ci		}
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	info->curr = NULL;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	kfree(dir);
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic void scsifront_read_backend_params(struct xenbus_device *dev,
10558c2ecf20Sopenharmony_ci					  struct vscsifrnt_info *info)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	unsigned int sg_grant, nr_segs;
10588c2ecf20Sopenharmony_ci	struct Scsi_Host *host = info->host;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	sg_grant = xenbus_read_unsigned(dev->otherend, "feature-sg-grant", 0);
10618c2ecf20Sopenharmony_ci	nr_segs = min_t(unsigned int, sg_grant, SG_ALL);
10628c2ecf20Sopenharmony_ci	nr_segs = max_t(unsigned int, nr_segs, VSCSIIF_SG_TABLESIZE);
10638c2ecf20Sopenharmony_ci	nr_segs = min_t(unsigned int, nr_segs,
10648c2ecf20Sopenharmony_ci			VSCSIIF_SG_TABLESIZE * PAGE_SIZE /
10658c2ecf20Sopenharmony_ci			sizeof(struct scsiif_request_segment));
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	if (!info->pause && sg_grant)
10688c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "using up to %d SG entries\n", nr_segs);
10698c2ecf20Sopenharmony_ci	else if (info->pause && nr_segs < host->sg_tablesize)
10708c2ecf20Sopenharmony_ci		dev_warn(&dev->dev,
10718c2ecf20Sopenharmony_ci			 "SG entries decreased from %d to %u - device may not work properly anymore\n",
10728c2ecf20Sopenharmony_ci			 host->sg_tablesize, nr_segs);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	host->sg_tablesize = nr_segs;
10758c2ecf20Sopenharmony_ci	host->max_sectors = (nr_segs - 1) * PAGE_SIZE / 512;
10768c2ecf20Sopenharmony_ci}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cistatic void scsifront_backend_changed(struct xenbus_device *dev,
10798c2ecf20Sopenharmony_ci				      enum xenbus_state backend_state)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	pr_debug("%s: %p %u %u\n", __func__, dev, dev->state, backend_state);
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	switch (backend_state) {
10868c2ecf20Sopenharmony_ci	case XenbusStateUnknown:
10878c2ecf20Sopenharmony_ci	case XenbusStateInitialising:
10888c2ecf20Sopenharmony_ci	case XenbusStateInitWait:
10898c2ecf20Sopenharmony_ci	case XenbusStateInitialised:
10908c2ecf20Sopenharmony_ci		break;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	case XenbusStateConnected:
10938c2ecf20Sopenharmony_ci		scsifront_read_backend_params(dev, info);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		if (info->pause) {
10968c2ecf20Sopenharmony_ci			scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_READD_LUN);
10978c2ecf20Sopenharmony_ci			xenbus_switch_state(dev, XenbusStateConnected);
10988c2ecf20Sopenharmony_ci			info->pause = 0;
10998c2ecf20Sopenharmony_ci			return;
11008c2ecf20Sopenharmony_ci		}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci		if (xenbus_read_driver_state(dev->nodename) ==
11038c2ecf20Sopenharmony_ci		    XenbusStateInitialised)
11048c2ecf20Sopenharmony_ci			scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci		if (dev->state != XenbusStateConnected)
11078c2ecf20Sopenharmony_ci			xenbus_switch_state(dev, XenbusStateConnected);
11088c2ecf20Sopenharmony_ci		break;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	case XenbusStateClosed:
11118c2ecf20Sopenharmony_ci		if (dev->state == XenbusStateClosed)
11128c2ecf20Sopenharmony_ci			break;
11138c2ecf20Sopenharmony_ci		fallthrough;	/* Missed the backend's Closing state */
11148c2ecf20Sopenharmony_ci	case XenbusStateClosing:
11158c2ecf20Sopenharmony_ci		scsifront_disconnect(info);
11168c2ecf20Sopenharmony_ci		break;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	case XenbusStateReconfiguring:
11198c2ecf20Sopenharmony_ci		scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_DEL_LUN);
11208c2ecf20Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateReconfiguring);
11218c2ecf20Sopenharmony_ci		break;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	case XenbusStateReconfigured:
11248c2ecf20Sopenharmony_ci		scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
11258c2ecf20Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateConnected);
11268c2ecf20Sopenharmony_ci		break;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic const struct xenbus_device_id scsifront_ids[] = {
11318c2ecf20Sopenharmony_ci	{ "vscsi" },
11328c2ecf20Sopenharmony_ci	{ "" }
11338c2ecf20Sopenharmony_ci};
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_cistatic struct xenbus_driver scsifront_driver = {
11368c2ecf20Sopenharmony_ci	.ids			= scsifront_ids,
11378c2ecf20Sopenharmony_ci	.probe			= scsifront_probe,
11388c2ecf20Sopenharmony_ci	.remove			= scsifront_remove,
11398c2ecf20Sopenharmony_ci	.resume			= scsifront_resume,
11408c2ecf20Sopenharmony_ci	.suspend		= scsifront_suspend,
11418c2ecf20Sopenharmony_ci	.otherend_changed	= scsifront_backend_changed,
11428c2ecf20Sopenharmony_ci};
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int __init scsifront_init(void)
11458c2ecf20Sopenharmony_ci{
11468c2ecf20Sopenharmony_ci	if (!xen_domain())
11478c2ecf20Sopenharmony_ci		return -ENODEV;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	return xenbus_register_frontend(&scsifront_driver);
11508c2ecf20Sopenharmony_ci}
11518c2ecf20Sopenharmony_cimodule_init(scsifront_init);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_cistatic void __exit scsifront_exit(void)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	xenbus_unregister_driver(&scsifront_driver);
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_cimodule_exit(scsifront_exit);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xen SCSI frontend driver");
11608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
11618c2ecf20Sopenharmony_ciMODULE_ALIAS("xen:vscsi");
11628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Juergen Gross <jgross@suse.com>");
1163