]> err.no Git - util-linux/commitdiff
libblkid: read() optimization for small devices
authorKarel Zak <kzak@redhat.com>
Mon, 18 Jan 2010 14:43:25 +0000 (15:43 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 16 Feb 2010 08:58:56 +0000 (09:58 +0100)
 - don't read the begin (69kB) of the device by one large read()
 - fill in the SB buffer dynamically
 - use extra buffer for FATs root dir entries (FAT FS label)
   on small devices to avoid large reads

Detect FAT12 on 1.4MB device (number of bytes):

Old version:

  $ strace -e read blkid -p floppy.img 2>&1 | \
awk -F ' = ' 'BEGIN {x=0} /read/ && !/.*ELF/ {x += $2} END { print x }'
  73292

New version:
  $ strace -e read blkid -p floppy.img 2>&1 | \
awk -F ' = ' 'BEGIN {x=0} /read/ && !/.*ELF/ {x += $2} END { print x }'
  8192

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/blkid/src/blkidP.h
shlibs/blkid/src/probe.c
shlibs/blkid/src/superblocks/vfat.c

index 20cf765ab313db43697841860ec4cfe0a862c829..4a3ae200b107f457ff9ffd4d26ecb0c875e67e74 100644 (file)
@@ -358,9 +358,10 @@ extern void blkid_free_dev(blkid_dev dev);
 
 /* probe.c */
 extern int blkid_probe_is_tiny(blkid_probe pr);
-
 extern unsigned char *blkid_probe_get_buffer(blkid_probe pr,
                                 blkid_loff_t off, blkid_loff_t len);
+extern unsigned char *blkid_probe_get_extra_buffer(blkid_probe pr,
+                                blkid_loff_t off, blkid_loff_t len);
 
 extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector);
 
index 6aad7ab458233e35ae2f3aec4ea49b1fa42105b5..2d3cafec4aa74682fea6dad659e2a6229fcd9aa3 100644 (file)
@@ -470,6 +470,102 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
        return 0;
 }
 
+static int blkid_probe_has_buffer(blkid_probe pr,
+                               blkid_loff_t off, blkid_loff_t len)
+{
+       return pr && (off + len <= pr->sbbuf_len ||
+                    (pr->buf_off < off && off + len < pr->buf_len));
+}
+
+/*
+ * Returns buffer from the begin (69kB) of the device.
+ */
+static unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr,
+                               blkid_loff_t off, blkid_loff_t len)
+{
+       if (off + len > BLKID_SB_BUFSIZ)
+               return NULL;
+       if (!pr->sbbuf) {
+               pr->sbbuf = malloc(BLKID_SB_BUFSIZ);
+               if (!pr->sbbuf)
+                       return NULL;
+       }
+       if (off + len > pr->sbbuf_len) {
+               /*
+                * The sbbuf is not completely in memory.
+                *
+                * We don't read whole BLKID_SB_BUFSIZ by one read(), it's too
+                * aggresive to small devices (floppies). We read necessary
+                * data to complete the current request (off + len) only.
+                */
+               ssize_t ret_read;
+
+               blkid_loff_t have = pr->sbbuf_len,
+                            want = off + len - have;
+
+               DBG(DEBUG_LOWPROBE,
+                       printf("\tsb-buffer read() off=%jd len=%jd\n", have, want));
+
+               if (lseek(pr->fd, pr->off + have, SEEK_SET) < 0)
+                       return NULL;
+
+               ret_read = read(pr->fd, pr->sbbuf + have, want);
+               if (ret_read < 0)
+                       ret_read = 0;
+               pr->sbbuf_len = have + ret_read;
+       }
+       if (off + len > pr->sbbuf_len)
+               return NULL;
+       return pr->sbbuf + off;
+}
+
+/*
+ * Returns pointer to the buffer on arbitrary offset on the device
+ */
+unsigned char *blkid_probe_get_extra_buffer(blkid_probe pr,
+                               blkid_loff_t off, blkid_loff_t len)
+{
+       unsigned char *newbuf = NULL;
+
+       if (off + len <= BLKID_SB_BUFSIZ &&
+           (!blkid_probe_is_tiny(pr) || blkid_probe_has_buffer(pr, off, len)))
+               /*
+                * Don't use extra buffer for superblock data if
+                *      - data are already in SB buffer
+                *      - or the device is large and we needn't extra
+                *        optimalization for tiny devices
+                */
+               return blkid_probe_get_sb_buffer(pr, off, len);
+
+       if (len > pr->buf_max) {
+               newbuf = realloc(pr->buf, len);
+               if (!newbuf)
+                       return NULL;
+               pr->buf = newbuf;
+               pr->buf_max = len;
+               pr->buf_off = 0;
+               pr->buf_len = 0;
+       }
+       if (newbuf || off < pr->buf_off ||
+           off + len > pr->buf_off + pr->buf_len) {
+               ssize_t ret_read;
+
+               if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
+                       return NULL;
+
+               DBG(DEBUG_LOWPROBE,
+                       printf("\textra-buffer read: off=%jd len=%jd\n", off, len));
+
+               ret_read = read(pr->fd, pr->buf, len);
+               if (ret_read != (ssize_t) len)
+                       return NULL;
+               pr->buf_off = off;
+               pr->buf_len = len;
+       }
+       return off ? pr->buf + (off - pr->buf_off) : pr->buf;
+}
+
+
 /*
  * @off: offset within probing area
  * @len: size of requested buffer
@@ -493,62 +589,16 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
 unsigned char *blkid_probe_get_buffer(blkid_probe pr,
                                blkid_loff_t off, blkid_loff_t len)
 {
-       ssize_t ret_read = 0;
-
        if (off < 0 || len < 0) {
                DBG(DEBUG_LOWPROBE,
                        printf("unexpected offset or length of buffer requested\n"));
                return NULL;
        }
-
        if (off + len > pr->size)
                return NULL;
-
-       DBG(DEBUG_LOWPROBE,
-               printf("\tbuffer: offset=%jd size=%jd\n", off, len));
-
-       if (off + len <= BLKID_SB_BUFSIZ) {
-               if (!pr->sbbuf) {
-                       pr->sbbuf = malloc(BLKID_SB_BUFSIZ);
-                       if (!pr->sbbuf)
-                               return NULL;
-               }
-               if (!pr->sbbuf_len) {
-                       if (lseek(pr->fd, pr->off, SEEK_SET) < 0)
-                               return NULL;
-                       ret_read = read(pr->fd, pr->sbbuf, BLKID_SB_BUFSIZ);
-                       if (ret_read < 0)
-                               ret_read = 0;
-                       pr->sbbuf_len = ret_read;
-               }
-               if (off + len > pr->sbbuf_len)
-                       return NULL;
-               return pr->sbbuf + off;
-       } else {
-               unsigned char *newbuf = NULL;
-
-               if (len > pr->buf_max) {
-                       newbuf = realloc(pr->buf, len);
-                       if (!newbuf)
-                               return NULL;
-                       pr->buf = newbuf;
-                       pr->buf_max = len;
-                       pr->buf_off = 0;
-                       pr->buf_len = 0;
-               }
-               if (newbuf || off < pr->buf_off ||
-                   off + len > pr->buf_off + pr->buf_len) {
-                       if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
-                               return NULL;
-
-                       ret_read = read(pr->fd, pr->buf, len);
-                       if (ret_read != (ssize_t) len)
-                               return NULL;
-                       pr->buf_off = off;
-                       pr->buf_len = len;
-               }
-               return off ? pr->buf + (off - pr->buf_off) : pr->buf;
-       }
+       if (off + len <= BLKID_SB_BUFSIZ)
+               return blkid_probe_get_sb_buffer(pr, off, len);
+       return blkid_probe_get_extra_buffer(pr, off, len);
 }
 
 /*
@@ -620,7 +670,6 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
 
        if (!pr->size)
                goto err;
-
        DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%zd, size=%zd\n",
                                pr->off, pr->size));
        return 0;
index 7587f8dda62a5c44e54a10f13b5b4d6f66b1a51c..8ea6b08751d8490877740bcf609c8440119ce979 100644 (file)
@@ -248,8 +248,9 @@ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
                        (vs->vs_dir_entries[1] << 8);
 
                buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
+
                dir = (struct vfat_dir_entry *)
-                       blkid_probe_get_buffer(pr, root_start, buf_size);
+                       blkid_probe_get_extra_buffer(pr, root_start, buf_size);
                if (dir)
                        vol_label = search_fat_label(dir, root_dir_entries);
 
@@ -285,7 +286,7 @@ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
                                sector_size;
 
                        dir = (struct vfat_dir_entry *)
-                               blkid_probe_get_buffer(pr, next_off, buf_size);
+                               blkid_probe_get_extra_buffer(pr, next_off, buf_size);
                        if (dir == NULL)
                                break;
 
@@ -298,7 +299,7 @@ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
                        /* get FAT entry */
                        fat_entry_off = (reserved * sector_size) +
                                (next * sizeof(uint32_t));
-                       buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
+                       buf = blkid_probe_get_extra_buffer(pr, fat_entry_off, buf_size);
                        if (buf == NULL)
                                break;
 
@@ -319,7 +320,7 @@ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
                if (fsinfo_sect) {
                        struct fat32_fsinfo *fsinfo;
 
-                       buf = blkid_probe_get_buffer(pr,
+                       buf = blkid_probe_get_extra_buffer(pr,
                                        fsinfo_sect * sector_size,
                                        sizeof(struct fat32_fsinfo));
                        if (buf == NULL)