[RFC 1/2] scsi_transport_fc: pass through support through bsg interface

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

 



diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/ scsi_transport_fc.c
index cb971f0..a3c10ef 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -43,6 +43,13 @@ static void fc_vport_sched_delete(struct work_struct *work);
 static int fc_vport_setup(struct Scsi_Host *shost, int channel,
 	struct device *pdev, struct fc_vport_identifiers  *ids,
 	struct fc_vport **vport);
+static void fc_service_timedout(unsigned long);
+static void fc_service_done(struct fc_service *);
+static int
+fc_service_handler(struct Scsi_Host *, struct fc_rport *, struct request *);
+static void fc_bsg_remove(struct Scsi_Host *, struct fc_rport *);
+
+struct kmem_cache *fc_service_cache;

 /*
  * Redefine so that we can have same named attributes in the
@@ -642,6 +649,11 @@ static __init int fc_transport_init(void)
 	error = transport_class_register(&fc_rport_class);
 	if (error)
 		return error;
+	fc_service_cache = kmem_cache_create("fc_service",
+					     sizeof(struct fc_service),
+					     0, SLAB_HWCACHE_ALIGN, NULL);
+	if (!fc_service_cache)
+		return -ENOMEM;
 	return transport_class_register(&fc_transport_class);
 }

@@ -651,6 +663,7 @@ static void __exit fc_transport_exit(void)
 	transport_class_unregister(&fc_rport_class);
 	transport_class_unregister(&fc_host_class);
 	transport_class_unregister(&fc_vport_class);
+	kmem_cache_destroy(fc_service_cache);
 }

 /*
@@ -2057,6 +2070,9 @@ fc_attach_transport(struct fc_function_template *ft)

 	i->f = ft;

+	/* for FC services (FC-CT, ELS, etc.) */
+	i->f->fc_service_handler = fc_service_handler;
+
 	/* Transport uses the shost workq for scsi scanning */
 	i->t.create_work_queue = 1;

@@ -2406,12 +2422,235 @@ fc_rport_final_delete(struct work_struct *work)

 	transport_remove_device(dev);
 	device_del(dev);
+	fc_bsg_remove(shost, rport);
 	transport_destroy_device(dev);
 	put_device(&shost->shost_gendev);	/* for fc_host->rport list */
 	put_device(dev);			/* for self-reference */
 }


