From: sashiko-bot@kernel.org
To: "Santhosh Kumar K" <s-k6@ti.com>,
broonie@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, miquel.raynal@bootlin.com, richard@nod.at,
vigneshr@ti.com, pratyush@kernel.org, mwalle@kernel.org,
takahiro.kuwano@infineon.com
Cc: devicetree@vger.kernel.org, linux-mtd@lists.infradead.org,
praneeth@ti.com, sebin.francis@ti.com, u-kumar1@ti.com,
srikanth.boyapally@amd.com, m-shah@ti.com
Subject: Re: [PATCH v11 04/19] spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
Date: Thu, 05 Nov 2026 11:24:15 +0000 [thread overview]
Message-ID: <sashiko-outbox-198944@kernel.org> (raw)
In-Reply-To: <20261105105832.2134313-5-s-k6@ti.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] spi: spi-mem: false per_op_freq failure when post_config equals max_speed_hz
- [Medium] spi: spi-mem: spi_mem_adjust_op_freq() conflates max_freq with post-config
- [Medium] spi: spi-mem: missing controller max_speed_hz check for post-config ops
--
commit 69ed76e4edf90b58e3385f80fa249633f3ea7445
Author: Santhosh Kumar K <s-k6@ti.com>
spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
This commit extends spi_mem_adjust_op_freq() to bypass the max_speed_hz cap
when op->max_freq matches post_config_max_speed_hz, and updates
spi_mem_default_supports_op() to require the per_op_freq controller
capability for post-config operations.
> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
> index b94680b60af95..7916387a4f1d0 100644
> --- a/drivers/spi/spi-mem.c
> +++ b/drivers/spi/spi-mem.c
> @@ -206,7 +206,8 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
> return false;
>
> if (op->max_freq &&
> - op->max_freq < mem->spi->max_speed_hz) {
> + (op->max_freq < mem->spi->max_speed_hz ||
> + op->max_freq == mem->spi->post_config_max_speed_hz)) {
> if (!spi_mem_controller_is_capable(ctlr, per_op_freq))
> return false;
> }
[Severity: Medium]
As reported by Sashiko on v10 [1], what happens in
spi_mem_default_supports_op() when mem->spi->post_config_max_speed_hz equals
mem->spi->max_speed_hz on a controller that does not advertise per_op_freq in
ctlr->mem_caps?
Both spi_mem_supports_op() and spi_mem_exec_op() call
spi_mem_adjust_op_freq() before calling spi_mem_internal_supports_op() ->
spi_mem_default_supports_op(). For any standard untuned operation where
op->max_freq is 0 or exceeds mem->spi->max_speed_hz, spi_mem_adjust_op_freq()
sets:
op->max_freq = mem->spi->max_speed_hz;
If spi-max-frequency and spi-max-post-config-frequency-hz are configured to
the same rate in Device Tree (or if __spi_setup() clamps spi->max_speed_hz to
ctlr->max_speed_hz matching post_config_max_speed_hz), then
op->max_freq == mem->spi->post_config_max_speed_hz evaluates to true for
every operation even though op->max_freq is equal to the base rate
mem->spi->max_speed_hz and no per-op frequency switching is needed.
Wouldn't this cause spi_mem_default_supports_op() to reject all SPI memory
operations with -EOPNOTSUPP on controllers without per_op_freq, and would
checking op->max_freq != mem->spi->max_speed_hz avoid this regression?
> @@ -627,9 +628,18 @@ EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> * Some chips have per-op frequency limitations and must adapt the maximum
> * speed. This function allows SPI mem drivers to set @op->max_freq to the
> * maximum supported value.
> + *
> + * When @mem->spi->post_config_max_speed_hz is set, ops with @op->max_freq
> + * equal to that value are treated as post-configuration ops (e.g. PHY-tuned)
> + * and are allowed to run at the full post-config rate. All other ops are
> + * capped to @mem->spi->max_speed_hz, the always-reachable base rate.
> */
> void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
> {
> + if (mem->spi->post_config_max_speed_hz &&
> + op->max_freq == mem->spi->post_config_max_speed_hz)
> + return;
[Severity: Medium]
As reported by Sashiko on v6 [2], can identifying post-configuration
operations in spi_mem_adjust_op_freq() purely by comparing
op->max_freq == mem->spi->post_config_max_speed_hz conflate static datasheet
frequency limits with tuned post-config operations?
In drivers/spi/spi.c, of_spi_parse_dt() populates
spi->post_config_max_speed_hz unconditionally during Device Tree parsing,
before any SPI memory driver probes or runs controller tuning:
if (!of_property_read_u32(nc, "spi-max-post-config-frequency-hz", &value))
spi->post_config_max_speed_hz = value;
Meanwhile, flash drivers such as drivers/mtd/nand/spi/winbond.c define static
op variant tables (e.g., read_cache_octal_variants) with non-zero datasheet
max_freq values alongside unrestricted variants with max_freq == 0:
SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
During initial chip detection in spinand_match_and_init() ->
spinand_select_op_variant() -> spinand_op_find_best_variant() in
drivers/mtd/nand/spi/core.c, each static variant is evaluated through
spi_mem_adjust_op_freq(), spi_mem_supports_op(), and
spi_mem_calc_op_duration() before tuning has occurred:
spi_mem_adjust_op_freq(spinand->spimem, &op);
if (!spi_mem_supports_op(spinand->spimem, &op))
break;
nbytes -= op.data.nbytes;
op_duration_ns += spi_mem_calc_op_duration(spinand->spimem, &op);
If a board configures spi-max-post-config-frequency-hz to a value that
happens to match a static variant's datasheet max_freq while
spi-max-frequency is set to a lower base rate, won't spi_mem_adjust_op_freq()
leave that variant uncapped while capping unrestricted variants (max_freq ==
0) and other frequency-limited variants to max_speed_hz? That can skew
spi_mem_calc_op_duration() into selecting a suboptimal variant, cause
spi_mem_default_supports_op() to reject matching variants on controllers
without per_op_freq, or allow an untuned op to execute at the post-config
rate before tuning has succeeded.
[Severity: Medium]
As reported by Sashiko on v10 [3], does returning early in
spi_mem_adjust_op_freq() when op->max_freq equals
mem->spi->post_config_max_speed_hz bypass the controller's hardware maximum
frequency limit (mem->spi->controller->max_speed_hz)?
In drivers/spi/spi.c, __spi_setup() clamps spi->max_speed_hz to
spi->controller->max_speed_hz:
if (spi->controller->max_speed_hz &&
(!spi->max_speed_hz ||
spi->max_speed_hz > spi->controller->max_speed_hz))
spi->max_speed_hz = spi->controller->max_speed_hz;
However, spi->post_config_max_speed_hz is never clamped to
spi->controller->max_speed_hz, and spi_mem_default_supports_op() only checks
mem->spi->controller->min_speed_hz:
if (op->max_freq && mem->spi->controller->min_speed_hz &&
op->max_freq < mem->spi->controller->min_speed_hz)
return false;
If spi-max-post-config-frequency-hz in Device Tree exceeds
mem->spi->controller->max_speed_hz, won't this early return pass an
op->max_freq above the controller's maximum supported speed to
spi_mem_supports_op(), spi_mem_calc_op_duration(), and
ctlr->mem_ops->exec_op()?
[1] https://lore.kernel.org/r/20260929063529.1235417-5-s-k6@ti.com
[2] https://lore.kernel.org/r/20260723113443.06C2D1F000E9@smtp.kernel.org
[3] https://lore.kernel.org/r/20260929063529.1235417-5-s-k6@ti.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105105832.2134313-1-s-k6@ti.com?part=4
WARNING: multiple messages have this Message-ID (diff)
From: sashiko-bot@kernel.org
To: "Santhosh Kumar K" <s-k6@ti.com>,
broonie@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, miquel.raynal@bootlin.com, richard@nod.at,
vigneshr@ti.com, pratyush@kernel.org, mwalle@kernel.org,
takahiro.kuwano@infineon.com
Cc: devicetree@vger.kernel.org, linux-mtd@lists.infradead.org,
praneeth@ti.com, sebin.francis@ti.com, u-kumar1@ti.com,
srikanth.boyapally@amd.com, m-shah@ti.com
Subject: Re: [PATCH v11 04/19] spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
Date: Thu, 05 Nov 2026 11:24:15 +0000 [thread overview]
Message-ID: <sashiko-outbox-198944@kernel.org> (raw)
In-Reply-To: <20261105105832.2134313-5-s-k6@ti.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] spi: spi-mem: false per_op_freq failure when post_config equals max_speed_hz
- [Medium] spi: spi-mem: spi_mem_adjust_op_freq() conflates max_freq with post-config
- [Medium] spi: spi-mem: missing controller max_speed_hz check for post-config ops
--
commit 69ed76e4edf90b58e3385f80fa249633f3ea7445
Author: Santhosh Kumar K <s-k6@ti.com>
spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
This commit extends spi_mem_adjust_op_freq() to bypass the max_speed_hz cap
when op->max_freq matches post_config_max_speed_hz, and updates
spi_mem_default_supports_op() to require the per_op_freq controller
capability for post-config operations.
> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
> index b94680b60af95..7916387a4f1d0 100644
> --- a/drivers/spi/spi-mem.c
> +++ b/drivers/spi/spi-mem.c
> @@ -206,7 +206,8 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
> return false;
>
> if (op->max_freq &&
> - op->max_freq < mem->spi->max_speed_hz) {
> + (op->max_freq < mem->spi->max_speed_hz ||
> + op->max_freq == mem->spi->post_config_max_speed_hz)) {
> if (!spi_mem_controller_is_capable(ctlr, per_op_freq))
> return false;
> }
[Severity: Medium]
As reported by Sashiko on v10 [1], what happens in
spi_mem_default_supports_op() when mem->spi->post_config_max_speed_hz equals
mem->spi->max_speed_hz on a controller that does not advertise per_op_freq in
ctlr->mem_caps?
Both spi_mem_supports_op() and spi_mem_exec_op() call
spi_mem_adjust_op_freq() before calling spi_mem_internal_supports_op() ->
spi_mem_default_supports_op(). For any standard untuned operation where
op->max_freq is 0 or exceeds mem->spi->max_speed_hz, spi_mem_adjust_op_freq()
sets:
op->max_freq = mem->spi->max_speed_hz;
If spi-max-frequency and spi-max-post-config-frequency-hz are configured to
the same rate in Device Tree (or if __spi_setup() clamps spi->max_speed_hz to
ctlr->max_speed_hz matching post_config_max_speed_hz), then
op->max_freq == mem->spi->post_config_max_speed_hz evaluates to true for
every operation even though op->max_freq is equal to the base rate
mem->spi->max_speed_hz and no per-op frequency switching is needed.
Wouldn't this cause spi_mem_default_supports_op() to reject all SPI memory
operations with -EOPNOTSUPP on controllers without per_op_freq, and would
checking op->max_freq != mem->spi->max_speed_hz avoid this regression?
> @@ -627,9 +628,18 @@ EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> * Some chips have per-op frequency limitations and must adapt the maximum
> * speed. This function allows SPI mem drivers to set @op->max_freq to the
> * maximum supported value.
> + *
> + * When @mem->spi->post_config_max_speed_hz is set, ops with @op->max_freq
> + * equal to that value are treated as post-configuration ops (e.g. PHY-tuned)
> + * and are allowed to run at the full post-config rate. All other ops are
> + * capped to @mem->spi->max_speed_hz, the always-reachable base rate.
> */
> void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
> {
> + if (mem->spi->post_config_max_speed_hz &&
> + op->max_freq == mem->spi->post_config_max_speed_hz)
> + return;
[Severity: Medium]
As reported by Sashiko on v6 [2], can identifying post-configuration
operations in spi_mem_adjust_op_freq() purely by comparing
op->max_freq == mem->spi->post_config_max_speed_hz conflate static datasheet
frequency limits with tuned post-config operations?
In drivers/spi/spi.c, of_spi_parse_dt() populates
spi->post_config_max_speed_hz unconditionally during Device Tree parsing,
before any SPI memory driver probes or runs controller tuning:
if (!of_property_read_u32(nc, "spi-max-post-config-frequency-hz", &value))
spi->post_config_max_speed_hz = value;
Meanwhile, flash drivers such as drivers/mtd/nand/spi/winbond.c define static
op variant tables (e.g., read_cache_octal_variants) with non-zero datasheet
max_freq values alongside unrestricted variants with max_freq == 0:
SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
During initial chip detection in spinand_match_and_init() ->
spinand_select_op_variant() -> spinand_op_find_best_variant() in
drivers/mtd/nand/spi/core.c, each static variant is evaluated through
spi_mem_adjust_op_freq(), spi_mem_supports_op(), and
spi_mem_calc_op_duration() before tuning has occurred:
spi_mem_adjust_op_freq(spinand->spimem, &op);
if (!spi_mem_supports_op(spinand->spimem, &op))
break;
nbytes -= op.data.nbytes;
op_duration_ns += spi_mem_calc_op_duration(spinand->spimem, &op);
If a board configures spi-max-post-config-frequency-hz to a value that
happens to match a static variant's datasheet max_freq while
spi-max-frequency is set to a lower base rate, won't spi_mem_adjust_op_freq()
leave that variant uncapped while capping unrestricted variants (max_freq ==
0) and other frequency-limited variants to max_speed_hz? That can skew
spi_mem_calc_op_duration() into selecting a suboptimal variant, cause
spi_mem_default_supports_op() to reject matching variants on controllers
without per_op_freq, or allow an untuned op to execute at the post-config
rate before tuning has succeeded.
[Severity: Medium]
As reported by Sashiko on v10 [3], does returning early in
spi_mem_adjust_op_freq() when op->max_freq equals
mem->spi->post_config_max_speed_hz bypass the controller's hardware maximum
frequency limit (mem->spi->controller->max_speed_hz)?
In drivers/spi/spi.c, __spi_setup() clamps spi->max_speed_hz to
spi->controller->max_speed_hz:
if (spi->controller->max_speed_hz &&
(!spi->max_speed_hz ||
spi->max_speed_hz > spi->controller->max_speed_hz))
spi->max_speed_hz = spi->controller->max_speed_hz;
However, spi->post_config_max_speed_hz is never clamped to
spi->controller->max_speed_hz, and spi_mem_default_supports_op() only checks
mem->spi->controller->min_speed_hz:
if (op->max_freq && mem->spi->controller->min_speed_hz &&
op->max_freq < mem->spi->controller->min_speed_hz)
return false;
If spi-max-post-config-frequency-hz in Device Tree exceeds
mem->spi->controller->max_speed_hz, won't this early return pass an
op->max_freq above the controller's maximum supported speed to
spi_mem_supports_op(), spi_mem_calc_op_duration(), and
ctlr->mem_ops->exec_op()?
[1] https://lore.kernel.org/r/20260929063529.1235417-5-s-k6@ti.com
[2] https://lore.kernel.org/r/20260723113443.06C2D1F000E9@smtp.kernel.org
[3] https://lore.kernel.org/r/20260929063529.1235417-5-s-k6@ti.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105105832.2134313-1-s-k6@ti.com?part=4
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
next prev parent reply other threads:[~2026-11-05 11:24 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 10:58 [PATCH v11 00/19] spi: cadence-quadspi: add PHY tuning support Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 01/19] spi: dt-bindings: add spi-max-post-config-frequency-hz property Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 16:41 ` Krzysztof Kozlowski
2026-11-05 16:41 ` Krzysztof Kozlowski
2026-11-05 16:42 ` Krzysztof Kozlowski
2026-11-05 16:42 ` Krzysztof Kozlowski
2026-11-05 10:58 ` [PATCH v11 02/19] spi: dt-bindings: add spi-phy-pattern-partition property Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 16:42 ` Krzysztof Kozlowski
2026-11-05 16:42 ` Krzysztof Kozlowski
2026-11-05 10:58 ` [PATCH v11 03/19] spi: parse spi-max-post-config-frequency-hz into post_config_max_speed_hz Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 11:18 ` sashiko-bot
2026-11-05 11:18 ` sashiko-bot
2026-11-05 10:58 ` [PATCH v11 04/19] spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 11:24 ` sashiko-bot [this message]
2026-11-05 11:24 ` sashiko-bot
2026-11-05 10:58 ` [PATCH v11 05/19] spi: spi-mem: add execute_tuning callback and spi_mem_execute_tuning() Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 06/19] spi: cadence-quadspi: move cqspi_readdata_capture earlier Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 07/19] spi: cadence-quadspi: add DQS support to read data capture Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 08/19] spi: cadence-quadspi: add PHY tuning support Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 11:22 ` sashiko-bot
2026-11-05 11:22 ` sashiko-bot
2026-11-05 10:58 ` [PATCH v11 09/19] spi: cadence-quadspi: skip DDR PHY tuning for 2-byte-address ops (i2383) Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 10/19] spi: cadence-quadspi: refactor direct read path for PHY support Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 11/19] spi: cadence-quadspi: enable PHY for direct reads Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 11:27 ` sashiko-bot
2026-11-05 11:27 ` sashiko-bot
2026-11-05 10:58 ` [PATCH v11 12/19] spi: cadence-quadspi: enable PHY for indirect writes Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 11:21 ` sashiko-bot
2026-11-05 11:21 ` sashiko-bot
2026-11-05 10:58 ` [PATCH v11 13/19] spi: cadence-quadspi: reprogram CS timing on every chip-select switch Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 14/19] spi: cadence-quadspi: reprogram PHY DLL on runtime resume Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 15/19] mtd: spinand: extract variant ranking logic into spinand_op_find_best_variant() Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 16/19] mtd: spinand: negotiate optimal controller operating point before dirmap creation Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 11:31 ` sashiko-bot
2026-11-05 11:31 ` sashiko-bot
2026-11-05 10:58 ` [PATCH v11 17/19] mtd: spi-nor: extract read op template construction into helper Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 18/19] mtd: spi-nor: run controller optimization before dirmap creation Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
2026-11-05 10:58 ` [PATCH v11 19/19] mtd: spi-nor: reset max_read_op frequency for security register reads Santhosh Kumar K
2026-11-05 10:58 ` Santhosh Kumar K
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-198944@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=broonie@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=m-shah@ti.com \
--cc=miquel.raynal@bootlin.com \
--cc=mwalle@kernel.org \
--cc=praneeth@ti.com \
--cc=pratyush@kernel.org \
--cc=richard@nod.at \
--cc=robh@kernel.org \
--cc=s-k6@ti.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=sebin.francis@ti.com \
--cc=srikanth.boyapally@amd.com \
--cc=takahiro.kuwano@infineon.com \
--cc=u-kumar1@ti.com \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.