Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple device arguments to dasdfmt, and allow them to be formatted in parallel. #115

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

markkp
Copy link
Contributor

@markkp markkp commented Jun 29, 2021

No description provided.

@frank-heimes
Copy link
Contributor

That's a great enhancement!

@markkp
Copy link
Contributor Author

markkp commented Jun 29, 2021

Hmm. I did not intend for the boot menu message commit to be part of this pull request. I guess I need to learn how to use github better. :(

@hoeppnerj hoeppnerj self-assigned this Jul 7, 2021
@markkp
Copy link
Contributor Author

markkp commented Aug 3, 2021 via email

Copy link
Contributor

@hoeppnerj hoeppnerj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a more descriptive commit message as well.

}
if (!numdev)
error("%s: No device specified!\n",
prog_name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be written in one line.
Also, there is a check in get_device_name()

if (optind >= argc)
error("No device specified!");

that checks for "No device" already. You're calling get_device_name() in line 1721 in the loop. The check could be moved out before entering the loop and then removed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure what you mean. If you're referring to the "while (optind < argc)" as the loop, then are you saying move the call to get_device_name on line 1735 to just before "while (optind < argc)", or something else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, within the while (optind < argc) loop your first call is get_device_name(). Within that function the first check
is the one I referenced above (optind >= argc). You are essentially doing the exact same check after the loop
with if (!numdev). That check however, is pretty much a dead code path as get_device_name() would fail first.

With moving the check out of the loop, I mean that the check could be done right before while (optind < argc)
and can then be removed from get_device_name().
I also just figured, that the check for the label if (numdev > 1 && g.labelspec) could also be moved before
the loop.

It would look like this:

        if (optind >= argc)                                                                                          
                error("No device specified!");                                                                       
        if ((argc - optind) > 1 && g.labelspec)                                                                      
                error("Specifying a volser to be written doesn't make sense when formatting multiple DASD volumes.");
                                                                                                                     
        while (optind < argc) {                                                                                      
                get_device_name(optind, argc, argv);                                                                 
                strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path));                                   
                strncpy(g.dev_node_array[numdev], g.dev_node, strlen(g.dev_node));                                   
                                                                                                                     
                optind++;                                                                                            
                numdev++;                                                                                            
        }                                                                                                            

@@ -1644,6 +1696,9 @@ int main(int argc, char *argv[])
break; /* exit loop if finished */
}

/* Reset the value of rc since we're going to use it again later. */
rc = 0;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should go into the second patch. It's not relevant here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll double check that to be sure I didn't get something else wrong.

for (i = 0; i < numdev; i++)
{
strncpy(g.dev_path, g.dev_path_array[i], strlen(g.dev_path_array[i])+1);
strncpy(g.dev_node, g.dev_node_array[i], strlen(g.dev_node_array[i])+1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines produce some warnings and I think the string operations can be avoided.
See below for a different approach.

In function ‘strncpy’,
    inlined from ‘main’ at dasdfmt.c:1742:3:
/usr/include/s390x-linux-gnu/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ specified bound depends on the length of the source argument [-Wstringop-overflow=]
  106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dasdfmt.c: In function ‘main’:
dasdfmt.c:1742:44: note: length computed here
 1742 |   strncpy(g.dev_path, g.dev_path_array[i], strlen(g.dev_path_array[i])+1);
      |                                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~

char *tmp_path = g.dev_path;                 
char *tmp_node = g.dev_node;                 
                                             
for (i = 0; i < numdev; i++)                 
{                                            
        g.dev_path = g.dev_path_array[i];    
        g.dev_node = g.dev_node_array[i];    
        process_dasd(&vlabel, format_params);
}                                            
                                             
free(tmp_path);                              
free(tmp_node);                              

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I don't get that warning, or I would have worked to eliminate it. What version of gcc are you using?
Not being particularly strong in C, I don't understand what you're doing here. How do the *tmp_ variables come into play here?

while (optind < argc) {
get_device_name(optind, argc, argv);
strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path));
strncpy(g.dev_node_array[numdev], g.dev_node, strlen(g.dev_node));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strncpy() is tricky here. As it is, the following warning is produced:

In function ‘strncpy’,
    inlined from ‘main’ at dasdfmt.c:1722:3:
/usr/include/s390x-linux-gnu/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ output truncated before terminating nul copying as many bytes from a string as its length [-Wstringop-truncation]
  106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dasdfmt.c: In function ‘main’:
dasdfmt.c:1722:3: note: length computed here
 1722 |   strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path));
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Adding +1 to make sure the terminating nul is copied as well will produce another warning:

