vdmmirr(7) DG/UX R4.11MU05 vdmmirr(7)
NAME
vdmmirr - Virtual Disk Manager Mirror Subdriver
DESCRIPTION
The Mirror Subdriver provides multiple identical copies of a virtual
disk, so that if one of them becomes unavailable due to a hardware
problem, the applications can continue to run using the remaining
copies. These copies are called "mirror images".
Pictorially, a mirror containing three images looks like this:
+-------------------+
| mirror "X11" |
| is 120000 blocks |
+-------------------+
| | |
+----------------+ | +----------------+
| | |
+-------------------+ +-------------------+ +-------------------+
| image 1 | | image 2 | | image 3 |
| partition | | partition | | partition |
| starts at 50000 | | starts at 1000 | | starts at 2000 |
| is 120000 blocks | | is 120000 blocks | | is 120000 blocks |
+-------------------+ +-------------------+ +-------------------+
| | |
| | |
| | |
+-------------------+ +-------------------+ +-------------------+
| physical | | physical | | physical |
| sd(ncsc(0,7),0,0) | | sd(ncsc(0,7),1,0) | | sd(ncsc(0,7),2,0) |
+-------------------+ +-------------------+ +-------------------+
| | |
| | |
---------- ---------- ----------
( ) ( ) ( )
( ) ( ) ( )
( disk ) ( disk ) ( disk )
( drive ) ( drive ) ( drive )
( 1 ) ( 2 ) ( 3 )
( ) ( ) ( )
---------- ---------- ----------
Mirrors refer to their children as images. So in this example, the
mirror has three images, each a partition on a different physical
disk.
In order to use the Mirror Subdriver, you must configure vdmmirr()
into your kernel. It appears in all new system files by default, so
it is usually already configured.
For more details about how the Mirror Subdriver fits into the Virtual
Disk Manager, see the vdm(7) man page.
MIRROR IMAGES
A mirror can have up to three images in it. All the images must be
the same size, since they all have to contain the same data.
Ideally, you want to create each image on a different physical disk.
This provides the greatest protection for the data, since if one
physical disk fails, the images on the other disks can still be used.
If possible, also place the disks on different disk controllers so
that a single controller can also fail while access to the mirror is
maintained. If all the disks were attached to the same controller,
the controller would represent a single point of failure.
A mirror image that contains the most up to date information is said
to be "in sync". An image that does not contain the information is
said to be "corrupt".
In normal processing, reads to a mirror are directed to one of the
images in the mirror only. Since all the in sync images contain the
same data, it doesn't matter which one the read goes to. To help
load balance the I/Os, the mirror will dispatch them in a round robin
fashion to the images. This does increase read performance from
mirrors as compared to simple partitions, since multiple requests can
be processing in parallel on the different disk drives.
Writes will be sent to each mirror image in sequence. The write is
first sent to the most up to date image, then the next most up to
date, etc. This ordering ensures that the images are kept in sync,
and that the most up to date image always contains all of the data.
After all the images have been written, the I/O request will be
returned to the caller. This means that writes to mirrors are slower
than writes to simple partitions.
The mirror maintains a timestamp for each image of the mirror. When
a mirror is idle, all the timestamps are the same, indicating that
all the images are in sync. When the first write is done to a mirror
after it is opened, the mirror will reset the timestamps so that the
images are slightly out of date from one another. The one with the
highest timestamp is considered the image that is most up to date
from that point on. Which image is assigned the highest timestamp is
arbitrary.
When the mirror is normally closed, the timestamps are reset to be
the same. This indicates that they are all completely in sync with
one another again, and all contain exact copies of the data.
Mirroring increases the reliability of the data, making it more
likely that it will be available to the applications. This is
because if one image of a mirror fails, the mirror can continue to
run with the remaining images. The risk of a total mirror failure is
is less than if the data were stored in a simple partition on a
single disk drive.
CORRUPT IMAGES
If a read or write I/O fails to an image, that image will be declared
corrupt, and it will no longer be used by the mirror. The next image
in timestamp order will become the most up to date image, and all
future I/O will be sent to the remaining in sync images in the
mirror.
The user I/O in this case will complete successfully, since it will
be sent to the remaining in sync images. Therefore, the application
will continue to run, and will not be interrupted by the hardware
failure of the corrupt image.
The mirror will send a message to the operator's console when a image
is declared corrupt, so that you will know that the physical disk it
was on needs to be repaired.
As long as one image of a mirror remains usable, the mirror will
remain usable. If the last remaining in sync image of a mirror
fails, the mirror is no longer usable. In this case, the mirror will
start to issue I/O errors to the applications so that they will know
that the data is no longer being stored to disk.
Once an image is declared corrupt, it will remain corrupt until it is
resynced with the most up to date image.
When a one image mirror is created, the single image is considered in
sync. Then additional images are linked to the mirror, which are
considered corrupt. Finally, you synchronize the corrupt images to
the in sync image, and then all the images are usable. This is the
normal way mirrors are created.
You can also create a two or three image mirror from the start, if
you care to. However, in this case the mirror does not know which
image (if any) contains your data, so all of the images are declared
corrupt and the mirror is not usable. You must explicitly declare
the image containing the data as in sync by naming it as the sync
source and the other images as the sync destination when you sync the
mirror.
For this reason, when you insert a mirror, you should always insert
it with just one image, so that the mirror will be immediately
usable. If a multi-image mirror is inserted in an active instance
hierarchy, the applications will immediately start getting I/O errors
because the mirror will not be usable until it is synced.
RESYNCING MIRROR IMAGES
When an image is declared corrupt, or starts out as corrupt, it must
be resynced with the most up to date image before it can be used
again. This synchronization copies all the data blocks from the in
sync image to the corrupt image. Once the sync is finished, the
corrupt image is marked in sync again, and becomes usable.
While an image is being synced, user write I/Os will be directed to
it so that it keeps up to date with the new data being written to the
mirror. However, user read I/Os will only be directed to the already
in sync images, since the corrupt image does not yet contain all of
the old data for the mirror.
Mirror syncs can either be done explicitly by you, or automatically
when a system reboots. While the system is up, you can start a sync
from an in sync image to a corrupt image. This can be done after you
have repaired the damaged image.
Additionally, if you set the mirror up to do so, on system reboots
the mirror can automatically start a sync using the most up to date
image to bring the corrupt images back into sync. This is the normal
way you run mirrors, so that if a system goes down for some reason,
the images will be brought back into sync automatically when it is
rebooted. This avoids you having to start syncs to the corrupt
images manually.
While a sync is running it may negatively impact the performance of
the mirror or system. This can be controlled by setting the throttle
speed for the sync. Normally, syncs run with a throttle speed of
zero, which means they run as fast as possible. This will get the
corrupt images back into sync the fastest, but you may find the
performance impact unacceptable.
If you set the throttle value to a positive number, say 100, it means
that the sync is to wait 100 milliseconds between each of the I/Os it
issues to do the sync. This time gap will allow the application's
I/Os to run with less performance impact. A 100 millisecond throttle
value is a good "slow" sync speed. If it still impacts the system
too much, set it to a higher number to slow the sync down even more.
The higher the throttle value, the longer it will take the sync to
complete, and the less the impact to the running applications will
be. Generally, you should not set the throttle value to more than
one second, since doing so will mean the sync may take forever to
finish.
You can set a default throttle value for a mirror. This default will
be used if you do not later specify a throttle value when you
explicitly start a mirror sync, and it will be used for any automatic
syncs the mirror starts on a system reboot.
You can also change the throttle value of a mirror sync while it is
running. You can set it higher to make the sync run slower and
impact the system less, or you can set it lower to make the sync run
faster and complete sooner.
FRACTURED IMAGES
Mirror fracturing is designed to be used for doing on-line backups of
a virtual disk. Set up a two or three image mirror normally. When
you want to do the on-line backup, fracture one of the images in the
mirror.
This marks the image as corrupt, but it remains part of the mirror.
You then do the on-line backup from the fractured image. Once it is
complete, issue a sync on the fractured image to bring it back into
sync with the mirror quickly.
While the image is fractured, reads are not done to the image.
Writes are not done either, but are instead logged in memory. A
table is built up in the mirror of the blocks written to while the
image was fractured. Later, when you sync the mirror, only those
blocks in the table that were written to are resynced from the source
image. Since the other blocks in the fractured image were not
written to, they remain the same as the in sync image.
The fast minimal fracture sync can take up to 75% less time than a
full sync would, depending on the number of blocks written to the
mirror while the image was fractured during the backup.
Without fractured images, to do the backup you would have to unlink
one of the images of the mirror, do the backup from it, link it back
into the mirror, then do a normal sync on it. This normal sync will
copy all the blocks of the source image to the destination image,
regardless of whether they were written to or not during the backup,
and will take far longer than a fracture sync would to complete.
Instead of maintaining a block by block table in memory (which would
require one bit for each block in the mirror), a fractured mirror
maintains a table of groups of blocks written. This group of blocks
is called the change granularity size. For example, if you chose a
change granularity size of 100 blocks, a write to any block from 0 to
99 of the mirror would turn on the first bit in the table. A write
to any block from 100 to 199 would turn on the second bit.
This one bit per group of blocks table saves considerable space in
memory while the image is fractured. When the fracture sync is done,
each range of blocks that has been marked modified is copied from the
source to the destination image. So, a small change granularity uses
more memory, but results in fewer blocks being synced, and hence is
somewhat faster. A large change granularity results in much less
memory being used, but a larger number of blocks being synced, and
hence is somewhat slower. Generally, a larger change granularity is
the more efficient tradeoff.
If you do not specify the granularity when you fracture an image, the
mirror will choose an appropriate default value. The default
granularity size is 128 blocks, and is a good general choice.
If the system goes down for some reason while a fractured image
exists, the mirror will do a normal sync on the image when the system
reboots. This is because the table of blocks written to is
maintained in memory only, so it is lost when the system goes down.
ON-LINE BACKUP APPLICATION INTERACTION
In order to make an on-line backup of a mirror useful, the
application using the mirror must flush all its buffers to the
mirror, and quiesce itself while the unlink or fracture is done.
Then the application can unquiesce itself and continue running. This
results in a consistent broken or fractured image with which to do
the backup. Once the backup is done, the image must be synced back
into the mirror.
If a mirror contains a DG/UX file system, the file system will be
flushed and marked consistent when an image is broken or fractured.
It will then unquiesce itself and continue running on the remaining
images. This causes a momentary pause in the file system while the
flushing is happening. The resulting broken or fractured image can
be mounted read-only to do the backup. There is no need to fsck the
image before doing the backup, since the file system made it
consistent before it was broken or fractured. The file system does
this work transparently, so you do not need to do anything to
initiate it, other than to do the break or fracture of the image.
If the mirror contains a database, it is the responsibility of the
database software to be able to quiesce itself, then you can issue
the break or fracture. Finally, you can tell the database to
unquiesce itself and continue running. If the database software does
not contain the ability to quiesce itself, you will not be able to
get a consistent on-line backup from it. In this case, you must shut
down the database before breaking or fracturing the image for backup.
Once the image is removed from the mirror, you can restart the
database. Do the backup on the removed image, and when it is done,
resync the image into the mirror.
See the administration manuals for your application to see if it
contains a self quiescing feature that would allow it to be used with
mirrors to do on-line backups.
USABILITY
For you to be able to use a mirror (i.e., open it), only one of
images of the mirror has to be usable. The other two images of the
mirror may be missing or corrupt due to hardware failures. However,
there are cases where you may want to require multiple images always
be present in the mirror to guarantee the safety of the data. In
these cases, you can set parameters for the mirror to require it to
have more than one image present and usable before it allows the
mirror itself to be made usable.
This is useful for extremely critical data where you would rather
have the application be down than risk the potential loss of data due
to a hardware failure.
There are two parameters that you can set to control when a mirror
will be usable. They are:
Minimum images required to use
This sets the minimum number of images that are required to be
available in the mirror before it can be used. If not enough
images are available, the mirror will not be able to be used.
Setting this value to the total number of images in the mirror
requires that all images be present before the mirror can be
used. If set to -1, the value will not be used to determine
the usability of the mirror.
The default when you create a mirror through sysadm(1M) or
admvdisk(1M) is for the minimum number of images required to
use the mirror to be set to one.
Maximum missing images to use
This sets the maximum number of images that are allowed to be
missing to put the mirror into use. If too many images are
missing, the mirror will not be able to be used.
Setting this value to zero requires that all images be present
before the mirror can be used. If set to -1, the value will
not be used to determine the usability of the mirror.
The default when you create a mirror through sysadm(1M) or
admvdisk(1M) is for the maximum allowable number of missing
images to use the mirror to be set to one.
Choose the values for the two parameters carefully, since they affect
the reliability and availability of the mirror.
A mirror will hold off making its usability decision until all the
disks that can be registered on the system have been registered. So,
if a given disk fails, the mirror will look at the other images on
the other disks that could be found, and then determine if the mirror
mets the usability requirements that you have set.
This is pointed out to emphasize that the mirror does not decide to
become usable when it sees the first set of images that meets the
usability test. This handles the cases where the last image seen is
the most up to date one, which should be the image used to start any
automatic syncs.
EXPANDING AND SHRINKING MIRRORS
Mirrors cannot be expanded or shrunk. In order to change the size of
a mirror, all the images must be changed to be the same size
atomically, and that cannot be done.
Instead, tear down the mirror by unlinking all but one of the images,
then extract the mirror. Expand or shrink the remaining virtual disk
as needed. Also expand or shrink the virtual disks for the other
disconnected images so that they will be the same size as the
remaining virtual disk. Finally, rebuild the mirror by inserting it,
then linking and syncing the other images.
This is required because the mirror is not prepared to handle the
images changing size while they are in use.
CLUSTER DISKS
Mirrors are not supported on cluster disks. The VDM will refuse to
cluster a disk that contains mirrors. You must remove the mirror
before placing the disk into cluster mode.
Note that CLARiiON® hardware disk mirroring will work for cluster
disks without problems.
BOOTING FROM A MIRROR
You can boot from a mirror, but not directly. Instead of booting
from the mirror itself, boot from one of the images of the mirror.
So, root can be a mirror, but when you boot from it, boot from the
image of the mirror that resides on the disk you are booting from.
The same is true of usr (so that you can boot standalone sysadm).
Once the disks are registered by the VDM, the mirrors will be re-
established and root and usr access from then on is normal.
PROGRAMMING INTERFACE
The ioctl(2) system call is used for all communication with the
Mirror Subdriver. Use the open(2) system call to open a channel to
the /dev/vdm node, and issue all ioctl(2) commands through that
channel. See vdm(7) for more details on the programming interface.
The <sys/ioctl.h> include file is required for communicating with the
Mirror Subdriver. The <errno.h>, <sys/dg_sysctl.h>, <sys/types.h>,
<stdio.h>, and <unistd.h> include files will be helpful.
SUBDRIVER-SPECIFIC COMMANDS
DG_VDMMIRR_SYNC_IMAGES
Synchronize images of a mirror.
DG_VDMMIRR_TERMINATE_SYNC
Terminates a synchronization operation on an image of a mirror.
DG_VDMMIRR_THROTTLE_SYNC
Controls the speed of a synchronization operation on an image of a
mirror.
DG_VDMMIRR_FRACTURE_IMAGE
Fracture an image of a mirror so that normal I/O to it will be
prevented. This allows a consistent backup of the image to be done.
If the mirror contains a DG/UX file system, the file system will be
flushed to disk before the image is fractured. When the fractured
image is later synchronized to bring it back into the mirror, a fast
minimal synchronization will be used instead of a full one.
COMMON DISK COMMANDS
The following commands are common to all device drivers that manage
physical disks. The VDM and the subdrivers support the commands on
virtual disks as well, because they present the same interfaces as
physical disks.
DSKIOCGET
This command returns the size of the virtual disk in sectors. For
virtual disks, one sector is one disk block, or 512 bytes. The
command and associated structure is described in more detail in
dsk(7).
DSKIOCUSAGE
This command returns I/O count and performance statistics on the I/O
issued to a virtual disk. The command and associated packet is
described in more detail in dsk(7).
SUBDRIVER-SPECIFIC DEFINES
DG_VDMMIRR_SUBDRIVER_ID
The unique ID that identifies the Mirror Subdriver.
DG_VDMMIRR_IOCTL_PACKET_VERSION_0
The version zero stamp used in all Mirror Subdriver ioctl(2) packets.
The stamp indicates which version of the Mirror Subdriver interface
is being used and allows the Mirror Subdriver to support previous
versions without requiring that applications be recompiled.
DG_VDMMIRR_MAX_IMAGES
The maximum number of images that can exist in a mirror.
STRUCTURES
dg_vdmmirr_create_packet
The Mirror Subdriver ioctl(2) packet for creating a new mirror
instance. Put a pointer to this packet into the packet for the
DG_VDM_CREATE_INSTANCE command.
struct dg_vdmmirr_create_packet
{
int version;
unsigned int automatic_sync : 1;
short minimum_images_required_to_use;
short maximum_images_missing_to_use;
unsigned int milliseconds_between_sync_io;
unsigned short number_of_images;
dev_t image_device_numbers [DG_VDMMIRR_MAX_IMAGES];
};
version
The version of the packet.
automatic_sync
A flag indicating whether synchronization of corrupt images is
to be started automatically when the disks are registered. If
set to one, automatic synchronization will be started.
minimum_images_required_to_use
The minimum number of images required to be available to put
the mirror into use. If not enough images are available, the
mirror cannot be used. If set to -1, the value will not be
used to determine the usability of the mirror.
maximum_missing_images_to_use
The maximum number of images allowed to be missing to put the
mirror into use. If too many images are missing, the mirror
cannot be used. If set to -1, the value will not be used to
determine the usability of the mirror.
milliseconds_between_sync_io
The number of milliseconds to delay between I/Os when the
mirror is automatically being synchronized.
number_of_images
The number of images in the mirror.
image_device_numbers
An array of device numbers for the instances that are images
of the mirror. If one image is specified, it is considered to
be in sync. If more than one image is specified, all images
are considered to be out of sync. In this case you must start
a synchronization on the mirror naming the source and
destination images explicitly. Put the number of images into
number_of_images.
dg_vdmmirr_get_packet
The Mirror Subdriver ioctl(2) packet for getting the attributes of a
mirror instance. Put a pointer to this packet into the packet for
the DG_VDM_GET_ATTRIBUTES command.
struct dg_vdmmirr_get_packet
{
int version;
unsigned int automatic_sync : 1;
unsigned int sync_in_progress : 1;
short minimum_images_required_to_use;
short maximum_images_missing_to_use;
unsigned int milliseconds_between_sync_io;
};
version
The version of the packet.
automatic_sync
A returned flag indicating whether synchronization of corrupt
images will be started automatically when the disks are
registered. If set to one, automatic synchronization will be
started.
sync_in_progress
A returned flag indicating whether a synchronization is
currently in progress for the mirror. If set to one, a
synchronization is in progress.
minimum_images_required_to_use
The returned minimum number of images required to be available
to put the mirror into use. If not enough images are
available, the mirror cannot be used. If set to -1, the value
is not used to determine the usability of the mirror.
maximum_missing_images_to_use
The returned maximum number of images allowed to be missing to
put the mirror into use. If too many images are missing, the
mirror cannot be used. If set to -1, the value is not used to
determine the usability of the mirror.
milliseconds_between_sync_io
The returned number of milliseconds delayed between I/Os when
the mirror is automatically being synchronized.
dg_vdmmirr_update_packet
The Mirror Subdriver ioctl(2) packet for updating the attributes of a
mirror instance. Put a pointer to this packet into the packet for
the DG_VDM_UPDATE_ATTRIBUTES command.
All fields are input fields and must be set to the value desired. If
no change is desired, set the field to its current value from the
mirror's attributes.
struct dg_vdmmirr_update_packet
{
int version;
unsigned int automatic_sync : 1;
short minimum_images_required_to_use;
short maximum_images_missing_to_use;
unsigned int milliseconds_between_sync_io;
};
version
The version of the packet.
automatic_sync
A flag indicating whether synchronization of corrupt images is
to be started automatically when the disks are registered. If
set to one, automatic synchronization will be started.
minimum_images_required_to_use
The minimum number of images required to be available to put
the mirror into use. If not enough images are available, the
mirror cannot be used. If set to -1, the value will not be
used to determine the usability of the mirror.
maximum_missing_images_to_use
The maximum number of images allowed to be missing to put the
mirror into use. If too many images are missing, the mirror
cannot be used. If set to -1, the value will not be used to
determine the usability of the mirror.
milliseconds_between_sync_io
The number of milliseconds to delay between I/Os when the
mirror is automatically being synchronized.
dg_vdmmirr_get_child_packet
The Mirror Subdriver ioctl(2) packet for getting child role
information about the children of a mirror instance. Put a pointer
to this packet into the child instance sub-packet for the
DG_VDM_GET_CHILD_INSTANCE command.
struct dg_vdmmirr_get_child_packet
{
int version;
unsigned int corrupt : 1;
unsigned int sync_source : 1;
unsigned int sync_dest : 1;
unsigned int fractured : 1;
daddr_t sync_block;
unsigned int change_granularity;
dev_t source_image;
};
version
The version of the packet.
corrupt
A returned flag indicating whether the image is corrupt. If
set to one, the image is corrupt (out of date with the other
images of the mirror).
sync_source
A returned flag indicating whether the image is currently the
source of a synchronization operation. If set to one, the
image is a synchronization source.
sync_dest
A returned flag indicating whether the image is currently the
destination of a synchronization operation. If set to one,
the image is a synchronization destination.
fractured
A returned flag indicating whether the image is currently
fractured. If set to one, the image is fractured.
sync_block
The returned block number currently being synchronized, if the
image is a synchronization destination. This field is valid
only if sync_dest is set to one.
change_granularity
The returned change granularity for the image, if it is
fractured. This field is valid only if fractured is set to
one.
source_image
The returned device number of the synchronization source
image, if the image is a synchronization destination. This
field is valid only if sync_dest is set to one.
dg_vdmmirr_sync_images_packet
The Mirror Subdriver ioctl(2) packet for starting a synchronization
operation from one image to one or more other images of a mirror
instance. Put a pointer to this packet into the argument field of
the packet for the DG_VDM_IOCTL_SUBDRIVER command, and set the
command field to DG_VDMMIRR_SYNC_IMAGES. Put the device number of
the mirror instance to update in the instance device number field.
struct dg_vdmmirr_sync_images_packet
{
int version;
unsigned int milliseconds_between_sync_io;
dev_t source_image;
unsigned short number_of_destinations;
dev_t destination_device_numbers
[DG_VDMMIRR_MAX_IMAGES - 1];
};
version
The version of the packet.
milliseconds_between_io
The number of milliseconds to delay between I/Os during the
synchronization.
source_image
The device number of the source image for the synchronization.
number_of_destinations
The number of synchronization destination images.
destination_device_numbers
An array of device numbers for the synchronization destination
images. Put the number of images into number_of_destinations.
dg_vdmmirr_terminate_sync_packet
The Mirror Subdriver ioctl(2) packet for terminating an active
synchronization operation for a mirror instance. Put a pointer to
this packet into the argument field of the packet for the
DG_VDM_IOCTL_SUBDRIVER command, and set the command field to
DG_VDMMIRR_TERMINATE_SYNC. Put the device number of the mirror
instance to update in the instance device number field.
struct dg_vdmmirr_terminate_sync_packet
{
int version;
dev_t dest_image;
};
version
The version of the packet.
dest_image
The device number of the destination image that is currently
being synchronized.
dg_vdmmirr_throttle_sync_packet
The Mirror Subdriver ioctl(2) packet for controlling the speed of an
active synchronization operation for a mirror instance. Put a
pointer to this packet into the argument field of the packet for the
DG_VDM_IOCTL_SUBDRIVER command, and set the command field to
DG_VDMMIRR_THROTTLE_SYNC. Put the device number of the mirror
instance to update in the instance device number field.
struct dg_vdmmirr_throttle_sync_packet
{
int version;
unsigned int milliseconds_between_sync_io;
dev_t dest_image;
};
version
The version of the packet.
milliseconds_between_io
The number of milliseconds to delay between I/Os during the
synchronization.
dest_image
The device number of the destination image that is currently
being synchronized.
dg_vdmmirr_fracture_image_packet
The Mirror Subdriver ioctl(2) packet for fracturing an image of a
mirror instance. Put a pointer to this packet into the argument
field of the packet for the DG_VDM_IOCTL_SUBDRIVER command, and set
the command field to DG_VDMMIRR_FRACTURE_IMAGE. Put the device
number of the mirror instance to update in the instance device number
field.
Normal I/O to a fractured mirror image is prevented. This allows a
consistent backup of the image to be done. If the mirror contains a
DG/UX file system, the file system will be flushed to disk before the
image is fractured. When the fractured image is later synchronized
to bring it back into the mirror, a fast minimal synchronization will
be used instead of a full one.
struct dg_vdmmirr_fracture_image_packet
{
int version;
unsigned int change_granularity;
dev_t image_device_number;
};
version
The version of the packet.
change_granularity
On input, the number of blocks in each region to monitor for
mirror writes. Only the regions actually written to will be
synchronized later when the image is brought back into the
mirror. If this field is set to zero, the system will choose
an appropriate default value.
On output if the image is successfully fractured, the size of
the change region in blocks that was able to be used. The
system may change the input value if there is insufficient
memory available to use it as is.
image_device_number
The device number of the image to fracture.
EXAMPLES
The following code fragments illustrate how to use the packets and
commands described in this man page. The fragments are not complete
and are not intended to be compilable or executable. In particular,
initialization code, instance locating code, and error handling are
left out for brevity.
Common includes and variables
#include <errno.h>
#include <sys/dg_sysctl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int status; /* status from system call */
int vdm_channel; /* channel to /dev/vdm node */
Create a mirror
dev_t mirr_instance;
dev_t image_0_instance;
dev_t image_1_instance;
struct dg_vdm_create_instance_packet create_pkt;
struct dg_vdmmirr_create_packet mirr_create_pkt;
create_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
create_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
create_pkt.persistent = 1;
create_pkt.enable_disk_updates = 1;
strcpy (create_pkt.instance_name, "inventory_db");
create_pkt.subdriver_attributes_packet_ptr = &mirr_create_pkt;
mirr_create_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_create_pkt.automatic_sync = 1;
mirr_create_pkt.minimum_images_required_to_use = 1;
mirr_create_pkt.maximum_images_missing_to_use = 1;
mirr_create_pkt.milliseconds_between_sync_io = 0;
mirr_create_pkt.number_of_images = 2;
mirr_create_pkt.image_device_numbers [0] = image_0_instance;
mirr_create_pkt.image_device_numbers [1] = image_1_instance;
status = ioctl (vdm_channel,
DG_VDM_CREATE_INSTANCE,
&create_pkt);
mirr_instance = create_pkt.device_number;
printf ("Mirror device number = 0x%08x\n", mirr_instance);
Update a mirror's automatic sync setting
dev_t update_instance;
struct dg_vdm_get_attributes_packet get_attrs_pkt;
struct dg_vdm_update_attributes_packet update_attrs_pkt;
struct dg_vdmmirr_get_packet mirr_get_pkt;
struct dg_vdmmirr_update_packet mirr_update_pkt;
/* Get the current mirror attributes. */
get_attrs_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
get_attrs_pkt.instance_device_number = update_instance;
get_attrs_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
get_attrs_pkt.subdriver_attributes_packet_ptr = &mirr_get_pkt;
mirr_get_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
status = ioctl (vdm_channel,
DG_VDM_GET_ATTRIBUTES,
&get_attrs_pkt);
/* Copy the current attributes into the update packet. */
mirr_update_pkt.automatic_sync =
mirr_get_pkt.automatic_sync;
mirr_update_pkt.minimum_images_required_to_use =
mirr_get_pkt.minimum_images_required_to_use;
mirr_update_pkt.maximum_images_missing_to_use =
mirr_get_pkt.maximum_images_missing_to_use;
mirr_update_pkt.milliseconds_between_sync_io =
mirr_get_pkt.milliseconds_between_sync_io;
/* Update the automatic sync setting. */
update_attrs_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
update_attrs_pkt.instance_device_number = update_instance;
update_attrs_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
update_attrs_pkt.subdriver_attributes_packet_ptr = &mirr_update_pkt;
mirr_update_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_update_pkt.automatic_sync = 0;
status = ioctl (vdm_channel,
DG_VDM_UPDATE_ATTRIBUTES,
&update_attrs_pkt);
Get a mirror's attributes
dev_t get_instance;
struct dg_vdm_get_attributes_packet get_attrs_pkt;
struct dg_vdmmirr_get_packet mirr_get_pkt;
get_attrs_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
get_attrs_pkt.instance_device_number = get_instance;
get_attrs_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
get_attrs_pkt.subdriver_attributes_packet_ptr = &mirr_get_pkt;
mirr_get_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
status = ioctl (vdm_channel,
DG_VDM_GET_ATTRIBUTES,
&get_attrs_pkt);
printf ("Automatic sync = %s\n",
(mirr_get_pkt.automatic_sync) ? "true" : "false");
printf ("Sync in progress = %s\n",
(mirr_get_pkt.sync_in_progress) ? "true" : "false");
printf ("Min images required = %d\n",
mirr_get_pkt.minimum_images_required_to_use);
printf ("Max images missing = %d\n",
mirr_get_pkt.maximum_images_missing_to_use);
printf ("Milliseconds between sync I/O = %u\n",
mirr_get_pkt.milliseconds_between_sync_io);
Insert a mirror
dev_t mirr_instance;
dev_t dummy_instance;
dev_t insert_instance;
dev_t image_0_instance;
struct dg_vdm_create_instance_packet create_pkt;
struct dg_vdm_insert_instance_packet insert_pkt;
struct dg_vdmmirr_create_packet mirr_create_pkt;
/* Create a dummy instance. */
create_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
create_pkt.subdriver_id = DG_VDMDUMMY_SUBDRIVER_ID;
create_pkt.persistent = 1;
create_pkt.enable_disk_updates = 1;
create_pkt.instance_name [0] = '\0';
create_pkt.subdriver_attributes_packet_ptr = NULL;
status = ioctl (vdm_channel,
DG_VDM_CREATE_INSTANCE,
&create_pkt);
dummy_instance = create_pkt.device_number;
/* Insert a mirror. */
insert_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
insert_pkt.instance_to_change = insert_instance;
insert_pkt.dummy_instance = dummy_instance;
insert_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
insert_pkt.attributes_packet_ptr = &mirr_create_pkt;
mirr_create_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_create_pkt.automatic_sync = 1;
mirr_create_pkt.minimum_images_required_to_use = 1;
mirr_create_pkt.maximum_images_missing_to_use = 1;
mirr_create_pkt.milliseconds_between_sync_io = 0;
mirr_create_pkt.number_of_images = 1;
mirr_create_pkt.image_device_numbers [0] = dummy_instance;
status = ioctl (vdm_channel,
DG_VDM_INSERT_INSTANCE,
&insert_pkt);
mirr_instance = insert_instance;
image_instance = dummy_instance;
Extract a mirror
dev_t mirr_instance;
dev_t child_instance;
dev_t dummy_instance;
dev_t extract_instance;
struct dg_vdm_create_instance_packet create_pkt;
struct dg_vdm_delete_instance_packet delete_pkt;
struct dg_vdm_extract_instance_packet extract_pkt;
/* Create a dummy instance. */
create_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
create_pkt.subdriver_id = DG_VDMDUMMY_SUBDRIVER_ID;
create_pkt.persistent = 1;
create_pkt.enable_disk_updates = 1;
create_pkt.instance_name [0] = '\0';
create_pkt.subdriver_attributes_packet_ptr = NULL;
status = ioctl (vdm_channel,
DG_VDM_CREATE_INSTANCE,
&create_pkt);
dummy_instance = create_pkt.device_number;
/* Extract the mirror. */
extract_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
extract_pkt.parent_device_number = extract_instance;
extract_pkt.new_parent_device_number = dummy_instance;
status = ioctl (vdm_channel,
DG_VDM_EXTRACT_INSTANCE,
&extract_pkt);
mirr_instance = dummy_instance;
dummy_instance = child_instance;
/* Delete the leftover instance and the child dummy instance. */
delete_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
delete_pkt.device_number = mirr_instance;
status = ioctl (vdm_channel,
DG_VDM_DELETE_INSTANCE,
&delete_pkt);
delete_pkt.device_number = dummy_instance;
status = ioctl (vdm_channel,
DG_VDM_DELETE_INSTANCE,
&delete_pkt);
Link an image to a mirror
dev_t link_instance;
dev_t new_image_instance;
struct dg_vdm_link_child_instance_packet link_pkt;
link_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
link_pkt.parent_device_number = link_instance;
link_pkt.parent_subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
link_pkt.child_device_number = new_image_instance;
link_pkt.child_packet_ptr = NULL;
status = ioctl (vdm_channel,
DG_VDM_LINK_CHILD_INSTANCE,
&link_pkt);
Get the children of a mirror
int i;
dev_t mirr_instance;
dev_t child_instance;
struct dg_vdm_child_instance_packet child_pkt;
struct dg_vdm_get_child_instance_packet get_child_pkt;
struct dg_vdmmirr_get_child_packet mirr_get_child_pkt;
get_child_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
get_child_pkt.key = 0;
get_child_pkt.parent_device_number = mirr_instance;
get_child_pkt.parent_subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
get_child_pkt.available = 0;
get_child_pkt.child_instance_array_ptr = NULL;
status = ioctl (vdm_channel,
DG_VDM_GET_CHILD_INSTANCE,
&get_child_pkt);
printf ("Number of children = %d\n",
get_child_pkt.number_of_children);
get_child_pkt.key = 0;
get_child_pkt.available = 1;
get_child_pkt.child_instance_array_ptr = &child_pkt;
child_pkt.child_role_packet_ptr = &mirr_get_child_pkt;
mirr_get_child_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
i = 0;
while (1)
{
status = ioctl (vdm_channel,
DG_VDM_GET_CHILD_INSTANCE,
&get_child_pkt);
if ((status == -1) && (errno == ENOENT))
{
break;
}
printf ("Image %d\n", i);
if (child_pkt.child_instance.device_number_used)
{
child_instance = child_instance_pkt.child_instance.
specifier_value.device_number;
printf ("Child device number = 0x%08x\n", child_instance);
if (child_pkt.child_subdriver_id ==
DG_VDM_SUBDRIVER_ID_OF_A_NON_VDM_DEVICE)
{
printf (" Non-vdm child\n");
}
else
{
printf (" Child subdriver 0x%08x\n",
child_pkt.child_subdriver_id);
}
}
else
{
printf (" Missing child ID %08x,%08x\n",
child_pkt.child_instance.specifier_value.id.generation,
child_pkt.child_instance.specifier_value.id.system_id);
}
if (mirr_get_child_pkt.corrupt)
{
printf (" Image is corrupt\n");
}
else
{
printf (" Image is in sync\n");
}
if (mirr_get_child_pkt.sync_source)
{
printf (" Is the source of an in-progress sync\n");
}
if (mirr_get_child_pkt.sync_dest)
{
printf (" Is the destination of an in-progress sync\n");
printf (" Currently syncing block %lu\n",
mirr_get_child_pkt.sync_block);
vdmt_convert_to_name (mirr_get_child_pkt.source_image);
printf (" Source for sync is %s\n", device_name);
}
if (mirr_get_child_pkt.fractured)
{
printf (" Image is fractured\n");
printf (" Change granularity is %u blocks\n",
mirr_get_child_pkt.change_granularity);
}
printf ("\n");
i++;
}
Unlink an image from a mirror
dev_t unlink_instance;
dev_t child_instance;
struct dg_vdm_unlink_child_instance_packet unlink_pkt;
unlink_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
unlink_pkt.parent_device_number = unlink_instance;
unlink_pkt.child_instance.device_number_used = 1;
unlink_pkt.child_instance.specifier_value.device_number =
child_instance;
status = ioctl (vdm_channel,
DG_VDM_UNLINK_CHILD_INSTANCE,
&unlink_pkt);
Sync one image of a mirror to another image
dev_t mirr_instance;
dev_t source_image;
dev_t destination_image;
struct dg_vdm_ioctl_subdriver_packet ioctl_pkt;
struct dg_vdmmirr_sync_images_packet mirr_sync_pkt;
ioctl_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
ioctl_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
ioctl_pkt.instance_device_number = mirr_instance;
ioctl_pkt.command = DG_VDMMIRR_SYNC_IMAGES;
ioctl_pkt.argument = (int) &mirr_sync_pkt;
mirr_sync_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_sync_pkt.milliseconds_between_sync_io = 0;
mirr_sync_pkt.source_image = source_image;
mirr_sync_pkt.number_of_destinations = 1;
mirr_sync_pkt.destination_device_numbers [0] = destination_image;
status = ioctl (vdm_channel,
DG_VDM_IOCTL_SUBDRIVER,
&ioctl_pkt);
Terminate a mirror sync while it is in progress
dev_t mirr_instance;
dev_t destination_image;
struct dg_vdm_ioctl_subdriver_packet ioctl_pkt;
struct dg_vdmmirr_terminate_sync_packet mirr_term_pkt;
ioctl_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
ioctl_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
ioctl_pkt.instance_device_number = mirr_instance;
ioctl_pkt.command = DG_VDMMIRR_TERMINATE_SYNC;
ioctl_pkt.argument = (int) &mirr_term_pkt;
mirr_term_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_term_pkt.dest_image = destination_image;
status = ioctl (vdm_channel,
DG_VDM_IOCTL_SUBDRIVER,
&ioctl_pkt);
Throttle a sync while it is in progress
dev_t mirr_instance;
dev_t destination_image;
struct dg_vdm_ioctl_subdriver_packet ioctl_pkt;
struct dg_vdmmirr_throttle_sync_packet mirr_throttle_pkt;
ioctl_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
ioctl_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
ioctl_pkt.instance_device_number = mirr_instance;
ioctl_pkt.command = DG_VDMMIRR_THROTTLE_SYNC;
ioctl_pkt.argument = (int) &mirr_throttle_pkt;
mirr_throttle_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_throttle_pkt.milliseconds_between_sync_io = 100;
mirr_throttle_pkt.dest_image = destination_image;
status = ioctl (vdm_channel,
DG_VDM_IOCTL_SUBDRIVER,
&ioctl_pkt);
Fracture a mirror image
dev_t mirr_instance;
dev_t destination_image;
struct dg_vdm_ioctl_subdriver_packet ioctl_pkt;
struct dg_vdmmirr_fracture_image_packet mirr_fracture_pkt;
ioctl_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
ioctl_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
ioctl_pkt.instance_device_number = mirr_instance;
ioctl_pkt.command = DG_VDMMIRR_FRACTURE_IMAGE;
ioctl_pkt.argument = (int) &mirr_fracture_pkt;
mirr_fracture_pkt.version = DG_VDMMIRR_IOCTL_PACKET_VERSION_0;
mirr_fracture_pkt.change_granularity = 0;
mirr_fracture_pkt.image_device_number = destination_image;
status = ioctl (vdm_channel,
DG_VDM_IOCTL_SUBDRIVER,
&ioctl_pkt);
Get the size of a mirror
dev_t ioctl_instance;
struct dg_vdm_ioctl_subdriver_packet ioctl_pkt;
struct dskget dskget_pkt;
ioctl_pkt.version = DG_VDM_IOCTL_PACKET_VERSION_0;
ioctl_pkt.subdriver_id = DG_VDMMIRR_SUBDRIVER_ID;
ioctl_pkt.instance_device_number = ioctl_instance;
ioctl_pkt.command = DSKIOCGET;
ioctl_pkt.argument = (int) &dskget_pkt;
status = ioctl (vdm_channel,
DG_VDM_IOCTL_SUBDRIVER,
&ioctl_pkt);
printf ("Total blocks = %u\n",
dskget_pkt.total_sectors *
(dskget_pkt.bytes_per_sector / 512));
FILES
/dev/dsk
Directory for the block special device nodes for virtual
disks. These are the buffered access device nodes.
/dev/rdsk
Directory for the character special device nodes for virtual
disks. These are the unbuffered (or raw) access device nodes.
/dev/vdm
Device node for issuing all ioctl(2) commands.
/usr/include/sys/ioctl.h
ioctl(2) definitions.
ACCESS CONTROL
Access control for the standard VDM framework commands is controlled
by the VDM itself. See the vdm(7) man page for a listing of these
controls.
Access control for the Mirror Subdriver commands is controlled by the
subdriver itself. Below is the listing of these commands.
All users may execute these commands: DG_VDM_GET_ATTRIBUTES,
DG_VDM_GET_CHILD_INSTANCE, DSKIOCGET, DSKIOCUSAGE.
Only users with appropriate privilege may execute these commands:
DG_VDM_CREATE_INSTANCE, DG_VDM_LINK_CHILD_INSTANCE,
DG_VDM_UNLINK_CHILD_INSTANCE, DG_VDM_UPDATE_ATTRIBUTES,
DG_VDMMIRR_SYNC_IMAGES, DG_VDMMIRR_TERMINATE_SYNC,
DG_VDMMIRR_THROTTLE_SYNC, DG_VDMMIRR_FRACTURE_IMAGE.
On a generic DG/UX system, appropriate privilege is granted by having
an effective UID of 0 (root). See the appropriate_privilege(5) man
page for more information.
On a system with DG/UX information security, appropriate privilege is
granted by having one or more specific capabilities enabled in the
effective capability set of the user. See the cap_defaults(5) man
page for the default capabilities for these commands.
RETURN VALUE
The return value from the ioctl(2) system call used to issue the
command will be one of the following:
0 The command was successful.
-1 An error occurred. errno is set to indicate the error.
DIAGNOSTICS
There are descriptive extended error message strings available for
most of the error codes. They can be retrieved with the
dg_ext_errno(2), extended_strerror(3C), and extended_perror(3C)
routines.
Errno may be set to one of the DG/UX standard error codes. See
/usr/include/sys/errno.h for a listing of those error codes.
Errno may be set to one of the standard error codes provided by the
VDM framework. See vdm(7) for a listing of those error codes.
Errno may also be set to one of the following error codes that are
specific to this subdriver:
EBUSY The image is involved in an active mirror synchronization.
EBUSY The destination image is already being synchronized.
EINVAL There are no mirror images specified.
EINVAL The source and destination images cannot be the same.
EINVAL Cannot start another synchronization using a different source
image.
EINVAL The minimum or maximum required images for the mirror is not
valid.
EINVAL The destination image is not corrupt and does not need to be
synchronized.
EINVAL There are too many images specified.
EINVAL The maximum number of images are already defined for the
mirror.
EINVAL All images in a mirror must be the same size.
EINVAL Cannot remove the last working image from a mirror.
EINVAL The destination image is not being synchronized.
EINVAL The source image is corrupt and cannot be used in a
synchronization.
EINVAL The number of milliseconds between synchronization I/O's is
too large.
EINVAL The target image of a fracture request is already fractured.
EINVAL The target image of a fracture is corrupt. It cannot be
fractured.
EINVAL Mirror sync terminated.
EINVAL Mirror sync terminated due to delete of a mirror or halt of
the system.
EINVAL The source image is fractured and cannot be used in a
synchronization.
EINVAL The fractured destination image cannot be synchronized because
it is still open.
EIO There are no remaining usable images in the mirror.
EIO The source image of a synchronization has become corrupt.
EIO All of the synchronization destination images have failed.
ENXIO The virtual disk is not an image of the mirror.
SEE ALSO
ioctl(2), dsk(7), rdsk(7), vdm(7).
Licensed material--property of copyright holder(s)