xref: /spdk/lib/scsi/task.c (revision 8afdeef3becfe9409cc9e7372bd0bc10e8b7d46d)
1  /*   SPDX-License-Identifier: BSD-3-Clause
2   *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3   *   Copyright (C) 2016 Intel Corporation.
4   *   All rights reserved.
5   */
6  
7  #include "scsi_internal.h"
8  #include "spdk/endian.h"
9  #include "spdk/env.h"
10  #include "spdk/util.h"
11  
12  static void
13  scsi_task_free_data(struct spdk_scsi_task *task)
14  {
15  	if (task->alloc_len != 0) {
16  		spdk_dma_free(task->iov.iov_base);
17  		task->alloc_len = 0;
18  	}
19  
20  	task->iov.iov_base = NULL;
21  	task->iov.iov_len = 0;
22  }
23  
24  void
25  spdk_scsi_task_put(struct spdk_scsi_task *task)
26  {
27  	if (!task) {
28  		return;
29  	}
30  
31  	assert(task->ref > 0);
32  	task->ref--;
33  
34  	if (task->ref == 0) {
35  		struct spdk_bdev_io *bdev_io = task->bdev_io;
36  
37  		if (bdev_io) {
38  			spdk_bdev_free_io(bdev_io);
39  		}
40  
41  		scsi_task_free_data(task);
42  
43  		task->free_fn(task);
44  	}
45  }
46  
47  void
48  spdk_scsi_task_construct(struct spdk_scsi_task *task,
49  			 spdk_scsi_task_cpl cpl_fn,
50  			 spdk_scsi_task_free free_fn)
51  {
52  	assert(task != NULL);
53  	assert(cpl_fn != NULL);
54  	assert(free_fn != NULL);
55  
56  	task->cpl_fn = cpl_fn;
57  	task->free_fn = free_fn;
58  
59  	task->ref++;
60  
61  	/*
62  	 * Pre-fill the iov_buffers to point to the embedded iov
63  	 */
64  	assert(task->iov.iov_base == NULL);
65  	task->iovs = &task->iov;
66  	task->iovcnt = 1;
67  }
68  
69  static void *
70  scsi_task_alloc_data(struct spdk_scsi_task *task, uint32_t alloc_len)
71  {
72  	uint32_t zmalloc_len;
73  
74  	assert(task->alloc_len == 0);
75  
76  	/* Some ULPs (such as iSCSI) need to round len up to nearest
77  	 * 4 bytes. We can help those ULPs by allocating memory here
78  	 * up to next 4 byte boundary, so they don't have to worry
79  	 * about handling out-of-bounds errors.
80  	 */
81  	zmalloc_len = 4 * spdk_divide_round_up(alloc_len, 4);
82  	task->iov.iov_base = spdk_dma_zmalloc(zmalloc_len, 0, NULL);
83  	task->iov.iov_len = alloc_len;
84  	task->alloc_len = alloc_len;
85  
86  	return task->iov.iov_base;
87  }
88  
89  int
90  spdk_scsi_task_scatter_data(struct spdk_scsi_task *task, const void *src, size_t buf_len)
91  {
92  	size_t len = 0;
93  	size_t buf_left = buf_len;
94  	int i;
95  	struct iovec *iovs = task->iovs;
96  	const uint8_t *pos;
97  
98  	if (buf_len == 0) {
99  		return 0;
100  	}
101  
102  	if (task->iovcnt == 1 && iovs[0].iov_base == NULL) {
103  		scsi_task_alloc_data(task, buf_len);
104  		iovs[0] = task->iov;
105  	}
106  
107  	for (i = 0; i < task->iovcnt; i++) {
108  		assert(iovs[i].iov_base != NULL);
109  		len += iovs[i].iov_len;
110  	}
111  
112  	if (len < buf_len) {
113  		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
114  					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
115  					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
116  					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
117  		return -1;
118  	}
119  
120  	pos = src;
121  
122  	for (i = 0; i < task->iovcnt; i++) {
123  		len = spdk_min(iovs[i].iov_len, buf_left);
124  		buf_left -= len;
125  		memcpy(iovs[i].iov_base, pos, len);
126  		pos += len;
127  	}
128  
129  	return buf_len;
130  }
131  
132  void *
133  spdk_scsi_task_gather_data(struct spdk_scsi_task *task, int *len)
134  {
135  	int i;
136  	struct iovec *iovs = task->iovs;
137  	size_t buf_len = 0;
138  	uint8_t *buf, *pos;
139  
140  	for (i = 0; i < task->iovcnt; i++) {
141  		/* It is OK for iov_base to be NULL if iov_len is 0. */
142  		assert(iovs[i].iov_base != NULL || iovs[i].iov_len == 0);
143  		buf_len += iovs[i].iov_len;
144  	}
145  
146  	if (buf_len == 0) {
147  		*len = 0;
148  		return NULL;
149  	}
150  
151  	buf = calloc(1, buf_len);
152  	if (buf == NULL) {
153  		*len = -1;
154  		return NULL;
155  	}
156  
157  	pos = buf;
158  	for (i = 0; i < task->iovcnt; i++) {
159  		memcpy(pos, iovs[i].iov_base, iovs[i].iov_len);
160  		pos += iovs[i].iov_len;
161  	}
162  
163  	*len = buf_len;
164  	return buf;
165  }
166  
167  void
168  spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len)
169  {
170  	assert(task->iovcnt == 1);
171  	assert(task->alloc_len == 0);
172  
173  	task->iovs[0].iov_base = data;
174  	task->iovs[0].iov_len = len;
175  }
176  
177  void
178  spdk_scsi_task_build_sense_data(struct spdk_scsi_task *task, int sk, int asc, int ascq)
179  {
180  	uint8_t *cp;
181  	int resp_code;
182  
183  	resp_code = 0x70; /* Current + Fixed format */
184  
185  	/* Sense Data */
186  	cp = task->sense_data;
187  
188  	/* VALID(7) RESPONSE CODE(6-0) */
189  	cp[0] = 0x80 | resp_code;
190  	/* Obsolete */
191  	cp[1] = 0;
192  	/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
193  	cp[2] = sk & 0xf;
194  	/* INFORMATION */
195  	memset(&cp[3], 0, 4);
196  
197  	/* ADDITIONAL SENSE LENGTH */
198  	cp[7] = 10;
199  
200  	/* COMMAND-SPECIFIC INFORMATION */
201  	memset(&cp[8], 0, 4);
202  	/* ADDITIONAL SENSE CODE */
203  	cp[12] = asc;
204  	/* ADDITIONAL SENSE CODE QUALIFIER */
205  	cp[13] = ascq;
206  	/* FIELD REPLACEABLE UNIT CODE */
207  	cp[14] = 0;
208  
209  	/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
210  	cp[15] = 0;
211  	cp[16] = 0;
212  	cp[17] = 0;
213  
214  	/* SenseLength */
215  	task->sense_data_len = 18;
216  }
217  
218  void
219  spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk,
220  			  int asc, int ascq)
221  {
222  	if (sc == SPDK_SCSI_STATUS_CHECK_CONDITION) {
223  		spdk_scsi_task_build_sense_data(task, sk, asc, ascq);
224  	}
225  	task->status = sc;
226  }
227  
228  void
229  spdk_scsi_task_copy_status(struct spdk_scsi_task *dst,
230  			   struct spdk_scsi_task *src)
231  {
232  	memcpy(dst->sense_data, src->sense_data, src->sense_data_len);
233  	dst->sense_data_len = src->sense_data_len;
234  	dst->status = src->status;
235  }
236  
237  void
238  spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task)
239  {
240  	uint8_t buffer[36];
241  	uint32_t allocation_len;
242  	uint32_t data_len;
243  
244  	task->length = task->transfer_len;
245  	if (task->cdb[0] == SPDK_SPC_INQUIRY) {
246  		/*
247  		 * SPC-4 states that INQUIRY commands to an unsupported LUN
248  		 *  must be served with PERIPHERAL QUALIFIER = 0x3 and
249  		 *  PERIPHERAL DEVICE TYPE = 0x1F.
250  		 */
251  		data_len = sizeof(buffer);
252  
253  		memset(buffer, 0, data_len);
254  		/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
255  		buffer[0] = 0x03 << 5 | 0x1f;
256  		/* ADDITIONAL LENGTH */
257  		buffer[4] = data_len - 5;
258  
259  		allocation_len = from_be16(&task->cdb[3]);
260  		if (spdk_scsi_task_scatter_data(task, buffer, spdk_min(allocation_len, data_len)) >= 0) {
261  			task->data_transferred = data_len;
262  			task->status = SPDK_SCSI_STATUS_GOOD;
263  		}
264  	} else {
265  		/* LOGICAL UNIT NOT SUPPORTED */
266  		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
267  					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
268  					  SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED,
269  					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
270  		task->data_transferred = 0;
271  	}
272  }
273  
274  void
275  spdk_scsi_task_process_abort(struct spdk_scsi_task *task)
276  {
277  	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
278  				  SPDK_SCSI_SENSE_ABORTED_COMMAND,
279  				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
280  				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
281  }
282