From: Przemek Kitszel <przemyslaw.kitszel@intel.com>
To: intel-wired-lan@lists.osuosl.org,
Michal Schmidt <mschmidt@redhat.com>,
Jakub Kicinski <kuba@kernel.org>, Jiri Pirko <jiri@resnulli.us>
Cc: netdev@vger.kernel.org, Simon Horman <horms@kernel.org>,
Tony Nguyen <anthony.l.nguyen@intel.com>,
Michal Swiatkowski <michal.swiatkowski@linux.intel.com>,
bruce.richardson@intel.com,
Vladimir Medvedkin <vladimir.medvedkin@intel.com>,
padraig.j.connolly@intel.com, ananth.s@intel.com,
timothy.miskell@intel.com,
Jacob Keller <jacob.e.keller@intel.com>,
Lukasz Czapnik <lukasz.czapnik@intel.com>,
Aleksandr Loktionov <aleksandr.loktionov@intel.com>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Paolo Abeni <pabeni@redhat.com>,
Saeed Mahameed <saeedm@nvidia.com>,
Leon Romanovsky <leon@kernel.org>,
Tariq Toukan <tariqt@nvidia.com>, Mark Bloch <mbloch@nvidia.com>,
Przemek Kitszel <przemyslaw.kitszel@intel.com>
Subject: [PATCH iwl-next v1 12/15] ice: introduce handling of virtchnl LARGE VF opcodes
Date: Fri, 8 May 2026 14:42:05 +0200 [thread overview]
Message-ID: <20260508124208.11622-13-przemyslaw.kitszel@intel.com> (raw)
In-Reply-To: <20260508124208.11622-1-przemyslaw.kitszel@intel.com>
From: Brett Creeley <brett.creeley@intel.com>
With new virtchnl offload/capability VFs are able to make use of more than
16 queues. But to old opcodes were designed with a max of 16 queues, so
new ones were added (by iavf/virtchnl commit of this series):
VIRTCHNL_OP_GET_MAX_RSS_QREGION, VIRTCHNL_OP_ENABLE_QUEUES_V2,
VIRTCHNL_OP_DISABLE_QUEUES_V2, VIRTCHNL_OP_MAP_QUEUE_VECTOR.
If a VF wishes to request >16 queues it should first make sure that the
PF supports the VIRTCHNL_VF_LARGE_NUM_QPAIRS capability.
Co-developed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Co-developed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com> # msglen val
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Brett Creeley <brett.creeley@intel.com>
---
drivers/net/ethernet/intel/ice/ice_vf_lib.h | 1 +
drivers/net/ethernet/intel/ice/virt/queues.h | 3 +
.../net/ethernet/intel/ice/virt/allowlist.c | 8 +
drivers/net/ethernet/intel/ice/virt/queues.c | 324 ++++++++++++++++++
4 files changed, 336 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
index 1b56f7150eb7..5411eaa1761c 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
@@ -125,6 +125,7 @@ struct ice_vf_ops {
void (*clear_reset_trigger)(struct ice_vf *vf);
void (*irq_close)(struct ice_vf *vf);
void (*post_vsi_rebuild)(struct ice_vf *vf);
+ struct ice_q_vector *(*get_q_vector)(struct ice_vsi *vsi, u16 vec_id);
};
/* Virtchnl/SR-IOV config info */
diff --git a/drivers/net/ethernet/intel/ice/virt/queues.h b/drivers/net/ethernet/intel/ice/virt/queues.h
index c4a792cecea1..223f609dd4f3 100644
--- a/drivers/net/ethernet/intel/ice/virt/queues.h
+++ b/drivers/net/ethernet/intel/ice/virt/queues.h
@@ -16,5 +16,8 @@ int ice_vc_cfg_q_bw(struct ice_vf *vf, u8 *msg);
int ice_vc_cfg_q_quanta(struct ice_vf *vf, u8 *msg);
int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg);
int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg);
+int ice_vc_ena_qs_v2_msg(struct ice_vf *vf, u8 *msg, u16 msglen);
+int ice_vc_dis_qs_v2_msg(struct ice_vf *vf, u8 *msg, u16 msglen);
+int ice_vc_map_q_vector_msg(struct ice_vf *vf, u8 *msg, u16 msglen);
#endif /* _ICE_VIRT_QUEUES_H_ */
diff --git a/drivers/net/ethernet/intel/ice/virt/allowlist.c b/drivers/net/ethernet/intel/ice/virt/allowlist.c
index a07efec19c45..ef769b843c6f 100644
--- a/drivers/net/ethernet/intel/ice/virt/allowlist.c
+++ b/drivers/net/ethernet/intel/ice/virt/allowlist.c
@@ -95,6 +95,13 @@ static const u32 tc_allowlist_opcodes[] = {
VIRTCHNL_OP_CONFIG_QUANTA,
};
+static const u32 large_num_qpairs_allowlist_opcodes[] = {
+ VIRTCHNL_OP_GET_MAX_RSS_QREGION,
+ VIRTCHNL_OP_ENABLE_QUEUES_V2,
+ VIRTCHNL_OP_DISABLE_QUEUES_V2,
+ VIRTCHNL_OP_MAP_QUEUE_VECTOR,
+};
+
struct allowlist_opcode_info {
const u32 *opcodes;
size_t size;
@@ -117,6 +124,7 @@ static const struct allowlist_opcode_info allowlist_opcodes[] = {
ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_VLAN_V2, vlan_v2_allowlist_opcodes),
ALLOW_ITEM(VIRTCHNL_VF_OFFLOAD_QOS, tc_allowlist_opcodes),
ALLOW_ITEM(VIRTCHNL_VF_CAP_PTP, ptp_allowlist_opcodes),
+ ALLOW_ITEM(VIRTCHNL_VF_LARGE_NUM_QPAIRS, large_num_qpairs_allowlist_opcodes),
};
/**
diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
index 1d9f69026d1b..b99f18a25024 100644
--- a/drivers/net/ethernet/intel/ice/virt/queues.c
+++ b/drivers/net/ethernet/intel/ice/virt/queues.c
@@ -1021,3 +1021,327 @@ int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg)
v_ret, (u8 *)vfres, sizeof(*vfres));
}
+static bool ice_vc_supported_queue_type(s32 queue_type)
+{
+ return queue_type == VIRTCHNL_QUEUE_TYPE_RX ||
+ queue_type == VIRTCHNL_QUEUE_TYPE_TX;
+}
+
+/**
+ * ice_vc_validate_qs_v2_msg - validate all qs_msg parameters
+ * @vf: VF the message was received from
+ * @qs_msg: contents of the message from the VF
+ * @msglen: length of @qs_msg
+ *
+ * Used to validate both the VIRTCHNL_OP_ENABLE_QUEUES_V2 and
+ * VIRTCHNL_OP_DISABLE_QUEUES_V2 messages. This should always be called before
+ * attempting to enable and/or disable queues on behalf of a VF in response to
+ * the previously mentioned opcodes.
+ *
+ * Return: If all checks succeed, then return true. Otherwise return
+ * false, indicating to the caller that the qs_msg is invalid.
+ */
+static bool ice_vc_validate_qs_v2_msg(struct ice_vf *vf,
+ struct virtchnl_del_ena_dis_queues *qs_msg,
+ u16 msglen)
+{
+ if (msglen < virtchnl_struct_size(qs_msg, chunks, 0))
+ return false;
+
+ if (msglen < virtchnl_struct_size(qs_msg, chunks, qs_msg->num_chunks))
+ return false;
+
+ if (!qs_msg->num_chunks)
+ return false;
+
+ if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states))
+ return false;
+
+ if (!ice_vc_isvalid_vsi_id(vf, qs_msg->vport_id))
+ return false;
+
+ for (int i = 0; i < qs_msg->num_chunks; i++) {
+ u32 max_queue_in_chunk;
+
+ if (!ice_vc_supported_queue_type(qs_msg->chunks[i].type))
+ return false;
+
+ if (!qs_msg->chunks[i].num_queues)
+ return false;
+
+ max_queue_in_chunk = qs_msg->chunks[i].start_queue_id +
+ qs_msg->chunks[i].num_queues;
+ if (max_queue_in_chunk > vf->num_vf_qs)
+ return false;
+ }
+
+ return true;
+}
+
+#define ice_for_each_q_in_chunk(chunk, q_id) \
+ for ((q_id) = (chunk)->start_queue_id; \
+ (q_id) < (chunk)->start_queue_id + (chunk)->num_queues; \
+ (q_id)++)
+
+static int
+ice_vc_ena_rxq_chunk(struct ice_vf *vf, struct virtchnl_queue_chunk *chunk)
+{
+ struct ice_vsi *vsi;
+ u32 vf_qid;
+
+ ice_for_each_q_in_chunk(chunk, vf_qid) {
+ int err;
+
+ vsi = ice_get_vf_vsi(vf);
+ err = ice_vf_vsi_ena_single_rxq(vf, vsi, vf_qid);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+ice_vc_ena_txq_chunk(struct ice_vf *vf, struct virtchnl_queue_chunk *chunk)
+{
+ struct ice_vsi *vsi;
+ u32 vf_qid;
+
+ ice_for_each_q_in_chunk(chunk, vf_qid) {
+ vsi = ice_get_vf_vsi(vf);
+ ice_vf_vsi_ena_single_txq(vf, vsi, vf_qid);
+ }
+
+ return 0;
+}
+
+/**
+ * ice_vc_ena_qs_v2_msg - message handling for VIRTCHNL_OP_ENABLE_QUEUES_V2
+ * @vf: source of the request
+ * @msg: message to handle
+ * @msglen: length of the @msg
+ *
+ * Return: 0 on success or negative on error.
+ */
+int ice_vc_ena_qs_v2_msg(struct ice_vf *vf, u8 *msg, u16 msglen)
+{
+ struct virtchnl_del_ena_dis_queues *ena_qs_msg =
+ (struct virtchnl_del_ena_dis_queues *)msg;
+ enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
+
+ if (!ice_vc_validate_qs_v2_msg(vf, ena_qs_msg, msglen))
+ goto error_param;
+
+ for (int i = 0; i < ena_qs_msg->num_chunks; i++) {
+ struct virtchnl_queue_chunk *chunk = &ena_qs_msg->chunks[i];
+
+ if (chunk->type == VIRTCHNL_QUEUE_TYPE_RX &&
+ ice_vc_ena_rxq_chunk(vf, chunk))
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ else if (chunk->type == VIRTCHNL_QUEUE_TYPE_TX &&
+ ice_vc_ena_txq_chunk(vf, chunk))
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+
+ if (v_ret != VIRTCHNL_STATUS_SUCCESS)
+ goto error_param;
+ }
+
+ set_bit(ICE_VF_STATE_QS_ENA, vf->vf_states);
+
+error_param:
+ return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ENABLE_QUEUES_V2,
+ v_ret, NULL, 0);
+}
+
+static int
+ice_vc_dis_rxq_chunk(struct ice_vf *vf, struct virtchnl_queue_chunk *chunk)
+{
+ struct ice_vsi *vsi;
+ u32 vf_qid;
+
+ ice_for_each_q_in_chunk(chunk, vf_qid) {
+ int err;
+
+ vsi = ice_get_vf_vsi(vf);
+ err = ice_vf_vsi_dis_single_rxq(vf, vsi, vf_qid);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+ice_vc_dis_txq_chunk(struct ice_vf *vf, struct virtchnl_queue_chunk *chunk)
+{
+ struct ice_vsi *vsi;
+ u32 vf_qid;
+
+ ice_for_each_q_in_chunk(chunk, vf_qid) {
+ int err;
+
+ vsi = ice_get_vf_vsi(vf);
+ err = ice_vf_vsi_dis_single_txq(vf, vsi, vf_qid);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_vc_dis_qs_v2_msg - message handling for VIRTCHNL_OP_DISABLE_QUEUES_V2
+ * @vf: source of the request
+ * @msg: message to handle
+ * @msglen: length of @msg
+ *
+ * Return: 0 on success or negative on error.
+ */
+int ice_vc_dis_qs_v2_msg(struct ice_vf *vf, u8 *msg, u16 msglen)
+{
+ struct virtchnl_del_ena_dis_queues *dis_qs_msg =
+ (struct virtchnl_del_ena_dis_queues *)msg;
+ enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
+
+ if (!ice_vc_validate_qs_v2_msg(vf, dis_qs_msg, msglen))
+ goto error_param;
+
+ for (int i = 0; i < dis_qs_msg->num_chunks; i++) {
+ struct virtchnl_queue_chunk *chunk = &dis_qs_msg->chunks[i];
+
+ if (chunk->type == VIRTCHNL_QUEUE_TYPE_RX &&
+ ice_vc_dis_rxq_chunk(vf, chunk))
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ else if (chunk->type == VIRTCHNL_QUEUE_TYPE_TX &&
+ ice_vc_dis_txq_chunk(vf, chunk))
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+
+ if (v_ret != VIRTCHNL_STATUS_SUCCESS)
+ goto error_param;
+ }
+
+ if (ice_vf_has_no_qs_ena(vf))
+ clear_bit(ICE_VF_STATE_QS_ENA, vf->vf_states);
+
+error_param:
+ return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DISABLE_QUEUES_V2,
+ v_ret, NULL, 0);
+}
+
+/**
+ * ice_vc_validate_qv_maps - validate parameters sent in the qs_msg structure
+ * @vf: VF the message was received from
+ * @qv_maps: contents of the message from the VF
+ * @msglen: length of the @qv_maps
+ *
+ * Used to validate VIRTCHNL_OP_MAP_QUEUE_VECTOR messages. This should always
+ * be called before attempting map interrupts to queues. If all checks succeed,
+ * then return success indicating to the caller that the qv_maps are valid.
+ * Otherwise return false, indicating to the caller that the qv_maps are
+ * invalid.
+ *
+ * Return: true if parameters are valid, false otherwise.
+ */
+static bool ice_vc_validate_qv_maps(struct ice_vf *vf,
+ struct virtchnl_queue_vector_maps *qv_maps,
+ u16 msglen)
+{
+ struct ice_vsi *vsi;
+ int total_vectors;
+
+ vsi = vf->pf->vsi[vf->lan_vsi_idx];
+ if (!vsi)
+ return false;
+
+ if (msglen < virtchnl_struct_size(qv_maps, qv_maps, 0))
+ return false;
+
+ if (msglen < virtchnl_struct_size(qv_maps, qv_maps, qv_maps->num_qv_maps))
+ return false;
+
+ if (!qv_maps->num_qv_maps)
+ return false;
+
+ if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states))
+ return false;
+
+ if (!ice_vc_isvalid_vsi_id(vf, qv_maps->vport_id))
+ return false;
+
+ total_vectors = vsi->num_q_vectors + ICE_NONQ_VECS_VF;
+
+ for (int i = 0; i < qv_maps->num_qv_maps; i++) {
+ if (!ice_vc_supported_queue_type(qv_maps->qv_maps[i].queue_type))
+ return false;
+
+ if (qv_maps->qv_maps[i].queue_id >= vf->num_vf_qs)
+ return false;
+
+ if (qv_maps->qv_maps[i].vector_id >= total_vectors ||
+ qv_maps->qv_maps[i].vector_id < ICE_NONQ_VECS_VF)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * ice_vc_map_q_vector_msg - message handling for VIRTCHNL_OP_MAP_QUEUE_VECTOR
+ * @vf: source of the request
+ * @msg: message to handle
+ * @msglen: length of @msg
+ *
+ * Return: 0 on success or negative on error
+ */
+int ice_vc_map_q_vector_msg(struct ice_vf *vf, u8 *msg, u16 msglen)
+{
+ enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
+ struct virtchnl_queue_vector_maps *qv_maps;
+ struct ice_vsi *vsi;
+
+ qv_maps = (struct virtchnl_queue_vector_maps *)msg;
+
+ if (!ice_vc_validate_qv_maps(vf, qv_maps, msglen)) {
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ goto error_param;
+ }
+
+ for (int i = 0; i < qv_maps->num_qv_maps; i++) {
+ struct virtchnl_queue_vector *qv_map = &qv_maps->qv_maps[i];
+ struct ice_q_vector *q_vector;
+ u16 vector_id;
+ int vsi_q_id;
+
+ vsi = ice_get_vf_vsi(vf);
+ vsi_q_id = qv_map->queue_id;
+ vector_id = qv_map->vector_id;
+
+ if (!vsi) {
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ goto error_param;
+ }
+
+ q_vector = vf->vf_ops->get_q_vector(vsi, vector_id);
+
+ if (!q_vector) {
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ goto error_param;
+ }
+
+ if (!ice_vc_isvalid_q_id(vsi, vsi_q_id))
+ return VIRTCHNL_STATUS_ERR_PARAM;
+
+ if (qv_map->queue_type == VIRTCHNL_QUEUE_TYPE_RX)
+ ice_cfg_rxq_interrupt(vsi, vsi_q_id,
+ q_vector->vf_reg_idx,
+ qv_map->itr_idx);
+ else if (qv_map->queue_type == VIRTCHNL_QUEUE_TYPE_TX)
+ ice_cfg_txq_interrupt(vsi, vsi_q_id,
+ q_vector->vf_reg_idx,
+ qv_map->itr_idx);
+ }
+
+error_param:
+ return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_MAP_QUEUE_VECTOR,
+ v_ret, NULL, 0);
+}
--
2.39.3
next prev parent reply other threads:[~2026-05-08 13:00 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-08 12:41 [PATCH iwl-next v1 00/15] devlink, mlx5, iavf, ice: XLVF for iavf Przemek Kitszel
2026-05-08 12:41 ` [PATCH iwl-next v1 01/15] devlink, mlx5: add init/fini ops for shared devlink Przemek Kitszel
2026-05-11 11:36 ` Jiri Pirko
2026-05-11 13:26 ` Przemek Kitszel
2026-05-08 12:41 ` [PATCH iwl-next v1 02/15] ice: use shared devlink to store ice_adapters instead of custom xarray Przemek Kitszel
2026-05-08 12:41 ` [PATCH iwl-next v1 03/15] ice: simplify ice_vc_dis_qs_msg() a little Przemek Kitszel
2026-05-08 13:31 ` Loktionov, Aleksandr
2026-05-08 12:41 ` [PATCH iwl-next v1 04/15] ice: add VF queue ena/dis helper functions Przemek Kitszel
2026-05-08 13:37 ` Loktionov, Aleksandr
2026-05-11 9:33 ` Przemek Kitszel
2026-05-08 12:41 ` [PATCH iwl-next v1 05/15] ice: add helpers for Global RSS LUT alloc, free, vsi_update Przemek Kitszel
2026-05-08 13:38 ` Loktionov, Aleksandr
2026-05-08 12:41 ` [PATCH iwl-next v1 06/15] ice: rename ICE_MAX_RSS_QS_PER_VF to ICE_MAX_QS_PER_VF_VCV1 Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 07/15] ice: bump to 256qs for VF Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 08/15] iavf: extend iavf_configure_queues() to support more queues Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 09/15] iavf: temporary rename of IAVF_MAX_REQ_QUEUES to IAVF_MAX_REQ_QUEUES_VCV1 Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 10/15] iavf: increase max number of queues to 256 Przemek Kitszel
2026-05-08 16:49 ` Loktionov, Aleksandr
2026-05-11 9:37 ` Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 11/15] iavf: use new opcodes to request more than 16 queues Przemek Kitszel
2026-05-08 12:42 ` Przemek Kitszel [this message]
2026-05-08 16:55 ` [PATCH iwl-next v1 12/15] ice: introduce handling of virtchnl LARGE VF opcodes Loktionov, Aleksandr
2026-05-11 9:39 ` Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 13/15] devlink: give user option to allocate resources Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 14/15] ice: represent RSS LUTs as devlink resources Przemek Kitszel
2026-05-08 17:03 ` Loktionov, Aleksandr
2026-05-11 9:41 ` Przemek Kitszel
2026-05-08 12:42 ` [PATCH iwl-next v1 15/15] ice: support up to 256 VF queues Przemek Kitszel
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=20260508124208.11622-13-przemyslaw.kitszel@intel.com \
--to=przemyslaw.kitszel@intel.com \
--cc=aleksandr.loktionov@intel.com \
--cc=ananth.s@intel.com \
--cc=andrew+netdev@lunn.ch \
--cc=anthony.l.nguyen@intel.com \
--cc=bruce.richardson@intel.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=intel-wired-lan@lists.osuosl.org \
--cc=jacob.e.keller@intel.com \
--cc=jiri@resnulli.us \
--cc=kuba@kernel.org \
--cc=leon@kernel.org \
--cc=lukasz.czapnik@intel.com \
--cc=mbloch@nvidia.com \
--cc=michal.swiatkowski@linux.intel.com \
--cc=mschmidt@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=padraig.j.connolly@intel.com \
--cc=saeedm@nvidia.com \
--cc=tariqt@nvidia.com \
--cc=timothy.miskell@intel.com \
--cc=vladimir.medvedkin@intel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox