Index: head/sys/conf/files =================================================================== --- head/sys/conf/files (revision 221981) +++ head/sys/conf/files (working copy) @@ -2133,6 +2133,7 @@ geom/eli/g_eli_privacy.c optional geom_eli geom/eli/pkcs5v2.c optional geom_eli geom/gate/g_gate.c optional geom_gate geom/geom_aes.c optional geom_aes +geom/geom_alias.c standard geom/geom_bsd.c optional geom_bsd geom/geom_bsd_enc.c optional geom_bsd geom/geom_ccd.c optional ccd | geom_ccd @@ -2165,7 +2166,6 @@ geom/label/g_label_msdosfs.c optional geom_label geom/label/g_label_ntfs.c optional geom_label geom/label/g_label_reiserfs.c optional geom_label geom/label/g_label_ufs.c optional geom_label -geom/label/g_label_gpt.c optional geom_label geom/linux_lvm/g_linux_lvm.c optional geom_linux_lvm geom/mirror/g_mirror.c optional geom_mirror geom/mirror/g_mirror_ctl.c optional geom_mirror Index: head/sys/modules/geom/geom_label/Makefile =================================================================== --- head/sys/modules/geom/geom_label/Makefile (revision 221981) +++ head/sys/modules/geom/geom_label/Makefile (working copy) @@ -5,7 +5,6 @@ KMOD= geom_label SRCS= g_label.c SRCS+= g_label_ext2fs.c -SRCS+= g_label_gpt.c SRCS+= g_label_iso9660.c SRCS+= g_label_msdosfs.c SRCS+= g_label_ntfs.c Index: head/sys/geom/label/g_label.c =================================================================== --- head/sys/geom/label/g_label.c (revision 221981) +++ head/sys/geom/label/g_label.c (working copy) @@ -85,8 +85,6 @@ const struct g_label_desc *g_labels[] = { &g_label_ext2fs, &g_label_reiserfs, &g_label_ntfs, - &g_label_gpt, - &g_label_gpt_uuid, NULL }; Index: head/sys/geom/label/g_label.h =================================================================== --- head/sys/geom/label/g_label.h (revision 221981) +++ head/sys/geom/label/g_label.h (working copy) @@ -85,8 +85,6 @@ extern struct g_label_desc g_label_msdosfs; extern struct g_label_desc g_label_ext2fs; extern struct g_label_desc g_label_reiserfs; extern struct g_label_desc g_label_ntfs; -extern struct g_label_desc g_label_gpt; -extern struct g_label_desc g_label_gpt_uuid; #endif /* _KERNEL */ struct g_label_metadata { Index: head/sys/geom/geom_int.h =================================================================== --- head/sys/geom/geom_int.h (revision 221981) +++ head/sys/geom/geom_int.h (working copy) @@ -66,6 +66,7 @@ void g_do_wither(void); extern struct class_list_head g_classes; extern char *g_wait_event, *g_wait_sim, *g_wait_up, *g_wait_down; int g_wither_washer(void); +void g_renew_provider(struct g_provider *pp); /* geom_io.c */ void g_io_init(void); Index: head/sys/geom/geom_alias.c =================================================================== --- head/sys/geom/geom_alias.c (revision 0) +++ head/sys/geom/geom_alias.c (revision 0) @@ -0,0 +1,249 @@ +/*- + * Copyright (c) 2011 Andrey V. Elsukov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#define G_ALIAS_PF_STALE 0x01 +#define G_ALIAS_CF_SPOILED 0x01 + +static g_ctl_destroy_geom_t g_alias_destroy_geom; +static g_access_t g_alias_access; +static g_orphan_t g_alias_orphan; +static g_spoiled_t g_alias_spoiled; +static g_start_t g_alias_start; +static g_taste_t g_alias_taste; + +static struct g_class g_alias_class = { + .name = "ALIAS", + .version = G_VERSION, + /* Class methods. */ + .destroy_geom = g_alias_destroy_geom, + /* Geom methods. */ + .access = g_alias_access, + .orphan = g_alias_orphan, + .spoiled = g_alias_spoiled, + .start = g_alias_start, + .taste = g_alias_taste +}; + +DECLARE_GEOM_CLASS(g_alias_class, g_alias); + +static int +g_alias_check_name(const char *name) +{ + const char *s; + + if (name == NULL || *name == '\0') + return (0); + /* Check if the label starts from ../ */ + if (strncmp(name, "../", 3) == 0) + return (0); + /* Check if the label contains /../ */ + if (strstr(name, "/../") != NULL) + return (0); + /* Check if the label ends at /.. */ + if ((s = strstr(name, "/..")) != NULL && s[3] == '\0') + return (0); + + return (1); +} + +void +g_alias_create(struct g_provider *pp, const char *name) +{ + struct g_geom *gp, *tgp; + struct g_provider *tpp; + struct g_consumer *cp; + int error; + + if (g_alias_check_name(name) == 0) + return; + + g_topology_assert(); + gp = NULL; + LIST_FOREACH(cp, &pp->consumers, consumers) { + if (cp->geom->class == &g_alias_class) { + gp = cp->geom; + break; + } + } + if (gp == NULL) { + gp = g_new_geomf(&g_alias_class, pp->name); + cp = g_new_consumer(gp); + error = g_attach(cp, pp); + if (error != 0) { + g_destroy_consumer(cp); + g_destroy_geom(gp); + return; + } + gp->flags |= G_GEOM_TASTER; + } + LIST_FOREACH(tgp, &g_alias_class.geom, geom) { + LIST_FOREACH(tpp, &tgp->provider, provider) { + /* Do not create new provider if we already have + * one with given name. + */ + if (strcmp(tpp->name, name) == 0 && + (tpp->flags & G_PF_ORPHAN) == 0) + return; + } + } + tpp = g_new_providerf(gp, name); + tpp->mediasize = pp->mediasize; + tpp->sectorsize = pp->sectorsize; + tpp->stripesize = pp->stripesize; + tpp->stripeoffset = pp->stripeoffset; + tpp->flags = pp->flags & G_PF_CANDELETE; + g_error_provider(tpp, 0); +} + + +void +g_alias_spoil(struct g_provider *pp) +{ + struct g_consumer *cp; + + g_topology_assert(); + LIST_FOREACH(cp, &pp->consumers, consumers) { + if (cp->geom->class == &g_alias_class) + break; + } + if (cp == NULL) + return; + LIST_FOREACH(pp, &cp->geom->provider, provider) { + /* Destroy providers which are not in use, else + * mark them as stale. + */ + if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) + g_wither_provider(pp, ENXIO); + else + pp->index = G_ALIAS_PF_STALE; + } +} + +static int +g_alias_destroy_geom(struct gctl_req *req, struct g_class *mp, + struct g_geom *gp) +{ + + g_topology_assert(); + g_wither_geom(gp, ENXIO); + return (0); +} + +static int +g_alias_access(struct g_provider *pp, int dr, int dw, int de) +{ + struct g_consumer *cp; + int error; + + cp = LIST_FIRST(&pp->geom->consumer); + g_trace(G_T_ACCESS, "%s: pp %s:(%d,%d,%d), cp:(%d,%d,%d)", __func__, + pp->name, dr, dw, de, cp->acr, cp->acw, cp->ace); + + error = g_access(cp, dr, dw, de); + /* Destroy stale provider. */ + if (error == 0 && pp->acr == 0 && pp->acw == 0 && pp->ace == 0 && + pp->index == G_ALIAS_PF_STALE) + g_wither_provider(pp, ENXIO); + + return (error); +} + +static void +g_alias_orphan(struct g_consumer *cp) +{ + + g_trace(G_T_TOPOLOGY, "%s(%s)", __func__, cp->geom->name); + g_topology_assert(); + g_wither_geom(cp->geom, cp->provider->error); +} + +static void +g_alias_start(struct bio *bp) +{ + struct bio *bp2; + struct g_consumer *cp; + + bp2 = g_clone_bio(bp); + if (bp2 == NULL) { + g_io_deliver(bp, ENOMEM); + return; + } + cp = LIST_FIRST(&bp->bio_to->geom->consumer); + bp2->bio_done = g_std_done; + g_io_request(bp2, cp); +} + +static void +g_alias_spoiled(struct g_consumer *cp) +{ + struct g_provider *pp; + + g_trace(G_T_TOPOLOGY, "%s(%s)", __func__, cp->geom->name); + g_topology_assert(); + + /* Spoil our consumers. */ + LIST_FOREACH(pp, &cp->geom->provider, provider) { + if (pp->ace != 0 || pp->acw != 0) + continue; + g_spoil(pp, NULL); + } + cp->index = G_ALIAS_CF_SPOILED; +} + +static struct g_geom* +g_alias_taste(struct g_class *mp, struct g_provider *pp, int flags) +{ + struct g_consumer *cp; + + g_topology_assert(); + if (pp->geom->class == &g_alias_class) + return (NULL); + LIST_FOREACH(cp, &pp->consumers, consumers) { + if (cp->geom->class == &g_alias_class) + break; + } + if (cp == NULL || cp->index != G_ALIAS_CF_SPOILED) + return (NULL); + g_trace(G_T_TOPOLOGY, "%s: %s:%s", __func__, pp->geom->class->name, + pp->name); + cp->index = 0; + LIST_FOREACH(pp, &cp->geom->provider, provider) { + g_renew_provider(pp); + } + return (NULL); +} Property changes on: head/sys/geom/geom_alias.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: head/sys/geom/geom_disk.c =================================================================== --- head/sys/geom/geom_disk.c (revision 221981) +++ head/sys/geom/geom_disk.c (working copy) @@ -89,6 +89,9 @@ static struct g_class g_disk_class = { SYSCTL_DECL(_kern_geom); SYSCTL_NODE(_kern_geom, OID_AUTO, disk, CTLFLAG_RW, 0, "GEOM_DISK stuff"); +static int disk_ident_enable = 1; +TUNABLE_INT("kern.geom.disk.ident_enable", &disk_ident_enable); + static void g_disk_init(struct g_class *mp __unused) { @@ -423,6 +426,8 @@ g_disk_dumpconf(struct sbuf *sb, const char *inden } } +#define G_DISK_IDENT_DIR "diskident" + static void g_disk_create(void *arg, int flag) { @@ -430,7 +435,7 @@ g_disk_create(void *arg, int flag) struct g_provider *pp; struct disk *dp; struct g_disk_softc *sc; - char tmpstr[80]; + char tmpstr[DISK_IDENT_SIZE + SPECNAMELEN]; if (flag == EV_CANCEL) return; @@ -466,6 +471,11 @@ g_disk_create(void *arg, int flag) pp->private = sc; dp->d_geom = gp; g_error_provider(pp, 0); + if (disk_ident_enable != 0 && dp->d_ident[0] != '\0') { + snprintf(tmpstr, SPECNAMELEN, "%s/%s", G_DISK_IDENT_DIR, + dp->d_ident); + g_alias_create(pp, tmpstr); + } } static void Index: head/sys/geom/geom_subr.c =================================================================== --- head/sys/geom/geom_subr.c (revision 221981) +++ head/sys/geom/geom_subr.c (working copy) @@ -546,7 +546,7 @@ g_new_provider_event(void *arg, int flag) LIST_FOREACH(cp, &pp->consumers, consumers) if (cp->geom->class == mp) break; - if (cp != NULL) + if (cp != NULL && !(cp->geom->flags & G_GEOM_TASTER)) continue; mp->taste(mp, pp, 0); g_topology_assert(); @@ -592,6 +592,18 @@ g_new_providerf(struct g_geom *gp, const char *fmt } void +g_renew_provider(struct g_provider *pp) +{ + + g_topology_assert(); + G_VALID_PROVIDER(pp); + KASSERT(!(pp->geom->flags & G_GEOM_WITHER), + ("renew provider on WITHERing geom(%s) (class %s)", + pp->geom->name, pp->geom->class->name)); + g_post_event(g_new_provider_event, pp, M_WAITOK, pp, NULL); +} + +void g_error_provider(struct g_provider *pp, int error) { @@ -988,7 +1000,6 @@ g_spoil(struct g_provider *pp, struct g_consumer * g_topology_assert(); G_VALID_PROVIDER(pp); - G_VALID_CONSUMER(cp); LIST_FOREACH(cp2, &pp->consumers, consumers) { if (cp2 == cp) Index: head/sys/geom/part/g_part_pc98.c =================================================================== --- head/sys/geom/part/g_part_pc98.c (revision 221981) +++ head/sys/geom/part/g_part_pc98.c (working copy) @@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$"); FEATURE(geom_part_pc98, "GEOM partitioning class for PC-9800 disk partitions"); +static int pc98_volume_enable = 1; +TUNABLE_INT("kern.geom.part.pc98_volume_enable", &pc98_volume_enable); + #define SECSIZE 512 #define MENUSIZE 7168 #define BOOTSIZE 8192 @@ -85,6 +88,7 @@ static const char *g_part_pc98_type(struct g_part_ static int g_part_pc98_write(struct g_part_table *, struct g_consumer *); static int g_part_pc98_resize(struct g_part_table *, struct g_part_entry *, struct g_part_parms *); +static void g_part_pc98_labels(struct g_part_table *, struct g_part_entry *); static kobj_method_t g_part_pc98_methods[] = { KOBJMETHOD(g_part_add, g_part_pc98_add), @@ -93,6 +97,7 @@ static kobj_method_t g_part_pc98_methods[] = { KOBJMETHOD(g_part_destroy, g_part_pc98_destroy), KOBJMETHOD(g_part_dumpconf, g_part_pc98_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_pc98_dumpto), + KOBJMETHOD(g_part_labels, g_part_pc98_labels), KOBJMETHOD(g_part_modify, g_part_pc98_modify), KOBJMETHOD(g_part_resize, g_part_pc98_resize), KOBJMETHOD(g_part_name, g_part_pc98_name), @@ -606,3 +611,26 @@ g_part_pc98_write(struct g_part_table *basetable, error = g_write_data(cp, SECSIZE*2, table->menu, MENUSIZE); return (error); } + +#define G_PART_PC98_VOLUME_DIR "pc98" + +static void +g_part_pc98_labels(struct g_part_table *basetable, + struct g_part_entry *baseentry) +{ + struct g_part_pc98_entry *entry; + char name[sizeof(entry->ent.dp_name) + 1]; + struct sbuf *sb; + + entry = (struct g_part_pc98_entry *)baseentry; + if (entry->ent.dp_name[0] == '\0' || pc98_volume_enable == 0) + return; + sb = sbuf_new_auto(); + strncpy(name, entry->ent.dp_name, sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + sbuf_printf(sb, "%s/%s", G_PART_PC98_VOLUME_DIR, name); + sbuf_finish(sb); + g_alias_create(baseentry->gpe_pp, sbuf_data(sb)); + sbuf_delete(sb); +} + Index: head/sys/geom/part/g_part_if.m =================================================================== --- head/sys/geom/part/g_part_if.m (revision 221981) +++ head/sys/geom/part/g_part_if.m (working copy) @@ -71,6 +71,13 @@ CODE { { return (ENOSYS); } + + static void + default_labels(struct g_part_table *t __unused, + struct g_part_entry *e __unused) + { + + } }; # add() - scheme specific processing for the add verb. @@ -120,6 +127,12 @@ METHOD void fullname { const char *pfx; } DEFAULT default_fullname; +# labels() - create scheme specific parition labels. +METHOD void labels { + struct g_part_table *table; + struct g_part_entry *entry; +} DEFAULT default_labels; + # modify() - scheme specific processing for the modify verb. METHOD int modify { struct g_part_table *table; Index: head/sys/geom/part/g_part_gpt.c =================================================================== --- head/sys/geom/part/g_part_gpt.c (revision 221981) +++ head/sys/geom/part/g_part_gpt.c (working copy) @@ -50,6 +50,11 @@ __FBSDID("$FreeBSD$"); FEATURE(geom_part_gpt, "GEOM partitioning class for GPT partitions support"); +static int gpt_id_enable = 1; +static int gpt_volume_enable = 1; +TUNABLE_INT("kern.geom.part.gpt_volume_enable", &gpt_volume_enable); +TUNABLE_INT("kern.geom.part.gpt_id_enable", &gpt_id_enable); + CTASSERT(offsetof(struct gpt_hdr, padding) == 92); CTASSERT(sizeof(struct gpt_ent) == 128); @@ -111,6 +116,7 @@ static int g_part_gpt_write(struct g_part_table *, static int g_part_gpt_resize(struct g_part_table *, struct g_part_entry *, struct g_part_parms *); static int g_part_gpt_recover(struct g_part_table *); +static void g_part_gpt_labels(struct g_part_table *, struct g_part_entry *); static kobj_method_t g_part_gpt_methods[] = { KOBJMETHOD(g_part_add, g_part_gpt_add), @@ -119,6 +125,7 @@ static kobj_method_t g_part_gpt_methods[] = { KOBJMETHOD(g_part_destroy, g_part_gpt_destroy), KOBJMETHOD(g_part_dumpconf, g_part_gpt_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_gpt_dumpto), + KOBJMETHOD(g_part_labels, g_part_gpt_labels), KOBJMETHOD(g_part_modify, g_part_gpt_modify), KOBJMETHOD(g_part_resize, g_part_gpt_resize), KOBJMETHOD(g_part_name, g_part_gpt_name), @@ -1150,3 +1157,34 @@ g_gpt_utf8_to_utf16(const uint8_t *s8, uint16_t *s if (utfbytes != 0 && s16idx < s16len) s16[s16idx++] = htole16(0xfffd); } + +#define G_PART_GPT_VOLUME_DIR "gpt" +#define G_PART_GPT_ID_DIR "gptid" + +static void +g_part_gpt_labels(struct g_part_table *basetable, + struct g_part_entry *baseentry) +{ + struct g_part_gpt_entry *entry; + struct sbuf *sb; + + entry = (struct g_part_gpt_entry *)baseentry; + if (entry->ent.ent_name[0] != 0 && gpt_volume_enable != 0) { + sb = sbuf_new_auto(); + sbuf_printf(sb, "%s/", G_PART_GPT_VOLUME_DIR); + g_gpt_printf_utf16(sb, entry->ent.ent_name, + sizeof(entry->ent.ent_name) >> 1); + sbuf_finish(sb); + g_alias_create(baseentry->gpe_pp, sbuf_data(sb)); + sbuf_delete(sb); + } + if (gpt_id_enable != 0) { + sb = sbuf_new_auto(); + sbuf_printf(sb, "%s/", G_PART_GPT_ID_DIR); + sbuf_printf_uuid(sb, &entry->ent.ent_uuid); + sbuf_finish(sb); + g_alias_create(baseentry->gpe_pp, sbuf_data(sb)); + sbuf_delete(sb); + } +} + Index: head/sys/geom/part/g_part_apm.c =================================================================== --- head/sys/geom/part/g_part_apm.c (revision 221981) +++ head/sys/geom/part/g_part_apm.c (working copy) @@ -49,6 +49,9 @@ __FBSDID("$FreeBSD$"); FEATURE(geom_part_apm, "GEOM partitioning class for Apple-style partitions"); +static int apm_volume_enable = 1; +TUNABLE_INT("kern.geom.part.apm_volume_enable", &apm_volume_enable); + struct g_part_apm_table { struct g_part_table base; struct apm_ddr ddr; @@ -79,6 +82,7 @@ static const char *g_part_apm_type(struct g_part_t static int g_part_apm_write(struct g_part_table *, struct g_consumer *); static int g_part_apm_resize(struct g_part_table *, struct g_part_entry *, struct g_part_parms *); +static void g_part_apm_labels(struct g_part_table *, struct g_part_entry *); static kobj_method_t g_part_apm_methods[] = { KOBJMETHOD(g_part_add, g_part_apm_add), @@ -86,6 +90,7 @@ static kobj_method_t g_part_apm_methods[] = { KOBJMETHOD(g_part_destroy, g_part_apm_destroy), KOBJMETHOD(g_part_dumpconf, g_part_apm_dumpconf), KOBJMETHOD(g_part_dumpto, g_part_apm_dumpto), + KOBJMETHOD(g_part_labels, g_part_apm_labels), KOBJMETHOD(g_part_modify, g_part_apm_modify), KOBJMETHOD(g_part_resize, g_part_apm_resize), KOBJMETHOD(g_part_name, g_part_apm_name), @@ -561,3 +566,26 @@ g_part_apm_write(struct g_part_table *basetable, s return (0); } + +#define G_PART_APM_VOLUME_DIR "apm" + +static void +g_part_apm_labels(struct g_part_table *basetable, + struct g_part_entry *baseentry) +{ + struct g_part_apm_entry *entry; + char name[APM_ENT_NAMELEN + 1]; + struct sbuf *sb; + + entry = (struct g_part_apm_entry *)baseentry; + if (entry->ent.ent_name[0] == '\0' || apm_volume_enable == 0) + return; + sb = sbuf_new_auto(); + strncpy(name, entry->ent.ent_name, APM_ENT_NAMELEN); + name[APM_ENT_NAMELEN] = '\0'; + sbuf_printf(sb, "%s/%s", G_PART_APM_VOLUME_DIR, name); + sbuf_finish(sb); + g_alias_create(baseentry->gpe_pp, sbuf_data(sb)); + sbuf_delete(sb); +} + Index: head/sys/geom/part/g_part.c =================================================================== --- head/sys/geom/part/g_part.c (revision 221981) +++ head/sys/geom/part/g_part.c (working copy) @@ -350,6 +350,7 @@ g_part_new_provider(struct g_geom *gp, struct g_pa if (pp->stripesize > 0) entry->gpe_pp->stripeoffset %= pp->stripesize; g_error_provider(entry->gpe_pp, 0); + G_PART_LABELS(table, entry); } static struct g_geom* @@ -1100,6 +1101,10 @@ g_part_ctl_modify(struct gctl_req *req, struct g_p if (!entry->gpe_created) entry->gpe_modified = 1; + /* Update partition labels. */ + g_alias_spoil(entry->gpe_pp); + G_PART_LABELS(table, entry); + /* Provide feedback if so requested. */ if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { sb = sbuf_new_auto(); Index: head/sys/geom/geom.h =================================================================== --- head/sys/geom/geom.h (revision 221981) +++ head/sys/geom/geom.h (working copy) @@ -136,6 +136,7 @@ struct g_geom { unsigned flags; #define G_GEOM_WITHER 1 #define G_GEOM_VOLATILE_BIO 2 +#define G_GEOM_TASTER 4 }; /* @@ -214,6 +215,10 @@ struct g_classifier_hook { #define G_STATE_RESYNC 2 #define G_STATE_ACTIVE 3 +/* geom_alias.c */ +void g_alias_create(struct g_provider *pp, const char *name); +void g_alias_spoil(struct g_provider *pp); + /* geom_dev.c */ struct cdev; void g_dev_print(void);