SHA256
1
0
forked from pool/s390-tools
s390-tools/s390-tools-sles12sp3-dasdfmt-07-Add-quick-format-support.patch
2017-02-21 11:14:26 +00:00

556 lines
18 KiB
Diff

Subject: [PATCH] [FEAT LS1501] dasdfmt: Add new formatting modes
From: Jan Höppner <hoeppner@linux.vnet.ibm.com>
Summary: dasdfmt: Add new formatting modes
Description: Introduce new formatting modes 'quick' and 'expand' to either
format an earlier formatted DASD that could potentially be
re-initialized very easily or format unformatted tracks at the
end of a device that was previously extended.
Also add the command line argument --check to provide a function
that checks a DASD volume for correct formatting.
Upstream-ID: -
Problem-ID: LS1501
Upstream-Description:
dasdfmt: Add quick format support
On occasion, a DASD device might have been formatted earlier and could
potentially be re-initialized very easily. For device initalization it
is necessary to write disk information (i.e. volume label (CDL or LDL)
and VTOC) to the first two tracks. Currently you'd need to format the
entire disk, which can take a long time and is totally unnecessary.
Therefore, add a quick format mode which formats only the first two
tracks and fills them with data accordingly.
Before any formatting is done, several checks regarding the
expected device format are performed.
The mode can be specified with the new command line switch -M (--mode)
<mode>. Where 'mode' can be either 'full' (default) or 'quick'.
Document the switch -M (--mode) and its options full and quick in the
man page.
Signed-off-by: Jan Höppner <hoeppner@linux.vnet.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.vnet.ibm.com>
---
dasdfmt/dasdfmt.8 | 15 ++
dasdfmt/dasdfmt.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
dasdfmt/dasdfmt.h | 51 +++++++++
3 files changed, 344 insertions(+), 12 deletions(-)
--- a/dasdfmt/dasdfmt.8
+++ b/dasdfmt/dasdfmt.8
@@ -67,7 +67,7 @@ Print version number and exit.
.TP
\fB-F\fR or \fB--force\fR
-Formats the device without checking, if the device is in use.
+Formats the device without performing sanity checking.
.TP
\fB-C\fR or \fB--check_host_count\fR
@@ -108,6 +108,19 @@ The value will be at least as big as the
.br
.TP
+\fB-M\fR \fImode\fR or \fB--mode\fR=\fImode\fR
+Specify the \fImode\fR to be used to format the device. Valid modes are:
+.RS
+.IP full
+Format the entire disk with the specified blocksize. (default)
+.IP quick
+Format the first two tracks and write label and partition information. Only use
+this option if you are sure that the target DASD already contains a regular
+format with the specified blocksize. A blocksize can optionally be specified
+using \fB-b\fR (\fB--blocksize\fR).
+.RE
+
+.TP
\fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR
Number of cylinders to be processed in one formatting step.
The value must be an integer in the range 1 - 255.
--- a/dasdfmt/dasdfmt.c
+++ b/dasdfmt/dasdfmt.c
@@ -30,6 +30,7 @@ static const char copyright_notice[] = "
static int filedes;
static int disk_disabled;
static format_data_t format_params;
+static format_mode_t mode;
char *prog_name;
volatile sig_atomic_t program_interrupt_in_progress;
int reqsize;
@@ -53,6 +54,7 @@ static void exit_usage(int exitcode)
" [-b <blocksize> | --blocksize=<blocksize>]\n"
" [-d <disk layout> | --disk_layout=<disk layout>]\n"
" [-r <cylinder> | --requestsize=<cylinder>]\n"
+ " [-M <mode> | --mode=<mode>]\n"
" <device>\n\n", prog_name);
printf(" -t or --test means testmode\n"
@@ -65,7 +67,7 @@ static void exit_usage(int exitcode)
" -r x or --requestsize=x means use x cylinders in one "
"format step\n"
" -v means verbose mode\n"
- " -F means don't check if the device is in use\n"
+ " -F means format without performing sanity checking\n"
" -k means keep volume serial\n"
" -C or --check_host_count means force dasdfmt to check\n"
" the host access open count to ensure the device\n"
@@ -79,7 +81,10 @@ static void exit_usage(int exitcode)
" <disk layout> is either\n"
" 'cdl' for compatible disk layout (default) or\n"
" 'ldl' for linux disk layout\n"
- " <device> device node of the device to format\n");
+ " <device> device node of the device to format\n"
+ " <mode> is either\n"
+ " 'full' to fully format the device (default)\n"
+ " 'quick' to format only the first two tracks\n");
exit(exitcode);
}
@@ -102,6 +107,131 @@ static int reread_partition_table(void)
}
/*
+ * Helper function for recs_per_track.
+ */
+static inline unsigned int ceil_quot(unsigned int d1, unsigned int d2)
+{
+ return (d1 + (d2 - 1)) / d2;
+}
+
+/*
+ * Calculate records per track depending on the device characteristics.
+ */
+static unsigned int recs_per_track(struct dasd_eckd_characteristics *rdc,
+ unsigned int kl, unsigned int dl)
+{
+ int dn, kn;
+
+ switch (rdc->dev_type) {
+ case 0x3380:
+ if (kl)
+ return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) +
+ ceil_quot(dl + 12, 32));
+ else
+ return 1499 / (15 + ceil_quot(dl + 12, 32));
+ case 0x3390:
+ dn = ceil_quot(dl + 6, 232) + 1;
+ if (kl) {
+ kn = ceil_quot(kl + 6, 232) + 1;
+ return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) +
+ 9 + ceil_quot(dl + 6 * dn, 34));
+ } else
+ return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));
+ case 0x9345:
+ dn = ceil_quot(dl + 6, 232) + 1;
+ if (kl) {
+ kn = ceil_quot(kl + 6, 232) + 1;
+ return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) +
+ ceil_quot(dl + 6 * dn, 34));
+ } else
+ return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34));
+ }
+ return 0;
+}
+
+/*
+ * Evaluate errors recognized by format checks and print appropriate error
+ * messages depending on the content of cdata.
+ */
+static void evaluate_format_error(dasdfmt_info_t *info, format_check_t *cdata,
+ unsigned int heads)
+{
+ struct dasd_eckd_characteristics *rdc;
+ /* Special blocksize values of the first 3 records of trk 0 of cyl 0 */
+ const int blksizes_trk0[] = { 24, 144, 80 };
+ /* Special blocksize value of trk 1 cyl 0 */
+ const int blksize_trk1 = 96;
+ unsigned int cyl;
+ unsigned int head;
+ unsigned int rpt;
+ unsigned int kl = 0;
+ int blksize = cdata->expect.blksize;
+
+ /*
+ * Reading record zero will never happen. If the record in error is 0
+ * nonetheless, the device is not formatted at all!
+ */
+ if (cdata->rec == 0) {
+ ERRMSG("WARNING: The specified device is not "
+ "formatted at all.\n");
+ return;
+ }
+
+ cyl = cdata->unit / heads;
+ head = cyl >> 16;
+ head <<= 4;
+ head |= cdata->unit % heads;
+
+ /*
+ * Set special expected values for the first 3 records of trk 0 of cyl 0
+ * and trk 1 of cyl 0 when checking a CDL formatted device.
+ */
+ if ((cdata->expect.intensity & DASD_FMT_INT_COMPAT) &&
+ cyl == 0 && head == 0 && cdata->rec < 4) {
+ kl = 4;
+ blksize = blksizes_trk0[cdata->rec - 1];
+ }
+ if ((cdata->expect.intensity & DASD_FMT_INT_COMPAT) &&
+ cyl == 0 && head == 1) {
+ kl = 44;
+ blksize = blksize_trk1;
+ }
+
+ rdc = (struct dasd_eckd_characteristics *)
+ &info->dasd_info.characteristics;
+
+ rpt = recs_per_track(rdc, kl, cdata->expect.blksize);
+
+ ERRMSG("WARNING: The specified device is not formatted as expected.\n");
+ switch (cdata->result) {
+ case DASD_FMT_ERR_TOO_FEW_RECORDS:
+ ERRMSG("Too few records (found %d, expected %d) "
+ "at cyl: %d trk: %d rec: %d.\n",
+ cdata->num_records, rpt, cyl, head, cdata->rec);
+ break;
+ case DASD_FMT_ERR_TOO_MANY_RECORDS:
+ ERRMSG("Too many records (found %d, expected %d) "
+ "at cyl: %d trk: %d rec: %d.\n",
+ cdata->num_records, rpt, cyl, head, cdata->rec);
+ break;
+ case DASD_FMT_ERR_BLKSIZE:
+ ERRMSG("Invalid blocksize (found %d, expected %d) "
+ "at cyl: %d trk: %d rec: %d.\n",
+ cdata->blksize, blksize, cyl, head, cdata->rec);
+ break;
+ case DASD_FMT_ERR_RECORD_ID:
+ ERRMSG("Invalid record ID at cyl: %d trk: %d rec: %d.\n",
+ cyl, head, cdata->rec);
+ break;
+ case DASD_FMT_ERR_KEY_LENGTH:
+ ERRMSG("Invalid key length (found %d, expected %d) "
+ "at cyl: %d trk: %d rec: %d.\n",
+ cdata->key_length, kl, cyl, head, cdata->rec);
+ break;
+ }
+}
+
+/*
* signal handler:
* enables the disk again in case of SIGTERM, SIGINT and SIGQUIT
*/
@@ -263,6 +393,25 @@ static void get_blocksize(unsigned int *
}
/*
+ * Check whether a specified blocksize matches the blocksize of the device
+ */
+static void check_blocksize(dasdfmt_info_t *info, unsigned int blksize)
+{
+ unsigned int dev_blksize;
+
+ if (!info->blksize_specified ||
+ info->dasd_info.format == DASD_FORMAT_NONE)
+ return;
+
+ get_blocksize(&dev_blksize);
+ if (dev_blksize != blksize) {
+ ERRMSG_EXIT(EXIT_FAILURE, "WARNING: Device is formatted with a "
+ "different blocksize (%d).\nUse --mode=full to "
+ "perform a clean format.\n", dev_blksize);
+ }
+}
+
+/*
* check for disk type and set some variables (e.g. usage count)
*/
static void check_disk(dasdfmt_info_t *info)
@@ -449,6 +598,35 @@ static void set_label(dasdfmt_info_t *in
}
/*
+ * This function checks whether a range of tracks is in regular format
+ * with the specified block size.
+ */
+static format_check_t check_track_format(format_data_t *p)
+{
+ format_check_t cdata = {
+ .expect = {
+ .blksize = p->blksize,
+ .intensity = p->intensity,
+ .start_unit = p->start_unit,
+ .stop_unit = p->stop_unit
+ }, 0
+ };
+
+ if (ioctl(filedes, BIODASDCHECKFMT, &cdata)) {
+ if (errno == ENOTTY) {
+ ERRMSG_EXIT(EXIT_FAILURE, "%s: Missing kernel support "
+ "for format checking (--force to "
+ "override)\n", prog_name);
+ }
+ ERRMSG_EXIT(EXIT_FAILURE, "%s: Could no check format: %s\n",
+ prog_name, strerror(errno));
+ }
+
+ return cdata;
+}
+
+
+/*
* ask the user to specify a blocksize
*/
static format_data_t ask_user_for_blksize(format_data_t params)
@@ -511,6 +689,8 @@ static void dasdfmt_print_info(dasdfmt_i
printf(" Compatible Disk Layout : %s\n",
(p->intensity & DASD_FMT_INT_COMPAT) ? "yes" : "no");
printf(" Blocksize : %d\n", p->blksize);
+ printf(" Mode : %s\n",
+ (mode == FULL) ? "Full" : "Quick");
if (info->testmode)
printf("Test mode active, omitting ioctl.\n");
@@ -869,6 +1049,62 @@ static void dasdfmt_prepare_and_format(d
disk_disabled = 0;
}
+/*
+ * This function will only format the first two tracks of a DASD.
+ * The rest of the DASD is untouched and left as is.
+ */
+static void dasdfmt_quick_format(dasdfmt_info_t *info, unsigned int cylinders,
+ unsigned int heads, format_data_t *p)
+{
+ format_check_t cdata = { .expect = {0}, 0 };
+ format_data_t tmp = *p;
+
+ if (info->force) {
+ printf("Skipping format check due to --force.\n");
+ } else {
+ check_blocksize(info, p->blksize);
+
+ printf("Checking the format of selected tracks...\n");
+
+ /* Check device format on the first and last 3 regular tracks */
+ tmp.start_unit = 2;
+ tmp.stop_unit = 4;
+ cdata = check_track_format(&tmp);
+ if (!cdata.result) {
+ tmp.start_unit = (cylinders * heads) - 3;
+ tmp.stop_unit = (cylinders * heads) - 1;
+ cdata = check_track_format(&tmp);
+ }
+ if (cdata.result) {
+ evaluate_format_error(info, &cdata, heads);
+ ERRMSG_EXIT(EXIT_FAILURE, "Use --mode=full to perform "
+ "a clean format.\n");
+ } else {
+ printf("Done. Disk seems fine.\n");
+ }
+ }
+
+ if (!((info->withoutprompt) && (info->verbosity < 1)))
+ printf("Formatting the first two tracks of the device.\n");
+
+ /* Disable the device before we do anything */
+ if (ioctl(filedes, BIODASDDISABLE, p))
+ ERRMSG_EXIT(EXIT_FAILURE, "%s: the ioctl to disable the device "
+ "failed. (%s)\n", prog_name, strerror(errno));
+ disk_disabled = 1;
+
+ /* Now do the actual formatting of our first two tracks */
+ if (ioctl(filedes, BIODASDFMT, p))
+ ERRMSG_EXIT(EXIT_FAILURE, "%s: the ioctl to format the device "
+ "failed. (%s)\n", prog_name, strerror(errno));
+
+ /* Re-Enable the device so that we can continue working with it */
+ if (ioctl(filedes, BIODASDENABLE, p))
+ ERRMSG_EXIT(EXIT_FAILURE, "%s: the ioctl to enable the device "
+ "failed. (%s)\n", prog_name, strerror(errno));
+ disk_disabled = 0;
+}
+
static void do_format_dasd(dasdfmt_info_t *info, volume_label_t *vlabel,
format_data_t *p, unsigned int cylinders,
unsigned int heads)
@@ -877,7 +1113,15 @@ static void do_format_dasd(dasdfmt_info_
int count;
p->start_unit = 0;
- p->stop_unit = (cylinders * heads) - 1;
+
+ switch (mode) {
+ case FULL: /* all tracks */
+ p->stop_unit = (cylinders * heads) - 1;
+ break;
+ case QUICK: /* just the first two */
+ p->stop_unit = 1;
+ break;
+ }
if ((info->verbosity > 0) || !info->withoutprompt || info->testmode)
dasdfmt_print_info(info, vlabel, cylinders, heads, p);
@@ -919,11 +1163,17 @@ static void do_format_dasd(dasdfmt_info_
}
}
- if (!((info->withoutprompt) && (info->verbosity < 1)))
- printf("Formatting the device. This may take a "
- "while (get yourself a coffee).\n");
-
- dasdfmt_prepare_and_format(info, cylinders, heads, p);
+ switch (mode) {
+ case FULL:
+ if (!((info->withoutprompt) && (info->verbosity < 1)))
+ printf("Formatting the device. This may take a "
+ "while (get yourself a coffee).\n");
+ dasdfmt_prepare_and_format(info, cylinders, heads, p);
+ break;
+ case QUICK:
+ dasdfmt_quick_format(info, cylinders, heads, p);
+ break;
+ }
if (!info->yast_mode)
printf("Finished formatting the device.\n");
@@ -981,6 +1231,8 @@ int main(int argc, char *argv[])
format_params.blksize = DEFAULT_BLOCKSIZE;
format_params.intensity = DASD_FMT_INT_COMPAT;
+ mode = FULL;
+
/*************** parse parameters **********************/
while (1) {
@@ -1082,6 +1334,18 @@ int main(int argc, char *argv[])
case 'C':
info.force_host = 1;
break;
+ case 'M':
+ if (strcasecmp(optarg, "full") == 0)
+ mode = FULL;
+ else if (strcasecmp(optarg, "quick") == 0)
+ mode = QUICK;
+ else
+ ERRMSG_EXIT(EXIT_FAILURE,
+ "%s: The specified mode '%s' is "
+ "invalid. Consult the man page for "
+ "more information.\n",
+ prog_name, optarg);
+ break;
case -1:
/* End of options string - start of devices list */
info.device_id = optind;
@@ -1194,8 +1458,14 @@ int main(int argc, char *argv[])
get_device_info(&info);
- if (!info.blksize_specified)
- format_params = ask_user_for_blksize(format_params);
+ /* Either let the user specify the blksize or get it from the kernel */
+ if (!info.blksize_specified) {
+ if (!(mode == FULL ||
+ info.dasd_info.format == DASD_FORMAT_NONE))
+ get_blocksize(&format_params.blksize);
+ else
+ format_params = ask_user_for_blksize(format_params);
+ }
if (info.keep_volser) {
if (format_params.intensity == 0x00) {
--- a/dasdfmt/dasdfmt.h
+++ b/dasdfmt/dasdfmt.h
@@ -143,6 +143,14 @@ struct dasd_eckd_characteristics {
unsigned int long_no_cyl;
} __attribute__ ((packed));
+/*
+ * Represents possible format modes that can be specified when formatting
+ * a DASD.
+ */
+typedef enum format_mode_t {
+ FULL, /* default mode */
+ QUICK, /* format only the first 2 tracks */
+} format_mode_t;
/*
* struct format_data_t
@@ -168,6 +176,43 @@ typedef struct format_data_t {
#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */
+/*
+ * struct format_check_t
+ * represents all data necessary to evaluate the format of
+ * different tracks of a dasd
+ */
+typedef struct format_check_t {
+ /* Input */
+ struct format_data_t expect;
+
+ /* Output */
+ unsigned int result; /* Error indication (DASD_FMT_ERR_*) */
+ unsigned int unit; /* Track that is in error */
+ unsigned int rec; /* Record that is in error */
+ unsigned int num_records; /* Records in the track in error */
+ unsigned int blksize; /* Block-size of first record in error */
+ unsigned int key_length; /* Key length of first record in error */
+} format_check_t;
+
+/*
+ * values to be used in format_check_t for indicating
+ * possible format errors
+ */
+#define DASD_FMT_ERR_TOO_FEW_RECORDS 1
+#define DASD_FMT_ERR_TOO_MANY_RECORDS 2
+#define DASD_FMT_ERR_BLKSIZE 3
+#define DASD_FMT_ERR_RECORD_ID 4
+#define DASD_FMT_ERR_KEY_LENGTH 5
+
+/*
+ * values to be used for dasd_information2_t.format
+ * 0x00: NOT formatted
+ * 0x01: Linux disc layout
+ * 0x02: Common disc layout
+ */
+#define DASD_FORMAT_NONE 0
+#define DASD_FORMAT_LDL 1
+#define DASD_FORMAT_CDL 2
/* Disable the volume (for Linux) */
#define BIODASDDISABLE _IO(DASD_IOCTL_LETTER,0)
@@ -180,6 +225,9 @@ typedef struct format_data_t {
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
#define BIODASDFMT _IOW(DASD_IOCTL_LETTER,1,format_data_t)
+/* Check device format according to format_data_t */
+#define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t)
+
/****************************************************************************
* SECTION: Further IOCTL Definitions (see fs.h and hdreq.h ) *
****************************************************************************/
@@ -225,7 +273,7 @@ typedef struct format_data_t {
if (*endptr) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \
"is in invalid format\n",prog_name);}
-#define dasdfmt_getopt_string "b:n:l:f:d:m:r:hpQLtyvVFkCYP:"
+#define dasdfmt_getopt_string "b:n:l:f:d:m:M:r:hpQLtyvVFkCYP:"
static struct option dasdfmt_getopt_long_options[]=
{
@@ -247,6 +295,7 @@ static struct option dasdfmt_getopt_long
{ "keep_volser", 0, 0, 'k'},
{ "norecordzero", 0, 0, 'z'},
{ "check_host_count", 0, 0, 'C'},
+ { "mode", 1, 0, 'M'},
{0, 0, 0, 0}
};