Re: [PATCH] Add IOMMU support for Intel VT-d and AMD-Vi
Ok made more changes On Mon, Sep 14, 2020 at 08:19:18PM +0200, Mark Kettenis wrote: > > Date: Tue, 8 Sep 2020 21:43:39 -0500 > > From: Jordan Hargrave > > > > Made changes for the iommu_readq -> iommu_read_8 and also now > > dynamically allocate the hwdte for AMD IOMMU. > > Some more bits... > > > On Fri, Sep 04, 2020 at 09:17:18PM +0200, Mark Kettenis wrote: > > > > Date: Fri, 4 Sep 2020 00:50:44 -0500 > > > > From: Jordan Hargrave > > > > > > A few hints below... > > > > > > > > > + > > > > > > +/* Page Table Entry per domain */ > > > > > > +static struct ivhd_dte hwdte[65536] __aligned(PAGE_SIZE); > > > > > > + > > > > > > +/* Alias mapping */ > > > > > > +#define SID_INVALID 0x8000L > > > > > > +static uint32_t sid_flag[65536]; > > > > > > > > > > Can we avoid having these large arrays, or at least allocate them > > > > > dynamically? That would also avoid the explicit alignment which is > > > > > somewhat nasty since it affects the entire kernel. > > > > > > > > OK. But the hwdte does need the 2M area to be all contiguous but it is > > > > not > > > > needed for DMAR/Intel. You *can* have up to 8 different device table > > > > entries > > > > though to split up the area. > > > > > > The appropriate interface to use in this context is > > > bus_dmamem_alloc(9). You can specify alignment, and if you set nsegs > > > to 1, you will get memory that is physicaly contiguous. > > > > > > To map the memory into kernel address space you'll need create a map > > > using bus_dmamap_create(9) and map it using bus_dmamem_map(9). Then > > > instead of using pmap_extract(9) you use bus_dmamap_load_raw(9) which > > > then populates the physical addresses. > > > > > > Many of the drivers written by dlg@ define convenience functions to do > > > all these steps, although interestingly enough he tends to use > > > bus_dmamap_load(9) instead of bus_dmamap_load_raw(9) which is > > > sub-optimal. > > > > > > > > > + > > > > > > +struct domain_dev { > > > > > > + int sid; > > > > > > + int sec; > > > > > > + int sub; > > > > > > + TAILQ_ENTRY(domain_dev) link; > > > > > > +}; > > > > > > + > > > > > > +struct domain { > > > > > > + struct iommu_softc *iommu; > > > > > > + int did; > > > > > > + int gaw; > > > > > > + struct pte_entry*pte; > > > > > > + paddr_t ptep; > > > > > > + struct bus_dma_tag dmat; > > > > > > + int flag; > > > > > > + > > > > > > + struct mutexexlck; > > > > > > + charexname[32]; > > > > > > + struct extent *iovamap; > > > > > > + TAILQ_HEAD(,domain_dev) devices; > > > > > > + TAILQ_ENTRY(domain) link; > > > > > > +}; > > > > > > + > > > > > > +#define DOM_DEBUG 0x1 > > > > > > +#define DOM_NOMAP 0x2 > > > > > > + > > > > > > +struct dmar_devlist { > > > > > > + int type; > > > > > > + int bus; > > > > > > + int ndp; > > > > > > + struct acpidmar_devpath *dp; > > > > > > + TAILQ_ENTRY(dmar_devlist) link; > > > > > > +}; > > > > > > + > > > > > > +TAILQ_HEAD(devlist_head, dmar_devlist); > > > > > > + > > > > > > +struct ivhd_devlist { > > > > > > + int start_id; > > > > > > + int end_id; > > > > > > + int cfg; > > > > > > + TAILQ_ENTRY(ivhd_devlist) link; > > > > > > +}; > > > > > > + > > > > > > +struct rmrr_softc { > > > > > > + TAILQ_ENTRY(rmrr_softc) link; > > > > > > + struct devlist_head devices; > > > > > > + int segment; > > > > > > + uint64_tstart; > > > > > > + uint64_tend; > > > > > > +}; > > > > > > + > > > > > > +struct atsr_softc { > > > > > > + TAILQ_ENTRY(atsr_softc) link; > > > > > > + struct devlist_head devices; > > > > > > + int segment; > > > > > > + int flags; > > > > > > +}; > > > > > > + > > > > > > +struct iommu_pic { > > > > > > + struct pic pic; > > > > > > + struct iommu_softc *iommu; > > > > > > +}; > > > > > > + > > > > > > +#define IOMMU_FLAGS_CATCHALL 0x1 > > > > > > +#define IOMMU_FLAGS_BAD0x2 > > > > > > +#define IOMMU_FLAGS_SUSPEND0x4 > > > > > > + > > > > > > +struct iommu_softc { > > > > > > + TAILQ_ENTRY(iommu_softc)link; > > > > > > + struct devlist_head devices; > > > > > > + int id; > > > > > > + int flags; > > > > > > + int segment; > > > > > > + > > > > > > + struct mutexreg_lock; > > > > > > + > > > > > > + bus_space_tag_t iot; > > > > > > + bus_space_handle_t ioh; > > > > > > + > > > > > > + uint64_t
Re: PATCH: Add vmmpci device for passthrough PCI
Ping. Any replies or commeents on this? On Tue, Sep 15, 2020 at 12:54:49PM -0500, Jordan Hargrave wrote: > This adds a placeholder vmmpci device that will be used for VMD passthrough > PCI. > > Normally the device will fail to attach unless the PCI domain:bus.dev.func has > been registered with vmmpci_add. When the device is registered, it will > detach > any existing PCI device and reload as vmmpci. It also attaches an interrupt > handler > and keeps a running counter of triggered interrupts. VMD will use the counter > to > issue commands through to the guest. > > diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC > index 2c49f91a1..a69c72c26 100644 > --- a/sys/arch/amd64/conf/GENERIC > +++ b/sys/arch/amd64/conf/GENERIC > @@ -108,6 +109,7 @@ ksmn* at pci? # AMD K17 temperature > sensor > amas*at pci? disable # AMD memory configuration > pchtemp* at pci? # Intel C610 termperature sensor > ccp* at pci? # AMD Cryptographic Co-processor > +vmmpci* at pci? # VMM Placeholder > > # National Semiconductor LM7[89] and compatible hardware monitors > lm0 at isa? port 0x290 > diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64 > index 7a5d40bf4..74c7fe5a9 100644 > --- a/sys/arch/amd64/conf/files.amd64 > +++ b/sys/arch/amd64/conf/files.amd64 > @@ -132,6 +132,10 @@ device pchb: pcibus, agpbus > attach pchb at pci > file arch/amd64/pci/pchb.c pchb > > +device vmmpci > +attach vmmpci at pci > +file arch/amd64/pci/vmmpci.c vmmpci > + > # AMAS AMD memory address switch > device amas > attach amas at pci > diff --git a/sys/arch/amd64/include/vmmpci.h b/sys/arch/amd64/include/vmmpci.h > new file mode 100644 > index 0..e012194df > --- /dev/null > +++ b/sys/arch/amd64/include/vmmpci.h > @@ -0,0 +1,24 @@ > +/* $OpenBSD: vmmvar.h,v 1.70 2020/04/08 07:39:48 pd Exp $ */ > +/* > + * Copyright (c) 2020 Jordan Hargrave > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > +#ifndef _VMMPCI_H_ > +#define _VMMPCI_H_ > + > +int vmmpci_find(int, pcitag_t); > +int vmmpci_add(int, pcitag_t, int); > +int vmmpci_pending(int, pcitag_t, uint32_t *); > + > +#endif > diff --git a/sys/arch/amd64/pci/vmmpci.c b/sys/arch/amd64/pci/vmmpci.c > new file mode 100644 > index 0..a99efb502 > --- /dev/null > +++ b/sys/arch/amd64/pci/vmmpci.c > @@ -0,0 +1,186 @@ > +/* $OpenBSD: pcib.c,v 1.6 2013/05/30 16:15:01 deraadt Exp $*/ > +/* $NetBSD: pcib.c,v 1.6 1997/06/06 23:29:16 thorpej Exp $ */ > + > +/*- > + * Copyright (c) 1996 Jordan Hargrave > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include > +#include > +#include > + > +#include > + > +#include > +#include > + > +#include > + > +#include > +#include > + > +struct vmmpci_softc { > + struct device sc_dev; > + void*sc_ih; > + > + int sc_domain; > + pci_chipset_tag_t sc_pc; > + pcitag_tsc_tag; > + > + uint32_tpending;// pending interrupt > count > +}; > + > +#define VP_VALID 0x8000 > + > +/* Keep track of registered devices */ > +struct vmmpci_dev { > + int vp_flags; > + int vp_domain; > + pcitag_tvp_tag; > +}; > + > +int vmmpci_match(struct device *, void *, void *); > +void vmmpci_attach(struct device *, struct device *,
kstat(1): implement wait with setitimer(2)
Hi, Using nanosleep(2) to print the stats periodically causes the period to drift. If you use setitimer(2) it won't drift. ok? Index: kstat.c === RCS file: /cvs/src/usr.bin/kstat/kstat.c,v retrieving revision 1.6 diff -u -p -r1.6 kstat.c --- kstat.c 13 Aug 2020 12:37:16 - 1.6 +++ kstat.c 17 Sep 2020 23:24:36 - @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include +#include #include #include #include @@ -104,6 +106,7 @@ kstat_cmp(const struct kstat_entry *ea, RBT_PROTOTYPE(kstat_tree, kstat_entry, entry, kstat_cmp); RBT_GENERATE(kstat_tree, kstat_entry, entry, kstat_cmp); +static voidhandle_alrm(int); static struct kstat_filter * kstat_filter_parse(char *); static int kstat_filter_entry(struct kstat_filters *, @@ -130,20 +133,23 @@ main(int argc, char *argv[]) { struct kstat_filters kfs = TAILQ_HEAD_INITIALIZER(kfs); struct kstat_tree kt = RBT_INITIALIZER(); + struct itimerval itv; + time_t interval; unsigned int version; + sigset_t empty; int fd; const char *errstr; int ch; - struct timespec interval = { 0, 0 }; int i; + interval = 0; + while ((ch = getopt(argc, argv, "w:")) != -1) { switch (ch) { case 'w': - interval.tv_sec = strtonum(optarg, 1, 1, - ); + interval = strtonum(optarg, 1, 1, ); if (errstr != NULL) - errx(1, "wait %s: %s", optarg, errstr); + errx(1, "wait is %s: %s", optarg, errstr); break; default: usage(); @@ -168,17 +174,31 @@ main(int argc, char *argv[]) kstat_list(, fd, version, ); kstat_print(); - if (interval.tv_sec == 0) + if (interval == 0) return (0); - for (;;) { - nanosleep(, NULL); + signal(SIGALRM, handle_alrm); + sigemptyset(); + + itv.it_value.tv_sec = interval; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + if (setitimer(ITIMER_REAL, , NULL) == -1) + err(1, "setitimer"); + for (;;) { + sigsuspend(); kstat_read(, fd); kstat_print(); } return (0); +} + +static void +handle_alrm(int signo) +{ + return; } static struct kstat_filter *
ifconfig: consistent display of P2P link
All tunnels & point-to-point addresses are separated by "->" but inet. Denis Index: ifconfig.c === RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v retrieving revision 1.426 diff -u -p -r1.426 ifconfig.c --- ifconfig.c 15 Sep 2020 15:23:11 - 1.426 +++ ifconfig.c 17 Sep 2020 14:41:34 - @@ -3552,7 +3552,7 @@ in_status(int force) } (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); sin = (struct sockaddr_in *)_dstaddr; - printf(" --> %s", inet_ntoa(sin->sin_addr)); + printf(" -> %s", inet_ntoa(sin->sin_addr)); } printf(" netmask 0x%x", ntohl(netmask.sin_addr.s_addr)); if (flags & IFF_BROADCAST) {
Re: diff: pfctl: error message for nonexisting rtable
the condition was reversed. ok? Index: parse.y === RCS file: /cvs/src/sbin/pfctl/parse.y,v retrieving revision 1.702 diff -u -p -r1.702 parse.y --- parse.y 17 Sep 2020 10:09:43 - 1.702 +++ parse.y 17 Sep 2020 14:23:42 - @@ -1216,7 +1216,7 @@ antispoof_opt : LABEL label { if ($2 < 0 || $2 > RT_TABLEID_MAX) { yyerror("invalid rtable id"); YYERROR; - } else if (lookup_rtable($2) >= 1) { + } else if (lookup_rtable($2) < 1) { yyerror("rtable %lld does not exist", $2); YYERROR; } @@ -2003,7 +2003,7 @@ filter_opt: USER uids { if ($2 < 0 || $2 > RT_TABLEID_MAX) { yyerror("invalid rtable id"); YYERROR; - } else if (lookup_rtable($2) >= 1) { + } else if (lookup_rtable($2) < 1) { yyerror("rtable %lld does not exist", $2); YYERROR; }
[diff] Allow preferred source IP selection
This updated diff unbreak P2P links where local address was not the same as preferred source address. Sending to tech@ may help get more feedback on what I broke. Example usage : Set 2001:db8::1 as source : route source 2001:db8::1 Unset previously set IPv6 address on rdomain 10 : route -T10 source -inet6 default Show set address : route source Denis Index: sbin/route/keywords.h === RCS file: /cvs/src/sbin/route/keywords.h,v retrieving revision 1.34 diff -u -p -r1.34 keywords.h --- sbin/route/keywords.h 10 Aug 2017 13:44:48 - 1.34 +++ sbin/route/keywords.h 17 Sep 2020 09:59:25 - @@ -1,4 +1,4 @@ -/* $OpenBSD: keywords.h,v 1.34 2017/08/10 13:44:48 benno Exp $ */ +/* $OpenBSD$ */ /* WARNING! This file was generated by keywords.sh */ @@ -66,6 +66,7 @@ enum { K_SA, K_SENDPIPE, K_SHOW, + K_SOURCE, K_SSTHRESH, K_STATIC, K_SWAP, @@ -129,6 +130,7 @@ struct keytab keywords[] = { { "sa", K_SA }, { "sendpipe", K_SENDPIPE }, { "show", K_SHOW }, + { "source", K_SOURCE }, { "ssthresh", K_SSTHRESH }, { "static", K_STATIC }, { "swap", K_SWAP }, Index: sbin/route/keywords.sh === RCS file: /cvs/src/sbin/route/keywords.sh,v retrieving revision 1.32 diff -u -p -r1.32 keywords.sh --- sbin/route/keywords.sh 10 Aug 2017 13:44:48 - 1.32 +++ sbin/route/keywords.sh 17 Sep 2020 09:59:25 - @@ -67,6 +67,7 @@ rttvar sa sendpipe show +source ssthresh static swap Index: sbin/route/route.8 === RCS file: /cvs/src/sbin/route/route.8,v retrieving revision 1.91 diff -u -p -r1.91 route.8 --- sbin/route/route.8 19 Jan 2020 18:22:31 - 1.91 +++ sbin/route/route.8 17 Sep 2020 09:59:25 - @@ -195,6 +195,17 @@ or .Cm bgp . If the priority is negative, then routes that do not match the numeric priority are shown. +.It Xo +.Nm route +.Op Fl T Ar rtable +.Tg +.Cm source +.Ar address +.Xc +Set the preferred source address. If +.Ar address +is the word "default", 0.0.0.0 or ::, source address will be chosen by +the kernel for the matching address family. .El .Pp .Tg destination Index: sbin/route/route.c === RCS file: /cvs/src/sbin/route/route.c,v retrieving revision 1.248 diff -u -p -r1.248 route.c --- sbin/route/route.c 7 Jul 2020 14:53:36 - 1.248 +++ sbin/route/route.c 17 Sep 2020 09:59:25 - @@ -68,7 +68,8 @@ const struct if_status_description if_status_descriptions[] = LINK_STATE_DESCRIPTIONS; -union sockunion so_dst, so_gate, so_mask, so_ifa, so_ifp, so_src, so_label; +union sockunion so_dst, so_gate, so_mask, so_ifa, so_ifp, so_src, so_label, +so_source; typedef union sockunion *sup; pid_t pid; @@ -85,6 +86,7 @@ struct rt_metrics rt_metrics; int flushroutes(int, char **); int newroute(int, char **); +int setsource(int, char **); int show(int, char *[]); int keycmp(const void *, const void *); int keyword(char *); @@ -132,7 +134,8 @@ usage(char *cp) "usage: %s [-dnqtv] [-T rtable] command [[modifiers] args]\n", __progname); fprintf(stderr, - "commands: add, change, delete, exec, flush, get, monitor, show\n"); + "commands: add, change, delete, exec, flush, get, monitor, show, " + "source\n"); exit(1); } @@ -258,6 +261,10 @@ main(int argc, char **argv) case K_FLUSH: exit(flushroutes(argc, argv)); break; + case K_SOURCE: + nflag = 1; + exit(setsource(argc, argv)); + break; } if (pledge("stdio dns", NULL) == -1) @@ -450,6 +457,52 @@ set_metric(char *value, int key) locking = 0; } + +int +setsource(int argc, char **argv) +{ + char *cmd, *srcaddr = ""; + int af = AF_UNSPEC, ret = 0; + struct hostent *hp = NULL; + int key; + + if (uid) + errx(1, "must be root to alter source address"); + cmd = argv[0]; + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; + case K_INET6: + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); + break; + } + } else if ((rtm_addrs & RTA_IFA) == 0) { + srcaddr = *argv; +
Re: diff: pfctl: error message for nonexisting rtable
Hi, I just committed yours. Thanks, On Wed, 16 Sep 2020 16:07:40 +0200 Klemens Nanni wrote: > On Wed, Sep 16, 2020 at 07:49:19PM +0900, YASUOKA Masahiko wrote: >> New diff is using -1 for ENOENT. >> >> Also domainid == 0 is a valid domain id, but previous diff cannot make >> a cache of it since 0 is the default value. So new diff is doing >> >> -static u_int found[RT_TABLEID_MAX+1]; >> +static struct { >> +int found; >> +int domainid; >> +}rtables[RT_TABLEID_MAX+1]; >> >> to distinguish the default 0 and domainid 0. > This looks more complicated than it needs to be, but I also don't want > to bikeshed it; given that the parser is happy with this and we plan to > remove this code alltogether anyway in the next release cycle: OK kn. > > Alternatively, here's a much simpler diff resembling what I had in mind. > Feel free to commit this instead (with my OK), give me an OK for it or > go ahead with yours. > > It uses the same function and reflects the fact that every rdomain is a > rtable but not every rtable is also a rdomain (your choice of `domainid' > seems inconsistent with that). > > Index: parse.y > === > RCS file: /cvs/src/sbin/pfctl/parse.y,v > retrieving revision 1.701 > diff -u -p -r1.701 parse.y > --- parse.y 28 Jan 2020 15:40:35 - 1.701 > +++ parse.y 16 Sep 2020 13:58:23 - > @@ -392,7 +392,7 @@ intinvalid_redirect(struct node_host * > u_int16_t parseicmpspec(char *, sa_family_t); > int kw_casecmp(const void *, const void *); > int map_tos(char *string, int *); > -int rdomain_exists(u_int); > +int lookup_rtable(u_int); > int filteropts_to_rule(struct pf_rule *, struct filter_opts *); > > TAILQ_HEAD(loadanchorshead, loadanchors) > @@ -1216,6 +1216,9 @@ antispoof_opt : LABEL label { > if ($2 < 0 || $2 > RT_TABLEID_MAX) { > yyerror("invalid rtable id"); > YYERROR; > + } else if (lookup_rtable($2) >= 1) { > + yyerror("rtable %lld does not exist", $2); > + YYERROR; > } > antispoof_opts.rtableid = $2; > } > @@ -2000,6 +2003,9 @@ filter_opt : USER uids { > if ($2 < 0 || $2 > RT_TABLEID_MAX) { > yyerror("invalid rtable id"); > YYERROR; > + } else if (lookup_rtable($2) >= 1) { > + yyerror("rtable %lld does not exist", $2); > + YYERROR; > } > filter_opts.rtableid = $2; > } > @@ -2475,7 +2481,7 @@ if_item : STRING{ > | RDOMAIN NUMBER{ > if ($2 < 0 || $2 > RT_TABLEID_MAX) > yyerror("rdomain %lld outside range", $2); > - else if (rdomain_exists($2) != 1) > + else if (lookup_rtable($2) != 2) > yyerror("rdomain %lld does not exist", $2); > > $$ = calloc(1, sizeof(struct node_if)); > @@ -5868,37 +5874,38 @@ map_tos(char *s, int *val) > } > > int > -rdomain_exists(u_int rdomain) > +lookup_rtable(u_int rtableid) > { > size_t len; > struct rt_tableinfo info; > int mib[6]; > static u_int found[RT_TABLEID_MAX+1]; > > - if (found[rdomain] == 1) > - return 1; > + if (found[rtableid]) > + return found[rtableid]; > > mib[0] = CTL_NET; > mib[1] = PF_ROUTE; > mib[2] = 0; > mib[3] = 0; > mib[4] = NET_RT_TABLE; > - mib[5] = rdomain; > + mib[5] = rtableid; > > len = sizeof(info); > if (sysctl(mib, 6, , , NULL, 0) == -1) { > if (errno == ENOENT) { > /* table nonexistent */ > + found[rtableid] = 0; > return 0; > } > err(1, "%s", __func__); > } > - if (info.rti_domainid == rdomain) { > - found[rdomain] = 1; > - return 1; > + if (info.rti_domainid == rtableid) { > + found[rtableid] = 2; > + return 2; > } > - /* rdomain is a table, but not an rdomain */ > - return 0; > + found[rtableid] = 1; > + return 1; > } > > int
Re: drop support for afs, nnpfs, and procfs from security(8)
Hi Todd, Todd C. Miller wrote on Wed, Sep 16, 2020 at 01:36:09PM -0600: > On Wed, 16 Sep 2020 18:17:36 +0200, Ingo Schwarze wrote: >> Does anyone think that explicitely excluding these file system >> types might still be useful, or is the following simplification >> OK? No functional change intended. > I think those bits can go. OK millert@ Committed, thanks for checking. Ingo