Blame view

block/bsg-lib.c 8.7 KB
aa387cc89   Mike Christie   block: add bsg he...
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
  /*
   *  BSG helper library
   *
   *  Copyright (C) 2008   James Smart, Emulex Corporation
   *  Copyright (C) 2011   Red Hat, Inc.  All rights reserved.
   *  Copyright (C) 2011   Mike Christie
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   */
  #include <linux/slab.h>
  #include <linux/blkdev.h>
  #include <linux/delay.h>
  #include <linux/scatterlist.h>
  #include <linux/bsg-lib.h>
6adb1236b   Paul Gortmaker   block: Change mod...
28
  #include <linux/export.h>
aa387cc89   Mike Christie   block: add bsg he...
29
  #include <scsi/scsi_cmnd.h>
17cb960f2   Christoph Hellwig   bsg: split handli...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  #include <scsi/sg.h>
  
  #define uptr64(val) ((void __user *)(uintptr_t)(val))
  
  static int bsg_transport_check_proto(struct sg_io_v4 *hdr)
  {
  	if (hdr->protocol != BSG_PROTOCOL_SCSI  ||
  	    hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_TRANSPORT)
  		return -EINVAL;
  	if (!capable(CAP_SYS_RAWIO))
  		return -EPERM;
  	return 0;
  }
  
  static int bsg_transport_fill_hdr(struct request *rq, struct sg_io_v4 *hdr,
  		fmode_t mode)
  {
  	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
  
  	job->request_len = hdr->request_len;
  	job->request = memdup_user(uptr64(hdr->request), hdr->request_len);
472554919   zhong jiang   block/bsg-lib: us...
51
52
  
  	return PTR_ERR_OR_ZERO(job->request);
17cb960f2   Christoph Hellwig   bsg: split handli...
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
  }
  
  static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr)
  {
  	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
  	int ret = 0;
  
  	/*
  	 * The assignments below don't make much sense, but are kept for
  	 * bug by bug backwards compatibility:
  	 */
  	hdr->device_status = job->result & 0xff;
  	hdr->transport_status = host_byte(job->result);
  	hdr->driver_status = driver_byte(job->result);
  	hdr->info = 0;
  	if (hdr->device_status || hdr->transport_status || hdr->driver_status)
  		hdr->info |= SG_INFO_CHECK;
  	hdr->response_len = 0;
  
  	if (job->result < 0) {
  		/* we're only returning the result field in the reply */
  		job->reply_len = sizeof(u32);
  		ret = job->result;
  	}
  
  	if (job->reply_len && hdr->response) {
  		int len = min(hdr->max_response_len, job->reply_len);
  
  		if (copy_to_user(uptr64(hdr->response), job->reply, len))
  			ret = -EFAULT;
  		else
  			hdr->response_len = len;
  	}
  
  	/* we assume all request payload was transferred, residual == 0 */
  	hdr->dout_resid = 0;
  
  	if (rq->next_rq) {
  		unsigned int rsp_len = job->reply_payload.payload_len;
  
  		if (WARN_ON(job->reply_payload_rcv_len > rsp_len))
  			hdr->din_resid = 0;
  		else
  			hdr->din_resid = rsp_len - job->reply_payload_rcv_len;
  	} else {
  		hdr->din_resid = 0;
  	}
  
  	return ret;
  }
  
  static void bsg_transport_free_rq(struct request *rq)
  {
  	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
  
  	kfree(job->request);
  }
  
  static const struct bsg_ops bsg_transport_ops = {
  	.check_proto		= bsg_transport_check_proto,
  	.fill_hdr		= bsg_transport_fill_hdr,
  	.complete_rq		= bsg_transport_complete_rq,
  	.free_rq		= bsg_transport_free_rq,
  };
aa387cc89   Mike Christie   block: add bsg he...
117
118
  
  /**
50b4d4855   Benjamin Block   bsg-lib: fix kern...
119
   * bsg_teardown_job - routine to teardown a bsg job
aa98192de   Bart Van Assche   block: Fix kernel...
120
   * @kref: kref inside bsg_job that is to be torn down
aa387cc89   Mike Christie   block: add bsg he...
121
   */
50b4d4855   Benjamin Block   bsg-lib: fix kern...
122
  static void bsg_teardown_job(struct kref *kref)
