On Tue, Oct 21, 2025 at 09:56:43AM +0800, Jie Gan wrote:
[...]
> > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> > index b07fcdb3fe1a..d0fac958c614 100644
> > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> > @@ -1241,6 +1241,8 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
> > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL;
> > + WARN_ON(coresight_get_mode(csdev) != CS_MODE_SYSFS);
>
> I think we should check the WARN_ON result and exit if there is an error?
When run at here, it should be in Sysfs mode. Here the check is for
debugging purpose in case any mismatch.
[...]
> > +static void tmc_release_mode(struct coresight_device *csdev, enum cs_mode mode)
> > +{
> > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > +
> > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock);
> > +
> > + if (WARN_ON(coresight_get_mode(csdev) != mode))
> > + return;
>
> the mode here could be set to any CS_MODE, so I think it's possible to
> encounter the secenario below:
>
> coresight_get_mode(csdev) == CS_MODE_DISABLED, mode == CS_MODE_DISABLED,
>
> With the condition, the csdev->refcnt will go to negative number?
The parameter "mode" might cause complexity, will drop it. The
correctness will be ensured by the callers.
Thanks for review!
Leo
On Mon, Oct 20, 2025 at 05:06:46PM +0800, Xiaoqi Zhuang wrote:
> When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed
> and enabled again, currently sysfs_buf will point to the newly
> allocated memory(buf_new) and free the old memory(buf_old). But the
> etr_buf that is being used by the ETR remains pointed to buf_old, not
> updated to buf_new. In this case, it will result in a memory
> use-after-free issue.
I struggled to understand how to reproduce the issue under the condition
"if the buffer size is changed and enabled again."
I don't think the flow below where the trace is re-enabled would cause
an issue:
- Step 1: Enable trace path between ETM0 -> ETR0;
- Step 2: Change the buffer size for ETR0;
- Step 3: Disable trace path between ETM0 -> ETR0;
- Step 4: Enable again trace path between ETM0 -> ETR0.
In this case, step3 releases the buffer and update "drvdata->etr_buf" to
NULL, and step 4 allocates a new buffer and assign it to
"drvdata->etr_buf".
The problem should occur when operating on two trace paths, E.g.,
- Step 1: Enable trace path between ETM0 -> ETR0;
- Step 2: Change the buffer size for ETR0;
- Step 3: Enable trace path between ETM1 -> ETR0;
In step3, the driver releases the existed buffer and allocate a new one.
At the meantime, "drvdata->etr_buf" still holds the buffer allocated in
step 1.
> Fix this by checking ETR's mode before updating and releasing buf_old,
> if the mode is CS_MODE_SYSFS, then skip updating and releasing it.
Given that we now have a couple of reported issues related to ETR mode,
I'd like to refactor the ETR mode handling and its reference counting
thoroughly. I've drafted a large change (it's quite big, but we can
split it into small patches if we agree to proceed).
Thanks for reporting the issue!
Leo
---8<---
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index b07fcdb3fe1a..d0fac958c614 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1241,6 +1241,8 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL;
+ WARN_ON(coresight_get_mode(csdev) != CS_MODE_SYSFS);
+
/*
* If we are enabling the ETR from disabled state, we need to make
* sure we have a buffer with the right size. The etr_buf is not reset
@@ -1263,7 +1265,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
}
- if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
+ if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
@@ -1292,30 +1294,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
int ret = 0;
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct etr_buf *sysfs_buf = tmc_etr_get_sysfs_buffer(csdev);
+ struct etr_buf *sysfs_buf;
+ sysfs_buf = tmc_etr_get_sysfs_buffer(csdev);
if (IS_ERR(sysfs_buf))
return PTR_ERR(sysfs_buf);
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
-
- /*
- * In sysFS mode we can have multiple writers per sink. Since this
- * sink is already enabled no memory is needed and the HW need not be
- * touched, even if the buffer size has changed.
- */
- if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
- csdev->refcnt++;
- goto out;
- }
-
ret = tmc_etr_enable_hw(drvdata, sysfs_buf);
- if (!ret) {
- coresight_set_mode(csdev, CS_MODE_SYSFS);
- csdev->refcnt++;
- }
-
-out:
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (!ret)
@@ -1735,11 +1721,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle);
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
- /* Don't use this sink if it is already claimed by sysFS */
- if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
- rc = -EBUSY;
- goto unlock_out;
- }
if (WARN_ON(!etr_perf || !etr_perf->etr_buf)) {
rc = -EINVAL;
@@ -1759,18 +1740,14 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
* No HW configuration is needed if the sink is already in
* use for this session.
*/
- if (drvdata->pid == pid) {
- csdev->refcnt++;
+ if (drvdata->pid == pid)
goto unlock_out;
- }
rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf);
if (!rc) {
/* Associate with monitored process. */
drvdata->pid = pid;
- coresight_set_mode(csdev, CS_MODE_PERF);
drvdata->perf_buf = etr_perf->etr_buf;
- csdev->refcnt++;
}
unlock_out:
@@ -1778,17 +1755,76 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
return rc;
}
+static int tmc_acquire_mode(struct coresight_device *csdev, enum cs_mode mode)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ if (mode != CS_MODE_SYSFS && mode != CS_MODE_PERF)
+ return -EINVAL;
+
+ scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock);
+
+ if (coresight_get_mode(csdev) == CS_MODE_DISABLED) {
+ if (!csdev->refcnt)
+ coresight_set_mode(csdev, mode);
+ csdev->refcnt++;
+ } else if (coresight_get_mode(csdev) != mode) {
+ ret = -EBUSY;
+ }
+
+ return csdev->refcnt;
+}
+
+static void tmc_release_mode(struct coresight_device *csdev, enum cs_mode mode)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock);
+
+ if (WARN_ON(coresight_get_mode(csdev) != mode))
+ return;
+
+ csdev->refcnt--;
+ if (!csdev->refcnt)
+ coresight_set_mode(csdev, CS_MODE_DISABLED);
+}
+
static int tmc_enable_etr_sink(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
+ unsigned long flags;
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ int ret;
+
+ ret = tmc_acquire_mode(csdev, mode);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * For sysfs mode, the higher level mutex ensures exclusively
+ * enabling sink, it is safe to bail out if this is not the
+ * first time to enable sink.
+ *
+ * A perf session can enable the same sink simultaneously, fall
+ * through to call tmc_enable_etr_sink_perf() to ensure the sink
+ * has been enabled.
+ */
+ if (mode == CS_MODE_SYSFS && ret > 1)
+ return 0;
+
switch (mode) {
case CS_MODE_SYSFS:
- return tmc_enable_etr_sink_sysfs(csdev);
+ ret = tmc_enable_etr_sink_sysfs(csdev);
case CS_MODE_PERF:
- return tmc_enable_etr_sink_perf(csdev, data);
+ ret = tmc_enable_etr_sink_perf(csdev, data);
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+
+ if (ret)
+ tmc_release_mode(csdev, mode);
+
+ return ret;
}
static int tmc_disable_etr_sink(struct coresight_device *csdev)
@@ -1796,30 +1832,20 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ tmc_release_mode(csdev, mode);
- if (drvdata->reading) {
- raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
- return -EBUSY;
- }
+ scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock);
- csdev->refcnt--;
- if (csdev->refcnt) {
- raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ if (csdev->refcnt || drvdata->reading)
return -EBUSY;
- }
- /* Complain if we (somehow) got out of sync */
- WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
+ if (drvdata->pid == -1)
+ return 0;
+
tmc_etr_disable_hw(drvdata);
- /* Dissociate from monitored process. */
- drvdata->pid = -1;
- coresight_set_mode(csdev, CS_MODE_DISABLED);
/* Reset perf specific data */
drvdata->perf_buf = NULL;
- raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
dev_dbg(&csdev->dev, "TMC-ETR disabled\n");
return 0;
}
On 20/10/2025 11:18, Jie Gan wrote:
>
>
> On 10/20/2025 5:06 PM, Xiaoqi Zhuang wrote:
>> When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed
>> and enabled again, currently sysfs_buf will point to the newly
>> allocated memory(buf_new) and free the old memory(buf_old). But the
>> etr_buf that is being used by the ETR remains pointed to buf_old, not
>> updated to buf_new. In this case, it will result in a memory
>> use-after-free issue.
>>
>> Fix this by checking ETR's mode before updating and releasing buf_old,
>> if the mode is CS_MODE_SYSFS, then skip updating and releasing it.
>>
>
> I think we need a fix tag for the fix patch:
> Fixes: de5461970b3e ("coresight: tmc: allocating memory when needed")
>
> minor comment:
> Since ETR is enabled, we can't switch the sysfs buffer, can we exit
> earlier by checking the CS_MODE to avoid allocating memory unnecessarily?
+1
Something like this:
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c
b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index b07fcdb3fe1a..a9ddb05c10d9 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1252,6 +1252,12 @@ static struct etr_buf
*tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) {
+ /*
+ * If the ETR is already enabled, continue with the existing
+ * buffer.
+ */
+ if (coresight_get_mode(csdev) == CS_MODE_SYSFS)
+ goto out;
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Allocate memory with the locks released */
Suzuki>
> Besides,
> Looks good to me.
>
> Thanks,
> Jie
>
>> Signed-off-by: Xiaoqi Zhuang <xiaoqi.zhuang(a)oss.qualcomm.com>
>> ---
>> drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 +++++++
>> 1 file changed, 7 insertions(+)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/
>> drivers/hwtracing/coresight/coresight-tmc-etr.c
>> index b07fcdb3fe1a..3e73cf2c38a3 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> @@ -1268,6 +1268,13 @@ static struct etr_buf
>> *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
>> goto out;
>> }
>> + /*
>> + * Since this sink is already enabled, the existing buffer should
>> not
>> + * be released even if the buffer size has changed.
>> + */
>> + if (coresight_get_mode(csdev) == CS_MODE_SYSFS)
>> + goto out;
>> +
>> /*
>> * If we don't have a buffer or it doesn't match the requested
>> size,
>> * use the buffer allocated above. Otherwise reuse the existing
>> buffer.
>>
>> ---
>> base-commit: 98ac9cc4b4452ed7e714eddc8c90ac4ae5da1a09
>> change-id: 20251020-fix_etr_issue-02c706dbc899
>>
>> Best regards,
>
Do some cleanups then add a new format attribute to set the timestamp
interval for ETMv4 in Perf mode. The current interval is too high for
most use cases, and particularly on the FVP the number of timestamps
generated is excessive.
Although it would be good to make only SYNC timestamps the default and
have counter timestamps opt-in, this would be a breaking change. We
can always do that later, or disable counter timestamps from Perf.
This is added as an event format attribute, rather than a Coresight
config because it's something that the driver is already configuring
automatically in Perf mode with any unused counter, so it's not possible
to modify this with a config.
Applies to coresight/next
Signed-off-by: James Clark <james.clark(a)linaro.org>
---
James Clark (6):
coresight: Change syncfreq to be a u8
coresight: Fix holes in struct etmv4_config
coresight: Repack struct etmv4_drvdata
coresight: Refactor etm4_config_timestamp_event()
coresight: Add format attribute for setting the timestamp interval
coresight: docs: Document etm4x ts_interval
Documentation/trace/coresight/coresight.rst | 14 +++
drivers/hwtracing/coresight/coresight-etm-perf.c | 6 +-
drivers/hwtracing/coresight/coresight-etm4x-core.c | 110 +++++++++++++--------
drivers/hwtracing/coresight/coresight-etm4x.h | 86 ++++++++++------
4 files changed, 144 insertions(+), 72 deletions(-)
---
base-commit: a80198ba650f50d266d7fc4a6c5262df9970f9f2
change-id: 20250724-james-cs-syncfreq-7c2257a38ed3
Best regards,
--
James Clark <james.clark(a)linaro.org>
Do some cleanups then add a new format attribute to set the timestamp
interval for ETMv4 in Perf mode. The current interval is too high for
most use cases, and particularly on the FVP the number of timestamps
generated is excessive.
Although it would be good to make only SYNC timestamps the default and
have counter timestamps opt-in, this would be a breaking change. We
can always do that later, or disable counter timestamps from Perf.
This is added as an event format attribute, rather than a Coresight
config because it's something that the driver is already configuring
automatically in Perf mode with any unused counter, so it's not possible
to modify this with a config.
Applies to coresight/next
Signed-off-by: James Clark <james.clark(a)linaro.org>
---
Changes in v2:
- Only show the attribute for ETMv4 to improve usability and fix the
arm32 build error. Wrapping everything in
IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X) isn't ideal, but the -perf.c
file is shared between ETMv3 and ETMv4, and there is already precedent
for doing it this way.
- Link to v1: https://lore.kernel.org/r/20250811-james-cs-syncfreq-v1-0-b001cd6e3404@lina…
---
James Clark (6):
coresight: Change syncfreq to be a u8
coresight: Fix holes in struct etmv4_config
coresight: Repack struct etmv4_drvdata
coresight: Refactor etm4_config_timestamp_event()
coresight: Add format attribute for setting the timestamp interval
coresight: docs: Document etm4x ts_interval
Documentation/trace/coresight/coresight.rst | 14 +++
drivers/hwtracing/coresight/coresight-etm-perf.c | 13 ++-
drivers/hwtracing/coresight/coresight-etm4x-core.c | 110 +++++++++++++--------
drivers/hwtracing/coresight/coresight-etm4x.h | 86 ++++++++++------
4 files changed, 151 insertions(+), 72 deletions(-)
---
base-commit: a80198ba650f50d266d7fc4a6c5262df9970f9f2
change-id: 20250724-james-cs-syncfreq-7c2257a38ed3
Best regards,
--
James Clark <james.clark(a)linaro.org>
On Thu, 25 Sept 2025 at 16:04, Sean Anderson <sean.anderson(a)linux.dev> wrote:
>
> To make it easier to determine where to add new release actions, reorder
> the actions in coresight_device_release to be the reverse of
> coresight_register.
>
> Signed-off-by: Sean Anderson <sean.anderson(a)linux.dev>
> ---
>
> Changes in v5:
> - New
>
> drivers/hwtracing/coresight/coresight-core.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> index 022c8384b98d..305b1773cfbe 100644
> --- a/drivers/hwtracing/coresight/coresight-core.c
> +++ b/drivers/hwtracing/coresight/coresight-core.c
> @@ -1046,8 +1046,8 @@ static void coresight_device_release(struct device *dev)
> {
> struct coresight_device *csdev = to_coresight_device(dev);
>
> - fwnode_handle_put(csdev->dev.fwnode);
> free_percpu(csdev->perf_sink_id_map.cpu_map);
> + fwnode_handle_put(csdev->dev.fwnode);
> kfree(csdev);
> }
>
> --
> 2.35.1.1320.gc452695387.dirty
>
Reviewed-by: Mike Leach <mike.leach(a)linaro.org>
--
Mike Leach
Principal Engineer, ARM Ltd.
Manchester Design Centre. UK
On Thu, 25 Sept 2025 at 16:04, Sean Anderson <sean.anderson(a)linux.dev> wrote:
>
> If registering the CPU map fails, we need to put the fwnode. free_percpu
> works when called with a NULL pointer, so just use
> coresight_device_release.
>
> Fixes: 5ad628a76176 ("coresight: Use per-sink trace ID maps for Perf sessions")
> Signed-off-by: Sean Anderson <sean.anderson(a)linux.dev>
> ---
>
> (no changes since v4)
>
> Changes in v4:
> - New
>
> drivers/hwtracing/coresight/coresight-core.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> index fa758cc21827..022c8384b98d 100644
> --- a/drivers/hwtracing/coresight/coresight-core.c
> +++ b/drivers/hwtracing/coresight/coresight-core.c
> @@ -1352,7 +1352,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
> raw_spin_lock_init(&csdev->perf_sink_id_map.lock);
> csdev->perf_sink_id_map.cpu_map = alloc_percpu(atomic_t);
> if (!csdev->perf_sink_id_map.cpu_map) {
> - kfree(csdev);
> + coresight_device_release(&csdev->dev);
> ret = -ENOMEM;
> goto err_out;
> }
> --
> 2.35.1.1320.gc452695387.dirty
>
Reviewed-by: Mike Leach <mike.leach(a)linaro.org>
--
Mike Leach
Principal Engineer, ARM Ltd.
Manchester Design Centre. UK
Hi,
On Tue, 23 Sept 2025 at 02:49, Jie Gan <jie.gan(a)oss.qualcomm.com> wrote:
>
>
>
> On 9/23/2025 1:31 AM, Carl Worth wrote:
> > Jie Gan <jie.gan(a)oss.qualcomm.com> writes:
> >> From: Carl Worth <carl(a)os.amperecomputing.com>
> >>
> >> The handle is essential for retrieving the AUX_EVENT of each CPU and is
> >> required in perf mode. It has been added to the coresight_path so that
> >> dependent devices can access it from the path when needed.
> >
> > I'd still like to have the original command I used to trigger the bug in
> > the commit message. I really like having reproduction steps captured in
> > commit messages when I look back at commits in the future. So, that was:
> >
> > perf record -e cs_etm//k -C 0-9 dd if=/dev/zero of=/dev/null
> >
>
> Sure, I’ll include your commit message in the formal patch series, I
> think it's V3 since you have submitted two versions, if you're okay with
> me sending it out.
>
> >> /**
> >> * struct coresight_path - data needed by enable/disable path
> >> - * @path_list: path from source to sink.
> >> - * @trace_id: trace_id of the whole path.
> >> + * @path_list: path from source to sink.
> >> + * @trace_id: trace_id of the whole path.
> >> + * struct perf_output_handle: handle of the aux_event.
> >> */
> >
> > Fixing to "@handle" was mentioned in another comment already.
> >
> > Something about the above still feels a little off to me. It feels like
> > we're throwing new data into a structure just because it happens to be
> > conveniently at hand for the code paths we're needing, and not because
> > it really _belongs_ there.
> >
>
This data is perf specific - not path generic; so I agree that this
structure should go elsewhere.
I would suggest in the csdev (coresight_device) structure itself. We
already have some sink specific data in here e.g. perf_sink_id_map.
This could then be set/clear in the functions coresight-etm-perf.c
file, where there is a significant amount of code dealing with the
perf handle and ensuring it is valid and in scope.
This can then be set only when appropriate - for source / sink devices
and only when in perf mode, and avoid the need to pass the handle
around as API call parameters.
Regards
Mike.
> The core idea behind coresight_path is that it can hold all the data
> potentially needed by any device along the path.
>
> For example with the path ETM->Link->ETR->CATU:
>
> All the mentioned devices operate by forming a path, for which the
> system constructs a coresight_path. This 'path' is then passed to each
> device along the route, allowing any device to directly access the
> required data from coresight_path instead of receiving it as a separate
> argument.
>
> Imagine a device that requires more than two or three arguments, and you
> want to pass them through coresight_enable_path or similar functions...
>
> For certain coresight_path instances, we may not need the handle or
> other parameters. Since these values are initialized, it's acceptable to
> leave them as NULL or 0.
>
>
> > Or, maybe it's the right place for it, and the cause of my concern is
> > that "path" is an overly-narrow name in struct coresight_path?
> >
>
> It defines the direction of data flow—serving as the path for trace data.
>
> Thanks,
> Jie
>
> > But if a renaming of this structure would improve the code, I'd also be
> > fine with that happening in a subsequent commit, so I won't try to hold
> > up the current series based on that.
> >
> > -Carl
>
--
Mike Leach
Principal Engineer, ARM Ltd.
Manchester Design Centre. UK