From: Karel Zak Date: Mon, 18 Jan 2010 14:43:25 +0000 (+0100) Subject: libblkid: read() optimization for small devices X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1ca17f911a4a039941185077b285378129eafb09;p=util-linux libblkid: read() optimization for small devices - 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 Signed-off-by: Karel Zak --- diff --git a/shlibs/blkid/src/blkidP.h b/shlibs/blkid/src/blkidP.h index 20cf765a..4a3ae200 100644 --- a/shlibs/blkid/src/blkidP.h +++ b/shlibs/blkid/src/blkidP.h @@ -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); diff --git a/shlibs/blkid/src/probe.c b/shlibs/blkid/src/probe.c index 6aad7ab4..2d3cafec 100644 --- a/shlibs/blkid/src/probe.c +++ b/shlibs/blkid/src/probe.c @@ -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; diff --git a/shlibs/blkid/src/superblocks/vfat.c b/shlibs/blkid/src/superblocks/vfat.c index 7587f8dd..8ea6b087 100644 --- a/shlibs/blkid/src/superblocks/vfat.c +++ b/shlibs/blkid/src/superblocks/vfat.c @@ -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)