aboutsummaryrefslogtreecommitdiffstats
path: root/include/scsi/scsi_tcq.h
blob: fe4a70299419cedc7160b08942bce7367517f3ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#ifndef _SCSI_SCSI_TCQ_H
#define _SCSI_SCSI_TCQ_H

#include <linux/blkdev.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>

#define MSG_SIMPLE_TAG	0x20
#define MSG_HEAD_TAG	0x21
#define MSG_ORDERED_TAG	0x22
#define MSG_ACA_TAG	0x24	/* unsupported */

#define SCSI_NO_TAG	(-1)    /* identify no tag in use */


#ifdef CONFIG_BLOCK

int scsi_change_queue_type(struct scsi_device *sdev, int tag_type);

/**
 * scsi_get_tag_type - get the type of tag the device supports
 * @sdev:	the scsi device
 */
static inline int scsi_get_tag_type(struct scsi_device *sdev)
{
	if (!sdev->tagged_supported)
		return 0;
	if (sdev->simple_tags)
		return MSG_SIMPLE_TAG;
	return 0;
}

static inline void scsi_set_tag_type(struct scsi_device *sdev, int tag)
{
	switch (tag) {
	case MSG_ORDERED_TAG:
	case MSG_SIMPLE_TAG:
		sdev->simple_tags = 1;
		break;
	case 0:
		/* fall through */
	default:
		sdev->simple_tags = 0;
		break;
	}
}

static inline struct scsi_cmnd *scsi_mq_find_tag(struct Scsi_Host *shost,
						 int unique_tag)
{
	u16 hwq = blk_mq_unique_tag_to_hwq(unique_tag);
	struct request *req = NULL;

	if (hwq < shost->tag_set.nr_hw_queues)
		req = blk_mq_tag_to_rq(shost->tag_set.tags[hwq],
				       blk_mq_unique_tag_to_tag(unique_tag));
	return req ? (struct scsi_cmnd *)req->special : NULL;
}

/**
 * scsi_find_tag - find a tagged command by device
 * @SDpnt:	pointer to the ScSI device
 * @tag:	tag generated by blk_mq_unique_tag()
 *
 * Notes:
 *	Only works with tags allocated by the generic blk layer.
 **/
static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag)
{
        struct request *req;

        if (tag != SCSI_NO_TAG) {
		if (shost_use_blk_mq(sdev->host))
			return scsi_mq_find_tag(sdev->host, tag);

		req = blk_queue_find_tag(sdev->request_queue, tag);
	        return req ? (struct scsi_cmnd *)req->special : NULL;
	}

	/* single command, look in space */
	return sdev->current_cmnd;
}


/**
 * scsi_init_shared_tag_map - create a shared tag map
 * @shost:	the host to share the tag map among all devices
 * @depth:	the total depth of the map
 */
static inline int scsi_init_shared_tag_map(struct Scsi_Host *shost, int depth)
{
	/*
	 * We always have a shared tag map around when using blk-mq.
	 */
	if (shost_use_blk_mq(shost))
		return 0;

	/*
	 * If the shared tag map isn't already initialized, do it now.
	 * This saves callers from having to check ->bqt when setting up
	 * devices on the shared host (for libata)
	 */
	if (!shost->bqt) {
		shost->bqt = blk_init_tags(depth);
		if (!shost->bqt)
			return -ENOMEM;
	}

	return 0;
}

/**
 * scsi_host_find_tag - find the tagged command by host
 * @shost:	pointer to scsi_host
 * @tag:	tag generated by blk_mq_unique_tag()
 *
 * Notes:
 *	Only works with tags allocated by the generic blk layer.
 **/
static inline struct scsi_cmnd *scsi_host_find_tag(struct Scsi_Host *shost,
						int tag)
{
	struct request *req;

	if (tag != SCSI_NO_TAG) {
		if (shost_use_blk_mq(shost))
			return scsi_mq_find_tag(shost, tag);
		req = blk_map_queue_find_tag(shost->bqt, tag);
		return req ? (struct scsi_cmnd *)req->special : NULL;
	}
	return NULL;
}

#endif /* CONFIG_BLOCK */
#endif /* _SCSI_SCSI_TCQ_H */