Backport: Add alignment option for partitions
authorGui Chen <gui.chen@intel.com>
Fri, 23 Mar 2012 07:05:31 +0000 (15:05 +0800)
committerJF Ding <jian-feng.ding@intel.com>
Fri, 23 Mar 2012 08:04:06 +0000 (16:04 +0800)
This a backport of Marko's mic2 patch with a few changes:
https://meego.gitorious.org/meego-developer-tools/image-creator/merge_requests/28

use '--align' to specify the alignment in ks file

e.g. in kickstart, 'part /boot --size 256 --ondisk sda --align=4' to
specify the alignment of boot partition as 4k, align option is counted by KB.

Signed-off-by: Gui Chen <gui.chen@intel.com>
mic/imager/raw.py
mic/kickstart/__init__.py
mic/kickstart/custom_commands/__init__.py
mic/kickstart/custom_commands/partition.py [new file with mode: 0644]
mic/utils/partitionedfs.py

index 7c20079..b313b47 100644 (file)
@@ -23,7 +23,7 @@ from pykickstart.urlgrabber import progress
 
 from mic import kickstart, msger
 from mic.utils import fs_related, runner
-from mic.utils.partitionedfs import PartitionedMount
+from mic.utils.partitionedfs import PartitionedMount, MBR_SECTOR_LEN, SECTOR_SIZE
 from mic.utils.errors import CreatorError, MountError
 
 from baseimager import BaseImageCreator
@@ -154,6 +154,11 @@ class RawImageCreator(BaseImageCreator):
 
             size =   parts[i].size * 1024L * 1024L
 
+            # If we have alignment set for partition we need to enlarge the
+            # drive, so that the alignment changes fits there as well
+            if parts[i].align:
+                size += parts[i].align * 1024L
+
             found = False
             for j in range(len(self._diskinfo)):
                 if self._diskinfo[j]['name'] == disk:
@@ -166,6 +171,11 @@ class RawImageCreator(BaseImageCreator):
             if not found:
                 self._diskinfo.append({ 'name': disk, 'size': size })
 
+        # We need to add a bit space for the disk for the MBR.
+        # NOTE: This could be optimized in some cases when alignment is used.
+        for j in range(len(self._diskinfo)):
+            self._diskinfo[j]['size'] += MBR_SECTOR_LEN * SECTOR_SIZE
+
         return self._diskinfo
 
     #
@@ -178,10 +188,10 @@ class RawImageCreator(BaseImageCreator):
 
         #create disk
         for item in self.get_diskinfo():
-            msger.debug("Adding disk %s as %s/%s-%s.raw" % (item['name'],
-                                                            self.__imgdir,
-                                                            self.name,
-                                                            item['name']))
+            msger.debug("Adding disk %s as %s/%s-%s.raw with size %s bytes" %
+                        (item['name'], self.__imgdir, self.name, item['name'],
+                         item['size']))
+
             disk = fs_related.SparseLoopbackDisk("%s/%s-%s.raw" % (
                                                                 self.__imgdir,
                                                                 self.name,
@@ -197,7 +207,8 @@ class RawImageCreator(BaseImageCreator):
                                           p.mountpoint,
                                           p.fstype,
                                           fsopts = p.fsopts,
-                                          boot = p.active)
+                                          boot = p.active,
+                                          align = p.align)
 
         self.__instloop.mount()
         self._create_mkinitrd_config()
index baa6143..36e8feb 100644 (file)
@@ -36,6 +36,7 @@ from pykickstart.handlers.control import dataMap
 import custom_commands.desktop as desktop
 import custom_commands.moblinrepo as moblinrepo
 import custom_commands.micboot as micboot
+import custom_commands.partition as partition
 
 def read_kickstart(path):
     """Parse a kickstart file and return a KickstartParser instance.
@@ -54,7 +55,10 @@ def read_kickstart(path):
     commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
     commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
     commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
+    commandMap[using_version]["part"] = partition.MeeGo_Partition
+    commandMap[using_version]["partition"] = partition.MeeGo_Partition
     dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
+    dataMap[using_version]["PartData"] = partition.MeeGo_PartData
     superclass = ksversion.returnClassForVersion(version=using_version)
 
     class KSHandlers(superclass):
index 7123ac1..dcfb56e 100644 (file)
@@ -1,8 +1,10 @@
 import desktop
 import moblinrepo
+import partition
 
 __all__ = (
     "Moblin_Desktop",
     "Moblin_Repo",
     "Moblin_RepoData",
+    "MeeGo_Partition",
 )
diff --git a/mic/kickstart/custom_commands/partition.py b/mic/kickstart/custom_commands/partition.py
new file mode 100644 (file)
index 0000000..7b78319
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python -tt
+#
+# Marko Saukko <marko.saukko@cybercom.com>
+#
+# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from pykickstart.commands.partition import *
+
+class MeeGo_PartData(FC4_PartData):
+    removedKeywords = FC4_PartData.removedKeywords
+    removedAttrs = FC4_PartData.removedAttrs
+
+    def __init__(self, *args, **kwargs):
+        FC4_PartData.__init__(self, *args, **kwargs)
+        self.deleteRemovedAttrs()
+        self.align = kwargs.get("align", None)
+
+    def _getArgsAsStr(self):
+        retval = FC4_PartData._getArgsAsStr(self)
+
+        if self.align:
+            retval += " --align"
+
+        return retval
+
+class MeeGo_Partition(FC4_Partition):
+    removedKeywords = FC4_Partition.removedKeywords
+    removedAttrs = FC4_Partition.removedAttrs
+
+    def _getParser(self):
+        op = FC4_Partition._getParser(self)
+        # The alignment value is given in kBytes. e.g., value 8 means that
+        # the partition is aligned to start from 8096 byte boundary.
+        op.add_option("--align", type="int", action="store", dest="align",
+                      default=None)
+        return op
index 63abb0c..2136401 100644 (file)
@@ -25,6 +25,12 @@ from mic.utils import runner
 from mic.utils.errors import MountError
 from mic.utils.fs_related import *
 
+# Lenght of MBR in sectors
+MBR_SECTOR_LEN = 1
+
+# Size of a sector in bytes
+SECTOR_SIZE = 512
+
 class PartitionedMount(Mount):
     def __init__(self, disks, mountdir, skipformat = False):
         Mount.__init__(self, mountdir)
@@ -37,9 +43,8 @@ class PartitionedMount(Mount):
                                  # Partitions with part num higher than 3 will
                                  # be put inside extended partition.
                                  'extended': 0, # Size of extended partition
-                                 # Sector 0 is used by the MBR and can't be used
-                                 # as the start, so setting offset to 1.
-                                 'offset': 1 } # Offset of next partition (in sectors)
+                                 # Offset of next partition (in sectors)
+                                 'offset': 0 }
 
         self.partitions = []
         self.subvolumes = []
@@ -55,10 +60,10 @@ class PartitionedMount(Mount):
         self.skipformat = skipformat
         self.snapshot_created = self.skipformat
         # Size of a sector used in calculations
-        self.sector_size = 512
+        self.sector_size = SECTOR_SIZE
 
-    def add_partition(self, size, disk, mountpoint, fstype = None, fsopts = None, boot = False):
-        # Converting M to s for parted
+    def add_partition(self, size, disk, mountpoint, fstype = None, fsopts = None, boot = False, align = None):
+        # Converting MB to sectors for parted
         size = size * 1024 * 1024 / self.sector_size
 
         """ We need to handle subvolumes for btrfs """
@@ -102,12 +107,14 @@ class PartitionedMount(Mount):
                                     'device': None, # kpartx device node for partition
                                     'mount': None, # Mount object
                                     'num': None, # Partition number
-                                    'boot': boot}) # Bootable flag
+                                    'boot': boot, # Bootable flag
+                                    'align': align}) # Partition alignment
 
-    def __create_part_to_image(self,device, parttype, fstype, start, size):
+    def __create_part_to_image(self, device, parttype, fstype, start, size):
         # Start is included to the size so we need to substract one from the end.
         end = start+size-1
-        msger.debug("Added '%s' part at %d of size %d" % (parttype,start,end))
+        msger.debug("Added '%s' part at Sector %d with size %d sectors" %
+                    (parttype, start, end))
         part_cmd = [self.parted, "-s", device, "unit", "s", "mkpart", parttype]
         if fstype:
             part_cmd.extend([fstype])
@@ -123,22 +130,52 @@ class PartitionedMount(Mount):
     def __format_disks(self):
         msger.debug("Assigning partitions to disks")
 
-        mbr_sector_skipped = False
-
+        # Go through partitions in the order they are added in .ks file
         for n in range(len(self.partitions)):
             p = self.partitions[n]
 
             if not self.disks.has_key(p['disk']):
                 raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint']))
 
-            if not mbr_sector_skipped:
-                # This hack is used to remove one sector from the first partition,
-                # that is the used to the MBR.
-                p['size'] -= 1
-                mbr_sector_skipped = True
-
+            # Get the disk where the partition is located
             d = self.disks[p['disk']]
             d['numpart'] += 1
+
+            # alignment in sectors
+            align_sectors = None
+            # if first partition then we need to skip the first sector
+            # where the MBR is located, if the alignment isn't set
+            # See: https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey
+            if d['numpart'] == 1:
+                if p['align'] and p['align'] > 0:
+                    align_sectors = p['align'] * 1024 / self.sector_size
+                else:
+                    align_sectors = MBR_SECTOR_LEN
+            elif p['align']:
+                # If not first partition and we do have alignment set we need
+                # to align the partition.
+                # FIXME: This leaves a empty spaces to the disk. To fill the
+                # gaps we could enlargea the previous partition?
+
+                # Calc how much the alignment is off.
+                align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size)
+                # We need to move forward to the next alignment point
+                align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors
+
+            if align_sectors:
+                if p['align'] and p['align'] > 0:
+                    msger.debug("Realignment for %s%s with %s sectors, original"
+                                " offset %s, target alignment is %sK." %
+                                (p['disk'], d['numpart'], align_sectors,
+                                 d['offset'], p['align']))
+                # p['size'] already converted in secctors
+                if p['size'] <= align_sectors:
+                    raise MountError("Partition for %s is too small to handle "
+                                     "the alignment change." % p['mountpoint'])
+
+                # increase the offset so we actually start the partition on right alignment
+                d['offset'] += align_sectors
+
             if d['numpart'] > 3:
                 # Increase allocation of extended partition to hold this partition
                 d['extended'] += p['size']
@@ -151,7 +188,10 @@ class PartitionedMount(Mount):
             p['start'] = d['offset']
             d['offset'] += p['size']
             d['partitions'].append(n)
-            msger.debug("Assigned %s to %s%d at %d at size %d" % (p['mountpoint'], p['disk'], p['num'], p['start'], p['size']))
+            msger.debug("Assigned %s to %s%d at Sector %d with size %d sectors "
+                        "/ %d bytes." % (p['mountpoint'], p['disk'], p['num'],
+                                         p['start'], p['size'],
+                                         p['size'] * self.sector_size))
 
         if self.skipformat:
             msger.debug("Skipping disk format, because skipformat flag is set.")