contiguous read
This commit is contained in:
parent
bd671cc4fe
commit
d8a2bcf564
1 changed files with 178 additions and 118 deletions
|
@ -398,13 +398,93 @@ transform_sector (grub_disk_t disk, grub_disk_addr_t sector)
|
||||||
return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
|
return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Small read (less than cache size and not pass across cache unit boundaries).
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
char *data;
|
||||||
|
char *tmp_buf;
|
||||||
|
|
||||||
|
/* Fetch the cache. */
|
||||||
|
data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
/* Just copy it! */
|
||||||
|
grub_memcpy (buf, data + offset, size);
|
||||||
|
grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a temporary buffer. */
|
||||||
|
tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
|
||||||
|
if (! tmp_buf)
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
|
/* Otherwise read data from the disk actually. */
|
||||||
|
if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
|
||||||
|
|| sector + GRUB_DISK_CACHE_SIZE
|
||||||
|
< (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
|
||||||
|
{
|
||||||
|
grub_err_t err;
|
||||||
|
err = (disk->dev->read) (disk, transform_sector (disk, sector),
|
||||||
|
1 << (GRUB_DISK_CACHE_BITS
|
||||||
|
+ GRUB_DISK_SECTOR_BITS
|
||||||
|
- disk->log_sector_size), tmp_buf);
|
||||||
|
if (!err)
|
||||||
|
{
|
||||||
|
/* Copy it and store it in the disk cache. */
|
||||||
|
grub_memcpy (buf, tmp_buf + offset, size);
|
||||||
|
grub_disk_cache_store (disk->dev->id, disk->id,
|
||||||
|
sector, tmp_buf);
|
||||||
|
grub_free (tmp_buf);
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_errno = GRUB_ERR_NONE;
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Uggh... Failed. Instead, just read necessary data. */
|
||||||
|
unsigned num;
|
||||||
|
grub_disk_addr_t aligned_sector;
|
||||||
|
|
||||||
|
sector += (offset >> GRUB_DISK_SECTOR_BITS);
|
||||||
|
offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
|
||||||
|
aligned_sector = (sector & ~((1 << (disk->log_sector_size
|
||||||
|
- GRUB_DISK_SECTOR_BITS))
|
||||||
|
- 1));
|
||||||
|
offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
|
||||||
|
num = ((size + offset + (1 << (disk->log_sector_size))
|
||||||
|
- 1) >> (disk->log_sector_size));
|
||||||
|
|
||||||
|
tmp_buf = grub_malloc (num << disk->log_sector_size);
|
||||||
|
if (!tmp_buf)
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
|
if ((disk->dev->read) (disk, transform_sector (disk, aligned_sector),
|
||||||
|
num, tmp_buf))
|
||||||
|
{
|
||||||
|
grub_error_push ();
|
||||||
|
grub_dprintf ("disk", "%s read failed\n", disk->name);
|
||||||
|
grub_error_pop ();
|
||||||
|
return grub_errno;
|
||||||
|
}
|
||||||
|
grub_memcpy (buf, tmp_buf + offset, size);
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Read data from the disk. */
|
/* Read data from the disk. */
|
||||||
grub_err_t
|
grub_err_t
|
||||||
grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
|
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 offset, grub_size_t size, void *buf)
|
||||||
{
|
{
|
||||||
char *tmp_buf;
|
grub_off_t real_offset;
|
||||||
unsigned real_offset;
|
grub_disk_addr_t real_sector;
|
||||||
|
grub_size_t real_size;
|
||||||
|
|
||||||
/* First of all, check if the region is within the disk. */
|
/* First of all, check if the region is within the disk. */
|
||||||
if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE)
|
if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE)
|
||||||
|
@ -416,139 +496,119 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
real_sector = sector;
|
||||||
real_offset = offset;
|
real_offset = offset;
|
||||||
|
real_size = size;
|
||||||
|
|
||||||
/* Allocate a temporary buffer. */
|
/* First read until first cache boundary. */
|
||||||
tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
|
if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
|
||||||
if (! tmp_buf)
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
/* Until SIZE is zero... */
|
|
||||||
while (size)
|
|
||||||
{
|
{
|
||||||
char *data;
|
|
||||||
grub_disk_addr_t start_sector;
|
grub_disk_addr_t start_sector;
|
||||||
grub_size_t len;
|
|
||||||
grub_size_t pos;
|
grub_size_t pos;
|
||||||
|
grub_err_t err;
|
||||||
|
grub_size_t len;
|
||||||
|
|
||||||
/* For reading bulk data. */
|
|
||||||
start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
|
start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
|
||||||
pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
|
pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
|
||||||
len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
|
len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
|
||||||
- pos - real_offset);
|
- pos - offset);
|
||||||
if (len > size)
|
if (len > size)
|
||||||
len = size;
|
len = size;
|
||||||
|
err = grub_disk_read_small (disk, start_sector,
|
||||||
|
offset + pos, len, buf);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
buf = (char *) buf + len;
|
||||||
|
size -= len;
|
||||||
|
offset += len;
|
||||||
|
sector += (offset >> GRUB_DISK_SECTOR_BITS);
|
||||||
|
offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Until SIZE is zero... */
|
||||||
|
while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
|
||||||
|
{
|
||||||
|
char *data = NULL;
|
||||||
|
grub_disk_addr_t agglomerate;
|
||||||
|
grub_err_t err;
|
||||||
|
|
||||||
|
/* agglomerate read until we find a first cached entry. */
|
||||||
|
for (agglomerate = 0; agglomerate
|
||||||
|
< (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS));
|
||||||
|
agglomerate++)
|
||||||
|
{
|
||||||
|
data = grub_disk_cache_fetch (disk->dev->id, disk->id,
|
||||||
|
sector + (agglomerate
|
||||||
|
<< GRUB_DISK_CACHE_BITS));
|
||||||
|
if (data)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agglomerate)
|
||||||
|
{
|
||||||
|
grub_disk_addr_t i;
|
||||||
|
|
||||||
|
err = (disk->dev->read) (disk, transform_sector (disk, sector),
|
||||||
|
agglomerate << (GRUB_DISK_CACHE_BITS
|
||||||
|
+ GRUB_DISK_SECTOR_BITS
|
||||||
|
- disk->log_sector_size),
|
||||||
|
buf);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 0; i < agglomerate; i ++)
|
||||||
|
grub_disk_cache_store (disk->dev->id, disk->id,
|
||||||
|
sector + (i << GRUB_DISK_CACHE_BITS),
|
||||||
|
(char *) buf
|
||||||
|
+ (i << (GRUB_DISK_CACHE_BITS
|
||||||
|
+ GRUB_DISK_SECTOR_BITS)));
|
||||||
|
|
||||||
|
sector += agglomerate << GRUB_DISK_CACHE_BITS;
|
||||||
|
size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
|
||||||
|
buf = (char *) buf
|
||||||
|
+ (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
|
||||||
|
}
|
||||||
|
|
||||||
/* Fetch the cache. */
|
|
||||||
data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector);
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
/* Just copy it! */
|
grub_memcpy (buf, data, GRUB_DISK_CACHE_SIZE);
|
||||||
grub_memcpy (buf, data + pos + real_offset, len);
|
sector += GRUB_DISK_CACHE_SIZE;
|
||||||
grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector);
|
buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
|
||||||
|
size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
|
||||||
|
grub_disk_cache_unlock (disk->dev->id, disk->id,
|
||||||
|
sector + (agglomerate
|
||||||
|
<< GRUB_DISK_CACHE_BITS));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Otherwise read data from the disk actually. */
|
|
||||||
if ((disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN
|
|
||||||
&& start_sector + GRUB_DISK_CACHE_SIZE
|
|
||||||
> (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
|
|
||||||
|| (disk->dev->read) (disk, transform_sector (disk, start_sector),
|
|
||||||
1 << (GRUB_DISK_CACHE_BITS
|
|
||||||
+ GRUB_DISK_SECTOR_BITS
|
|
||||||
- disk->log_sector_size), tmp_buf)
|
|
||||||
!= GRUB_ERR_NONE)
|
|
||||||
{
|
|
||||||
/* Uggh... Failed. Instead, just read necessary data. */
|
|
||||||
unsigned num;
|
|
||||||
char *p;
|
|
||||||
grub_disk_addr_t aligned_sector;
|
|
||||||
|
|
||||||
grub_errno = GRUB_ERR_NONE;
|
|
||||||
|
|
||||||
aligned_sector = (sector & ~((1 << (disk->log_sector_size
|
|
||||||
- GRUB_DISK_SECTOR_BITS))
|
|
||||||
- 1));
|
|
||||||
real_offset += ((sector - aligned_sector)
|
|
||||||
<< GRUB_DISK_SECTOR_BITS);
|
|
||||||
num = ((size + real_offset + (1 << (disk->log_sector_size))
|
|
||||||
- 1) >> (disk->log_sector_size));
|
|
||||||
|
|
||||||
p = grub_realloc (tmp_buf, num << disk->log_sector_size);
|
|
||||||
if (!p)
|
|
||||||
goto finish;
|
|
||||||
|
|
||||||
tmp_buf = p;
|
|
||||||
|
|
||||||
if ((disk->dev->read) (disk, transform_sector (disk,
|
|
||||||
aligned_sector),
|
|
||||||
num, tmp_buf))
|
|
||||||
{
|
|
||||||
grub_error_push ();
|
|
||||||
grub_dprintf ("disk", "%s read failed\n", disk->name);
|
|
||||||
grub_error_pop ();
|
|
||||||
goto finish;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_memcpy (buf, tmp_buf + real_offset, size);
|
/* And now read the last part. */
|
||||||
|
if (size)
|
||||||
/* Call the read hook, if any. */
|
|
||||||
if (disk->read_hook)
|
|
||||||
while (size)
|
|
||||||
{
|
{
|
||||||
grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size;
|
grub_err_t err;
|
||||||
(disk->read_hook) (sector, real_offset,
|
err = grub_disk_read_small (disk, sector, 0, size, buf);
|
||||||
to_read);
|
if (err)
|
||||||
if (grub_errno != GRUB_ERR_NONE)
|
return err;
|
||||||
goto finish;
|
|
||||||
|
|
||||||
sector++;
|
|
||||||
size -= to_read - real_offset;
|
|
||||||
real_offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This must be the end. */
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy it and store it in the disk cache. */
|
|
||||||
grub_memcpy (buf, tmp_buf + pos + real_offset, len);
|
|
||||||
grub_disk_cache_store (disk->dev->id, disk->id,
|
|
||||||
start_sector, tmp_buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call the read hook, if any. */
|
/* Call the read hook, if any. */
|
||||||
if (disk->read_hook)
|
if (disk->read_hook)
|
||||||
{
|
{
|
||||||
grub_disk_addr_t s = sector;
|
grub_disk_addr_t s = real_sector;
|
||||||
grub_size_t l = len;
|
grub_size_t l = real_size;
|
||||||
|
grub_off_t o = real_offset;
|
||||||
|
|
||||||
while (l)
|
while (l)
|
||||||
{
|
{
|
||||||
(disk->read_hook) (s, real_offset,
|
(disk->read_hook) (s, o,
|
||||||
((l > GRUB_DISK_SECTOR_SIZE)
|
((l > GRUB_DISK_SECTOR_SIZE)
|
||||||
? GRUB_DISK_SECTOR_SIZE
|
? GRUB_DISK_SECTOR_SIZE
|
||||||
: l));
|
: l));
|
||||||
|
|
||||||
if (l < GRUB_DISK_SECTOR_SIZE - real_offset)
|
|
||||||
break;
|
|
||||||
|
|
||||||
s++;
|
s++;
|
||||||
l -= GRUB_DISK_SECTOR_SIZE - real_offset;
|
l -= GRUB_DISK_SECTOR_SIZE - o;
|
||||||
real_offset = 0;
|
o = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sector = start_sector + GRUB_DISK_CACHE_SIZE;
|
|
||||||
buf = (char *) buf + len;
|
|
||||||
size -= len;
|
|
||||||
real_offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish:
|
|
||||||
|
|
||||||
grub_free (tmp_buf);
|
|
||||||
|
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue