Blame view

drivers/s390/char/tape_char.c 11 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
   *    character device frontend for tape device driver
   *
   *  S390 and zSeries version
a53c8fab3   Heiko Carstens   s390/comments: un...
6
   *    Copyright IBM Corp. 2001, 2006
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
10
11
   *    Author(s): Carsten Otte <cotte@de.ibm.com>
   *		 Michael Holzheu <holzheu@de.ibm.com>
   *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
   *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
   */
bb5099124   Michael Holzheu   [S390] tape: Add ...
12
13
  #define KMSG_COMPONENT "tape"
  #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/proc_fs.h>
  #include <linux/mtio.h>
c54060797   Heiko Carstens   [S390] tape_char:...
18
  #include <linux/compat.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19

7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
20
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
  
  #define TAPE_DBF_AREA	tape_core_dbf
  
  #include "tape.h"
  #include "tape_std.h"
  #include "tape_class.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
33
34
35
  #define TAPECHAR_MAJOR		0	/* get dynamic major */
  
  /*
   * file operation structure for tape character frontend
   */
  static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
  static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
  static int tapechar_open(struct inode *,struct file *);
  static int tapechar_release(struct inode *,struct file *);
369a46325   Martin Schwidefsky   [S390] tape: remo...
36
  static long tapechar_ioctl(struct file *, unsigned int, unsigned long);
c54060797   Heiko Carstens   [S390] tape_char:...
37
38
39
  #ifdef CONFIG_COMPAT
  static long tapechar_compat_ioctl(struct file *, unsigned int, unsigned long);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40

