]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Rewrite blocklist functions in order to get progress when
authorVladimir Serbinenko <phcoder@gmail.com>
Fri, 1 Nov 2013 22:28:03 +0000 (23:28 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Fri, 1 Nov 2013 22:28:03 +0000 (23:28 +0100)
reading large extents and decrease amount of blocklist hook calls.

20 files changed:
ChangeLog
grub-core/commands/blocklist.c
grub-core/commands/loadenv.c
grub-core/commands/testload.c
grub-core/disk/ata.c
grub-core/disk/cryptodisk.c
grub-core/disk/diskfilter.c
grub-core/disk/efi/efidisk.c
grub-core/disk/i386/pc/biosdisk.c
grub-core/disk/loopback.c
grub-core/disk/memdisk.c
grub-core/disk/scsi.c
grub-core/fs/cbfs.c
grub-core/kern/disk.c
grub-core/kern/emu/hostdisk.c
grub-core/lib/disk.c
grub-core/osdep/linux/blocklist.c
grub-core/osdep/windows/blocklist.c
include/grub/disk.h
util/setup.c

index c59def2b2a3f45aa1aecc0dbe0ccf50d3ecf43bf..33a60b13408af606351a8dfdb70da7a9bca79c18 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-11-01  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Rewrite blocklist functions in order to get progress when
+       reading large extents and decrease amount of blocklist hook calls.
+
 2013-11-01  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/term/serial.c (options), (grub_cmd_serial): Fix handling
index c531e4449f1c45fd13f104c692d1e30fdd222abb..d1a47b504bf25258a32beeecdab237fd6696e3d5 100644 (file)
@@ -62,23 +62,47 @@ read_blocklist (grub_disk_addr_t sector, unsigned offset, unsigned length,
   if (ctx->num_sectors > 0)
     {
       if (ctx->start_sector + ctx->num_sectors == sector
-         && offset == 0 && length == GRUB_DISK_SECTOR_SIZE)
+         && offset == 0 && length >= GRUB_DISK_SECTOR_SIZE)
        {
-         ctx->num_sectors++;
-         return;
+         ctx->num_sectors += length >> GRUB_DISK_SECTOR_BITS;
+         sector += length >> GRUB_DISK_SECTOR_BITS;
+         length &= (GRUB_DISK_SECTOR_SIZE - 1);
        }
 
+      if (!length)
+       return;
       print_blocklist (ctx->start_sector, ctx->num_sectors, 0, 0, ctx);
       ctx->num_sectors = 0;
     }
 
-  if (offset == 0 && length == GRUB_DISK_SECTOR_SIZE)
+  if (offset)
     {
-      ctx->start_sector = sector;
-      ctx->num_sectors++;
+      unsigned l = length + offset;
+      l &= (GRUB_DISK_SECTOR_SIZE - 1);
+      l -= offset;
+      print_blocklist (sector, 0, offset, l, ctx);
+      length -= l;
+      sector++;
+      offset = 0;
+    }
+
+  if (!length)
+    return;
+
+  if (length & (GRUB_DISK_SECTOR_SIZE - 1))
+    {
+      if (length >> GRUB_DISK_SECTOR_BITS)
+       {
+         print_blocklist (sector, length >> GRUB_DISK_SECTOR_BITS, 0, 0, ctx);
+         sector += length >> GRUB_DISK_SECTOR_BITS;
+       }
+      print_blocklist (sector, 0, 0, length & (GRUB_DISK_SECTOR_SIZE - 1), ctx);
     }
   else
-    print_blocklist (sector, 0, offset, length, ctx);
+    {
+      ctx->start_sector = sector;
+      ctx->num_sectors = length >> GRUB_DISK_SECTOR_BITS;
+    }
 }
 
 static grub_err_t
index 64c2b7ecf398d6c0010d9224ab0c290ac1dc4881..2a0b7ab24c8a801481d2bebe844ec944e0eac1b9 100644 (file)
@@ -259,10 +259,28 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
   for (p = blocklists; p; p = p->next)
     {
       struct blocklist *q;
+      /* Check if any pair of blocks overlap.  */
       for (q = p->next; q; q = q->next)
         {
-          /* Check if any pair of blocks overlap.  */
-          if (p->sector == q->sector)
+         grub_disk_addr_t s1, s2;
+         grub_disk_addr_t e1, e2, t;
+
+         s1 = p->sector;
+         e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
+
+         s2 = q->sector;
+         e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
+
+         if (s2 > s1)
+           {
+             t = s2;
+             s2 = s1;
+             s1 = t;
+             t = e2;
+             e2 = e1;
+             e1 = t;
+           }
+          if (e1 > s2)
             {
               /* This might be actually valid, but it is unbelievable that
                  any filesystem makes such a silly allocation.  */
@@ -286,9 +304,18 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
   part_start = grub_partition_get_start (disk->partition);
 
   buf = grub_envblk_buffer (envblk);
+  char *blockbuf = NULL;
+  grub_size_t blockbuf_len = 0;
   for (p = blocklists, index = 0; p; index += p->length, p = p->next)
     {
-      char blockbuf[GRUB_DISK_SECTOR_SIZE];
+      if (p->length > blockbuf_len)
+       {
+         grub_free (blockbuf);
+         blockbuf_len = 2 * p->length;
+         blockbuf = grub_malloc (blockbuf_len);
+         if (!blockbuf)
+           return grub_errno;
+       }
 
       if (grub_disk_read (disk, p->sector - part_start,
                           p->offset, p->length, blockbuf))
@@ -340,10 +367,6 @@ save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
   struct grub_cmd_save_env_ctx *ctx = data;
   struct blocklist *block;
 
-  if (offset + length > GRUB_DISK_SECTOR_SIZE)
-    /* Seemingly a bug.  */
-    return;
-
   block = grub_malloc (sizeof (*block));
   if (! block)
     return;
index 4d0280a74d494b0ed3c8df8415ae831a2b3877e3..cfab6763dc3f63faeefb99d71b9b53e35a81ddbb 100644 (file)
@@ -35,10 +35,13 @@ GRUB_MOD_LICENSE ("GPLv3+");
 static void
 read_progress (grub_disk_addr_t sector __attribute__ ((unused)),
               unsigned offset __attribute__ ((unused)),
-              unsigned len __attribute__ ((unused)),
+              unsigned len,
               void *data __attribute__ ((unused)))
 {
-  grub_xputs (".");
+  for (; len >= GRUB_DISK_SECTOR_SIZE; len -= GRUB_DISK_SECTOR_SIZE)
+    grub_xputs (".");
+  if (len)
+    grub_xputs (".");
   grub_refresh ();
 }
 
index dada56d00aa80ba305d070254fb5ee9ec1ed50c6..a79519cc7dac34be87c53b27ef1ec5f9156ba8ff 100644 (file)
@@ -285,7 +285,6 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
 
   if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
     {
-      batch = 65536;
       if (ata->dma)
        {
          cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT;
@@ -301,10 +300,6 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
     {
       if (addressing == GRUB_ATA_LBA48)
        addressing = GRUB_ATA_LBA;
-      if (addressing != GRUB_ATA_CHS)
-       batch = 256;
-      else
-       batch = 1;
       if (ata->dma)
        {
          cmd = GRUB_ATA_CMD_READ_SECTORS_DMA;
@@ -317,8 +312,10 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
        }
     }
 
-  if (batch > (ata->maxbuffer >> ata->log_sector_size))
-    batch = (ata->maxbuffer >> ata->log_sector_size);
+  if (addressing != GRUB_ATA_CHS)
+    batch = 256;
+  else
+    batch = 1;
 
   while (nsectors < size)
     {
@@ -464,6 +461,10 @@ grub_ata_open (const char *name, grub_disk_t disk)
     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
 
   disk->total_sectors = ata->size;
+  disk->max_agglomerate = (ata->maxbuffer >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
+  if (disk->max_agglomerate > (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size)))
+    disk->max_agglomerate = (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size));
+
   disk->log_sector_size = ata->log_sector_size;
 
   disk->id = grub_make_scsi_id (id, bus, 0);
index 673101001573ebe885fdd93ebb2974616a89a1b7..1b40afc9d175134710a24508109e5864d9ad7855 100644 (file)
@@ -517,6 +517,7 @@ grub_cryptodisk_open (const char *name, grub_disk_t disk)
 
   disk->data = dev;
   disk->total_sectors = dev->total_length;
+  disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
   disk->id = dev->id;
   dev->ref++;
   return GRUB_ERR_NONE;
index 44794bfcacaf73573c6e5c68eb9eabf4ba96c3ff..b917c3eab14b5ec6ead0aa0ca2394059f1863896 100644 (file)
@@ -445,6 +445,7 @@ grub_diskfilter_open (const char *name, grub_disk_t disk)
   disk->data = lv;
 
   disk->total_sectors = lv->size;
+  disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
   return 0;
 }
 
index 65683afb035f22d04e67c176224b552e9ae7583d..8dfe00343bc5b87c985632fd1b4e5a8d35f53d10 100644 (file)
@@ -518,6 +518,8 @@ grub_efidisk_open (const char *name, struct grub_disk *disk)
   grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
                m, (unsigned long long) m->last_block, m->block_size);
   disk->total_sectors = m->last_block + 1;
+  /* Don't increase this value due to bug in some EFI.  */
+  disk->max_agglomerate = 0xa0000 >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
   if (m->block_size & (m->block_size - 1) || !m->block_size)
     return grub_error (GRUB_ERR_IO, "invalid sector size %d",
                       m->block_size);
@@ -550,24 +552,11 @@ grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector,
   d = disk->data;
   bio = d->block_io;
 
-  while (size > 0)
-    {
-      grub_size_t len;
-      len = 0x500;
-      if (len > size)
-       len = size;
-      status = efi_call_5 ((wr ? bio->write_blocks : bio->read_blocks), bio,
-                          bio->media->media_id,
-                          (grub_efi_uint64_t) sector,
-                          (grub_efi_uintn_t) len << disk->log_sector_size,
-                          buf);
-      size -= len;
-      buf += len << disk->log_sector_size;
-      sector += len;
-      if (status != GRUB_EFI_SUCCESS)
-       return status;
-    }
-  return GRUB_EFI_SUCCESS;
+  return efi_call_5 ((wr ? bio->write_blocks : bio->read_blocks), bio,
+                    bio->media->media_id,
+                    (grub_efi_uint64_t) sector,
+                    (grub_efi_uintn_t) size << disk->log_sector_size,
+                    buf);
 }
 
 static grub_err_t
index 5e6d0603047effa08015923eb1234b025e9cc7cc..7458c7a0f5d842fb069b262ec9e3783728d34eea 100644 (file)
@@ -424,6 +424,9 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
     }
 
   disk->total_sectors = total_sectors;
+  /* Limit the max to 0x7f because of Phoenix EDD.  */
+  disk->max_agglomerate = 0x7f >> GRUB_DISK_CACHE_BITS;
+
   disk->data = data;
 
   return GRUB_ERR_NONE;
@@ -548,10 +551,6 @@ get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
 
   size = sectors - offset;
 
-  /* Limit the max to 0x7f because of Phoenix EDD.  */
-  if (size > ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size))
-    size = ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size);
-
   return size;
 }
 
index fed88decd35d3935673298193421ef3b630a766b..d9fa1f3c57df219042579fad2f15d887eb0592be 100644 (file)
@@ -167,6 +167,10 @@ grub_loopback_open (const char *name, grub_disk_t disk)
                           / GRUB_DISK_SECTOR_SIZE);
   else
     disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN;
+  /* Avoid reading more than 512M.  */
+  disk->max_agglomerate = 1 << (29 - GRUB_DISK_SECTOR_BITS
+                               - GRUB_DISK_CACHE_BITS);
+
   disk->id = (unsigned long) dev;
 
   disk->data = dev;
index 4ad1cb12f377f03dbedd0ced39c57a763654cbb8..20c3f02ba34d573b9baec912511369d635826d89 100644 (file)
@@ -46,6 +46,7 @@ grub_memdisk_open (const char *name, grub_disk_t disk)
       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a memdisk");
 
   disk->total_sectors = memdisk_size / GRUB_DISK_SECTOR_SIZE;
+  disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
   disk->id = (unsigned long) "mdsk";
 
   return GRUB_ERR_NONE;
index 90ac379e8df3dbf418507b92f12b2321faf95da0..b6cb2f42109b1ed7b7292b7802d3d9c5a24bdfc9 100644 (file)
@@ -606,6 +606,13 @@ grub_scsi_open (const char *name, grub_disk_t disk)
        }
 
       disk->total_sectors = scsi->last_block + 1;