aa387cc89   Mike Christie   block: add bsg he...
123
  {
bf0f2d380   Johannes Thumshirn   block: add refere...
124
  	struct bsg_job *job = container_of(kref, struct bsg_job, kref);
ef6fa64f9   Christoph Hellwig   bsg-lib: remove b...
125
  	struct request *rq = blk_mq_rq_from_pdu(job);
c00da4c90   Johannes Thumshirn   scsi: fc: Use bsg...
126

aa387cc89   Mike Christie   block: add bsg he...
127
128
129
130
  	put_device(job->dev);	/* release reference for the request */
  
  	kfree(job->request_payload.sg_list);
  	kfree(job->reply_payload.sg_list);
50b4d4855   Benjamin Block   bsg-lib: fix kern...
131
132
  
  	blk_end_request_all(rq, BLK_STS_OK);
aa387cc89   Mike Christie   block: add bsg he...
133
  }
fb6f7c8d8   Johannes Thumshirn   block: add bsg_jo...
134
135
  void bsg_job_put(struct bsg_job *job)
  {
50b4d4855   Benjamin Block   bsg-lib: fix kern...
136
  	kref_put(&job->kref, bsg_teardown_job);
fb6f7c8d8   Johannes Thumshirn   block: add bsg_jo...
137
138
139
140
141
142
143
144
  }
  EXPORT_SYMBOL_GPL(bsg_job_put);
  
  int bsg_job_get(struct bsg_job *job)
  {
  	return kref_get_unless_zero(&job->kref);
  }
  EXPORT_SYMBOL_GPL(bsg_job_get);
aa387cc89   Mike Christie   block: add bsg he...
145
146
147
148
149
150
151
152
153
154
155
156
  
  /**
   * bsg_job_done - completion routine for bsg requests
   * @job: bsg_job that is complete
   * @result: job reply result
   * @reply_payload_rcv_len: length of payload recvd
   *
   * The LLD should call this when the bsg job has completed.
   */
  void bsg_job_done(struct bsg_job *job, int result,
  		  unsigned int reply_payload_rcv_len)
  {
17cb960f2   Christoph Hellwig   bsg: split handli...
157
158
159
  	job->result = result;
  	job->reply_payload_rcv_len = reply_payload_rcv_len;
  	blk_complete_request(blk_mq_rq_from_pdu(job));
aa387cc89   Mike Christie   block: add bsg he...
160
161
162
163
164
165
166
167
168
  }
  EXPORT_SYMBOL_GPL(bsg_job_done);
  
  /**
   * bsg_softirq_done - softirq done routine for destroying the bsg requests
   * @rq: BSG request that holds the job to be destroyed
   */
  static void bsg_softirq_done(struct request *rq)
  {
50b4d4855   Benjamin Block   bsg-lib: fix kern...
169
  	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
aa387cc89   Mike Christie   block: add bsg he...
170

fb6f7c8d8   Johannes Thumshirn   block: add bsg_jo...
171
  	bsg_job_put(job);
aa387cc89   Mike Christie   block: add bsg he...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  }
  
  static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req)
  {
  	size_t sz = (sizeof(struct scatterlist) * req->nr_phys_segments);
  
  	BUG_ON(!req->nr_phys_segments);
  
  	buf->sg_list = kzalloc(sz, GFP_KERNEL);
  	if (!buf->sg_list)
  		return -ENOMEM;
  	sg_init_table(buf->sg_list, req->nr_phys_segments);
  	buf->sg_cnt = blk_rq_map_sg(req->q, req, buf->sg_list);
  	buf->payload_len = blk_rq_bytes(req);
  	return 0;
  }
  
  /**
50b4d4855   Benjamin Block   bsg-lib: fix kern...
190
   * bsg_prepare_job - create the bsg_job structure for the bsg request
aa387cc89   Mike Christie   block: add bsg he...
191
192
193
   * @dev: device that is being sent the bsg request
   * @req: BSG request that needs a job structure
   */
17cb960f2   Christoph Hellwig   bsg: split handli...
194
  static bool bsg_prepare_job(struct device *dev, struct request *req)
