1/*
2 *  drivers/s390/char/tape_block.c
3 *    block device frontend for tape device driver
4 *
5 *  S390 and zSeries version
6 *    Copyright (C) 2001,2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
7 *    Author(s): Carsten Otte <cotte@de.ibm.com>
8 *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
9 *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
10 *		 Stefan Bader <shbader@de.ibm.com>
11 */
12
13#include <linux/fs.h>
14#include <linux/module.h>
15#include <linux/blkdev.h>
16#include <linux/interrupt.h>
17#include <linux/buffer_head.h>
18#include <linux/kernel.h>
19
20#include <asm/debug.h>
21
22#define TAPE_DBF_AREA	tape_core_dbf
23
24#include "tape.h"
25
26#define PRINTK_HEADER "TAPE_BLOCK: "
27
28#define TAPEBLOCK_MAX_SEC	100
29#define TAPEBLOCK_MIN_REQUEUE	3
30
31
32/*
33 * file operation structure for tape block frontend
34 */
35static int tapeblock_open(struct inode *, struct file *);
36static int tapeblock_release(struct inode *, struct file *);
37static int tapeblock_ioctl(struct inode *, struct file *, unsigned int,
38				unsigned long);
39static int tapeblock_medium_changed(struct gendisk *);
40static int tapeblock_revalidate_disk(struct gendisk *);
41
42static struct block_device_operations tapeblock_fops = {
43	.owner		 = THIS_MODULE,
44	.open		 = tapeblock_open,
45	.release	 = tapeblock_release,
46	.ioctl           = tapeblock_ioctl,
47	.media_changed   = tapeblock_medium_changed,
48	.revalidate_disk = tapeblock_revalidate_disk,
49};
50
51static int tapeblock_major = 0;
52
53static void
54tapeblock_trigger_requeue(struct tape_device *device)
55{
56	/* Protect against rescheduling. */
57	if (atomic_cmpxchg(&device->blk_data.requeue_scheduled, 0, 1) != 0)
58		return;
59	schedule_work(&device->blk_data.requeue_task);
60}
61
62/*
63 * Post finished request.
64 */
65static void
66tapeblock_end_request(struct request *req, int uptodate)
67{
68	if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
69		BUG();
70	end_that_request_last(req, uptodate);
71}
72
73static void
74__tapeblock_end_request(struct tape_request *ccw_req, void *data)
75{
76	struct tape_device *device;
77	struct request *req;
78
79	DBF_LH(6, "__tapeblock_end_request()\n");
80
81	device = ccw_req->device;
82	req = (struct request *) data;
83	tapeblock_end_request(req, ccw_req->rc == 0);
84	if (ccw_req->rc == 0)
85		/* Update position. */
86		device->blk_data.block_position =
87			(req->sector + req->nr_sectors) >> TAPEBLOCK_HSEC_S2B;
88	else
89		/* We lost the position information due to an error. */
90		device->blk_data.block_position = -1;
91	device->discipline->free_bread(ccw_req);
92	if (!list_empty(&device->req_queue) ||
93	    elv_next_request(device->blk_data.request_queue))
94		tapeblock_trigger_requeue(device);
95}
96
97/*
98 * Feed the tape device CCW queue with requests supplied in a list.
99 */
100static int
101tapeblock_start_request(struct tape_device *device, struct request *req)
102{
103	struct tape_request *	ccw_req;
104	int			rc;
105
106	DBF_LH(6, "tapeblock_start_request(%p, %p)\n", device, req);
107
108	ccw_req = device->discipline->bread(device, req);
109	if (IS_ERR(ccw_req)) {
110		DBF_EVENT(1, "TBLOCK: bread failed\n");
111		tapeblock_end_request(req, 0);
112		return PTR_ERR(ccw_req);
113	}
114	ccw_req->callback = __tapeblock_end_request;
115	ccw_req->callback_data = (void *) req;
116	ccw_req->retries = TAPEBLOCK_RETRIES;
117
118	rc = tape_do_io_async(device, ccw_req);
119	if (rc) {
120		/*
121		 * Start/enqueueing failed. No retries in
122		 * this case.
123		 */
124		tapeblock_end_request(req, 0);
125		device->discipline->free_bread(ccw_req);
126	}
127
128	return rc;
129}
130
131/*
132 * Move requests from the block device request queue to the tape device ccw
133 * queue.
134 */
135static void
136tapeblock_requeue(struct work_struct *work) {
137	struct tape_blk_data *	blkdat;
138	struct tape_device *	device;
139	request_queue_t *	queue;
140	int			nr_queued;
141	struct request *	req;
142	struct list_head *	l;
143	int			rc;
144
145	blkdat = container_of(work, struct tape_blk_data, requeue_task);
146	device = blkdat->device;
147	if (!device)
148		return;
149
150	spin_lock_irq(get_ccwdev_lock(device->cdev));
151	queue  = device->blk_data.request_queue;
152
153	/* Count number of requests on ccw queue. */
154	nr_queued = 0;
155	list_for_each(l, &device->req_queue)
156		nr_queued++;
157	spin_unlock(get_ccwdev_lock(device->cdev));
158
159	spin_lock(&device->blk_data.request_queue_lock);
160	while (
161		!blk_queue_plugged(queue) &&
162		elv_next_request(queue)   &&
163		nr_queued < TAPEBLOCK_MIN_REQUEUE
164	) {
165		req = elv_next_request(queue);
166		if (rq_data_dir(req) == WRITE) {
167			DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
168			blkdev_dequeue_request(req);
169			tapeblock_end_request(req, 0);
170			continue;
171		}
172		spin_unlock_irq(&device->blk_data.request_queue_lock);
173		rc = tapeblock_start_request(device, req);
174		spin_lock_irq(&device->blk_data.request_queue_lock);
175		blkdev_dequeue_request(req);
176		nr_queued++;
177	}
178	spin_unlock_irq(&device->blk_data.request_queue_lock);
179	atomic_set(&device->blk_data.requeue_scheduled, 0);
180}
181
182/*
183 * Tape request queue function. Called from ll_rw_blk.c
184 */
185static void
186tapeblock_request_fn(request_queue_t *queue)
187{
188	struct tape_device *device;
189
190	device = (struct tape_device *) queue->queuedata;
191	DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device);
192	BUG_ON(device == NULL);
193	tapeblock_trigger_requeue(device);
194}
195
196/*
197 * This function is called for every new tapedevice
198 */
199int
200tapeblock_setup_device(struct tape_device * device)
201{
202	struct tape_blk_data *	blkdat;
203	struct gendisk *	disk;
204	int			rc;
205
206	blkdat = &device->blk_data;
207	blkdat->device = device;
208	spin_lock_init(&blkdat->request_queue_lock);
209	atomic_set(&blkdat->requeue_scheduled, 0);
210
211	blkdat->request_queue = blk_init_queue(
212		tapeblock_request_fn,
213		&blkdat->request_queue_lock
214	);
215	if (!blkdat->request_queue)
216		return -ENOMEM;
217
218	elevator_exit(blkdat->request_queue->elevator);
219	rc = elevator_init(blkdat->request_queue, "noop");
220	if (rc)
221		goto cleanup_queue;
222
223	blk_queue_hardsect_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
224	blk_queue_max_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
225	blk_queue_max_phys_segments(blkdat->request_queue, -1L);
226	blk_queue_max_hw_segments(blkdat->request_queue, -1L);
227	blk_queue_max_segment_size(blkdat->request_queue, -1L);
228	blk_queue_segment_boundary(blkdat->request_queue, -1L);
229
230	disk = alloc_disk(1);
231	if (!disk) {
232		rc = -ENOMEM;
233		goto cleanup_queue;
234	}
235
236	disk->major = tapeblock_major;
237	disk->first_minor = device->first_minor;
238	disk->fops = &tapeblock_fops;
239	disk->private_data = tape_get_device_reference(device);
240	disk->queue = blkdat->request_queue;
241	set_capacity(disk, 0);
242	sprintf(disk->disk_name, "btibm%d",
243		device->first_minor / TAPE_MINORS_PER_DEV);
244
245	blkdat->disk = disk;
246	blkdat->medium_changed = 1;
247	blkdat->request_queue->queuedata = tape_get_device_reference(device);
248
249	add_disk(disk);
250
251	tape_get_device_reference(device);
252	INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
253
254	return 0;
255
256cleanup_queue:
257	blk_cleanup_queue(blkdat->request_queue);
258	blkdat->request_queue = NULL;
259
260	return rc;
261}
262
263void
264tapeblock_cleanup_device(struct tape_device *device)
265{
266	flush_scheduled_work();
267	tape_put_device(device);
268
269	if (!device->blk_data.disk) {
270		PRINT_ERR("(%s): No gendisk to clean up!\n",
271			device->cdev->dev.bus_id);
272		goto cleanup_queue;
273	}
274
275	del_gendisk(device->blk_data.disk);
276	device->blk_data.disk->private_data =
277		tape_put_device(device->blk_data.disk->private_data);
278	put_disk(device->blk_data.disk);
279
280	device->blk_data.disk = NULL;
281cleanup_queue:
282	device->blk_data.request_queue->queuedata = tape_put_device(device);
283
284	blk_cleanup_queue(device->blk_data.request_queue);
285	device->blk_data.request_queue = NULL;
286}
287
288static int
289tapeblock_revalidate_disk(struct gendisk *disk)
290{
291	struct tape_device *	device;
292	unsigned int		nr_of_blks;
293	int			rc;
294
295	device = (struct tape_device *) disk->private_data;
296	BUG_ON(!device);
297
298	if (!device->blk_data.medium_changed)
299		return 0;
300
301	PRINT_INFO("Detecting media size...\n");
302	rc = tape_mtop(device, MTFSFM, 1);
303	if (rc)
304		return rc;
305
306	rc = tape_mtop(device, MTTELL, 1);
307	if (rc < 0)
308		return rc;
309
310	DBF_LH(3, "Image file ends at %d\n", rc);
311	nr_of_blks = rc;
312
313	/* This will fail for the first file. Catch the error by checking the
314	 * position. */
315	tape_mtop(device, MTBSF, 1);
316
317	rc = tape_mtop(device, MTTELL, 1);
318	if (rc < 0)
319		return rc;
320
321	if (rc > nr_of_blks)
322		return -EINVAL;
323
324	DBF_LH(3, "Image file starts at %d\n", rc);
325	device->bof = rc;
326	nr_of_blks -= rc;
327
328	PRINT_INFO("Found %i blocks on media\n", nr_of_blks);
329	set_capacity(device->blk_data.disk,
330		nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512));
331
332	device->blk_data.block_position = 0;
333	device->blk_data.medium_changed = 0;
334	return 0;
335}
336
337static int
338tapeblock_medium_changed(struct gendisk *disk)
339{
340	struct tape_device *device;
341
342	device = (struct tape_device *) disk->private_data;
343	DBF_LH(6, "tapeblock_medium_changed(%p) = %d\n",
344		device, device->blk_data.medium_changed);
345
346	return device->blk_data.medium_changed;
347}
348
349/*
350 * Block frontend tape device open function.
351 */
352static int
353tapeblock_open(struct inode *inode, struct file *filp)
354{
355	struct gendisk *	disk;
356	struct tape_device *	device;
357	int			rc;
358
359	disk   = inode->i_bdev->bd_disk;
360	device = tape_get_device_reference(disk->private_data);
361
362	if (device->required_tapemarks) {
363		DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
364		PRINT_ERR("TBLOCK: Refusing to open tape with missing"
365			" end of file marks.\n");
366		rc = -EPERM;
367		goto put_device;
368	}
369
370	rc = tape_open(device);
371	if (rc)
372		goto put_device;
373
374	rc = tapeblock_revalidate_disk(disk);
375	if (rc)
376		goto release;
377
378	/*
379	 * Note: The reference to <device> is hold until the release function
380	 *       is called.
381	 */
382	tape_state_set(device, TS_BLKUSE);
383	return 0;
384
385release:
386	tape_release(device);
387 put_device:
388	tape_put_device(device);
389	return rc;
390}
391
392/*
393 * Block frontend tape device release function.
394 *
395 * Note: One reference to the tape device was made by the open function. So
396 *       we just get the pointer here and release the reference.
397 */
398static int
399tapeblock_release(struct inode *inode, struct file *filp)
400{
401	struct gendisk *disk = inode->i_bdev->bd_disk;
402	struct tape_device *device = disk->private_data;
403
404	tape_state_set(device, TS_IN_USE);
405	tape_release(device);
406	tape_put_device(device);
407
408	return 0;
409}
410
411/*
412 * Support of some generic block device IOCTLs.
413 */
414static int
415tapeblock_ioctl(
416	struct inode *		inode,
417	struct file *		file,
418	unsigned int		command,
419	unsigned long		arg
420) {
421	int rc;
422	int minor;
423	struct gendisk *disk;
424	struct tape_device *device;
425
426	rc     = 0;
427	disk   = inode->i_bdev->bd_disk;
428	BUG_ON(!disk);
429	device = disk->private_data;
430	BUG_ON(!device);
431	minor  = iminor(inode);
432
433	DBF_LH(6, "tapeblock_ioctl(0x%0x)\n", command);
434	DBF_LH(6, "device = %d:%d\n", tapeblock_major, minor);
435
436	switch (command) {
437		/* Refuse some IOCTL calls without complaining (mount). */
438		case 0x5310:		/* CDROMMULTISESSION */
439			rc = -EINVAL;
440			break;
441		default:
442			PRINT_WARN("invalid ioctl 0x%x\n", command);
443			rc = -EINVAL;
444	}
445
446	return rc;
447}
448
449/*
450 * Initialize block device frontend.
451 */
452int
453tapeblock_init(void)
454{
455	int rc;
456
457	/* Register the tape major number to the kernel */
458	rc = register_blkdev(tapeblock_major, "tBLK");
459	if (rc < 0)
460		return rc;
461
462	if (tapeblock_major == 0)
463		tapeblock_major = rc;
464	PRINT_INFO("tape gets major %d for block device\n", tapeblock_major);
465	return 0;
466}
467
468/*
469 * Deregister major for block device frontend
470 */
471void
472tapeblock_exit(void)
473{
474	unregister_blkdev(tapeblock_major, "tBLK");
475}
476