Re: [RFC] pass through support in fc transport: via bsg (block SG)

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

 




On Aug 19, 2008, at 10:42 AM, Seokmann Ju wrote:


On Aug 18, 2008, at 12:04 PM, Seokmann Ju wrote:

This is a patch for the FC transport in an effort to support pass through services based on previous discussion as below,
http://marc.info/?t=121569558300005&r=1&w=2

Following are remarks for the changes,
- the SMP in the SAS transport has referenced, so the layout of the changes are quite similar to it - at this stage, the ELS/CT service packet serviced one at a time, synchronously
- there is dedicated handler in the LLDD for ELS and CT, respectively
- the sense data area of the 'request' structure will hold service reply - device files are created in following directories for each of discovered initiator/targets,
	= /sys/class/bsg/rport-<host_no>:<bus>-<id>
	= /dev/rport-<host_no>:<bus>-<id>

And following are to-dos,
- abort mechanism
- asynchronous packet handling
- detailing error handling, etc.

The patch has created against scsi-misc.
With this patch, qla2xxx is under testing for the feature.
Just realized that the one that submitted before is not the latest, and here is the latest.
I apologize for any inconveniences.
I just want to re-stroke this thread to make sure it is not skipped.

One question came to me while testing qla2xxx module with the changes.
When the application issues ELS/CT packet, should the packet include the FC frame header? And if the answer is 'yes', should the driver also include the FC frame header when it returns response to the application (just like any other frame exchanges in between two ports on the FC)? The FC frame header has type field that identifies what type of the payload (ELS / CT / etc.) the frame contains and the type field is being checked by the FC transport layer before it calls LLDD's handler.

Thank you,
Seokmann


Seokmann
---
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/ scsi_transport_fc.c
index 56823fd..a4cef16 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -40,6 +40,7 @@

static int fc_queue_work(struct Scsi_Host *, struct work_struct *);
static void fc_vport_sched_delete(struct work_struct *work);
+static void fc_bsg_remove(struct Scsi_Host *, struct fc_rport *);

/*
* This is a temporary carrier for creating a vport. It will eventually
@@ -67,6 +68,12 @@ struct fc_vport_identifiers {
static int fc_vport_create(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 *);
+
+struct kmem_cache *fc_service_cache;

/*
 * Redefine so that we can have same named attributes in the
@@ -666,6 +673,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);
}

@@ -675,6 +687,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);
}

/*
@@ -2081,6 +2094,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;

@@ -2430,12 +2446,240 @@ 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(&service->timer))
+		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 "invalid FC service type = %2x\n",
+		    frame->fh_type);
+		return -EINVAL;
+	}
+
+	service = fc_alloc_service(GFP_KERNEL);
+	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) {
+		if (!i->f->execute_fc_els_service)
+			goto ex_err;
+		res = i->f->execute_fc_els_service(service);
+	} else if (service->service_type == FC_FRAME_TYPE_FC_CT) {
+		if (!i->f->execute_fc_ct_service)
+			goto ex_err;
+		res = i->f->execute_fc_ct_service(service);
+	} else
+		printk(KERN_WARNING "%s: ERROR: invalid "
+		    "FC service type = %2x\n", __func__,
+		    service->service_type);
+
+	if (res) {
+		del_timer(&service->timer);
+		printk(KERN_ERR "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 "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 "FC service aborted and not done\n");
+			goto ex_err;
+		}
+	}
+	if (service->service_status.resp == FC_SERVICE_COMPLETE)
+		res = 0;
+	else {
+		printk(KERN_ERR "%s: FC service to rport %p"
+		    " response: 0x%x", __func__, rport,
+		    service->service_status.resp);
+		fc_free_service(service);
+		service = NULL;
+	}
+ex_err:
+	BUG_ON(service == NULL);
+	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_WARNING "%s: space for a FC service"
+		   " response is missing\n", __func__);
+		return -EINVAL;
+	}
+
+	/* do we need to support multiple segments? */
+	if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
+		printk(KERN_WARNING "%s: multiple segments req %u %u,"
+		    " rsp %u %u\n", __func__, req->bio->bi_vcnt,
+		    req->data_len, rsp->bio->bi_vcnt, rsp->data_len);
+		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) {
+		printk(KERN_WARNING "%s can't handle FC service requests\n",
+		    shost->hostt->name);
+		return 0;
+	}
+
+	if (!rport) {
+		printk(KERN_WARNING " ERROR: rport is NULL\n");
+		return -ENOMEM;
+	}
+
+	q = blk_init_queue(fc_service_request, NULL);
+	dev = &rport->dev;
+	name = dev->bus_id;
+	release = NULL;
+
+	if (!q)
+		return -ENOMEM;
+
+	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) {
+		printk(KERN_WARNING "%s: ERROR: invalid queue\n",
+		    __func__);
+		return;
+	}
+
+	bsg_unregister_queue(q);
+}
+
/**
 * fc_rport_create - allocates and creates a remote FC port.
 * @shost:	scsi host the remote port is connected to.
@@ -2495,6 +2739,7 @@ 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 */

	spin_unlock_irqrestore(shost->host_lock, flags);
@@ -2515,6 +2760,9 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
	transport_add_device(dev);
	transport_configure_device(dev);

+	if (fc_bsg_initialize(shost, rport))
+ printk(KERN_WARNING "fail to a bsg device %s\n", rport- >dev.bus_id);
+
	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 878373c..3cb3847 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
@@ -333,6 +333,8 @@ 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: */
@@ -495,6 +497,132 @@ 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_UNDELIVERED = -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  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 service_status;
+	void *reply_seq;   /* pointer to sense area of request */
+	void   (*service_done)(struct fc_service *);
+
+	struct work_struct abort_work;
+};
+
#define shost_to_fc_host(x) \
	((struct fc_host_attrs *)(x)->shost_data)

@@ -593,6 +721,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;
@@ -713,6 +849,31 @@ 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(gfp_t flags)
+{
+ struct fc_service *service = kmem_cache_zalloc(fc_service_cache, flags);
+
+	if (service) {
+		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 *);
---
--
To unsubscribe from this list: send the line "unsubscribe linux- scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[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