[dm-devel] [PATCH RFT/RFC 2/4] add dm_scsi helpers

Mike Christie michaelc at cs.wisc.edu
Fri Sep 30 19:42:16 UTC 2005


I took this function from the cleanup I am doing in the
scsi layer. Unfortunately that work needs some things
fleshed out so I do not know when it will ever be merged.
For now so that dm can move forward I put only what we need
in dm-hw-handler.c and when the scsi-ml work gets
finished I hope we can merge the two or most likely add a
wrapper around the scsi function since we need mempools for
some of our structs.

Still on the todo is to evaluate the rq->errors in more depth,
incase we need to retry the same path or pg immediately or
not report a failure on some errors.


Signed-off-by: Mike Christie <michaelc at cs.wisc.edu>

diff --git a/drivers/md/dm-hw-handler.c b/drivers/md/dm-hw-handler.c
--- a/drivers/md/dm-hw-handler.c
+++ b/drivers/md/dm-hw-handler.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005 Mike Christie, All rights reserved.
  *
  * This file is released under the GPL.
  *
@@ -10,6 +11,9 @@
 #include "dm-hw-handler.h"
 
 #include <linux/slab.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_cmnd.h>
 
 struct hwh_internal {
 	struct hw_handler_type hwht;
@@ -149,6 +153,129 @@ int dm_unregister_hw_handler(struct hw_h
 	return 0;
 }
 
+struct dm_scsi_io_context {
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	struct scsi_sense_hdr sense_hdr;
+	struct hw_handler *hwh;
+	struct path *path;
+};
+
+int dm_scsi_init_context_pool(struct hw_handler *hwh)
+{
+	struct dm_scsi_io_context *scsi_ioc;
+
+	scsi_ioc = kzalloc(sizeof(*scsi_ioc), GFP_KERNEL);
+	if (!scsi_ioc)
+		return -ENOMEM;
+
+	hwh->pg_init_context = scsi_ioc;
+	return 0;	
+}
+
+void dm_scsi_destroy_context_pool(struct hw_handler *hwh)
+{
+	kfree(hwh->pg_init_context);
+}
+
+/*
+ * TODO: we should check the error values a little more closely
+ * to decide what we want to do.
+ *
+ * For example for DID_BUS_BUSY, we may just want to try another
+ * pg, but retry this one if the next pg fails with a hard error.
+ * However, we may also want to allow hw_handlers to get more info
+ * so we will see what they need.
+ */
+static void dm_scsi_end_rq(struct request *rq)
+{
+	struct request_queue *q = rq->q;
+	struct dm_scsi_io_context *scsi_ioc = rq->end_io_data;
+	struct hw_handler *hwh = scsi_ioc->hwh;
+	struct path *path = scsi_ioc->path;
+	unsigned err;
+
+	if (host_byte(rq->errors) != DID_OK ||
+	    msg_byte(rq->errors) != COMMAND_COMPLETE) {
+		err = MP_FAIL_PATH;
+		goto done;
+	}
+
+	switch (status_byte(rq->errors)) {
+	case GOOD:
+		err = 0;
+		break;
+	case CHECK_CONDITION:
+		scsi_normalize_sense(rq->sense, SCSI_SENSE_BUFFERSIZE,
+				     &scsi_ioc->sense_hdr);
+		err = hwh->type->rq_error(path, rq->errors,
+					  &scsi_ioc->sense_hdr);
+		break;
+	default:
+		err = MP_FAIL_PATH;
+	};
+done:
+	dm_pg_init_complete(path, err);
+	__blk_put_request(q, rq);
+	blk_put_queue(q);
+}
+
+int dm_scsi_execute_rq(struct hw_handler *hwh, struct path *path,
+		       const unsigned char *cmd, int data_direction,
+		       void *buffer, unsigned bufflen, int timeout,
+		       unsigned int gfp_mask)
+{
+	struct request_queue *q = bdev_get_queue(path->dev->bdev);
+	struct block_device *bdev = path->dev->bdev;
+	struct dm_scsi_io_context *scsi_ioc = hwh->pg_init_context;
+	struct request *rq;
+
+	BUG_ON(!scsi_ioc);
+
+	if (!q) {
+		DMERR("Could not get queue.");
+		return -EINVAL;
+	}
+
+	/*
+	 * make sure someone does not free this queue while we are
+	 * touching it
+	 */
+	if (blk_get_queue(q))
+		return -EINVAL;
+
+	rq = blk_get_request(q, data_direction == DMA_TO_DEVICE, gfp_mask);
+	if (!rq)
+		goto put_queue;
+
+	if (bufflen && blk_rq_map_kern(q, rq, buffer, bufflen, gfp_mask))
+		goto put_rq;
+
+	rq->sense = scsi_ioc->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd_len = COMMAND_SIZE(cmd[0]);
+	memcpy(rq->cmd, cmd, rq->cmd_len);
+
+	scsi_ioc->hwh = hwh;
+	scsi_ioc->path = path;
+	rq->end_io_data = scsi_ioc;
+
+	rq->timeout = timeout;
+	rq->flags |= (REQ_BLOCK_PC | REQ_FAILFAST | REQ_NOMERGE);
+
+	blk_execute_rq_nowait(q, bdev->bd_contains->bd_disk, rq, 1,
+			      dm_scsi_end_rq);
+	return 0;
+
+put_rq:
+	blk_put_request(rq);
+put_queue:
+	blk_put_queue(q);
+	return -ENOMEM;
+}
+
 unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
 {
 #if 0
@@ -214,3 +341,6 @@ unsigned dm_scsi_err_handler(struct hw_h
 EXPORT_SYMBOL_GPL(dm_register_hw_handler);
 EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
 EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
+EXPORT_SYMBOL_GPL(dm_scsi_execute_rq);
+EXPORT_SYMBOL_GPL(dm_scsi_init_context_pool);
+EXPORT_SYMBOL_GPL(dm_scsi_destroy_context_pool);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
--- a/drivers/md/dm-hw-handler.h
+++ b/drivers/md/dm-hw-handler.h
@@ -10,15 +10,17 @@
 #define	DM_HW_HANDLER_H
 
 #include <linux/device-mapper.h>
-
+#include <scsi/scsi_cmnd.h>
 #include "dm-mpath.h"
 
 struct hw_handler_type;
 struct hw_handler {
 	struct hw_handler_type *type;
 	void *context;
+	void *pg_init_context;		/* for protocol (SCSI, DASD, etc) use */
 };
 
+struct scsi_sense_hdr;
 /*
  * Constructs a hardware handler object, takes custom arguments
  */
@@ -36,6 +38,8 @@ struct hw_handler_type {
 	unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
 	int (*status) (struct hw_handler *hwh, status_type_t type,
 		       char *result, unsigned int maxlen);
+	unsigned (*rq_error) (struct path *path, int result,
+			      struct scsi_sense_hdr *sense);
 };
 
 /* Register a hardware handler */
@@ -53,6 +57,19 @@ void dm_put_hw_handler(struct hw_handler
 /* Default err function */
 unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
 
+/* Create/destroy scsi context pool for failover */
+int dm_scsi_init_context_pool(struct hw_handler *hwh);
+void dm_scsi_destroy_context_pool(struct hw_handler *hwh);
+
+/*
+ * execute a request, the hw_handler will be notified of the result
+ * in its rq_error function.
+ */
+int dm_scsi_execute_rq(struct hw_handler *hwh, struct path *path,
+		       const unsigned char *cmd, int data_direction,
+		       void *buffer, unsigned bufflen, int timeout,
+		       unsigned int gfp_mask);
+
 /* Error flags for err and dm_pg_init_complete */
 #define MP_FAIL_PATH 1
 #define MP_BYPASS_PG 2








More information about the dm-devel mailing list