diff options
author | marcel <marcel@FreeBSD.org> | 2007-06-17 22:19:19 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2007-06-17 22:19:19 +0000 |
commit | 3455d229dacc36fe2bcdfa70b8392ace695dede3 (patch) | |
tree | 02ec688cd6aad10a407c74fd904b7109fee8e9eb /sys/geom/part | |
parent | 6711a4448272405991d7cf2b868158b0bacb8a9a (diff) | |
download | FreeBSD-src-3455d229dacc36fe2bcdfa70b8392ace695dede3.zip FreeBSD-src-3455d229dacc36fe2bcdfa70b8392ace695dede3.tar.gz |
Have gpart synthesize a disk geometry if the underlying provider
don't have it. Some partitioning schemes, as well as file systems,
operate on the geometry and without it such schemes (e.g. MBR)
and file systems (e.g. FAT) can't be created. This is useful for
memory disks.
Diffstat (limited to 'sys/geom/part')
-rw-r--r-- | sys/geom/part/g_part.c | 139 | ||||
-rw-r--r-- | sys/geom/part/g_part.h | 15 | ||||
-rw-r--r-- | sys/geom/part/g_part_mbr.c | 90 |
3 files changed, 189 insertions, 55 deletions
diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c index b79923e..9748d6f 100644 --- a/sys/geom/part/g_part.c +++ b/sys/geom/part/g_part.c @@ -136,6 +136,81 @@ g_part_alias_name(enum g_part_alias alias) return (NULL); } +void +g_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs, + u_int *bestheads) +{ + static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 }; + off_t chs, cylinders; + u_int heads; + int idx; + + *bestchs = 0; + *bestheads = 0; + for (idx = 0; candidate_heads[idx] != 0; idx++) { + heads = candidate_heads[idx]; + cylinders = blocks / heads / sectors; + if (cylinders < heads || cylinders < sectors) + break; + if (cylinders > 1023) + continue; + chs = cylinders * heads * sectors; + if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) { + *bestchs = chs; + *bestheads = heads; + } + } +} + +static void +g_part_geometry(struct g_part_table *table, struct g_consumer *cp, + off_t blocks) +{ + static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 }; + off_t chs, bestchs; + u_int heads, sectors; + int idx; + + if (g_getattr("GEOM::fwsectors", cp, §ors) != 0 || + sectors < 1 || sectors > 63 || + g_getattr("GEOM::fwheads", cp, &heads) != 0 || + heads < 1 || heads > 255) { + table->gpt_fixgeom = 0; + table->gpt_heads = 0; + table->gpt_sectors = 0; + bestchs = 0; + for (idx = 0; candidate_sectors[idx] != 0; idx++) { + sectors = candidate_sectors[idx]; + g_part_geometry_heads(blocks, sectors, &chs, &heads); + if (chs == 0) + continue; + /* + * Prefer a geometry with sectors > 1, but only if + * it doesn't bump down the numbver of heads to 1. + */ + if (chs > bestchs || (chs == bestchs && heads > 1 && + table->gpt_sectors == 1)) { + bestchs = chs; + table->gpt_heads = heads; + table->gpt_sectors = sectors; + } + } + /* + * If we didn't find a geometry at all, then the disk is + * too big. This means we can use the maximum number of + * heads and sectors. + */ + if (bestchs == 0) { + table->gpt_heads = 255; + table->gpt_sectors = 63; + } + } else { + table->gpt_fixgeom = 1; + table->gpt_heads = heads; + table->gpt_sectors = sectors; + } +} + struct g_part_entry * g_part_new_entry(struct g_part_table *table, int index, quad_t start, quad_t end) @@ -574,6 +649,12 @@ g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp) g_topology_unlock(); + /* Make sure the provider has media. */ + if (pp->mediasize == 0 || pp->sectorsize == 0) { + error = ENODEV; + goto fail; + } + /* Make sure we can nest and if so, determine our depth. */ error = g_getattr("PART::isleaf", cp, &attr); if (!error && attr) { @@ -583,6 +664,14 @@ g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp) error = g_getattr("PART::depth", cp, &attr); table->gpt_depth = (!error) ? attr + 1 : 0; + /* + * Synthesize a disk geometry. Some partitioning schemes + * depend on it and since some file systems need it even + * when the partitition scheme doesn't, we do it here in + * scheme-independent code. + */ + g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); + error = G_PART_CREATE(table, gpp); if (error) goto fail; @@ -1217,6 +1306,15 @@ g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) g_topology_unlock(); + /* + * Short-circuit the whole probing galore when there's no + * media present. + */ + if (pp->mediasize == 0 || pp->sectorsize == 0) { + error = ENODEV; + goto fail; + } + /* Make sure we can nest and if so, determine our depth. */ error = g_getattr("PART::isleaf", cp, &attr); if (!error && attr) { @@ -1231,6 +1329,15 @@ g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) goto fail; table = gp->softc; + + /* + * Synthesize a disk geometry. Some partitioning schemes + * depend on it and since some file systems need it even + * when the partitition scheme doesn't, we do it here in + * scheme-independent code. + */ + g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); + error = G_PART_READ(table, cp); if (error) goto fail; @@ -1311,6 +1418,10 @@ g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, (uintmax_t)table->gpt_first); sbuf_printf(sb, "%s<last>%ju</last>\n", indent, (uintmax_t)table->gpt_last); + sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent, + table->gpt_sectors); + sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent, + table->gpt_heads); G_PART_DUMPCONF(table, NULL, sb, indent); } } @@ -1349,7 +1460,6 @@ g_part_start(struct bio *bp) struct g_part_table *table; struct g_kerneldump *gkd; struct g_provider *pp; - int attr; pp = bp->bio_to; gp = pp->geom; @@ -1387,6 +1497,14 @@ g_part_start(struct bio *bp) case BIO_FLUSH: break; case BIO_GETATTR: + if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads)) + return; + if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors)) + return; + if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf)) + return; + if (g_handleattr_int(bp, "PART::depth", table->gpt_depth)) + return; if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) { /* * Check that the partition is suitable for kernel @@ -1405,25 +1523,6 @@ g_part_start(struct bio *bp) if (gkd->offset + gkd->length > pp->mediasize) gkd->length = pp->mediasize - gkd->offset; gkd->offset += entry->gpe_offset; - } else if (!strcmp("PART::isleaf", bp->bio_attribute)) { - if (bp->bio_length != sizeof(int)) { - g_io_deliver(bp, EFAULT); - return; - } - attr = table->gpt_isleaf ? 1 : 0; - bcopy(&attr, bp->bio_data, sizeof(int)); - bp->bio_completed = sizeof(int); - g_io_deliver(bp, 0); - return; - } else if (!strcmp("PART::depth", bp->bio_attribute)) { - if (bp->bio_length != sizeof(int)) { - g_io_deliver(bp, EFAULT); - return; - } - bcopy(&table->gpt_depth, bp->bio_data, sizeof(int)); - bp->bio_completed = sizeof(int); - g_io_deliver(bp, 0); - return; } break; default: diff --git a/sys/geom/part/g_part.h b/sys/geom/part/g_part.h index 8c6bbd5..08be641 100644 --- a/sys/geom/part/g_part.h +++ b/sys/geom/part/g_part.h @@ -87,12 +87,25 @@ struct g_part_table { */ uint32_t gpt_smhead; uint32_t gpt_smtail; + /* + * gpt_sectors and gpt_heads are the fixed or synchesized number + * of sectors per track and heads (resp) that make up a disks + * geometry. This is to support partitioning schemes as well as + * file systems that work on a geometry. The MBR scheme and the + * MS-DOS (FAT) file system come to mind. + * We keep track of whether the geometry is fixed or synchesized + * so that a partitioning scheme can correct the synthesized + * geometry, based on the on-disk metadata. + */ + uint32_t gpt_sectors; + uint32_t gpt_heads; int gpt_depth; /* Sub-partitioning level. */ int gpt_isleaf:1; /* Cannot be sub-partitioned. */ int gpt_created:1; /* Newly created. */ int gpt_modified:1; /* Table changes have been made. */ int gpt_opened:1; /* Permissions obtained. */ + int gpt_fixgeom:1; /* Geometry is fixed. */ }; struct g_part_entry *g_part_new_entry(struct g_part_table *, int, quad_t, @@ -127,4 +140,6 @@ struct g_part_parms { unsigned int gpp_version; }; +void g_part_geometry_heads(off_t, u_int, off_t *, u_int *); + #endif /* !_GEOM_PART_H_ */ diff --git a/sys/geom/part/g_part_mbr.c b/sys/geom/part/g_part_mbr.c index 4a0d9e5..b096b33 100644 --- a/sys/geom/part/g_part_mbr.c +++ b/sys/geom/part/g_part_mbr.c @@ -50,7 +50,6 @@ __FBSDID("$FreeBSD$"); struct g_part_mbr_table { struct g_part_table base; u_char mbr[MBRSIZE]; - int spt; /* Sectors/track. */ }; struct g_part_mbr_entry { @@ -119,31 +118,52 @@ mbr_parse_type(const char *type, u_char *dp_typ) return (EINVAL); } +static void +mbr_set_chs(struct g_part_table *table, uint32_t lba, u_char *cylp, u_char *hdp, + u_char *secp) +{ + uint32_t cyl, hd, sec; + + sec = lba % table->gpt_sectors + 1; + lba /= table->gpt_sectors; + hd = lba % table->gpt_heads; + lba /= table->gpt_heads; + cyl = lba; + if (cyl > 1023) + sec = hd = cyl = ~0; + + *cylp = cyl & 0xff; + *hdp = hd & 0xff; + *secp = (sec & 0x3f) | ((cyl >> 2) & 0xc0); +} + static int g_part_mbr_add(struct g_part_table *basetable, struct g_part_entry *baseentry, struct g_part_parms *gpp) { struct g_part_mbr_entry *entry; struct g_part_mbr_table *table; - uint32_t start, size; + uint32_t start, size, sectors; if (gpp->gpp_parms & G_PART_PARM_LABEL) return (EINVAL); + sectors = basetable->gpt_sectors; + entry = (struct g_part_mbr_entry *)baseentry; table = (struct g_part_mbr_table *)basetable; start = gpp->gpp_start; size = gpp->gpp_size; - if (size < table->spt) + if (size < sectors) return (EINVAL); - if (start % table->spt) { - size = size - table->spt + (start % table->spt); - start = start - (start % table->spt) + table->spt; + if (start % sectors) { + size = size - sectors + (start % sectors); + start = start - (start % sectors) + sectors; } - if (size % table->spt) - size = size - (size % table->spt); - if (size < table->spt) + if (size % sectors) + size = size - (size % sectors); + if (size < sectors) return (EINVAL); if (baseentry->gpe_deleted) @@ -155,6 +175,10 @@ g_part_mbr_add(struct g_part_table *basetable, struct g_part_entry *baseentry, baseentry->gpe_end = start + size - 1; entry->ent.dp_start = start; entry->ent.dp_size = size; + mbr_set_chs(basetable, baseentry->gpe_start, &entry->ent.dp_scyl, + &entry->ent.dp_shd, &entry->ent.dp_ssect); + mbr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, + &entry->ent.dp_ehd, &entry->ent.dp_esect); return (mbr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); } @@ -165,27 +189,19 @@ g_part_mbr_create(struct g_part_table *basetable, struct g_part_parms *gpp) struct g_provider *pp; struct g_part_mbr_table *table; uint64_t msize; - int error, spt; pp = gpp->gpp_provider; cp = LIST_FIRST(&pp->consumers); - error = g_getattr("GEOM::fwsectors", cp, &spt); - if (error) - spt = 17; - else if (spt == 0) - spt = 1; - - if (pp->sectorsize < MBRSIZE || pp->mediasize < spt * pp->sectorsize) + if (pp->sectorsize < MBRSIZE) return (ENOSPC); msize = pp->mediasize / pp->sectorsize; - basetable->gpt_first = spt; - basetable->gpt_last = msize - (msize % spt) - 1; + basetable->gpt_first = basetable->gpt_sectors; + basetable->gpt_last = msize - (msize % basetable->gpt_sectors) - 1; table = (struct g_part_mbr_table *)basetable; le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); - table->spt = spt; return (0); } @@ -262,23 +278,13 @@ g_part_mbr_read(struct g_part_table *basetable, struct g_consumer *cp) struct g_part_mbr_table *table; struct g_part_mbr_entry *entry; u_char *buf, *p; - uint64_t msize; - int error, index, spt; - - error = g_getattr("GEOM::fwsectors", cp, &spt); - if (error) - spt = 17; - else if (spt == 0) - spt = 1; + off_t chs, msize; + u_int sectors, heads; + int error, index; pp = cp->provider; table = (struct g_part_mbr_table *)basetable; - table->spt = spt; - msize = pp->mediasize / pp->sectorsize; - basetable->gpt_first = spt; - basetable->gpt_last = msize - (msize % spt) - 1; - basetable->gpt_entries = NDOSPART; buf = g_read_data(cp, 0L, pp->sectorsize, &error); if (buf == NULL) @@ -303,10 +309,19 @@ g_part_mbr_read(struct g_part_table *basetable, struct g_consumer *cp) continue; if (ent.dp_start == 0 || ent.dp_size == 0) continue; - if ((ent.dp_start % spt) != 0) + sectors = ent.dp_esect & 0x3f; + if (sectors > basetable->gpt_sectors && + !basetable->gpt_fixgeom) { + g_part_geometry_heads(msize, sectors, &chs, &heads); + if (chs != 0) { + basetable->gpt_sectors = sectors; + basetable->gpt_heads = heads; + } + } + if ((ent.dp_start % basetable->gpt_sectors) != 0) printf("GEOM: %s: partition %d does not start on a " "track boundary.\n", pp->name, index + 1); - if ((ent.dp_size % spt) != 0) + if ((ent.dp_size % basetable->gpt_sectors) != 0) printf("GEOM: %s: partition %d does not end on a " "track boundary.\n", pp->name, index + 1); @@ -314,6 +329,11 @@ g_part_mbr_read(struct g_part_table *basetable, struct g_consumer *cp) index + 1, ent.dp_start, ent.dp_start + ent.dp_size - 1); entry->ent = ent; } + + basetable->gpt_entries = NDOSPART; + basetable->gpt_first = basetable->gpt_sectors; + basetable->gpt_last = msize - (msize % basetable->gpt_sectors) - 1; + return (0); } |