+      /* PATA doesn't support more than 32K reads.
+        Not sure about AHCI and USB. If it's confirmed that either of
+        them can do bigger reads reliably this value can be moved to 'scsi'
+        structure.  */
+      disk->max_agglomerate = 32768 >> (GRUB_DISK_SECTOR_BITS
+                                       + GRUB_DISK_CACHE_BITS);
+
       if (scsi->blocksize & (scsi->blocksize - 1) || !scsi->blocksize)
        {
          grub_free (scsi);
@@ -647,40 +654,27 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
 
   scsi = disk->data;
 
-  while (size)
+  grub_err_t err;
+  /* Depending on the type, select a read function.  */
+  switch (scsi->devtype)
     {
-      /* PATA doesn't support more than 32K reads.
-        Not sure about AHCI and USB. If it's confirmed that either of
-        them can do bigger reads reliably this value can be moved to 'scsi'
-        structure.  */
-      grub_size_t len = 32768 >> disk->log_sector_size;
-      grub_err_t err;
-      if (len > size)
-       len = size;
-      /* Depending on the type, select a read function.  */
-      switch (scsi->devtype)
-       {
-       case grub_scsi_devtype_direct:
-         if (sector >> 32)
-           err = grub_scsi_read16 (disk, sector, len, buf);
-         else
-           err = grub_scsi_read10 (disk, sector, len, buf);
-         if (err)
-           return err;
-         break;
-
-       case grub_scsi_devtype_cdrom:
-         if (sector >> 32)
-           err = grub_scsi_read16 (disk, sector, len, buf);
-         else
-           err = grub_scsi_read12 (disk, sector, len, buf);
-         if (err)
-           return err;
-         break;
-       }
-      size -= len;
-      sector += len;
-      buf += len << disk->log_sector_size;
+    case grub_scsi_devtype_direct:
+      if (sector >> 32)
+       err = grub_scsi_read16 (disk, sector, size, buf);
+      else
+       err = grub_scsi_read10 (disk, sector, size, buf);
+      if (err)
+       return err;
+      break;
+
+    case grub_scsi_devtype_cdrom:
+      if (sector >> 32)
+       err = grub_scsi_read16 (disk, sector, size, buf);
+      else
+       err = grub_scsi_read12 (disk, sector, size, buf);
+      if (err)
+       return err;
+      break;
     }
 
   return GRUB_ERR_NONE;
@@ -718,10 +712,10 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
 }
 
 static grub_err_t
-grub_scsi_write (grub_disk_t disk __attribute((unused)),
-                grub_disk_addr_t sector __attribute((unused)),
-                grub_size_t size __attribute((unused)),
-                const char *buf __attribute((unused)))
+grub_scsi_write (grub_disk_t disk,
+                grub_disk_addr_t sector,
+                grub_size_t size,
+                const char *buf)
 {
   grub_scsi_t scsi;
 
@@ -730,31 +724,18 @@ grub_scsi_write (grub_disk_t disk __attribute((unused)),
   if (scsi->devtype == grub_scsi_devtype_cdrom)
     return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM"));
 
-  while (size)
+  grub_err_t err;
+  /* Depending on the type, select a read function.  */
+  switch (scsi->devtype)
     {
-      /* PATA doesn't support more than 32K reads.
-        Not sure about AHCI and USB. If it's confirmed that either of
-        them can do bigger reads reliably this value can be moved to 'scsi'
-        structure.  */
-      grub_size_t len = 32768 >> disk->log_sector_size;
-      grub_err_t err;
-      if (len > size)
-       len = size;
-      /* Depending on the type, select a read function.  */
-      switch (scsi->devtype)
-       {
-       case grub_scsi_devtype_direct:
-         if (sector >> 32)
-           err = grub_scsi_write16 (disk, sector, len, buf);
-         else
-           err = grub_scsi_write10 (disk, sector, len, buf);
-         if (err)
-           return err;
-         break;
-       }
-      size -= len;
-      sector += len;
-      buf += len << disk->log_sector_size;
+    case grub_scsi_devtype_direct:
+      if (sector >> 32)
+       err = grub_scsi_write16 (disk, sector, size, buf);
+      else
+       err = grub_scsi_write10 (disk, sector, size, buf);
+      if (err)
+       return err;
+      break;
     }
 
   return GRUB_ERR_NONE;
index c79ec433a95acafe75517b8da284aa09388eafea..0530c9b8a7392b10c7ef278b1dfeb569e51e2719 100644 (file)
@@ -289,6 +289,7 @@ grub_cbfsdisk_open (const char *name, grub_disk_t disk)
       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a cbfsdisk");
 
   disk->total_sectors = cbfsdisk_size / GRUB_DISK_SECTOR_SIZE;
+  disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
   disk->id = (unsigned long) "cbfs";
 
   return GRUB_ERR_NONE;
index 1d0a11cb2e2442109dfb8118fb9fdae65906fb26..2a44f65af695a79245f5cfd6b4d3a3c18dc2afde 100644 (file)
@@ -199,6 +199,9 @@ grub_disk_open (const char *name)
   if (! disk)
     return 0;
   disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
+  /* Default 1MiB of maximum agglomerate.  */
+  disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
+                                     + GRUB_DISK_CACHE_BITS);
 
   p = find_part_sep (name);
   if (p)
@@ -311,8 +314,8 @@ grub_disk_close (grub_disk_t disk)
    sector is already adjusted and is divisible by cache unit size.
  */
 static grub_err_t
-grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
-                     grub_off_t offset, grub_size_t size, void *buf)
+grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
+                          grub_off_t offset, grub_size_t size, void *buf)
 {
   char *data;
   char *tmp_buf;
@@ -389,15 +392,27 @@ grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
   }
 }
 
+static grub_err_t
+grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
+                     grub_off_t offset, grub_size_t size, void *buf)
+{
+  grub_err_t err;
+
+  err = grub_disk_read_small_real (disk, sector, offset, size, buf);
+  if (err)
+    return err;
+  if (disk->read_hook)
+    (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
+                      offset & (GRUB_DISK_SECTOR_SIZE - 1),
+                      size, disk->read_hook_data);
+  return GRUB_ERR_NONE;
+}
+
 /* Read data from the disk.  */
 grub_err_t
 grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
                grub_off_t offset, grub_size_t size, void *buf)
 {
-  grub_off_t real_offset;
-  grub_disk_addr_t real_sector;
-  grub_size_t real_size;
-
   /* First of all, check if the region is within the disk.  */
   if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
     {
@@ -408,10 +423,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
       return grub_errno;
     }
 
-  real_sector = sector;
-  real_offset = offset;
-  real_size = size;
-
   /* First read until first cache boundary.   */
   if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
     {
@@ -446,7 +457,8 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
 
       /* agglomerate read until we find a first cached entry.  */
       for (agglomerate = 0; agglomerate
-            < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS));
+            < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
+            && agglomerate < disk->max_agglomerate;
           agglomerate++)
        {
          data = grub_disk_cache_fetch (disk->dev->id, disk->id,
@@ -486,6 +498,11 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
                                   + (i << (GRUB_DISK_CACHE_BITS
                                            + GRUB_DISK_SECTOR_BITS)));
 
+
+         if (disk->read_hook)
+           (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
+                              disk->read_hook_data);
+
          sector += agglomerate << GRUB_DISK_CACHE_BITS;
          size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
          buf = (char *) buf 
@@ -494,6 +511,9 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
 
       if (data)
        {
+         if (disk->read_hook)
+           (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
+                              disk->read_hook_data);
          sector += GRUB_DISK_CACHE_SIZE;
          buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
          size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
@@ -509,26 +529,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
        return err;
     }
 
-  /* Call the read hook, if any.  */
-  if (disk->read_hook)
-    {
-      grub_disk_addr_t s = real_sector;
-      grub_size_t l = real_size;
-      grub_off_t o = real_offset;
-
-      while (l)
-       {
-         grub_size_t cl;
-         cl = GRUB_DISK_SECTOR_SIZE - o;
-         if (cl > l)
-           cl = l;
-         (disk->read_hook) (s, o, cl, disk->read_hook_data);
-         s++;
-         l -= cl;
-         o = 0;
-       }
-    }
-
   return grub_errno;
 }
 
index 0121cc113cd602e8851989c70b9cf6055668749d..e00c8fbed67c4d57e5ad4d814cf970e8c63ed564 100644 (file)
@@ -155,6 +155,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk)
     disk->total_sectors = grub_util_get_fd_size (fd, map[drive].device,
                                                 &disk->log_sector_size);
     disk->total_sectors >>= disk->log_sector_size;
+    disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
 
 #if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
     {
index 9527e2956e682b85666a91c9d25691a7975c9bcc..c7ba68007a87434a81e43e776e74cfe39cdc5fae 100644 (file)
@@ -123,6 +123,13 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
          len = size & ~((1 << disk->log_sector_size) - 1);
          n = size >> disk->log_sector_size;
 
+         if (n > (disk->max_agglomerate
+                  << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
+                      - disk->log_sector_size)))
+           n = (disk->max_agglomerate
+                << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
+                    - disk->log_sector_size));
+
          if ((disk->dev->write) (disk, transform_sector (disk, sector),
                                  n, buf) != GRUB_ERR_NONE)
            goto finish;
index b8aa2bfa792a7b7945d17b3a2321608c938c428a..453b7ac8f95c9505266d041c8f5da70fb4600ada 100644 (file)
@@ -70,7 +70,7 @@ grub_install_get_blocklist (grub_device_t root_dev,
 
   if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0)
     {
-      int nblocks, i, j;
+      int nblocks, i;
       int bsize;
       int mul;
 
@@ -88,27 +88,25 @@ grub_install_get_blocklist (grub_device_t root_dev,
       for (i = 0; i < nblocks; i++)
        {
          unsigned blk = i;
+         int rest;
          if (ioctl (fd, FIBMAP, &blk) < 0)
            grub_util_error (_("can't retrieve blocklists: %s"),
                             strerror (errno));
            
-         for (j = 0; j < mul; j++)
-           {
-             int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS);
-             if (rest <= 0)
-               break;
-             if (rest > GRUB_DISK_SECTOR_SIZE)
-               rest = GRUB_DISK_SECTOR_SIZE;
-             callback (((grub_uint64_t) blk) * mul + j
-                       + container_start,
-                       0, rest, hook_data);
-           }
+         rest = core_size - ((i * mul) << GRUB_DISK_SECTOR_BITS);
+         if (rest <= 0)
+           break;
+         if (rest > GRUB_DISK_SECTOR_SIZE * mul)
+           rest = GRUB_DISK_SECTOR_SIZE * mul;
+         callback (((grub_uint64_t) blk) * mul
+                   + container_start,
+                   0, rest, hook_data);
        }
     }
   else
     {
       struct fiemap *fie2;
-      int i, j;
+      int i;
       fie2 = xmalloc (sizeof (*fie2)
                      + fie1.fm_mapped_extents
                      * sizeof (fie1.fm_extents[1]));
@@ -122,22 +120,12 @@ grub_install_get_blocklist (grub_device_t root_dev,
                         strerror (errno));
       for (i = 0; i < fie2->fm_mapped_extents; i++)
        {
-         for (j = 0;
-              j < ((fie2->fm_extents[i].fe_length
-                    + GRUB_DISK_SECTOR_SIZE - 1)
-                   >> GRUB_DISK_SECTOR_BITS);
-              j++)
-           {
-             size_t len = (fie2->fm_extents[i].fe_length
-                           - j * GRUB_DISK_SECTOR_SIZE);
-             if (len > GRUB_DISK_SECTOR_SIZE)
-               len = GRUB_DISK_SECTOR_SIZE;
-             callback ((fie2->fm_extents[i].fe_physical
-                        >> GRUB_DISK_SECTOR_BITS)
-                       + j + container_start,
-                       fie2->fm_extents[i].fe_physical
-                       & (GRUB_DISK_SECTOR_SIZE - 1), len, hook_data);
-           }
+         callback ((fie2->fm_extents[i].fe_physical
+                    >> GRUB_DISK_SECTOR_BITS)
+                   + container_start,
+                   fie2->fm_extents[i].fe_physical
+                   & (GRUB_DISK_SECTOR_SIZE - 1),
+                   fie2->fm_extents[i].fe_length, hook_data);
        }
     }
   close (fd);
index bddec367da04a90b1c14affce447c01d2370ee8d..b9119ec3ec095fc324f45cc18a760d6296f4e5d2 100644 (file)
@@ -49,7 +49,7 @@ grub_install_get_blocklist (grub_device_t root_dev,
   DWORD rets;
   RETRIEVAL_POINTERS_BUFFER *extbuf;
   size_t extbuf_size;
-  DWORD i, j, k;
+  DWORD i;
   grub_uint64_t sec_per_lcn;
   grub_uint64_t curvcn = 0;
   STARTING_VCN_INPUT_BUFFER start_vcn;
@@ -108,12 +108,8 @@ grub_install_get_blocklist (grub_device_t root_dev,
   CloseHandle (filehd);
 
   for (i = 0; i < extbuf->ExtentCount; i++)
-    {
-      for (j = 0; j < extbuf->Extents[i].NextVcn.QuadPart - curvcn; j++)
-       for (k = 0; k < sec_per_lcn; k++)
-         callback ((extbuf->Extents[i].Lcn.QuadPart + j)
-                   * sec_per_lcn + first_lcn
-                   + k, 0, 512, hook_data);
-    }
+    callback (extbuf->Extents[i].Lcn.QuadPart
+             * sec_per_lcn + first_lcn,
+             0, 512 * sec_per_lcn * (extbuf->Extents[i].NextVcn.QuadPart - curvcn), hook_data);
   free (extbuf);
 }
index 42d170e3190eee8499013bd6bdaee86674aa7829..3e61c96423dfcec524b417e81e441d277d51667f 100644 (file)
@@ -125,6 +125,9 @@ struct grub_disk
   /* Logarithm of sector size.  */
   unsigned int log_sector_size;
 
+  /* Maximum number of sectors read divided by GRUB_DISK_CACHE_SIZE.  */
+  unsigned int max_agglomerate;
+
   /* The id used by the disk cache manager.  */
   unsigned long id;
 
@@ -164,6 +167,8 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
 #define GRUB_DISK_CACHE_BITS   6
 #define GRUB_DISK_CACHE_SIZE   (1 << GRUB_DISK_CACHE_BITS)
 
+#define GRUB_DISK_MAX_MAX_AGGLOMERATE ((1 << (30 - GRUB_DISK_CACHE_BITS - GRUB_DISK_SECTOR_BITS)) - 1)
+
 /* Return value of grub_disk_get_size() in case disk size is unknown. */
 #define GRUB_DISK_SIZE_UNKNOWN  0xffffffffffffffffULL
 
index 5b7c38430ca11c1fc04a0a11641bc28268bb83e9..337c304ef17ec05e877aa2e08f9783f8364923c6 100644 (file)
@@ -149,33 +149,40 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length,
 {
   struct blocklists *bl = data;
   struct grub_boot_blocklist *prev = bl->block + 1;
+  grub_uint64_t seclen;
 
   grub_util_info ("saving <%" PRIuGRUB_UINT64_T ",%u,%u>",
                  sector, offset, length);
 
   if (bl->first_sector == (grub_disk_addr_t) -1)
     {
-      if (offset != 0 || length != GRUB_DISK_SECTOR_SIZE)
+      if (offset != 0 || length < GRUB_DISK_SECTOR_SIZE)
        grub_util_error ("%s", _("the first sector of the core file is not sector-aligned"));
 
       bl->first_sector = sector;
-      return;
+      sector++;
+      length -= GRUB_DISK_SECTOR_SIZE;
+      if (!length)
+       return;
     }
 
-  if (offset != 0 || bl->last_length != GRUB_DISK_SECTOR_SIZE)
+  if (offset != 0 || bl->last_length != 0)
     grub_util_error ("%s", _("non-sector-aligned data is found in the core file"));
 
+  seclen = (length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS;
+
   if (bl->block != bl->first_block
       && (grub_target_to_host64 (prev->start)
          + grub_target_to_host16 (prev->len)) == sector)
     {
-      grub_uint16_t t = grub_target_to_host16 (prev->len) + 1;
+      grub_uint16_t t = grub_target_to_host16 (prev->len);
+      t += seclen;
       prev->len = grub_host_to_target16 (t);
     }
   else
     {
       bl->block->start = grub_host_to_target64 (sector);
-      bl->block->len = grub_host_to_target16 (1);
+      bl->block->len = grub_host_to_target16 (seclen);
 #ifdef GRUB_SETUP_BIOS
       bl->block->segment = grub_host_to_target16 (bl->current_segment);
 #endif
@@ -185,9 +192,9 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length,
        grub_util_error ("%s", _("the sectors of the core file are too fragmented"));
     }
 
-  bl->last_length = length;
+  bl->last_length = length & (GRUB_DISK_SECTOR_SIZE - 1);
 #ifdef GRUB_SETUP_BIOS
-  bl->current_segment += GRUB_DISK_SECTOR_SIZE >> 4;
+  bl->current_segment += seclen << (GRUB_DISK_SECTOR_BITS - 4);
 #endif
 }
 
@@ -260,7 +267,7 @@ SETUP (const char *dir,
   bl.current_segment =
     GRUB_BOOT_I386_PC_KERNEL_SEG + (GRUB_DISK_SECTOR_SIZE >> 4);
 #endif
-  bl.last_length = GRUB_DISK_SECTOR_SIZE;
+  bl.last_length = 0;
 
   /* Read the boot image by the OS service.  */
   boot_path = grub_util_get_path (dir, boot_file);
@@ -731,6 +738,8 @@ unable_to_embed:
        if ((char *) bl.block <= core_img)
          grub_util_error ("%s", _("no terminator in the core image"));
       }
+    if (len)
+      grub_util_error ("%s", _("blocklists are incomplete"));
     core_dev->disk->partition = container;
     free (buf);
   }