+static void fc_service_timedout(unsigned long _service)
+{
+	struct fc_service *service = (void *) _service;
+	unsigned long flags;
+
+	spin_lock_irqsave(&service->service_state_lock, flags);
+	if (!(service->service_state_flags & FC_SERVICE_STATE_DONE))
+		service->service_state_flags |= FC_SERVICE_STATE_ABORTED;
+	spin_unlock_irqrestore(&service->service_state_lock, flags);
+
+	complete(&service->completion);
+}
+
+static void fc_service_done(struct fc_service *service)
+{
+	if (!del_timer_sync(&service->timer)) {
+		printk(KERN_ERR "ERROR: FC timed out for rport %p\n",
+		    service->rport);
+		return;
+	}
+	complete(&service->completion);
+}
+
+static int
+issue_fc_service(struct fc_rport *rport, void *req, int req_size,
+    void *resp, int resp_size, void *service_reply)
+{
+	int res = -ECOMM;
+	struct fc_service *service = NULL;
+	struct Scsi_Host *shost = rport_to_shost(rport);
+	struct fc_internal *i = to_fc_internal(shost->transportt);
+	struct fc_frame_header *frame = req;
+
+	if (!((frame->fh_type == FC_FRAME_TYPE_ELS) ||
+	    (frame->fh_type == FC_FRAME_TYPE_FC_CT))) {
+		printk(KERN_ERR "ERROR: invalid FC service type = %2x\n",
+		    frame->fh_type);
+		return -EINVAL;
+	}
+
+	if (frame->fh_type == FC_FRAME_TYPE_ELS) {
+		if (!i->f->execute_fc_els_service)
+			goto ex_err;
+	} else if (frame->fh_type == FC_FRAME_TYPE_FC_CT) {
+		if (!i->f->execute_fc_ct_service)
+			goto ex_err;
+	}
+
+	service = fc_alloc_service();
+	if (!service)
+		return -ENOMEM;
+
+	service->service_type = frame->fh_type;
+	service->ct_frame = (struct fc_ct_frame *) req;
+	service->req_size = req_size;
+	service->response = resp;
+	service->resp_size = resp_size;
+	service->rport = rport;
+	/* sense area of the request structure */
+	service->reply_seq = service_reply;
+	service->service_done = fc_service_done;
+
+	service->timer.data = (unsigned long) service;
+	service->timer.function = fc_service_timedout;
+	service->timer.expires = jiffies + FC_SERVICE_TIMEOUT*HZ;
+
+	sg_init_one(&service->sg_req, req, req_size);
+	sg_init_one(&service->sg_resp, resp, resp_size);
+
+	add_timer(&service->timer);
+
+	if (service->service_type == FC_FRAME_TYPE_ELS)
+		res = i->f->execute_fc_els_service(service);
+	else if (service->service_type == FC_FRAME_TYPE_FC_CT)
+		res = i->f->execute_fc_ct_service(service);
+
+	if (res) {
+		del_timer(&service->timer);
+		printk(KERN_ERR "ERROR: executing FC service failed:%d\n",
+		    res);
+		goto ex_err;
+	}
+
+	wait_for_completion(&service->completion);
+
+	if ((service->service_state_flags & FC_SERVICE_STATE_ABORTED)) {
+		printk(KERN_ERR "ERROR: FC service timed out or aborted\n");
+		i->f->abort_fc_service(service);
+		if (!(service->service_state_flags &
+		     FC_SERVICE_STATE_DONE)) {
+			printk(KERN_ERR "ERROR: FC service aborted"
+			    " and not done\n");
+			goto ex_err;
+		}
+	}
+	if (service->status.resp == FC_SERVICE_COMPLETE)
+		res = 0;
+	else {
+		printk(KERN_ERR "ERROR: FC service to rport %p"
+		    " response: 0x%x\n", rport, service->status.resp);
+		fc_free_service(service);
+		service = NULL;
+		return res;
+	}
+ex_err:
+	if (service != NULL)
+		fc_free_service(service);
+
+	return res;
+}
+
+static int
+fc_service_handler(struct Scsi_Host *shost, struct fc_rport *rport,
+			  struct request *req)
+{
+	int ret;
+	struct request *rsp = req->next_rq;
+
+	if (!rsp) {
+		printk(KERN_ERR "ERROR: space for a FC service"
+		   " response is missing\n");
+		return -EINVAL;
+	}
+
+	/* ELS doesn't support multiple SG */
+	if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
+		printk(KERN_WARNING "ERROR: multiple segments req %u,"
+		    " rsp %u  are not supported for ELS services\n",
+		    req->bio->bi_vcnt, rsp->bio->bi_vcnt);
+		return -EINVAL;
+	}
+
+	ret = issue_fc_service(rport, bio_data(req->bio), req->data_len,
+			bio_data(rsp->bio), rsp->data_len, req->sense);
+
+	if (ret > 0) {
+		/* positive number is the untransferred residual */
+		rsp->data_len = ret;
+		req->data_len = 0;
+		ret = 0;
+	} else if (ret == 0) {
+		rsp->data_len = 0;
+		req->data_len = 0;
+	}
+
+	return ret;
+}
+
+static void fc_service_request(struct request_queue *q)
+{
+	struct request *req;
+	struct fc_rport *rport = q->queuedata;
+	struct Scsi_Host *shost = rport_to_shost(rport);
+	int (*handler)(struct Scsi_Host *, struct fc_rport *,
+		       struct request *);
+
+	while (!blk_queue_plugged(q)) {
+		req = elv_next_request(q);
+		if (!req)
+			break;
+
+		blkdev_dequeue_request(req);
+		spin_unlock_irq(q->queue_lock);
+		handler = to_fc_internal
+			(shost->transportt)->f->fc_service_handler;
+		if (handler)
+			req->errors = handler(shost, rport, req);
+
+		spin_lock_irq(q->queue_lock);
+		req->end_io(req, req->errors);
+	}
+}
+
+static int
+fc_bsg_initialize(struct Scsi_Host *shost, struct fc_rport *rport)
+{
+	struct request_queue *q;
+	int error;
+	struct device *dev;
+	const char *name;
+	void (*release)(struct device *);
+
+	if (!to_fc_internal(shost->transportt)->f->fc_service_handler)
+		return 0;
+
+	if (!rport) {
+		printk(KERN_ERR "ERROR: rport is NULL\n");
+		return -ENOMEM;
+	}
+
+	q = blk_init_queue(fc_service_request, NULL);
+	if (!q)
+		return -ENOMEM;
+
+	dev = &rport->dev;
+	name = dev->bus_id;
+	release = NULL;
+
+	error = bsg_register_queue(q, dev, name, release);
+	if (error) {
+		blk_cleanup_queue(q);
+		return -ENOMEM;
+	}
+
+	rport->q = q;
+	q->queuedata = rport;
+	queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
+
+	return 0;
+}
+
+static void
+fc_bsg_remove(struct Scsi_Host *shost, struct fc_rport *rport)
+{
+	struct request_queue *q = rport->q;
+
+	if (!q)
+		return;
+
+	bsg_unregister_queue(q);
+}
+
 /**
  * fc_rport_create - allocates and creates a remote FC port.
  * @shost:	scsi host the remote port is connected to.
@@ -2471,8 +2710,8 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
 	else
 		rport->scsi_target_id = -1;
 	list_add_tail(&rport->peers, &fc_host->rports);
-	get_device(&shost->shost_gendev);	/* for fc_host->rport list */

+	get_device(&shost->shost_gendev);	/* for fc_host->rport list */
 	spin_unlock_irqrestore(shost->host_lock, flags);

 	dev = &rport->dev;
@@ -2491,6 +2730,8 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
 	transport_add_device(dev);
 	transport_configure_device(dev);

+	fc_bsg_initialize(shost, rport);
+
 	if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
 		/* initiate a scan of the target */
 		rport->flags |= FC_RPORT_SCAN_PENDING;
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/ scsi_transport_fc.h
index 21018a4..5cef236 100644
--- a/include/scsi/scsi_transport_fc.h
+++ b/include/scsi/scsi_transport_fc.h
@@ -32,7 +32,7 @@
 #include <scsi/scsi_netlink.h>

 struct scsi_transport_template;
-
+extern struct kmem_cache *fc_service_cache;

 /*
  * FC Port definitions - Following FC HBAAPI guidelines
@@ -352,6 +352,7 @@ struct fc_rport {	/* aka fc_starget_attrs */
  	struct delayed_work fail_io_work;
  	struct work_struct stgt_delete_work;
 	struct work_struct rport_delete_work;
+	struct request_queue *q;
 } __attribute__((aligned(sizeof(unsigned long))));

 /* bit field values for struct fc_rport "flags" field: */
@@ -514,6 +515,134 @@ struct fc_host_attrs {
 	struct workqueue_struct *devloss_work_q;
 };

