[gparted] Avoid libparted failing to inform the kernel about partition changes (#790418)



commit f49f0bb2b8114d867469dcbed6b501e69c078b0d
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Mon Nov 27 13:24:29 2017 +0000

    Avoid libparted failing to inform the kernel about partition changes (#790418)
    
    Operations involving modifications to a partition are sometimes failing
    with a libparted error informing the kernel about modifications to
    partitions.  For example I encountered these errors when just creating a
    fourth partition on CentOS 7 in a VirtualBox VM.  Operation results:
    
        Create Primary Partition #1 (ext4, 4.73 GiB) on /dev/sdb   (ERROR)
        * create empty partition                                   (ERROR)
          * libparted messages                                     (ERROR)
            * Error informing the kernel about modification to partition
              /dev/sdb1 -- Device or resource busy.  This means Linux won't
              know about any changes you made to /dev/sdb1 until you reboot
              -- so you shouldn't mount it or use it in any way before
              rebooting.
            * Failed to add partition 1 (Resource temporarily unavailable)
    
    Those two libparted messages were presented in "Libparted Error" dialogs
    and [Cancel] was selected both times.
    
            Libparted Error
        (-) Error informing the kernel about modifications to partition
            /dev/sdb1 -- Device or resource busy.  This means Linux won't
            know about any changes you made to /dev/sdb1 until you reboot --
            so you shouldn't mount it or use it in any way before rebooting.
                                                       [ Cancel ] [ Ignore ]
    
            Libparted Error
        (-) Failed to add partition 1 (Resource temporarily unavailable)
                                                        [ Retry ] [ Cancel ]
    
    This is the edited output showing GParted print debugging, stracing of
    GParted and monitoring of udev events for this case.
    
        # ./gpartedbin /dev/sdb
        ======================
        libparted : 3.1
        ======================
        ...
         24.541604 +23.923435 create_partition()   start (new_partition, optdet, min_size=0)  
new_partition.device_path="/dev/sdb"
         24.556101 +0.014497 create_partition()   type=PED_PARTITION_NORMAL
         24.556354 +0.000253 commit()             start (lp_disk)  lp_disk->dev->path="/dev/sdb"
        D: strace pid 18054.  Press [Return] to continue.
        ^Z
        [1]+  Stopped          ./gpartedbin /dev/sdb
    
        # udevadm monitor &
        [2] 18124
        monitor will print the received events for:
        UDEV - the event which udev sends out after rule processing
        KERNEL - the kernel uevent
    
        # strace -p 18054 -e open,ioctl,write,close &
        [3] 18129
        strace: Process 18054 attached
    
        # fg %1
        ./gpartedbin /dev/sdb
        128.175811 +103.619457 commit()             calling ped_disk_commit_to_dev(lp_disk) ...
        open("/dev/sdb", O_RDWR)                = 6
        ioctl(6, BLKFLSBUF)                     = 0
        write(6, 
"\372\270\0\20\216\320\274\0\260\270\0\0\216\330\216\300\373\276\0|\277\0\6\271\0\2\363\244\352!\6\0"..., 
512) = 512
        ioctl(6, BLKFLSBUF)                     = 0
        close(6)
        128.181352 +0.005542 commit()             ped_disk_commit_to_dev(lp_disk) returned true
        128.181475 +0.000122 commit_to_os()       start (lp_disk, timeout=10)  lp_disk->dev->path="/dev/sdb"
        128.181527 +0.000052 commit_to_os()       calling ped_disk_commit_to_os(lp_disk) ...
        open("/dev/sdb", O_RDWR)                = 6
        ioctl(6, BLKFLSBUF)                     = 0
        open("/sys/block/sdb/ext_range", O_RDONLY) = 7
        close(7)                                = 0
    
        KERNEL[1158935.380543] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
        KERNEL[1158935.380565] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        KERNEL[1158935.380578] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
    
        ioctl(6, BLKPG, {BLKPG_DEL_PARTITION, flags=0, datalen=152, data={start=0, length=0, pno=1, 
devname="", volname=""}}) = -1 ENXIO (No such device or address)
        ioctl(6, BLKPG, {BLKPG_DEL_PARTITION, flags=0, datalen=152, data={start=0, length=0, pno=2, 
devname="", volname=""}}) = -1 ENXIO (No such device or address)
        ioctl(6, BLKPG, {BLKPG_DEL_PARTITION, flags=0, datalen=152, data={start=0, length=0, pno=3, 
devname="", volname=""}}) = -1 ENXIO (No such device or address)
        ...
    
        KERNEL[1158935.380977] change   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb (block)
        KERNEL[1158935.381296] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
        KERNEL[1158935.381367] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        KERNEL[1158935.381432] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        KERNEL[1158935.382992] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb4 (block)
    
        ioctl(6, BLKPG, {BLKPG_DEL_PARTITION, flags=0, datalen=152, data={start=0, length=0, pno=62, 
devname="", volname=""}}) = -1 ENXIO (No such device or address)
        ioctl(6, BLKPG, {BLKPG_DEL_PARTITION, flags=0, datalen=152, data={start=0, length=0, pno=63, 
devname="", volname=""}}) = -1 ENXIO (No such device or address)
        ioctl(6, BLKPG, {BLKPG_DEL_PARTITION, flags=0, datalen=152, data={start=0, length=0, pno=64, 
devname="", volname=""}}) = -1 ENXIO (No such device or address)
        ioctl(6, BLKPG, {BLKPG_ADD_PARTITION, flags=0, datalen=152, data={start=1048576, length=1073741824, 
pno=1, devname="/dev/sdb1", volname=""}}) = -1 EBUSY (Device or resource busy)
        write(2, "Error informing the kernel about"..., 251) = 251
        Error informing the kernel about modifications to partition
        /dev/sdb1 -- Device or resource busy.  This means Linux won't know
        about any changes you made to /dev/sdb1 until you reboot -- so you
        shouldn't mount it or use it in any way before rebooting.
    
        UDEV  [1158935.384641] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        UDEV  [1158935.390203] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
        UDEV  [1158935.390243] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        UDEV  [1158935.462866] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb4 (block)
        UDEV  [1158935.469207] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        UDEV  [1158935.471512] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        UDEV  [1158935.492173] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
    
        write(2, "Failed to add partition 1 (Resou"..., 60) = 60
        Failed to add partition 1 (Resource temporarily unavailable)
        close(6)
    
        KERNEL[1158955.730960] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
        KERNEL[1158955.731095] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        KERNEL[1158955.731314] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        KERNEL[1158955.731397] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb4 (block)
        KERNEL[1158955.731817] change   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb (block)
        KERNEL[1158955.731981] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
        KERNEL[1158955.732166] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        KERNEL[1158955.732232] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        KERNEL[1158955.733955] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb4 (block)
    
        148.533154 +20.351627 commit_to_os()       ped_disk_commit_to_os(lp_disk) returned false
    
        UDEV  [1158955.738262] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
        UDEV  [1158955.738460] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        UDEV  [1158955.738525] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
    
        148.537648 +0.004494 execute_command()    udevadm settle --timeout=10
    
        UDEV  [1158955.740864] remove   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb4 (block)
        UDEV  [1158955.760192] change   
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb (block)
        UDEV  [1158955.801211] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb4 (block)
        UDEV  [1158955.815262] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb3 (block)
        UDEV  [1158955.815314] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb2 (block)
        UDEV  [1158955.828134] add      
/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/sdb1 (block)
    
        148.630797 +0.093149 execute_command()              exit status 0
        148.630882 +0.000085 commit_to_os()                 return false
        D: stop strace pid 18054.  Press [Return] to continue.
        ^Z
        [1]+  Stopped          ./gpartedbin /dev/sdb
    
        # kill %3
        strace: Process 18054 detached
        [3]-  Done             strace -p 18054 -e open,ioctl,write,close
    
        # kill %2
        [2]   Done             udevadm monitor
    
        # fg %1
        ./gpartedbin /dev/sdb
        173.700143 +25.069261 commit()             return false
        173.700470 +0.000327 create_partition()   return false
    
    What happens is that GParted calls ped_disk_commit_to_dev() which opens
    the device, writes the updated partition table and closes the device.
    When the device closes the kernel initiates asynchronous uevents and
    user space udev rules which remove and re-add all the partitions.  In
    the mean time GParted calls ped_disk_commit_to_os() to inform the kernel
    of the changes to the partition table.  This involves opening the
    device, using ioctl() to remove all possible partitions [1] and re-add
    needed partitions.  It finds partitions 1 to 3 already removed and
    accepts this along with all other non-existent partitions up to 64.
    When it tries to re-add partition 1 the ioctl() BLKPG_ADD_PARTITION call
    returns EBUSY.  Presumably because the partition is in use by udev which
    is in the process of running the user space rules associated with
    removing and re-adding it.  Then ped_disk_commit_to_os() closes the
    device which initiates a second round of asynchronous uevents and user
    space udev rules removing and re-adding all the partitions again.
    
    So in summary the kernel and udev are removing and re-adding the
    partitions exactly when libparted is trying to do exactly the same
    thing!
    
    [1] The algorithm in libparted 3.1 is to try to remove all possible
        partitions, 64 for this kernel, followed by re-adding the needed
        partitions.
    
        parted/libparted/arch/linux.c:_disk_sync_part_table()
        http://git.savannah.gnu.org/cgit/parted.git/tree/libparted/arch/linux.c?h=v3.1#n2541
    
    Partprobe has had exactly the same issue with failing to inform the
    kernel about modifications to the partition table [2].  This was fixed
    in libparted post v3.2 release by this commit [3].
    
    [2] rhbz#1339705 - ceph-disk prepare: Error: partprobe /dev/vdb failed :
        Error: Error informing the kernel about modifications to partition
        /dev/vdb1 -- Device or resource busy.
    
        https://bugzilla.redhat.com/show_bug.cgi?id=1339705
    
    [3] partprobe: Open the device once for probing
    
        Previously there were 3 open/close pairs for the device, which may
        result in triggering extra udev actions. Instead, open it once at
        the start of process_dev and close it at the end.
    
        http://git.savannah.gnu.org/cgit/parted.git/commit/?id=cfafa4394998a11f871a0f8d172b13314f9062c2
    
    Implement the same fix as implemented for partprobe.  Hold a file handle
    open which libparted can use internally to avoid having to open() and
    close() the device itself twice, once for each of the calls
    ped_disk_commit_to_dev() and ped_disk_commit_to_os().  This avoids the
    first close() initiating the kernel and udev to remove and re-add the
    partitions exactly when ped_disk_commit_to_os() is trying to do the same
    thing.
    
    Bug 790418 - "Unable to inform the kernel of the change" message may
                 lead to corrupted partition table

 src/GParted_Core.cc |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
---
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index 47358f6..47bd271 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -4231,10 +4231,20 @@ void GParted_Core::destroy_device_and_disk( PedDevice*& lp_device, PedDisk*& lp_
 
 bool GParted_Core::commit( PedDisk* lp_disk )
 {
+       // (#790418) Hold a file handle open across the ped_disk_commit_to_dev() and
+       // commit_to_os()->ped_disk_commit_to_os() calls to avoid libparted having to open
+       // and close the device twice itself.  This avoids the kernel and udev events
+       // removing and re-adding partitions exactly when ped_disk_commit_to_os() is
+       // trying to doing the same thing.
+       bool opened = ped_device_open( lp_disk->dev );
+
        bool succes = ped_disk_commit_to_dev( lp_disk ) ;
        
        succes = commit_to_os( lp_disk, SETTLE_DEVICE_APPLY_MAX_WAIT_SECONDS ) && succes;
 
+       if ( opened )
+               ped_device_close( lp_disk->dev );
+
        return succes ;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]