Museum

Home

Lab Overview

Retrotechnology Articles

⇒ Online Manual

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

ioctl(2)

dsk(7)

rdsk(7)

vdm(7)



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)

Typewritten Software • bear@typewritten.org • Edmonds, WA 98026