aa387cc89   Mike Christie   block: add bsg he...
195
196
  {
  	struct request *rsp = req->next_rq;
50b4d4855   Benjamin Block   bsg-lib: fix kern...
197
  	struct bsg_job *job = blk_mq_rq_to_pdu(req);
aa387cc89   Mike Christie   block: add bsg he...
198
  	int ret;
31156ec37   Christoph Hellwig   bsg-lib: introduc...
199
  	job->timeout = req->timeout;
50b4d4855   Benjamin Block   bsg-lib: fix kern...
200

aa387cc89   Mike Christie   block: add bsg he...
201
202
203
204
205
206
207
208
209
210
211
212
213
  	if (req->bio) {
  		ret = bsg_map_buffer(&job->request_payload, req);
  		if (ret)
  			goto failjob_rls_job;
  	}
  	if (rsp && rsp->bio) {
  		ret = bsg_map_buffer(&job->reply_payload, rsp);
  		if (ret)
  			goto failjob_rls_rqst_payload;
  	}
  	job->dev = dev;
  	/* take a reference for the request */
  	get_device(job->dev);
bf0f2d380   Johannes Thumshirn   block: add refere...
214
  	kref_init(&job->kref);
17cb960f2   Christoph Hellwig   bsg: split handli...
215
  	return true;
aa387cc89   Mike Christie   block: add bsg he...
216
217
218
219
  
  failjob_rls_rqst_payload:
  	kfree(job->request_payload.sg_list);
  failjob_rls_job:
17cb960f2   Christoph Hellwig   bsg: split handli...
220
221
  	job->result = -ENOMEM;
  	return false;
aa387cc89   Mike Christie   block: add bsg he...
222
  }
aa387cc89   Mike Christie   block: add bsg he...
223
224
225
226
227
  /**
   * bsg_request_fn - generic handler for bsg requests
   * @q: request queue to manage
   *
   * On error the create_bsg_job function should return a -Exyz error value
17d5363b8   Christoph Hellwig   scsi: introduce a...
228
   * that will be set to ->result.
aa387cc89   Mike Christie   block: add bsg he...
229
230
231
   *
   * Drivers/subsys should pass this to the queue init function.
   */
8ae94eb65   Christoph Hellwig   block/bsg: move q...
232
  static void bsg_request_fn(struct request_queue *q)
dbb3ab035   Bart Van Assche   bsg: Add sparse a...
233
234
  	__releases(q->queue_lock)
  	__acquires(q->queue_lock)
aa387cc89   Mike Christie   block: add bsg he...
235
236
237
  {
  	struct device *dev = q->queuedata;
  	struct request *req;
aa387cc89   Mike Christie   block: add bsg he...
238
239
240
241
242
243
244
245
246
247
  	int ret;
  
  	if (!get_device(dev))
  		return;
  
  	while (1) {
  		req = blk_fetch_request(q);
  		if (!req)
  			break;
  		spin_unlock_irq(q->queue_lock);
17cb960f2   Christoph Hellwig   bsg: split handli...
248
  		if (!bsg_prepare_job(dev, req)) {
2a842acab   Christoph Hellwig   block: introduce ...
249
  			blk_end_request_all(req, BLK_STS_OK);
aa387cc89   Mike Christie   block: add bsg he...
250
251
252
  			spin_lock_irq(q->queue_lock);
  			continue;
  		}
50b4d4855   Benjamin Block   bsg-lib: fix kern...
253
  		ret = q->bsg_job_fn(blk_mq_rq_to_pdu(req));
aa387cc89   Mike Christie   block: add bsg he...
254
255
256
257
258
259
260
261
262
  		spin_lock_irq(q->queue_lock);
  		if (ret)
  			break;
  	}
  
  	spin_unlock_irq(q->queue_lock);
  	put_device(dev);
  	spin_lock_irq(q->queue_lock);
  }
aa387cc89   Mike Christie   block: add bsg he...
263

17cb960f2   Christoph Hellwig   bsg: split handli...
264
  /* called right after the request is allocated for the request_queue */
50b4d4855   Benjamin Block   bsg-lib: fix kern...
265
266
267
  static int bsg_init_rq(struct request_queue *q, struct request *req, gfp_t gfp)
  {
  	struct bsg_job *job = blk_mq_rq_to_pdu(req);
eab40cf33   Benjamin Block   bsg-lib: fix use-...
268

17cb960f2   Christoph Hellwig   bsg: split handli...
269
270
  	job->reply = kzalloc(SCSI_SENSE_BUFFERSIZE, gfp);
  	if (!job->reply)
eab40cf33   Benjamin Block   bsg-lib: fix use-...
271
  		return -ENOMEM;
eab40cf33   Benjamin Block   bsg-lib: fix use-...
272
273
  	return 0;
  }
17cb960f2   Christoph Hellwig   bsg: split handli...
274
  /* called right before the request is given to the request_queue user */
eab40cf33   Benjamin Block   bsg-lib: fix use-...
275
276
277
  static void bsg_initialize_rq(struct request *req)
  {
  	struct bsg_job *job = blk_mq_rq_to_pdu(req);
17cb960f2   Christoph Hellwig   bsg: split handli...
278
  	void *reply = job->reply;
eab40cf33   Benjamin Block   bsg-lib: fix use-...
279

50b4d4855   Benjamin Block   bsg-lib: fix kern...
280
  	memset(job, 0, sizeof(*job));
17cb960f2   Christoph Hellwig   bsg: split handli...
281
282
  	job->reply = reply;
  	job->reply_len = SCSI_SENSE_BUFFERSIZE;
50b4d4855   Benjamin Block   bsg-lib: fix kern...
283
  	job->dd_data = job + 1;
50b4d4855   Benjamin Block   bsg-lib: fix kern...
284
285
286
287
288
  }
  
  static void bsg_exit_rq(struct request_queue *q, struct request *req)
  {
  	struct bsg_job *job = blk_mq_rq_to_pdu(req);
50b4d4855   Benjamin Block   bsg-lib: fix kern...
289

17cb960f2   Christoph Hellwig   bsg: split handli...
290
  	kfree(job->reply);
50b4d4855   Benjamin Block   bsg-lib: fix kern...
291
  }
aa387cc89   Mike Christie   block: add bsg he...
292
293
294
  /**
   * bsg_setup_queue - Create and add the bsg hooks so we can receive requests
   * @dev: device to attach bsg device to
aa387cc89   Mike Christie   block: add bsg he...
295
296
297
   * @name: device to give bsg device
   * @job_fn: bsg job handler
   * @dd_job_size: size of LLD data needed for each job
aa387cc89   Mike Christie   block: add bsg he...
298
   */
c1225f01a   Christoph Hellwig   scsi: bsg-lib: pa...
299
  struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
5de815a7e   Christoph Hellwig   block: remove par...
300
  		bsg_job_fn *job_fn, int dd_job_size)
aa387cc89   Mike Christie   block: add bsg he...
301
  {
8ae94eb65   Christoph Hellwig   block/bsg: move q...
302
  	struct request_queue *q;
aa387cc89   Mike Christie   block: add bsg he...
303
  	int ret;
82ed4db49   Christoph Hellwig   block: split scsi...
304
  	q = blk_alloc_queue(GFP_KERNEL);
8ae94eb65   Christoph Hellwig   block/bsg: move q...
305
306
  	if (!q)
  		return ERR_PTR(-ENOMEM);
50b4d4855   Benjamin Block   bsg-lib: fix kern...
307
308
309
  	q->cmd_size = sizeof(struct bsg_job) + dd_job_size;
  	q->init_rq_fn = bsg_init_rq;
  	q->exit_rq_fn = bsg_exit_rq;
eab40cf33   Benjamin Block   bsg-lib: fix use-...
310
  	q->initialize_rq_fn = bsg_initialize_rq;
82ed4db49   Christoph Hellwig   block: split scsi...
311
312
313
314
315
  	q->request_fn = bsg_request_fn;
  
  	ret = blk_init_allocated_queue(q);
  	if (ret)
  		goto out_cleanup_queue;
8ae94eb65   Christoph Hellwig   block/bsg: move q...
316

aa387cc89   Mike Christie   block: add bsg he...
317
  	q->queuedata = dev;
aa387cc89   Mike Christie   block: add bsg he...
318
  	q->bsg_job_fn = job_fn;
8b904b5b6   Bart Van Assche   block: Use blk_qu...
319
  	blk_queue_flag_set(QUEUE_FLAG_BIDI, q);
aa387cc89   Mike Christie   block: add bsg he...
320
321
  	blk_queue_softirq_done(q, bsg_softirq_done);
  	blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
5de815a7e   Christoph Hellwig   block: remove par...
322
  	ret = bsg_register_queue(q, dev, name, &bsg_transport_ops);
aa387cc89   Mike Christie   block: add bsg he...
323
324
325
326
  	if (ret) {
  		printk(KERN_ERR "%s: bsg interface failed to "
  		       "initialize - register queue
  ", dev->kobj.name);
82ed4db49   Christoph Hellwig   block: split scsi...
327
  		goto out_cleanup_queue;
aa387cc89   Mike Christie   block: add bsg he...
328
  	}
8ae94eb65   Christoph Hellwig   block/bsg: move q...
329
  	return q;
82ed4db49   Christoph Hellwig   block: split scsi...
330
331
332
  out_cleanup_queue:
  	blk_cleanup_queue(q);
  	return ERR_PTR(ret);
aa387cc89   Mike Christie   block: add bsg he...
333
334
  }
  EXPORT_SYMBOL_GPL(bsg_setup_queue);