+#define FC_STATUS_BUF_SIZE 96
+
+enum fc_frame_type {
+	FC_FRAME_TYPE_BS,
+	FC_FRAME_TYPE_ELS,
+	FC_FRAME_TYPE_IEC = 4,
+	FC_FRAME_TYPE_IP,
+	FC_FRAME_TYPE_FCP = 8,
+	FC_FRAME_TYPE_GPP,
+	FC_FRAME_TYPE_FC_CT = 0x20,
+};
+
+enum service_response {
+	FC_SERVICE_COMPLETE,
+	FC_SERVICE_ERROR = -1,
+};
+
+#define FC_SERVICE_STATE_PENDING      1
+#define FC_SERVICE_STATE_DONE         2
+#define FC_SERVICE_STATE_ABORTED      4
+#define FC_SERVICE_AT_INITIATOR       16
+
+struct fc_service_status {
+	enum service_response resp;
+	int  error;
+	int  buf_valid_size;
+
+	u8   buf[FC_STATUS_BUF_SIZE];
+
+	u32  residual;
+};
+
+/* NOTE - fc_frame_header is already defined in open-fcoe-upstream branch */
+/*
+ * Frame header
+ */
+struct fc_frame_header {
+	__u8          fh_r_ctl;	/* routing control */
+	__u8          fh_d_id[3];	/* Destination ID */
+
+	__u8          fh_cs_ctl;	/* class of service control / pri */
+	__u8          fh_s_id[3];	/* Source ID */
+
+	__u8          fh_type;		/* see enum fc_fh_type below */
+	__u8          fh_f_ctl[3];	/* frame control */
+
+	__u8          fh_seq_id;	/* sequence ID */
+	__u8          fh_df_ctl;	/* data field control */
+	__be16        fh_seq_cnt;	/* sequence count */
+
+	__be16        fh_ox_id;		/* originator exchange ID */
+	__be16        fh_rx_id;		/* responder exchange ID */
+	__be32        fh_parm_offset;	/* parameter or relative offset */
+};
+
+/* TBD - fc_els_hdr to move to ~/include/scsi/fc/fc_els.h */
+struct fc_els_hdr {
+	__u8	els_cmd;
+	__u8	els_param[3];
+};
+
+/* NOTE - fc_ct_hdr is already defined in open-fcoe-upstream branch */
+/*
+ * Fibre Channel Services - Common Transport.
+ * From T11.org FC-GS-2 Rev 5.3 November 1998.
+ */
+
+struct fc_ct_hdr {
+	__u8		ct_rev;		/* revision */
+	__u8		ct_in_id[3];	/* N_Port ID of original requestor */
+	__u8		ct_fs_type;	/* type of fibre channel service */
+	__u8		ct_fs_subtype;	/* subtype */
+	__u8		ct_options;
+	__u8		_ct_resvd1;
+	__be16		ct_cmd;		/* command / response code */
+	__be16		ct_mr_size;	/* maximum / residual size */
+	__u8		_ct_resvd2;
+	__u8		ct_reason;	/* reject reason */
+	__u8		ct_explan;	/* reason code explanation */
+	__u8		ct_vendor;	/* vendor unique data */
+};
+
+struct fc_els_frame {
+	struct fc_frame_header fc_hdr;
+	struct fc_els_hdr els_hdr;
+	u32 els_cmd[4];
+};
+
+struct fc_ct_frame {
+	struct fc_frame_header fc_hdr;
+	struct fc_ct_hdr ct_hdr;
+	u32 ct_cmd[1];
+};
+
+#define FC_SERVICE_TIMEOUT 10
+
+struct fc_service {
+	struct fc_rport *rport;
+	struct list_head      list;
+
+	spinlock_t   service_state_lock;
+	unsigned     service_state_flags;
+
+	enum   fc_frame_type   service_type;
+
+	/* Used by the discovery code. */
+	struct timer_list     timer;
+	struct completion     completion;
+
+	union {
+		struct fc_ct_frame *ct_frame;
+		struct fc_els_frame *els_frame;
+	};
+	int req_size;
+	void *response;
+	int resp_size;
+
+	struct scatterlist sg_req;
+	struct scatterlist sg_resp;
+
+	struct fc_service_status status;
+	void *reply_seq;   /* pointer to sense area of request */
+	void   (*service_done)(struct fc_service *);
+
+	void *lld_pkt;
+	struct work_struct abort_work;
+};
+
 #define shost_to_fc_host(x) \
 	((struct fc_host_attrs *)(x)->shost_data)

@@ -612,6 +741,14 @@ struct fc_function_template {
 	int     (* tsk_mgmt_response)(struct Scsi_Host *, u64, u64, int);
 	int     (* it_nexus_response)(struct Scsi_Host *, u64, int);

+	/* fibre-channel services */
+	int	(*fc_service_handler)(struct Scsi_Host *,
+				      struct fc_rport *rport,
+				      struct request *);
+	int	(*execute_fc_els_service)(struct fc_service *);
+	int	(*execute_fc_ct_service)(struct fc_service *);
+	int	(*abort_fc_service)(struct fc_service *);
+
 	/* allocation lengths for host-specific data */
 	u32	 			dd_fcrport_size;
 	u32	 			dd_fcvport_size;
@@ -732,6 +869,33 @@ fc_vport_set_state(struct fc_vport *vport, enum fc_vport_state new_state)
 	vport->vport_state = new_state;
 }

+static inline struct fc_service *
+fc_alloc_service(void)
+{
+	struct fc_service *service =
+	    kmem_cache_zalloc(fc_service_cache, GFP_KERNEL);
+
+	if (!service)
+		return NULL;
+
+	INIT_LIST_HEAD(&service->list);
+	spin_lock_init(&service->service_state_lock);
+	service->service_state_flags = FC_SERVICE_STATE_PENDING;
+	init_timer(&service->timer);
+	init_completion(&service->completion);
+
+	return service;
+}
+
+static inline void
+fc_free_service(struct fc_service *service)
+{
+	if (service) {
+		BUG_ON(!list_empty(&service->list));
+		kmem_cache_free(fc_service_cache, service);
+	}
+}
+

 struct scsi_transport_template *fc_attach_transport(
 			struct fc_function_template *);
---

Attachment: bidi-support-transport.patch
Description: Binary data



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux