st: Fix MTMKPART to work with newer drives

Change the MTMKPART operation of the MTIOCTOP ioctl so that it works
also with current drives (LTO-5/6, etc.). Send a separate FORMAT MEDIUM
command if the partition mode page indicates that this is required. Use
LOAD to position the tape at the beginning of tape.

The operation is extended so that if the argument is negative, its
absolute value specifies the size of partition 0, which is the
physically first partition of the tape.

Signed-off-by: Kai Mäkisara <kai.makisara@kolumbus.fi>
Tested-by: Shane M Seymour <shane.seymour@hpe.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Tested-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Kai Makisara 2016-02-09 21:56:55 +02:00 committed by Martin K. Petersen
parent ea1c928bb6
commit 8038e6456a
2 changed files with 121 additions and 16 deletions

View File

@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver.
The driver is currently maintained by Kai Mäkisara (email The driver is currently maintained by Kai Mäkisara (email
Kai.Makisara@kolumbus.fi) Kai.Makisara@kolumbus.fi)
Last modified: Sun Aug 29 18:25:47 2010 by kai.makisara Last modified: Tue Feb 9 21:54:16 2016 by kai.makisara
BASICS BASICS
@ -408,10 +408,15 @@ MTSETPART Moves the tape to the partition given by the argument at the
specified by MTSEEK. MTSETPART is inactive unless specified by MTSEEK. MTSETPART is inactive unless
MT_ST_CAN_PARTITIONS set. MT_ST_CAN_PARTITIONS set.
MTMKPART Formats the tape with one partition (argument zero) or two MTMKPART Formats the tape with one partition (argument zero) or two
partitions (the argument gives in megabytes the size of partitions (argument non-zero). If the argument is positive,
partition 1 that is physically the first partition of the it specifies the size of partition 1 in megabytes. For DDS
tape). The drive has to support partitions with size specified drives and several early drives this is the physically first
by the initiator. Inactive unless MT_ST_CAN_PARTITIONS set. partition of the tape. If the argument is negative, its absolute
value specifies the size of partition 0 in megabytes. This is
the physically first partition of many later drives, like the
LTO drives from LTO-5 upwards. The drive has to support partitions
with size specified by the initiator. Inactive unless
MT_ST_CAN_PARTITIONS set.
MTSETDRVBUFFER MTSETDRVBUFFER
Is used for several purposes. The command is obtained from count Is used for several purposes. The command is obtained from count
with mask MT_SET_OPTIONS, the low order bits are used as argument. with mask MT_SET_OPTIONS, the low order bits are used as argument.

View File

@ -9,7 +9,7 @@
Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
Michael Schaefer, J"org Weule, and Eric Youngdale. Michael Schaefer, J"org Weule, and Eric Youngdale.
Copyright 1992 - 2010 Kai Makisara Copyright 1992 - 2016 Kai Makisara
email Kai.Makisara@kolumbus.fi email Kai.Makisara@kolumbus.fi
Some small formal changes - aeb, 950809 Some small formal changes - aeb, 950809
@ -17,7 +17,7 @@
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
*/ */
static const char *verstr = "20101219"; static const char *verstr = "20160209";
#include <linux/module.h> #include <linux/module.h>
@ -3296,7 +3296,10 @@ static int switch_partition(struct scsi_tape *STp)
#define PP_OFF_RESERVED 7 #define PP_OFF_RESERVED 7
#define PP_BIT_IDP 0x20 #define PP_BIT_IDP 0x20
#define PP_BIT_FDP 0x80
#define PP_MSK_PSUM_MB 0x10 #define PP_MSK_PSUM_MB 0x10
#define PP_MSK_PSUM_UNITS 0x18
#define PP_MSK_POFM 0x04
/* Get the number of partitions on the tape. As a side effect reads the /* Get the number of partitions on the tape. As a side effect reads the
mode page into the tape buffer. */ mode page into the tape buffer. */
@ -3322,6 +3325,29 @@ static int nbr_partitions(struct scsi_tape *STp)
} }
static int format_medium(struct scsi_tape *STp, int format)
{
int result = 0;
int timeout = STp->long_timeout;
unsigned char scmd[MAX_COMMAND_SIZE];
struct st_request *SRpnt;
memset(scmd, 0, MAX_COMMAND_SIZE);
scmd[0] = FORMAT_UNIT;
scmd[2] = format;
if (STp->immediate) {
scmd[1] |= 1; /* Don't wait for completion */
timeout = STp->device->request_queue->rq_timeout;
}
DEBC_printk(STp, "Sending FORMAT MEDIUM\n");
SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
timeout, MAX_RETRIES, 1);
if (!SRpnt)
result = STp->buffer->syscall_result;
return result;
}
/* Partition the tape into two partitions if size > 0 or one partition if /* Partition the tape into two partitions if size > 0 or one partition if
size == 0. size == 0.
@ -3340,11 +3366,16 @@ static int nbr_partitions(struct scsi_tape *STp)
and 10 when 1 partition is defined (information from Eric Lee Green). This is and 10 when 1 partition is defined (information from Eric Lee Green). This is
is acceptable also to some other old drives and enforced if the first partition is acceptable also to some other old drives and enforced if the first partition
size field is used for the first additional partition size. size field is used for the first additional partition size.
For drives that advertize SCSI-3 or newer, use the SSC-3 methods.
*/ */
static int partition_tape(struct scsi_tape *STp, int size) static int partition_tape(struct scsi_tape *STp, int size)
{ {
int result; int result;
int target_partition;
bool scsi3 = STp->device->scsi_level >= SCSI_3, needs_format = false;
int pgo, psd_cnt, psdo; int pgo, psd_cnt, psdo;
int psum = PP_MSK_PSUM_MB, units = 0;
unsigned char *bp; unsigned char *bp;
result = read_mode_page(STp, PART_PAGE, 0); result = read_mode_page(STp, PART_PAGE, 0);
@ -3352,6 +3383,12 @@ static int partition_tape(struct scsi_tape *STp, int size)
DEBC_printk(STp, "Can't read partition mode page.\n"); DEBC_printk(STp, "Can't read partition mode page.\n");
return result; return result;
} }
target_partition = 1;
if (size < 0) {
target_partition = 0;
size = -size;
}
/* The mode page is in the buffer. Let's modify it and write it. */ /* The mode page is in the buffer. Let's modify it and write it. */
bp = (STp->buffer)->b_data; bp = (STp->buffer)->b_data;
pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH]; pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
@ -3359,9 +3396,52 @@ static int partition_tape(struct scsi_tape *STp, int size)
bp[pgo + MP_OFF_PAGE_LENGTH] + 2); bp[pgo + MP_OFF_PAGE_LENGTH] + 2);
psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2; psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
if (scsi3) {
needs_format = (bp[pgo + PP_OFF_FLAGS] & PP_MSK_POFM) != 0;
if (needs_format && size == 0) {
/* No need to write the mode page when clearing
* partitioning
*/
DEBC_printk(STp, "Formatting tape with one partition.\n");
result = format_medium(STp, 0);
goto out;
}
if (needs_format) /* Leave the old value for HP DATs claiming SCSI_3 */
psd_cnt = 2;
if ((bp[pgo + PP_OFF_FLAGS] & PP_MSK_PSUM_UNITS) == PP_MSK_PSUM_UNITS) {
/* Use units scaling for large partitions if the device
* suggests it and no precision lost. Required for IBM
* TS1140/50 drives that don't support MB units.
*/
if (size >= 1000 && (size % 1000) == 0) {
size /= 1000;
psum = PP_MSK_PSUM_UNITS;
units = 9; /* GB */
}
}
/* Try it anyway if too large to specify in MB */
if (psum == PP_MSK_PSUM_MB && size >= 65534) {
size /= 1000;
psum = PP_MSK_PSUM_UNITS;
units = 9; /* GB */
}
}
if (size >= 65535 || /* Does not fit into two bytes */
(target_partition == 0 && psd_cnt < 2)) {
result = -EINVAL;
goto out;
}
psdo = pgo + PART_PAGE_FIXED_LENGTH; psdo = pgo + PART_PAGE_FIXED_LENGTH;
if (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) { /* The second condition is for HP DDS which use only one partition size
bp[psdo] = bp[psdo + 1] = 0xff; /* Rest of the tape */ * descriptor
*/
if (target_partition > 0 &&
(psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS] ||
bp[pgo + PP_OFF_MAX_ADD_PARTS] != 1)) {
bp[psdo] = bp[psdo + 1] = 0xff; /* Rest to partition 0 */
psdo += 2; psdo += 2;
} }
memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2); memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
@ -3370,7 +3450,7 @@ static int partition_tape(struct scsi_tape *STp, int size)
psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS], psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
bp[pgo + PP_OFF_NBR_ADD_PARTS]); bp[pgo + PP_OFF_NBR_ADD_PARTS]);
if (size <= 0) { if (size == 0) {
bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0; bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS]) if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
bp[pgo + MP_OFF_PAGE_LENGTH] = 6; bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
@ -3378,22 +3458,37 @@ static int partition_tape(struct scsi_tape *STp, int size)
} else { } else {
bp[psdo] = (size >> 8) & 0xff; bp[psdo] = (size >> 8) & 0xff;
bp[psdo + 1] = size & 0xff; bp[psdo + 1] = size & 0xff;
if (target_partition == 0)
bp[psdo + 2] = bp[psdo + 3] = 0xff;
bp[pgo + 3] = 1; bp[pgo + 3] = 1;
if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8) if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8)
bp[pgo + MP_OFF_PAGE_LENGTH] = 8; bp[pgo + MP_OFF_PAGE_LENGTH] = 8;
DEBC_printk(STp, "Formatting tape with two partitions " DEBC_printk(STp,
"(1 = %d MB).\n", size); "Formatting tape with two partitions (%i = %d MB).\n",
target_partition, units > 0 ? size * 1000 : size);
} }
bp[pgo + PP_OFF_PART_UNITS] = 0; bp[pgo + PP_OFF_PART_UNITS] = 0;
bp[pgo + PP_OFF_RESERVED] = 0; bp[pgo + PP_OFF_RESERVED] = 0;
bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB; if (size != 1 || units != 0) {
bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | psum |
(bp[pgo + PP_OFF_FLAGS] & 0x07);
bp[pgo + PP_OFF_PART_UNITS] = units;
} else
bp[pgo + PP_OFF_FLAGS] = PP_BIT_FDP |
(bp[pgo + PP_OFF_FLAGS] & 0x1f);
bp[pgo + MP_OFF_PAGE_LENGTH] = 6 + psd_cnt * 2;
result = write_mode_page(STp, PART_PAGE, 1); result = write_mode_page(STp, PART_PAGE, 1);
if (!result && needs_format)
result = format_medium(STp, 1);
if (result) { if (result) {
st_printk(KERN_INFO, STp, "Partitioning of tape failed.\n"); st_printk(KERN_INFO, STp, "Partitioning of tape failed.\n");
result = (-EIO); result = (-EIO);
} }
out:
return result; return result;
} }
@ -3570,8 +3665,13 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
retval = (-EINVAL); retval = (-EINVAL);
goto out; goto out;
} }
if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 || i = do_load_unload(STp, file, 1);
(i = partition_tape(STp, mtc.mt_count)) < 0) { if (i < 0) {
retval = i;
goto out;
}
i = partition_tape(STp, mtc.mt_count);
if (i < 0) {
retval = i; retval = i;
goto out; goto out;
} }
@ -3581,7 +3681,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
STp->ps[i].last_block_valid = 0; STp->ps[i].last_block_valid = 0;
} }
STp->partition = STp->new_partition = 0; STp->partition = STp->new_partition = 0;
STp->nbr_partitions = 1; /* Bad guess ?-) */ STp->nbr_partitions = mtc.mt_count != 0 ? 2 : 1;
STps->drv_block = STps->drv_file = 0; STps->drv_block = STps->drv_file = 0;
retval = 0; retval = 0;
goto out; goto out;