diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index f4997e9c9..047a8d737 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -88,7 +88,8 @@ struct ovn_extend_table;
     OVNACT(OVNFIELD_LOAD,     ovnact_load)            \
     OVNACT(CHECK_PKT_LARGER,  ovnact_check_pkt_larger) \
     OVNACT(TRIGGER_EVENT,     ovnact_controller_event) \
-    OVNACT(BIND_VPORT,        ovnact_bind_vport)
+    OVNACT(BIND_VPORT,        ovnact_bind_vport)       \
+    OVNACT(HANDLE_SVC_CHECK,  ovnact_handle_svc_check)
 
 /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
 enum OVS_PACKED_ENUM ovnact_type {
@@ -352,6 +353,12 @@ struct ovnact_bind_vport {
     struct expr_field vport_parent;     /* Logical virtual port's port name. */
 };
 
+/* OVNACT_HANDLE_SVC_CHECK. */
+struct ovnact_handle_svc_check {
+    struct ovnact ovnact;
+    struct expr_field port;     /* Logical port name. */
+};
+
 /* Internal use by the helpers below. */
 void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
 void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
@@ -537,6 +544,14 @@ enum action_opcode {
      *    MFF_LOG_INPORT.
      */
     ACTION_OPCODE_BIND_VPORT,
+
+    /* "handle_svc_check(port)"."
+     *
+     * Arguments are passed through the packet metadata and data, as follows:
+     *
+     *     MFF_LOG_INPORT = port
+     */
+    ACTION_OPCODE_HANDLE_SVC_CHECK,
 };
 
 /* Header. */
diff --git a/lib/actions.c b/lib/actions.c
index a999a4fda..586d7b75d 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2814,6 +2814,46 @@ ovnact_bind_vport_free(struct ovnact_bind_vport *bp)
     free(bp->vport);
 }
 
+static void
+parse_handle_svc_check(struct action_context *ctx OVS_UNUSED)
+{
+     if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN)) {
+        return;
+    }
+
+    struct ovnact_handle_svc_check *svc_chk =
+        ovnact_put_HANDLE_SVC_CHECK(ctx->ovnacts);
+    action_parse_field(ctx, 0, false, &svc_chk->port);
+    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+format_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk,
+                        struct ds *s)
+{
+    ds_put_cstr(s, "handle_svc_check(");
+    expr_field_format(&svc_chk->port, s);
+    ds_put_cstr(s, ");");
+}
+
+static void
+encode_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk,
+                        const struct ovnact_encode_params *ep OVS_UNUSED,
+                        struct ofpbuf *ofpacts)
+{
+    const struct arg args[] = {
+        { expr_resolve_field(&svc_chk->port), MFF_LOG_INPORT },
+    };
+    encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
+    encode_controller_op(ACTION_OPCODE_HANDLE_SVC_CHECK, ofpacts);
+    encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
+}
+
+static void
+ovnact_handle_svc_check_free(struct ovnact_handle_svc_check *sc OVS_UNUSED)
+{
+}
+
 /* Parses an assignment or exchange or put_dhcp_opts action. */
 static void
 parse_set_action(struct action_context *ctx)
@@ -2931,6 +2971,8 @@ parse_action(struct action_context *ctx)
         parse_trigger_event(ctx, ovnact_put_TRIGGER_EVENT(ctx->ovnacts));
     } else if (lexer_match_id(ctx->lexer, "bind_vport")) {
         parse_bind_vport(ctx);
+    } else if (lexer_match_id(ctx->lexer, "handle_svc_check")) {
+        parse_handle_svc_check(ctx);
     } else {
         lexer_syntax_error(ctx->lexer, "expecting action");
     }
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 335f9031b..82167c488 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -2097,6 +2097,23 @@ tcp.flags = RST;
             set to <var>P</var>.
           </p>
         </dd>
+
+        <dt><code>handle_svc_check(<var>P</var>);</code></dt>
+        <dd>
+          <p>
+            <b>Parameters</b>: logical port string field <var>P</var>.
+          </p>
+
+          <p>
+            Handles the service monitor reply received from the VIF of
+            the logical port <var>P</var>. <code>ovn-controller</code>
+            periodically sends out the service monitor packets for the
+            services configured in the <ref table="Service_Monitor"/>
+            table and this action updates the status of those services.
+          </p>
+
+          <p><b>Example:</b> <code>handle_svc_check(inport);</code></p>
+        </dd>
       </dl>
     </column>
 
diff --git a/tests/ovn.at b/tests/ovn.at
index 410f4b514..b30f12c9a 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1468,6 +1468,19 @@ bind_vport("xyzzy",;
 bind_vport("xyzzy", inport;
     Syntax error at `;' expecting `)'.
 
+# handle_svc_check
+handle_svc_check(inport);
+    encodes as controller(userdata=00.00.00.12.00.00.00.00)
+
+handle_svc_check(outport);
+    encodes as push:NXM_NX_REG14[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],controller(userdata=00.00.00.12.00.00.00.00),pop:NXM_NX_REG14[]
+
+handle_svc_check();
+    Syntax error at `)' expecting field name.
+
+handle_svc_check(reg0);
+    Cannot use numeric field reg0 where string field is required.
+
 # Miscellaneous negative tests.
 ;
     Syntax error at `;'.
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index ea64dc673..19b82e6a4 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -2221,6 +2221,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
 
         case OVNACT_BIND_VPORT:
             break;
+
+        case OVNACT_HANDLE_SVC_CHECK:
+            break;
         }
     }
     ds_destroy(&s);