d54b1fdb1   Arjan van de Ven   [PATCH] mark stru...
41
  static const struct file_operations tape_fops =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
  {
  	.owner = THIS_MODULE,
  	.read = tapechar_read,
  	.write = tapechar_write,
369a46325   Martin Schwidefsky   [S390] tape: remo...
46
  	.unlocked_ioctl = tapechar_ioctl,
c54060797   Heiko Carstens   [S390] tape_char:...
47
  #ifdef CONFIG_COMPAT
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
48
  	.compat_ioctl = tapechar_compat_ioctl,
c54060797   Heiko Carstens   [S390] tape_char:...
49
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
  	.open = tapechar_open,
  	.release = tapechar_release,
6038f373a   Arnd Bergmann   llseek: automatic...
52
  	.llseek = no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  };
  
  static int tapechar_major = TAPECHAR_MAJOR;
  
  /*
   * This function is called for every new tapedevice
   */
  int
  tapechar_setup_device(struct tape_device * device)
  {
  	char	device_name[20];
  
  	sprintf(device_name, "ntibm%i", device->first_minor / 2);
  	device->nt = register_tape_dev(
  		&device->cdev->dev,
  		MKDEV(tapechar_major, device->first_minor),
  		&tape_fops,
  		device_name,
  		"non-rewinding"
  	);
  	device_name[0] = 'r';
  	device->rt = register_tape_dev(
  		&device->cdev->dev,
  		MKDEV(tapechar_major, device->first_minor + 1),
  		&tape_fops,
  		device_name,
  		"rewinding"
  	);
  
  	return 0;
  }
  
  void
  tapechar_cleanup_device(struct tape_device *device)
  {
92bf435f3   Michael Holzheu   [S390] tape: dupl...
88
  	unregister_tape_dev(&device->cdev->dev, device->rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  	device->rt = NULL;
92bf435f3   Michael Holzheu   [S390] tape: dupl...
90
  	unregister_tape_dev(&device->cdev->dev, device->nt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
  	device->nt = NULL;
  }
4d284cac7   Heiko Carstens   [S390] Avoid exce...
93
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
101
102
103
104
105
  tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
  {
  	struct idal_buffer *new;
  
  	if (device->char_data.idal_buf != NULL &&
  	    device->char_data.idal_buf->size == block_size)
  		return 0;
  
  	if (block_size > MAX_BLOCKSIZE) {
  		DBF_EVENT(3, "Invalid blocksize (%zd > %d)
  ",
  			block_size, MAX_BLOCKSIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
  		return -EINVAL;
  	}
  
  	/* The current idal buffer is not correct. Allocate a new one. */
  	new = idal_buffer_alloc(block_size, 0);
0983e5683   Julien Brunel   [S390] drivers/s3...
111
  	if (IS_ERR(new))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
120
121
122
123
124
  		return -ENOMEM;
  
  	if (device->char_data.idal_buf != NULL)
  		idal_buffer_free(device->char_data.idal_buf);
  
  	device->char_data.idal_buf = new;
  
  	return 0;
  }
  
  /*
   * Tape device read function
   */
2b67fc460   Heiko Carstens   [S390] Get rid of...
125
  static ssize_t
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
  {
  	struct tape_device *device;
  	struct tape_request *request;
  	size_t block_size;
  	int rc;
  
  	DBF_EVENT(6, "TCHAR:read
  ");
  	device = (struct tape_device *) filp->private_data;
  
  	/*
  	 * If the tape isn't terminated yet, do it now. And since we then
  	 * are at the end of the tape there wouldn't be anything to read
25985edce   Lucas De Marchi   Fix common misspe...
140
  	 * anyways. So we return immediately.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
  	 */
  	if(device->required_tapemarks) {
  		return tape_std_terminate_write(device);
  	}
  
  	/* Find out block size to use */
  	if (device->char_data.block_size != 0) {
  		if (count < device->char_data.block_size) {
  			DBF_EVENT(3, "TCHAR:read smaller than block "
  				  "size was requested
  ");
  			return -EINVAL;
  		}
  		block_size = device->char_data.block_size;
  	} else {
  		block_size = count;
  	}
  
  	rc = tapechar_check_idalbuffer(device, block_size);
  	if (rc)
  		return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
168
169
170
171
172
173
  	DBF_EVENT(6, "TCHAR:nbytes: %lx
  ", block_size);
  	/* Let the discipline build the ccw chain. */
  	request = device->discipline->read_block(device, block_size);
  	if (IS_ERR(request))
  		return PTR_ERR(request);
  	/* Execute it. */
  	rc = tape_do_io(device, request);
  	if (rc == 0) {
  		rc = block_size - request->rescnt;
  		DBF_EVENT(6, "TCHAR:rbytes:  %x
  ", rc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
182
183
184
185
  		/* Copy data from idal buffer to user space. */
  		if (idal_buffer_to_user(device->char_data.idal_buf,
  					data, rc) != 0)
  			rc = -EFAULT;
  	}
  	tape_free_request(request);
  	return rc;
  }
  
  /*
   * Tape device write function
   */
2b67fc460   Heiko Carstens   [S390] Get rid of...
186
  static ssize_t
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t *ppos)
  {
  	struct tape_device *device;
  	struct tape_request *request;
  	size_t block_size;
  	size_t written;
  	int nblocks;
  	int i, rc;
  
  	DBF_EVENT(6, "TCHAR:write
  ");
  	device = (struct tape_device *) filp->private_data;
  	/* Find out block size and number of blocks */
  	if (device->char_data.block_size != 0) {
  		if (count < device->char_data.block_size) {
  			DBF_EVENT(3, "TCHAR:write smaller than block "
  				  "size was requested
  ");
  			return -EINVAL;
  		}
  		block_size = device->char_data.block_size;
  		nblocks = count / block_size;
  	} else {
  		block_size = count;
  		nblocks = 1;
  	}
  
  	rc = tapechar_check_idalbuffer(device, block_size);
  	if (rc)
  		return rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  	DBF_EVENT(6,"TCHAR:nbytes: %lx
  ", block_size);
  	DBF_EVENT(6, "TCHAR:nblocks: %x
  ", nblocks);
  	/* Let the discipline build the ccw chain. */
  	request = device->discipline->write_block(device, block_size);
  	if (IS_ERR(request))
  		return PTR_ERR(request);
  	rc = 0;
  	written = 0;
  	for (i = 0; i < nblocks; i++) {
  		/* Copy data from user space to idal buffer. */
  		if (idal_buffer_from_user(device->char_data.idal_buf,
  					  data, block_size)) {
  			rc = -EFAULT;
  			break;
  		}
  		rc = tape_do_io(device, request);
  		if (rc)
  			break;
  		DBF_EVENT(6, "TCHAR:wbytes: %lx
  ",
  			  block_size - request->rescnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  		written += block_size - request->rescnt;
  		if (request->rescnt != 0)
  			break;
  		data += block_size;
  	}
  	tape_free_request(request);
  	if (rc == -ENOSPC) {
  		/*
  		 * Ok, the device has no more space. It has NOT written
  		 * the block.
  		 */
  		if (device->discipline->process_eov)
  			device->discipline->process_eov(device);
  		if (written > 0)
  			rc = 0;
  
  	}
  
  	/*
  	 * After doing a write we always need two tapemarks to correctly
  	 * terminate the tape (one to terminate the file, the second to
  	 * flag the end of recorded data.
  	 * Since process_eov positions the tape in front of the written
  	 * tapemark it doesn't hurt to write two marks again.
  	 */
  	if (!rc)
  		device->required_tapemarks = 2;
  
  	return rc ? rc : written;
  }
  
  /*
   * Character frontend tape device open function.
   */
2b67fc460   Heiko Carstens   [S390] Get rid of...
274
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
  tapechar_open (struct inode *inode, struct file *filp)
  {
  	struct tape_device *device;
  	int minor, rc;
  
  	DBF_EVENT(6, "TCHAR:open: %i:%i
  ",
496ad9aa8   Al Viro   new helper: file_...
282
283
  		imajor(file_inode(filp)),
  		iminor(file_inode(filp)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284

496ad9aa8   Al Viro   new helper: file_...
285
  	if (imajor(file_inode(filp)) != tapechar_major)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  		return -ENODEV;
496ad9aa8   Al Viro   new helper: file_...
287
  	minor = iminor(file_inode(filp));
8fd138c36   Martin Schwidefsky   [S390] tape: clea...
288
  	device = tape_find_device(minor / TAPE_MINORS_PER_DEV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  	if (IS_ERR(device)) {
8fd138c36   Martin Schwidefsky   [S390] tape: clea...
290
291
  		DBF_EVENT(3, "TCHAR:open: tape_find_device() failed
  ");
369a46325   Martin Schwidefsky   [S390] tape: remo...
292
  		return PTR_ERR(device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
  	rc = tape_open(device);
  	if (rc == 0) {
  		filp->private_data = device;
c5bf68fe0   Kirill Smelkov   *: convert stream...
297
  		stream_open(inode, filp);
369a46325   Martin Schwidefsky   [S390] tape: remo...
298
  	} else
764a4a8e5   Jonathan Corbet   drivers/s390: cde...
299
  		tape_put_device(device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
305
306
  
  	return rc;
  }
  
  /*
   * Character frontend tape device release function.
   */
2b67fc460   Heiko Carstens   [S390] Get rid of...
307
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  tapechar_release(struct inode *inode, struct file *filp)
  {
  	struct tape_device *device;
  
  	DBF_EVENT(6, "TCHAR:release: %x
  ", iminor(inode));
  	device = (struct tape_device *) filp->private_data;
  
  	/*
  	 * If this is the rewinding tape minor then rewind. In that case we
  	 * write all required tapemarks. Otherwise only one to terminate the
  	 * file.
  	 */
  	if ((iminor(inode) & 1) != 0) {
  		if (device->required_tapemarks)
  			tape_std_terminate_write(device);
  		tape_mtop(device, MTREW, 1);
  	} else {
  		if (device->required_tapemarks > 1) {
  			if (tape_mtop(device, MTWEOF, 1) == 0)
  				device->required_tapemarks--;
  		}
  	}
  
  	if (device->char_data.idal_buf != NULL) {
  		idal_buffer_free(device->char_data.idal_buf);
  		device->char_data.idal_buf = NULL;
  	}
  	tape_release(device);
8fd138c36   Martin Schwidefsky   [S390] tape: clea...
337
338
  	filp->private_data = NULL;
  	tape_put_device(device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
342
343
344
345
346
  
  	return 0;
  }
  
  /*
   * Tape device io controls.
   */
  static int
369a46325   Martin Schwidefsky   [S390] tape: remo...
347
  __tapechar_ioctl(struct tape_device *device,
1207045da   Arnd Bergmann   compat_ioctl: mov...
348
  		 unsigned int no, void __user *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  	int rc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
  	if (no == MTIOCTOP) {
  		struct mtop op;
1207045da   Arnd Bergmann   compat_ioctl: mov...
353
  		if (copy_from_user(&op, data, sizeof(op)) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  			return -EFAULT;
  		if (op.mt_count < 0)
  			return -EINVAL;
  
  		/*
  		 * Operations that change tape position should write final
  		 * tapemarks.
  		 */
  		switch (op.mt_op) {
  			case MTFSF:
  			case MTBSF:
  			case MTFSR:
  			case MTBSR:
  			case MTREW:
  			case MTOFFL:
  			case MTEOM:
  			case MTRETEN:
  			case MTBSFM:
  			case MTFSFM:
  			case MTSEEK:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  				if (device->required_tapemarks)
  					tape_std_terminate_write(device);
  			default:
  				;
  		}
  		rc = tape_mtop(device, op.mt_op, op.mt_count);
  
  		if (op.mt_op == MTWEOF && rc == 0) {
  			if (op.mt_count > device->required_tapemarks)
  				device->required_tapemarks = 0;
  			else
  				device->required_tapemarks -= op.mt_count;
  		}
  		return rc;
  	}
  	if (no == MTIOCPOS) {
  		/* MTIOCPOS: query the tape position. */
  		struct mtpos pos;
  
  		rc = tape_mtop(device, MTTELL, 1);
  		if (rc < 0)
  			return rc;
  		pos.mt_blkno = rc;
1207045da   Arnd Bergmann   compat_ioctl: mov...
397
  		return put_user_mtpos(data, &pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
401
402
403
404
405
  	}
  	if (no == MTIOCGET) {
  		/* MTIOCGET: query the tape drive status. */
  		struct mtget get;
  
  		memset(&get, 0, sizeof(get));
  		get.mt_type = MT_ISUNKNOWN;
  		get.mt_resid = 0 /* device->devstat.rescnt */;
9fc98ad0d   Stefan Haberland   s390/tape: fix MT...
406
407
408
  		get.mt_dsreg =
  			((device->char_data.block_size << MT_ST_BLKSIZE_SHIFT)
  			 & MT_ST_BLKSIZE_MASK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  		/* FIXME: mt_gstat, mt_erreg, mt_fileno */
  		get.mt_gstat = 0;
  		get.mt_erreg = 0;
  		get.mt_fileno = 0;
  		get.mt_gstat  = device->tape_generic_status;
  
  		if (device->medium_state == MS_LOADED) {
  			rc = tape_mtop(device, MTTELL, 1);
  
  			if (rc < 0)
  				return rc;
  
  			if (rc == 0)
  				get.mt_gstat |= GMT_BOT(~0);
  
  			get.mt_blkno = rc;
  		}
1207045da   Arnd Bergmann   compat_ioctl: mov...
426
  		return put_user_mtget(data, &get);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
  	}
  	/* Try the discipline ioctl function. */
  	if (device->discipline->ioctl_fn == NULL)
  		return -EINVAL;
1207045da   Arnd Bergmann   compat_ioctl: mov...
431
  	return device->discipline->ioctl_fn(device, no, (unsigned long)data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  }
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
433
  static long
369a46325   Martin Schwidefsky   [S390] tape: remo...
434
435
436
437
438
439
440
441
442
443
  tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data)
  {
  	struct tape_device *device;
  	long rc;
  
  	DBF_EVENT(6, "TCHAR:ioct
  ");
  
  	device = (struct tape_device *) filp->private_data;
  	mutex_lock(&device->mutex);
1207045da   Arnd Bergmann   compat_ioctl: mov...
444
  	rc = __tapechar_ioctl(device, no, (void __user *)data);
369a46325   Martin Schwidefsky   [S390] tape: remo...
445
446
447
  	mutex_unlock(&device->mutex);
  	return rc;
  }
c54060797   Heiko Carstens   [S390] tape_char:...
448
  #ifdef CONFIG_COMPAT
369a46325   Martin Schwidefsky   [S390] tape: remo...
449
  static long
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
450
451
452
  tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
  {
  	struct tape_device *device = filp->private_data;
1207045da   Arnd Bergmann   compat_ioctl: mov...
453
  	long rc;
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
454

1207045da   Arnd Bergmann   compat_ioctl: mov...
455
456
457
458
  	if (no == MTIOCPOS32)
  		no = MTIOCPOS;
  	else if (no == MTIOCGET32)
  		no = MTIOCGET;
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
459

1207045da   Arnd Bergmann   compat_ioctl: mov...
460
461
462
463
  	mutex_lock(&device->mutex);
  	rc = __tapechar_ioctl(device, no, compat_ptr(data));
  	mutex_unlock(&device->mutex);
  	return rc;
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
464
  }
c54060797   Heiko Carstens   [S390] tape_char:...
465
  #endif /* CONFIG_COMPAT */
f042e0f80   Christoph Hellwig   [PATCH] ->compat_...
466

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
473
474
475
476
477
478
  /*
   * Initialize character device frontend.
   */
  int
  tapechar_init (void)
  {
  	dev_t	dev;
  
  	if (alloc_chrdev_region(&dev, 0, 256, "tape") != 0)
  		return -1;
  
  	tapechar_major = MAJOR(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
481
482
483
484
485
486
487
488
  
  	return 0;
  }
  
  /*
   * cleanup
   */
  void
  tapechar_exit(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  	unregister_chrdev_region(MKDEV(tapechar_major, 0), 256);
  }