On Monday 2015-12-21 20:57, "Kai Mäkisara (Kolumbus)" wrote:

...
>I can look at the manuals during Christmas holidays and try to think about a 
>suggestion.
>All other suggestions are, of course, welcome. My view may be somewhat 
>theoretical
>because I don’t have access to current hardware.
>
I have had some time to look at manuals and even do some coding :-)

The partitioning is initiated with the MTMKPART operation of the 
MTIOCTOP ioctl. This has one int parameter mt_count that is used to 
specify the size in megabytes of the first partition on the tape. Note 
that, for historical reasons, the number of this partition used to be 1, 
not zero. So, the ioctls allocates the specified space to partition 1 
and the rest to partition 0. This intefarce does not allow specifying 
more than two partitions. We can make work for current drives quite 
easily. More than two partitions need a new API but do we have a real 
need for this?

The current code did not work basically because the current drives need 
a separate FORMAT MEDIUM command to do the partitioning specified by 
MODE SELECT. There are smaller details that could have also been a 
problem.

The patch at the end of this message is an attempt to make the 
partitioning work for both old and new drives. The patch is against st.c 
from the current git kernel, although I have tested it in 4.3.3. It 
seems to work with my HP DDS-4 drive. It includes some debugging printks 
and I have tried to see that the commands that would have been sent to 
newer drives are basically correct.

The patch uses the scsi level of the device to separate processing. The 
FORMAT MEDIUM command is defined in SCSI-3 and I suppose that no current 
drive is still SCSI-2. In addition to the "sane" changes using the 
method to specify partitions, the patch implements the following: if the 
size is 1, the driver tells the drive to use default partitioning for 
two partitions. For the HP Ultrium this should result in partition 0 of 
1425 GB and 1 of 37.5 GB. I don't know if this is a useful addition.


Kai

------------------------------------8<----------------------------------
--- ref/drivers/scsi/st.c       2015-12-21 18:54:05.068882001 +0200
+++ new/drivers/scsi/st.c       2015-12-25 16:49:54.345263675 +0200
@@ -17,7 +17,7 @@
    Last modified: 18-JAN-1998 Richard Gooch <rgo...@atnf.csiro.au> Devfs 
support
  */
 
-static const char *verstr = "20101219";
+static const char *verstr = "20151225";
 
 #include <linux/module.h>
 
@@ -3296,7 +3296,10 @@
 #define PP_OFF_RESERVED        7
 
 #define PP_BIT_IDP             0x20
+#define PP_BIT_FDP             0x80
 #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
    mode page into the tape buffer. */
@@ -3344,7 +3347,9 @@
 static int partition_tape(struct scsi_tape *STp, int size)
 {
        int result;
+       int scsi3 = STp->device->scsi_level >= SCSI_3, needs_format = 0;
        int pgo, psd_cnt, psdo;
+       int psum = PP_MSK_PSUM_MB, units = 0;
        unsigned char *bp;
 
        result = read_mode_page(STp, PART_PAGE, 0);
@@ -3357,10 +3362,32 @@
        pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
        DEBC_printk(STp, "Partition page length is %d bytes.\n",
                    bp[pgo + MP_OFF_PAGE_LENGTH] + 2);
-
-       psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 
2;
+       DEBC_printk(STp, "PP: max %u, add %u, xdp %u, psum %02x, pofmetc %u,"
+                   "rec %02x units %02x, sizes: %u %u\n",
+                   bp[pgo + PP_OFF_MAX_ADD_PARTS],
+                   bp[pgo + PP_OFF_NBR_ADD_PARTS],
+                   (bp[pgo + PP_OFF_FLAGS] & 0xe0) >> 5,
+                   (bp[pgo + PP_OFF_FLAGS] & 0x18) >> 3,
+                   bp[pgo + PP_OFF_FLAGS] & 0x03,
+                   bp[pgo + 5],
+                   bp[pgo + PP_OFF_PART_UNITS],
+                   bp[pgo + 8] * 256 + bp[pgo + 9],
+                   bp[pgo + 10] * 256 + bp[pgo + 11]);
+
+       if (scsi3) {
+               psd_cnt = size > 0 ? 2 : 1;
+               if (size >= 65535) {
+                       size /= 1000;
+                       psum = PP_MSK_PSUM_UNITS;
+                       units = 9; /* GB */
+               }
+       } else
+               psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 -
+                          PART_PAGE_FIXED_LENGTH) / 2;
+       if (size >= 65535)
+               return -EINVAL;
        psdo = pgo + PART_PAGE_FIXED_LENGTH;
-       if (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) {
+       if (scsi3 || psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) {
                bp[psdo] = bp[psdo + 1] = 0xff;  /* Rest of the tape */
                psdo += 2;
        }
@@ -3375,6 +3402,11 @@
                if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
                    bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
                DEBC_printk(STp, "Formatting tape with one partition.\n");
+       } else if (size == 1) {
+               bp[pgo + PP_OFF_NBR_ADD_PARTS] = 1;
+               if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
+                   bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
+               DEBC_printk(STp, "Formatting tape with two partitions 
(FDP).\n");
        } else {
                bp[psdo] = (size >> 8) & 0xff;
                bp[psdo + 1] = size & 0xff;
@@ -3382,13 +3414,55 @@
                if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8)
                    bp[pgo + MP_OFF_PAGE_LENGTH] = 8;
                DEBC_printk(STp, "Formatting tape with two partitions "
-                           "(1 = %d MB).\n", size);
+                           "(1 = %d MB).\n",
+                           units > 0 ? size * 1000 : size);
        }
        bp[pgo + PP_OFF_PART_UNITS] = 0;
        bp[pgo + PP_OFF_RESERVED] = 0;
-       bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB;
+       if (size != 1) {
+               bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | psum |
+                       (bp[pgo + PP_OFF_FLAGS] & 0x0F);
+               bp[pgo + PP_OFF_PART_UNITS] = units;
+       } else
+               bp[pgo + PP_OFF_FLAGS] = PP_BIT_FDP;
+       if (scsi3)
+               needs_format = (bp[pgo + PP_OFF_FLAGS] & PP_MSK_POFM) != 0;
+       bp[pgo + MP_OFF_PAGE_LENGTH] = 6 + psd_cnt * 2;
+       DEBC_printk(STp, "Sent partition page length is %d bytes. needs_format: 
%d\n",
+                   bp[pgo + MP_OFF_PAGE_LENGTH] + 2, needs_format);
+       DEBC_printk(STp, "PP: max %u, add %u, xdp %u, psum %02x, pofmetc %u,"
+                   "rec %02x units %02x, sizes: %u %u\n",
+                   bp[pgo + PP_OFF_MAX_ADD_PARTS],
+                   bp[pgo + PP_OFF_NBR_ADD_PARTS],
+                   (bp[pgo + PP_OFF_FLAGS] & 0xe0) >> 5,
+                   (bp[pgo + PP_OFF_FLAGS] & 0x18) >> 3,
+                   bp[pgo + PP_OFF_FLAGS] & 0x03,
+                   bp[pgo + 5],
+                   bp[pgo + PP_OFF_PART_UNITS],
+                   bp[pgo + 8] * 256 + bp[pgo + 9],
+                   bp[pgo + 10] * 256 + bp[pgo + 11]);
 
        result = write_mode_page(STp, PART_PAGE, 1);
+
+       if (!result && needs_format) {
+               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] = 1;
+               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;
+       }
+
        if (result) {
                st_printk(KERN_INFO, STp, "Partitioning of tape failed.\n");
                result = (-EIO);

Reply via email to