In function ‘strncpy’,
    inlined from ‘main’ at dasdfmt.c:1722:3:
/usr/include/s390x-linux-gnu/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ specified bound depends on the length of the source argument [-Wstringop-overflow=]
  106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dasdfmt.c: In function ‘main’:
dasdfmt.c:1722:49: note: length computed here
 1722 |   strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path) + 1);
      |                                                 ^~~~~~~~~~~~~~~~~~

In this scenario I think strcpy() should suffice. However, we have util_strlcpy() which is probably the even better solution to provide some safety. It would look like this and the warnings would be gone:

util_strlcpy(g.dev_path_array[numdev], g.dev_path, MAX_LENGTH);
util_strlcpy(g.dev_node_array[numdev], g.dev_node, MAX_LENGTH);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look at your util function.

{
volume_label_t vlabel;
char old_volser[7];

char str[ERR_LENGTH];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're touching this area anyway, would be nice to format it in reverse xmas tree here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you mean by that. Format what? and what is "reverse xmas tree?"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a format style that we try to follow where the order of the variable declarations is determined by the
length starting with the longest line at the top. In this example it would look like this:

 volume_label_t vlabel;
 char str[ERR_LENGTH];
 char old_volser[7];

@@ -25,6 +25,8 @@

#include "dasdfmt.h"

#define MAX_DEVICES 512
#define MAX_LENGTH 256
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a check in get_device_name() that should probably now check against that value:

if (strlen(argv[optind]) >= PATH_MAX)
error("device name too long!");

eval_format_mode();

/* Not sure this next line is needed in the new version of the code. */
memcpy(&vlabel, orig_vlabel, sizeof(vlabel));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think is line is relevant here. Just use orig_vlabel (and call it vlabel)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll give it a try.

@@ -96,7 +96,7 @@ Do not use this option if you are using a 3270 console,
running in background or redirecting the output to a file.

.TP
\fB-P\fR or \fB--percentage\fR
\fB-Q\fR or \fB--percentage\fR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's sad that both p and P are taken, but please don't change the behaviour of an established command line option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already warned you about this, so no surprise here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but still wanted to mentioned that we shouldn't change it.

dasdfmt/dasdfmt.8 Show resolved Hide resolved
strncpy(g.dev_node, g.dev_node_array[i], strlen(g.dev_node_array[i])+1);
process_dasd(&vlabel, format_params);
for (numproc = 0; numproc < numdev; numproc++) {
chpid = fork();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have one major issue with the fork() approach. The output is very clutttered:

[root@m83lp46 ~]# ./dasdfmt -y -p -b 4096 -P 4 /dev/dasdb /dev/dasdc /dev/dasdd /dev/dasde
                                                                                          
WARNING:                                                                                  
Disk /dev/dasdd is online on operating system instances in 58 different LPARs.            
Ensure that the disk is not being used by a system outside your LPAR.                     
Note: Your installation might include z/VM systems that are configured to                 
automatically vary on disks, regardless of whether they are subsequently used.            
                                                                                          
WARNING:                                                                                  
Disk /dev/dasdb is online on operating system instances in 58 different LPARs.            
Ensure that the disk is not being used by a system outside your LPAR.                     
Note: Your installation might include z/VM systems that are configured to                 
automatically vary on disks, regardless of whether they are subsequently used.            
                                                                                          
WARNING:                                                                                  
Disk /dev/dasde is online on operating system instances in 58 different LPARs.            
Ensure that the disk is not being used by a system outside your LPAR.                     
Note: Your installation might include z/VM systems that are configured to                 
automatically vary on disks, regardless of whether they are subsequently used.            
                                                                                          
WARNING:                                                                                  
Disk /dev/dasdc is online on operating system instances in 58 different LPARs.            
Ensure that the disk is not being used by a system outside your LPAR.                     
Note: Your installation might include z/VM systems that are configured to                 
automatically vary on disks, regardless of whether they are subsequently used.            
cyl    1113 of    1113 |#################################|100% [5s]                       
cyl    1113 of    1113 |#################################|100% [5s]                       
Finished formatting the /dev/dasdb device.                                                
Finished formatting the /dev/dasdd device.                                                
cyl    1113 of    1113 |#################################|100% [5s]                       
Rereading the partition table for /dev/dasdb... ok                                        
Rereading the partition table for /dev/dasdd... ok                                        
Finished formatting the /dev/dasdc device.                                                
Rereading the partition table for /dev/dasdc... ok                                        
cyl    1113 of    1113 |#################################|100% [5s]                       
Finished formatting the /dev/dasde device.                                                
Rereading the partition table for /dev/dasde... ok                                        

This is hard to consume and very confusing (and this is for only 4 devices).
This lies in the nature of fork() as the process is just cloned and all the output
is directed to the same terminal as it comes in.
I'd rather appreciate an approach using pthreads, which allows to track the progress of each
formatting run. Also, it should allow for more fine grained control over each formatting process
and outputs could then be displayed as required.

Copy link
Contributor Author

@markkp markkp Oct 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not be capable of writing anything with pthreads. Sorry. I wouldn't be opposed to someone else who knows that they're doing in that regard taking on that task.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would still need a solution to aggregate the output. If pthreads are to complicated at this point, one solution
that came to mind whilst discussing this with my team was pipe() (See man 2 pipe). Which seems rather
easy to implement and you could collect the status of each forked process and work with that in the main
process. That way, output could be reduced to a meaningful and easy to consume minimum.

Specify the number of disks to be formatted in parallel.
\fInumdisks\fR specifies the number of formatting processed,
independent on the overall number of disks to be formatted.
The maximum value for \fInumdisks\fR is 512. Default is 1.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the default value should be numdev and the number the user can set should
be an optional parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to think about that. I think that might be an unexpected change for some people.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have in mind that at one point we could allow the user to specify ranges of devices. Just like for other tools
like zdev. So, one could just do dasdfmt -b 4096 -y -p -n 0.0.9300-0.9330 and all devices would
automatically be formatted in parallel.
The number to specify the amount of devices that should be formatted in parallel would be optional.
That way the user doesn't need to bother with counting the amount of devices but still has the option to limit
it if resources are a constraint.

Copy link
Contributor Author

@markkp markkp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a more descriptive commit message as well.

Just what are you looking for here?

for (i = 0; i < numdev; i++)
{
strncpy(g.dev_path, g.dev_path_array[i], strlen(g.dev_path_array[i])+1);
strncpy(g.dev_node, g.dev_node_array[i], strlen(g.dev_node_array[i])+1);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I don't get that warning, or I would have worked to eliminate it. What version of gcc are you using?
Not being particularly strong in C, I don't understand what you're doing here. How do the *tmp_ variables come into play here?

while (optind < argc) {
get_device_name(optind, argc, argv);
strncpy(g.dev_path_array[numdev], g.dev_path, strlen(g.dev_path));
strncpy(g.dev_node_array[numdev], g.dev_node, strlen(g.dev_node));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look at your util function.

@@ -1644,6 +1696,9 @@ int main(int argc, char *argv[])
break; /* exit loop if finished */
}

/* Reset the value of rc since we're going to use it again later. */
rc = 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll double check that to be sure I didn't get something else wrong.

dasdfmt/dasdfmt.8 Show resolved Hide resolved
Specify the number of disks to be formatted in parallel.
\fInumdisks\fR specifies the number of formatting processed,
independent on the overall number of disks to be formatted.
The maximum value for \fInumdisks\fR is 512. Default is 1.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to think about that. I think that might be an unexpected change for some people.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants