diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index dfbac929c7..17be31d5a4 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -8788,7 +8788,8 @@ learner_params_get(struct learner *l)
 	params->n_keys_max = l->size;
 
 	/* Timeout. */
-	params->key_timeout = l->timeout;
+	params->key_timeout[0] = l->timeout;
+	params->n_key_timeouts = 1;
 
 	return params;
 
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index 381a35c6e0..51bb464f5f 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -2215,7 +2215,8 @@ __instr_learn_exec(struct rte_swx_pipeline *p,
 					   l->mailbox,
 					   t->time,
 					   action_id,
-					   &t->metadata[mf_offset]);
+					   &t->metadata[mf_offset],
+					   0);
 
 	TRACE("[Thread %2u] learner %u learn %s\n",
 	      p->thread_id,
diff --git a/lib/table/rte_swx_table_learner.c b/lib/table/rte_swx_table_learner.c
index 15576c2aa3..222acfc962 100644
--- a/lib/table/rte_swx_table_learner.c
+++ b/lib/table/rte_swx_table_learner.c
@@ -157,6 +157,17 @@ hash(void *key, void *key_mask, uint32_t key_size, uint32_t seed)
 	}
 }
 
+/* n_bytes needs to be a multiple of 8 bytes. */
+static void
+table_keycpy(void *dst, void *src, void *src_mask, uint32_t n_bytes)
+{
+	uint64_t *dst64 = dst, *src64 = src, *src_mask64 = src_mask;
+	uint32_t i;
+
+	for (i = 0; i < n_bytes / sizeof(uint64_t); i++)
+		dst64[i] = src64[i] & src_mask64[i];
+}
+
 /*
  * Return: 0 = Keys are NOT equal; 1 = Keys are equal.
  */
@@ -230,12 +241,16 @@ table_keycmp(void *a, void *b, void *b_mask, uint32_t n_bytes)
 
 #define TABLE_KEYS_PER_BUCKET 4
 
+#define TABLE_BUCKET_USEFUL_SIZE \
+	(TABLE_KEYS_PER_BUCKET * (sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t)))
+
 #define TABLE_BUCKET_PAD_SIZE \
-	(RTE_CACHE_LINE_SIZE - TABLE_KEYS_PER_BUCKET * (sizeof(uint32_t) + sizeof(uint32_t)))
+	(RTE_CACHE_LINE_SIZE - TABLE_BUCKET_USEFUL_SIZE)
 
 struct table_bucket {
 	uint32_t time[TABLE_KEYS_PER_BUCKET];
 	uint32_t sig[TABLE_KEYS_PER_BUCKET];
+	uint8_t key_timeout_id[TABLE_KEYS_PER_BUCKET];
 	uint8_t pad[TABLE_BUCKET_PAD_SIZE];
 	uint8_t key[0];
 };
@@ -284,8 +299,11 @@ struct table_params {
 	/* log2(bucket_size). Purpose: avoid multiplication with non-power of 2 numbers. */
 	size_t bucket_size_log2;
 
-	/* Timeout in CPU clock cycles. */
-	uint64_t key_timeout;
+	/* Set of all possible key timeout values measured in CPU clock cycles. */
+	uint64_t key_timeout[RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX];
+
+	/* Number of key timeout values. */
+	uint32_t n_key_timeouts;
 
 	/* Total memory size. */
 	size_t total_size;
@@ -302,18 +320,42 @@ struct table {
 	uint8_t buckets[0];
 } __rte_cache_aligned;
 
+/* The timeout (in cycles) is stored in the table as a 32-bit value by truncating its least
+ * significant 32 bits. Therefore, to make sure the time is always advancing when adding the timeout
+ * value on top of the current time, the minimum timeout value is 1^32 cycles, which is 2 seconds on
+ * a 2 GHz CPU.
+ */
+static uint64_t
+timeout_convert(uint32_t timeout_in_seconds)
+{
+	uint64_t timeout_in_cycles = timeout_in_seconds * rte_get_tsc_hz();
+
+	if (!(timeout_in_cycles >> 32))
+		timeout_in_cycles = 1LLU << 32;
+
+	return timeout_in_cycles;
+}
+
 static int
 table_params_get(struct table_params *p, struct rte_swx_table_learner_params *params)
 {
+	uint32_t i;
+
 	/* Check input parameters. */
 	if (!params ||
 	    !params->key_size ||
 	    (params->key_size > 64) ||
 	    !params->n_keys_max ||
 	    (params->n_keys_max > 1U << 31) ||
-	    !params->key_timeout)
+	    !params->key_timeout ||
+	    !params->n_key_timeouts ||
+	    (params->n_key_timeouts > RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX))
 		return -EINVAL;
 
+	for (i = 0; i < params->n_key_timeouts; i++)
+		if (!params->key_timeout[i])
+			return -EINVAL;
+
 	/* Key. */
 	p->key_size = params->key_size;
 
@@ -346,7 +388,13 @@ table_params_get(struct table_params *p, struct rte_swx_table_learner_params *pa
 	p->bucket_size_log2 = __builtin_ctzll(p->bucket_size);
 
 	/* Timeout. */
-	p->key_timeout = params->key_timeout * rte_get_tsc_hz();
+	for (i = 0; i < params->n_key_timeouts; i++)
+		p->key_timeout[i] = timeout_convert(params->key_timeout[i]);
+
+	p->n_key_timeouts = rte_align32pow2(params->n_key_timeouts);
+
+	for ( ; i < p->n_key_timeouts; i++)
+		p->key_timeout[i] = p->key_timeout[0];
 
 	/* Total size. */
 	p->total_size = sizeof(struct table) + p->n_buckets * p->bucket_size;
@@ -423,6 +471,23 @@ rte_swx_table_learner_free(void *table)
 	env_free(t, t->params.total_size);
 }
 
+int
+rte_swx_table_learner_timeout_update(void *table,
+				     uint32_t key_timeout_id,
+				     uint32_t key_timeout)
+{
+	struct table *t = table;
+
+	if (!t ||
+	    (key_timeout_id >= t->params.n_key_timeouts) ||
+	    !key_timeout)
+		return -EINVAL;
+
+	t->params.key_timeout[key_timeout_id] = timeout_convert(key_timeout);
+
+	return 0;
+}
+
 struct mailbox {
 	/* Writer: lookup state 0. Reader(s): lookup state 1, add(). */
 	struct table_bucket *bucket;
@@ -505,8 +570,6 @@ rte_swx_table_learner_lookup(void *table,
 				/* Hit. */
 				rte_prefetch0(data);
 
-				b->time[i] = (input_time + t->params.key_timeout) >> 32;
-
 				m->hit = 1;
 				m->bucket_key_pos = i;
 				m->state = 0;
@@ -536,23 +599,83 @@ rte_swx_table_learner_lookup(void *table,
 	}
 }
 
+void
+rte_swx_table_learner_rearm(void *table,
+			    void *mailbox,
+			    uint64_t input_time)
+{
+	struct table *t = table;
+	struct mailbox *m = mailbox;
+	struct table_bucket *b;
+	size_t bucket_key_pos;
+	uint64_t key_timeout;
+	uint32_t key_timeout_id;
+
+	if (!m->hit)
+		return;
+
+	b = m->bucket;
+	bucket_key_pos = m->bucket_key_pos;
+
+	key_timeout_id = b->key_timeout_id[bucket_key_pos];
+	key_timeout = t->params.key_timeout[key_timeout_id];
+	b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
+}
+
+void
+rte_swx_table_learner_rearm_new(void *table,
+				void *mailbox,
+				uint64_t input_time,
+				uint32_t key_timeout_id)
+{
+	struct table *t = table;
+	struct mailbox *m = mailbox;
+	struct table_bucket *b;
+	size_t bucket_key_pos;
+	uint64_t key_timeout;
+
+	if (!m->hit)
+		return;
+
+	b = m->bucket;
+	bucket_key_pos = m->bucket_key_pos;
+
+	key_timeout_id &= t->params.n_key_timeouts - 1;
+	key_timeout = t->params.key_timeout[key_timeout_id];
+	b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
+	b->key_timeout_id[bucket_key_pos] = (uint8_t)key_timeout_id;
+}
+
 uint32_t
 rte_swx_table_learner_add(void *table,
 			  void *mailbox,
 			  uint64_t input_time,
 			  uint64_t action_id,
-			  uint8_t *action_data)
+			  uint8_t *action_data,
+			  uint32_t key_timeout_id)
 {
 	struct table *t = table;
 	struct mailbox *m = mailbox;
 	struct table_bucket *b = m->bucket;
+	uint64_t key_timeout;
 	uint32_t i;
 
-	/* Lookup hit: The key, key signature and key time are already properly configured (the key
-	 * time was bumped by lookup), only the key data need to be updated.
+	/* Adjust the key timeout ID to fit the valid range. */
+	key_timeout_id &= t->params.n_key_timeouts - 1;
+	key_timeout = t->params.key_timeout[key_timeout_id];
+
+	/* Lookup hit: The following bucket fields need to be updated:
+	 * - key (key, sig): NO (already correctly set).
+	 * - key timeout (key_timeout_id, time): YES.
+	 * - key data (data): YES.
 	 */
 	if (m->hit) {
-		uint64_t *data = table_bucket_data_get(t, b, m->bucket_key_pos);
+		size_t bucket_key_pos = m->bucket_key_pos;
+		uint64_t *data = table_bucket_data_get(t, b, bucket_key_pos);
+
+		/* Install the key timeout. */
+		b->time[bucket_key_pos] = (input_time + key_timeout) >> 32;
+		b->key_timeout_id[bucket_key_pos] = (uint8_t)key_timeout_id;
 
 		/* Install the key data. */
 		data[0] = action_id;
@@ -576,10 +699,11 @@ rte_swx_table_learner_add(void *table,
 			uint8_t *key = table_bucket_key_get(t, b, i);
 			uint64_t *data = table_bucket_data_get(t, b, i);
 
-			/* Install the key. */
-			b->time[i] = (input_time + t->params.key_timeout) >> 32;
+			/* Install the key and the key timeout. */
+			b->time[i] = (input_time + key_timeout) >> 32;
 			b->sig[i] = m->input_sig;
-			memcpy(key, m->input_key, t->params.key_size);
+			b->key_timeout_id[i] = (uint8_t)key_timeout_id;
+			table_keycpy(key, m->input_key, t->key_mask0, t->params.key_size_pow2);
 
 			/* Install the key data. */
 			data[0] = action_id;
diff --git a/lib/table/rte_swx_table_learner.h b/lib/table/rte_swx_table_learner.h
index eb9d7689fd..1fee306806 100644
--- a/lib/table/rte_swx_table_learner.h
+++ b/lib/table/rte_swx_table_learner.h
@@ -18,13 +18,43 @@ extern "C" {
  * implementation of the "add on miss" scenario: whenever the lookup key is not found in the table
  * (lookup miss), the data plane can decide to add this key to the table with a given action with no
  * control plane intervention. Likewise, the table keys expire based on a configurable timeout and
- * are automatically deleted from the table with no control plane intervention.
+ * are thus automatically removed from the table with no control plane intervention.
+ *
+ * The keys are not automatically rearmed on lookup hit. To delay the key expiration, the key timer
+ * has to be explicitly reinitialized on lookup hit. The key will be kept in the table as long as it
+ * is frequently hit and explicitly rearmed on every hit.
+ *
+ * Operation overview:
+ * 1) Lookup miss:
+ *      a) add: Add the current input key (the key that missed the lookup) to the table with given
+ *              action, action parameters and expiration timeout. This is the way to populate the
+ *              table (which is empty initially). Data plane operation.
+ *      b) Do nothing: Keep the current input key out of the table.
+ * 2) Lookup hit:
+ *      a) add: Update the action, action parameters and/or the expiration timeout for the current
+ *              input key, which is already in the table. The expiration timer of the key is
+ *              automatically rearmed. Data plane operation.
+ *      b) rearm: Rearm the expiration timer for the current input key, which is already in the
+ *              table. The timeout value used for the expiration timer is either the same as the one
+ *              currently associated with the key or a new one can be provided as input. Data plane
+ *              operation.
+ *      c) delete: Delete the current input key from the table. The purpose of this operation is to
+ *              force the deletion of the key from the table before the key expires on timeout due
+ *              to inactivity. Data plane operation.
+ *      d) Do nothing: Keep the expiration timer of the current input key running down. This key
+ *              will thus expire naturally, unless it is hit again as part of a subsequent lookup
+ *              operation, when the key timer can be rearmed or re-added to prolong its life.
  */
 
 #include <stdint.h>
 
 #include <rte_compat.h>
 
+/** Maximum number of key timeout values per learner table. */
+#ifndef RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX
+#define RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX 16
+#endif
+
 /** Learner table creation parameters. */
 struct rte_swx_table_learner_params {
 	/** Key size in bytes. Must be non-zero. */
@@ -50,10 +80,16 @@ struct rte_swx_table_learner_params {
 	/** Maximum number of keys to be stored in the table together with their associated data. */
 	uint32_t n_keys_max;
 
-	/** Key timeout in seconds. Must be non-zero. Each table key expires and is automatically
-	 * deleted from the table after this many seconds.
+	/** The set of all possible key timeout values measured in seconds. Each value must be
+	 * non-zero. Each table key expires and is automatically deleted from the table after
+	 * this many seconds.
+	 */
+	uint32_t *key_timeout;
+
+	/** Number of possible key timeout values present in the *key_timeout* set. It must be less
+	 * than or equal to *RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX*.
 	 */
-	uint32_t key_timeout;
+	uint32_t n_key_timeouts;
 };
 
 /**
@@ -96,6 +132,25 @@ __rte_experimental
 void *
 rte_swx_table_learner_create(struct rte_swx_table_learner_params *params, int numa_node);
 
+/**
+ * Learner table key timeout update
+ *
+ * @param[in] table
+ *   Table handle.
+ * @param[in] key_timeout_id
+ *   Key timeout ID. Must be less than the configured *n_key_timeouts* value.
+ * @param[in] key_timeout
+ *   Key timeout value measured in seconds.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument(s).
+ */
+__rte_experimental
+int
+rte_swx_table_learner_timeout_update(void *table,
+				     uint32_t key_timeout_id,
+				     uint32_t key_timeout);
+
 /**
  * Learner table key lookup
  *
@@ -160,6 +215,8 @@ rte_swx_table_learner_lookup(void *table,
  *   ID of the action associated with the key.
  * @param[out] action_data
  *   Action data for the *action_id* action.
+ * @param[in] key_timeout_id
+ *   Key timeout ID.
  * @return
  *   0 on success, 1 or error (table full).
  */
@@ -169,7 +226,49 @@ rte_swx_table_learner_add(void *table,
 			  void *mailbox,
 			  uint64_t time,
 			  uint64_t action_id,
-			  uint8_t *action_data);
+			  uint8_t *action_data,
+			  uint32_t key_timeout_id);
+
+/**
+ * Learner table key rearm with same timeout value
+ *
+ * This operation takes the latest key that was looked up in the table and, in case of lookup hit,
+ * it rearms its expiration timer using the same timeout value currently associated with the key.
+ *
+ * @param[in] table
+ *   Table handle.
+ * @param[in] mailbox
+ *   Mailbox for the current operation.
+ * @param[in] time
+ *   Current time measured in CPU clock cycles.
+ */
+__rte_experimental
+void
+rte_swx_table_learner_rearm(void *table,
+			    void *mailbox,
+			    uint64_t time);
+
+/**
+ * Learner table key rearm with given timeout value
+ *
+ * This operation takes the latest key that was looked up in the table and, in case of lookup hit,
+ * it rearms its expiration timer using the given timeout value.
+ *
+ * @param[in] table
+ *   Table handle.
+ * @param[in] mailbox
+ *   Mailbox for the current operation.
+ * @param[in] time
+ *   Current time measured in CPU clock cycles.
+ * @param[in] key_timeout_id
+ *   Key timeout ID.
+ */
+__rte_experimental
+void
+rte_swx_table_learner_rearm_new(void *table,
+				void *mailbox,
+				uint64_t time,
+				uint32_t key_timeout_id);
 
 /**
  * Learner table key delete
diff --git a/lib/table/version.map b/lib/table/version.map
index efe5f6e52c..623709f886 100644
--- a/lib/table/version.map
+++ b/lib/table/version.map
@@ -45,4 +45,9 @@ EXPERIMENTAL {
 	rte_swx_table_learner_free;
 	rte_swx_table_learner_lookup;
 	rte_swx_table_learner_mailbox_size_get;
+
+	#added in 22.07
+	rte_swx_table_learner_rearm;
+	rte_swx_table_learner_rearm_new;
+	rte_swx_table_learner_timeout_update;
 };
