From nobody Wed Feb 23 19:06:40 2022 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 5FBEF19D3A27; Wed, 23 Feb 2022 19:06:52 +0000 (UTC) (envelope-from rew@freebsd.org) Received: from smtp.freebsd.org (smtp.freebsd.org [IPv6:2610:1c1:1:606c::24b:4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "smtp.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4K3lrr27L7z3mX9; Wed, 23 Feb 2022 19:06:52 +0000 (UTC) (envelope-from rew@freebsd.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1645643212; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=n7bmUvbtVEEdm1MELWCpJaViNoLywjyAQR8TsVnybrg=; b=kRBp9+zVRKOYpTrtfKCW3yiuuxhEK5QQYCcl+JfWx4VUiqmENdC8BniH3F8AiM/Fui/Zga lMFMkopchEqgFWtLdjaIXXEW4UJ5F4BGbqXVa7Vj9xP/wLVr6wuAEz+Cl3M5F+X1dKHU1l pHptdMXXDYS8BuNPgsnKFfrbsgW0Y5/IpFVgyc+hzSVaoWvPiYof+MWpUIVAve7t8BKFsJ vI7+JmkS6NnBDN80jV5TvROstNNO2+eqLcF+v/EQhDyu7h7gJ28rFDWE56NPHi9lUN7KHH 2mGFM0cIQaz2YcOk9SiI7koxZLL0RqSO7WNFXDm63YyViJJbO34cxffvlCCS8w== Received: from mail-ed1-f45.google.com (mail-ed1-f45.google.com [209.85.208.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "GTS CA 1D4" (verified OK)) (Authenticated sender: rew) by smtp.freebsd.org (Postfix) with ESMTPSA id 230502FECC; Wed, 23 Feb 2022 19:06:52 +0000 (UTC) (envelope-from rew@freebsd.org) Received: by mail-ed1-f45.google.com with SMTP id s1so12659976edd.13; Wed, 23 Feb 2022 11:06:52 -0800 (PST) X-Gm-Message-State: AOAM531vhbeh09+aYk8rusphaZORpiuiuuI17C1oUUP/Gfh0Ly2ExTZR JbU4o/CTjthytb8JUlyrb/UXDJKQYp07Z8mnq3w= X-Google-Smtp-Source: ABdhPJxmFGQkPeQl948M4lpdBosAGHH72z0eUenVaC+P+YUrsTLwNvc3UNtfNNYxxu5Zd+6RX8gpO1QVxiIJFeaEsBU= X-Received: by 2002:a05:6402:1d50:b0:413:d20:8ec7 with SMTP id dz16-20020a0564021d5000b004130d208ec7mr874478edb.334.1645643210965; Wed, 23 Feb 2022 11:06:50 -0800 (PST) List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 References: <202202231742.21NHg5PD085708@gitrepo.freebsd.org> In-Reply-To: <202202231742.21NHg5PD085708@gitrepo.freebsd.org> From: Rob Wing Date: Wed, 23 Feb 2022 10:06:40 -0900 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: git: a01bf3940271 - stable/13 - geom: add kqfilter support for geom dev To: src-committers@freebsd.org, dev-commits-src-all@freebsd.org, dev-commits-src-branches@freebsd.org Content-Type: multipart/alternative; boundary="0000000000000448cb05d8b42b12" ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1645643212; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=n7bmUvbtVEEdm1MELWCpJaViNoLywjyAQR8TsVnybrg=; b=gl+RHeFN+dzlBqDWiIDM6+uWW47hs2tuWb/PUP6SazEq/3BTjyzRMa1ByyjeGmtbyGjLPC EIVSUUzSohukAgH8mp37pnikVs8ALpbg+gv+fKZQ3rFHTuOzj+X5weu5dAaIruCsyMDVjk LjqftGTl2Q6mh+OIJtfF4rrAKALGn8KO3DyC6EQFI0ZSwapFVkoJDUZ9NsyqMcqXxe8Ou4 oh0jX2RM8ZERd5SPr4ljTtu7R3KhKrgteOP3WJfJdsEidRLp/H8GJWl5yRiAQNkH1QZ3O3 Rc8TCOehBtCt74j5Jqz2VZ33qTsL4Kk3hfR3CSrv7S4YWtKsMtJeQWbboA8Xag== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1645643212; a=rsa-sha256; cv=none; b=sbMk+5M72hRmaS7RczNbS5+q2IyxUrR3DkyJ/XjnXXzG+Jgyy02+axnq0ezUOhA3+jdQ65 Y3TPjDow0WaJdAOb63PZSJdy7Bp5zq3EF+sxscEB//cC6ZIYE7HH9akiKOq0RmSsVCovL9 U+k51Kc2CKNAOmfn5wsPImhEONFYCf/8FCWVpBxlHONeOIAZgBaVdC8UkANWi0W2i2Umny MNCrUOleB4BjVHY1VaRZ5JboqV5q4MqKQgKlmN36D5CPia9YA6I/c0bgRp9qJve29tTa6Z dglBc7KEBDOUVzHzDQasG3oxc5Cyq4IJ7pwzwfCDYoFMBTOk2Yw5zn7XmxM9gA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N --0000000000000448cb05d8b42b12 Content-Type: text/plain; charset="UTF-8" I forgot to pass the -x flag to git cherry pick on this one. This commit was cherry picked from a50e92cc203c1e3dd2cca03410b568f76694895c On Wed, Feb 23, 2022 at 8:42 AM Robert Wing wrote: > The branch stable/13 has been updated by rew: > > URL: > https://cgit.FreeBSD.org/src/commit/?id=a01bf3940271fa118d6fa306d80f44fc534f58b5 > > commit a01bf3940271fa118d6fa306d80f44fc534f58b5 > Author: Robert Wing > AuthorDate: 2022-01-18 19:54:59 +0000 > Commit: Robert Wing > CommitDate: 2022-02-23 17:26:49 +0000 > > geom: add kqfilter support for geom dev > > The only event hooked up is NOTE_ATTRIB, which is triggered when the > device is resized. Support for other NOTE_* events to follow. > > Reviewed by: kib, jhb > Differential Revision: https://reviews.freebsd.org/D33402 > --- > sys/geom/geom_dev.c | 59 > +++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 59 insertions(+) > > diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c > index 24ab14e90c1b..b94df9fcda67 100644 > --- a/sys/geom/geom_dev.c > +++ b/sys/geom/geom_dev.c > @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); > #include > #include > #include > +#include > #include > #include > #include > @@ -65,6 +66,7 @@ struct g_dev_softc { > struct cdev *sc_alias; > int sc_open; > u_int sc_active; > + struct selinfo sc_selinfo; > #define SC_A_DESTROY (1 << 31) > #define SC_A_OPEN (1 << 30) > #define SC_A_ACTIVE (SC_A_OPEN - 1) > @@ -74,6 +76,16 @@ static d_open_t g_dev_open; > static d_close_t g_dev_close; > static d_strategy_t g_dev_strategy; > static d_ioctl_t g_dev_ioctl; > +static d_kqfilter_t g_dev_kqfilter; > + > +static void gdev_filter_detach(struct knote *kn); > +static int gdev_filter_vnode(struct knote *kn, long hint); > + > +static struct filterops gdev_filterops_vnode = { > + .f_isfd = 1, > + .f_detach = gdev_filter_detach, > + .f_event = gdev_filter_vnode, > +}; > > static struct cdevsw g_dev_cdevsw = { > .d_version = D_VERSION, > @@ -85,6 +97,7 @@ static struct cdevsw g_dev_cdevsw = { > .d_strategy = g_dev_strategy, > .d_name = "g_dev", > .d_flags = D_DISK | D_TRACKCLOSE, > + .d_kqfilter = g_dev_kqfilter, > }; > > static g_init_t g_dev_init; > @@ -214,6 +227,8 @@ g_dev_destroy(void *arg, int flags __unused) > g_trace(G_T_TOPOLOGY, "g_dev_destroy(%p(%s))", cp, gp->name); > snprintf(buf, sizeof(buf), "cdev=%s", gp->name); > devctl_notify("GEOM", "DEV", "DESTROY", buf); > + knlist_clear(&sc->sc_selinfo.si_note, 0); > + knlist_destroy(&sc->sc_selinfo.si_note); > if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) > g_access(cp, -cp->acr, -cp->acw, -cp->ace); > g_detach(cp); > @@ -305,8 +320,12 @@ g_dev_attrchanged(struct g_consumer *cp, const char > *attr) > static void > g_dev_resize(struct g_consumer *cp) > { > + struct g_dev_softc *sc; > char buf[SPECNAMELEN + 6]; > > + sc = cp->private; > + KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, NOTE_ATTRIB); > + > snprintf(buf, sizeof(buf), "cdev=%s", cp->provider->name); > devctl_notify("GEOM", "DEV", "SIZECHANGE", buf); > } > @@ -378,6 +397,7 @@ g_dev_taste(struct g_class *mp, struct g_provider *pp, > int insist __unused) > dev = sc->sc_dev; > dev->si_flags |= SI_UNMAPPED; > dev->si_iosize_max = maxphys; > + knlist_init_mtx(&sc->sc_selinfo.si_note, &sc->sc_mtx); > error = init_dumpdev(dev); > if (error != 0) > printf("%s: init_dumpdev() failed (gp->name=%s, > error=%d)\n", > @@ -883,4 +903,43 @@ g_dev_orphan(struct g_consumer *cp) > destroy_dev_sched_cb(dev, g_dev_callback, cp); > } > > +static void > +gdev_filter_detach(struct knote *kn) > +{ > + struct g_dev_softc *sc; > + > + sc = kn->kn_hook; > + > + knlist_remove(&sc->sc_selinfo.si_note, kn, 0); > +} > + > +static int > +gdev_filter_vnode(struct knote *kn, long hint) > +{ > + kn->kn_fflags |= kn->kn_sfflags & hint; > + > + return (kn->kn_fflags != 0); > +} > + > +static int > +g_dev_kqfilter(struct cdev *dev, struct knote *kn) > +{ > + struct g_dev_softc *sc; > + > + sc = dev->si_drv1; > + > + if (kn->kn_filter != EVFILT_VNODE) > + return (EINVAL); > + > + /* XXX: extend support for other NOTE_* events */ > + if (kn->kn_sfflags != NOTE_ATTRIB) > + return (EINVAL); > + > + kn->kn_fop = &gdev_filterops_vnode; > + kn->kn_hook = sc; > + knlist_add(&sc->sc_selinfo.si_note, kn, 0); > + > + return (0); > +} > + > DECLARE_GEOM_CLASS(g_dev_class, g_dev); > --0000000000000448cb05d8b42b12 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I forgot to pass the -x flag to git cherry pick on th= is one.

This commit was cherry picked from a50e92c= c203c1e3dd2cca03410b568f76694895c

On Wed, Feb 23, 2022 at 8:42 AM Robe= rt Wing <rew@freebsd.org> wrot= e:
The branch st= able/13 has been updated by rew:

URL: https://cgit.= FreeBSD.org/src/commit/?id=3Da01bf3940271fa118d6fa306d80f44fc534f58b5
commit a01bf3940271fa118d6fa306d80f44fc534f58b5
Author:=C2=A0 =C2=A0 =C2=A0Robert Wing <rew@FreeBSD.org>
AuthorDate: 2022-01-18 19:54:59 +0000
Commit:=C2=A0 =C2=A0 =C2=A0Robert Wing <rew@FreeBSD.org>
CommitDate: 2022-02-23 17:26:49 +0000

=C2=A0 =C2=A0 geom: add kqfilter support for geom dev

=C2=A0 =C2=A0 The only event hooked up is NOTE_ATTRIB, which is triggered w= hen the
=C2=A0 =C2=A0 device is resized. Support for other NOTE_* events to follow.=

=C2=A0 =C2=A0 Reviewed by:=C2=A0 =C2=A0 kib, jhb
=C2=A0 =C2=A0 Differential Revision:=C2=A0 https://reviews.freebsd= .org/D33402
---
=C2=A0sys/geom/geom_dev.c | 59 ++++++++++++++++++++++++++++++++++++++++++++= +++++++++
=C2=A01 file changed, 59 insertions(+)

diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c
index 24ab14e90c1b..b94df9fcda67 100644
--- a/sys/geom/geom_dev.c
+++ b/sys/geom/geom_dev.c
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
=C2=A0#include <sys/disk.h>
=C2=A0#include <sys/fcntl.h>
=C2=A0#include <sys/limits.h>
+#include <sys/selinfo.h>
=C2=A0#include <sys/sysctl.h>
=C2=A0#include <geom/geom.h>
=C2=A0#include <geom/geom_int.h>
@@ -65,6 +66,7 @@ struct g_dev_softc {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 struct cdev=C2=A0 =C2=A0 =C2=A0*sc_alias;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 int=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 sc_open;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 u_int=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = sc_active;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct selinfo=C2=A0 =C2=A0sc_selinfo;
=C2=A0#define=C2=A0 =C2=A0 =C2=A0 =C2=A0 SC_A_DESTROY=C2=A0 =C2=A0 (1 <&= lt; 31)
=C2=A0#define=C2=A0 =C2=A0 =C2=A0 =C2=A0 SC_A_OPEN=C2=A0 =C2=A0 =C2=A0 =C2= =A0(1 << 30)
=C2=A0#define=C2=A0 =C2=A0 =C2=A0 =C2=A0 SC_A_ACTIVE=C2=A0 =C2=A0 =C2=A0(SC= _A_OPEN - 1)
@@ -74,6 +76,16 @@ static d_open_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 g_dev_open;
=C2=A0static d_close_t=C2=A0 =C2=A0 =C2=A0 =C2=A0g_dev_close;
=C2=A0static d_strategy_t=C2=A0 =C2=A0 g_dev_strategy;
=C2=A0static d_ioctl_t=C2=A0 =C2=A0 =C2=A0 =C2=A0g_dev_ioctl;
+static d_kqfilter_t=C2=A0 =C2=A0 g_dev_kqfilter;
+
+static void=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gdev_filter_detach(st= ruct knote *kn);
+static int=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0gdev_filter_vnod= e(struct knote *kn, long hint);
+
+static struct filterops gdev_filterops_vnode =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0.f_isfd =3D 1,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0.f_detach =3D gdev_filter_detach,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0.f_event =3D gdev_filter_vnode,
+};

=C2=A0static struct cdevsw g_dev_cdevsw =3D {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 .d_version =3D=C2=A0 =C2=A0 D_VERSION,
@@ -85,6 +97,7 @@ static struct cdevsw g_dev_cdevsw =3D {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 .d_strategy =3D=C2=A0 =C2=A0g_dev_strategy,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 .d_name =3D=C2=A0 =C2=A0 =C2=A0 =C2=A0"g_d= ev",
=C2=A0 =C2=A0 =C2=A0 =C2=A0 .d_flags =3D=C2=A0 =C2=A0 =C2=A0 D_DISK | D_TRA= CKCLOSE,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0.d_kqfilter =3D=C2=A0 =C2=A0g_dev_kqfilter,
=C2=A0};

=C2=A0static g_init_t g_dev_init;
@@ -214,6 +227,8 @@ g_dev_destroy(void *arg, int flags __unused)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_trace(G_T_TOPOLOGY, "g_dev_destroy(%p(%s= ))", cp, gp->name);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 snprintf(buf, sizeof(buf), "cdev=3D%s"= ;, gp->name);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 devctl_notify("GEOM", "DEV"= , "DESTROY", buf);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0knlist_clear(&sc->sc_selinfo.si_note, 0)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0knlist_destroy(&sc->sc_selinfo.si_note);=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (cp->acr > 0 || cp->acw > 0 || c= p->ace > 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 g_access(cp, -cp-&g= t;acr, -cp->acw, -cp->ace);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_detach(cp);
@@ -305,8 +320,12 @@ g_dev_attrchanged(struct g_consumer *cp, const char *a= ttr)
=C2=A0static void
=C2=A0g_dev_resize(struct g_consumer *cp)
=C2=A0{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct g_dev_softc *sc;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 char buf[SPECNAMELEN + 6];

+=C2=A0 =C2=A0 =C2=A0 =C2=A0sc =3D cp->private;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, = NOTE_ATTRIB);
+
=C2=A0 =C2=A0 =C2=A0 =C2=A0 snprintf(buf, sizeof(buf), "cdev=3D%s"= ;, cp->provider->name);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 devctl_notify("GEOM", "DEV"= , "SIZECHANGE", buf);
=C2=A0}
@@ -378,6 +397,7 @@ g_dev_taste(struct g_class *mp, struct g_provider *pp, = int insist __unused)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev =3D sc->sc_dev;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev->si_flags |=3D SI_UNMAPPED;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 dev->si_iosize_max =3D maxphys;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0knlist_init_mtx(&sc->sc_selinfo.si_note,= &sc->sc_mtx);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 error =3D init_dumpdev(dev);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (error !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 printf("%s: in= it_dumpdev() failed (gp->name=3D%s, error=3D%d)\n",
@@ -883,4 +903,43 @@ g_dev_orphan(struct g_consumer *cp)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 destroy_dev_sched_cb(dev, g_dev_callback, cp);<= br> =C2=A0}

+static void
+gdev_filter_detach(struct knote *kn)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct g_dev_softc *sc;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0sc =3D kn->kn_hook;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0knlist_remove(&sc->sc_selinfo.si_note, k= n, 0);
+}
+
+static int
+gdev_filter_vnode(struct knote *kn, long hint)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0kn->kn_fflags |=3D kn->kn_sfflags & h= int;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return (kn->kn_fflags !=3D 0);
+}
+
+static int
+g_dev_kqfilter(struct cdev *dev, struct knote *kn)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0struct g_dev_softc *sc;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0sc =3D dev->si_drv1;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (kn->kn_filter !=3D EVFILT_VNODE)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return (EINVAL); +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* XXX: extend support for other NOTE_* events = */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (kn->kn_sfflags !=3D NOTE_ATTRIB)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return (EINVAL); +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0kn->kn_fop =3D &gdev_filterops_vnode; +=C2=A0 =C2=A0 =C2=A0 =C2=A0kn->kn_hook =3D sc;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0knlist_add(&sc->sc_selinfo.si_note, kn, = 0);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return (0);
+}
+
=C2=A0DECLARE_GEOM_CLASS(g_dev_class, g_dev);
--0000000000000448cb05d8b42b12--