ipmi:msghandler: Export and fix panic messaging capability
authorCorey Minyard <corey@minyard.net>
Tue, 22 Apr 2025 17:06:02 +0000 (12:06 -0500)
committerCorey Minyard <corey@minyard.net>
Wed, 7 May 2025 22:25:48 +0000 (17:25 -0500)
Don't have the other users that do things at panic time (the watchdog)
do all this themselves, provide a function to do it.

Also, with the new design where most stuff happens at thread context,
a few things needed to be fixed to avoid doing locking in a panic
context.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
drivers/char/ipmi/ipmi_msghandler.c
include/linux/ipmi.h

index 3cb72f6b12ea10b313ebb49a091e1929211c62ad..8b2c3fca9c4cab9f09c44122409b7707c351faeb 100644 (file)
@@ -2284,6 +2284,7 @@ static int i_ipmi_request(struct ipmi_user     *user,
 {
        struct ipmi_smi_msg *smi_msg;
        struct ipmi_recv_msg *recv_msg;
+       int run_to_completion = READ_ONCE(intf->run_to_completion);
        int rv = 0;
 
        if (user) {
@@ -2317,7 +2318,8 @@ static int i_ipmi_request(struct ipmi_user     *user,
                }
        }
 
-       mutex_lock(&intf->users_mutex);
+       if (!run_to_completion)
+               mutex_lock(&intf->users_mutex);
        if (intf->in_shutdown) {
                rv = -ENODEV;
                goto out_err;
@@ -2363,7 +2365,8 @@ out_err:
 
                smi_send(intf, intf->handlers, smi_msg, priority);
        }
-       mutex_unlock(&intf->users_mutex);
+       if (!run_to_completion)
+               mutex_unlock(&intf->users_mutex);
 
 out:
        if (rv && user)
@@ -4559,7 +4562,7 @@ return_unspecified:
            && (msg->data[1] == IPMI_SEND_MSG_CMD)
            && (msg->user_data == NULL)) {
 
-               if (intf->in_shutdown)
+               if (intf->in_shutdown || intf->run_to_completion)
                        goto out;
 
                /*
@@ -4631,6 +4634,9 @@ return_unspecified:
                 */
                struct ipmi_recv_msg *recv_msg;
 
+               if (intf->run_to_completion)
+                       goto out;
+
                chan = msg->data[2] & 0x0f;
                if (chan >= IPMI_MAX_CHANNELS)
                        /* Invalid channel number */
@@ -4653,6 +4659,9 @@ process_response_response:
                   && (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
                struct ipmi_channel   *chans;
 
+               if (intf->run_to_completion)
+                       goto out;
+
                /* It's from the receive queue. */
                chan = msg->rsp[3] & 0xf;
                if (chan >= IPMI_MAX_CHANNELS) {
@@ -4727,6 +4736,9 @@ process_response_response:
        } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
                   && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) {
                /* It's an asynchronous event. */
+               if (intf->run_to_completion)
+                       goto out;
+
                requeue = handle_read_event_rsp(intf, msg);
        } else {
                /* It's a response from the local BMC. */
@@ -4855,15 +4867,6 @@ static void smi_work(struct work_struct *t)
 
                list_del(&msg->link);
 
-               /*
-                * I would like for this check (and user->destroyed)
-                * to go away, but it's possible that an interface is
-                * processing a message that belongs to the user while
-                * the user is being deleted.  When that response
-                * comes back, it could be queued after the user is
-                * destroyed.  This is simpler than handling it in the
-                * interface.
-                */
                if (refcount_read(&user->destroyed) == 0) {
                        ipmi_free_recv_msg(msg);
                } else {
@@ -5222,9 +5225,9 @@ static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
 /*
  * Inside a panic, send a message and wait for a response.
  */
-static void ipmi_panic_request_and_wait(struct ipmi_smi *intf,
-                                       struct ipmi_addr *addr,
-                                       struct kernel_ipmi_msg *msg)
+static void _ipmi_panic_request_and_wait(struct ipmi_smi *intf,
+                                        struct ipmi_addr *addr,
+                                        struct kernel_ipmi_msg *msg)
 {
        struct ipmi_smi_msg  smi_msg;
        struct ipmi_recv_msg recv_msg;
@@ -5254,6 +5257,15 @@ static void ipmi_panic_request_and_wait(struct ipmi_smi *intf,
                ipmi_poll(intf);
 }
 
+void ipmi_panic_request_and_wait(struct ipmi_user *user,
+                                struct ipmi_addr *addr,
+                                struct kernel_ipmi_msg *msg)
+{
+       user->intf->run_to_completion = 1;
+       _ipmi_panic_request_and_wait(user->intf, addr, msg);
+}
+EXPORT_SYMBOL(ipmi_panic_request_and_wait);
+
 static void event_receiver_fetcher(struct ipmi_smi *intf,
                                   struct ipmi_recv_msg *msg)
 {
@@ -5322,7 +5334,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
        }
 
        /* Send the event announcing the panic. */
-       ipmi_panic_request_and_wait(intf, &addr, &msg);
+       _ipmi_panic_request_and_wait(intf, &addr, &msg);
 
        /*
         * On every interface, dump a bunch of OEM event holding the
@@ -5358,7 +5370,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
        msg.data = NULL;
        msg.data_len = 0;
        intf->null_user_handler = device_id_fetcher;
-       ipmi_panic_request_and_wait(intf, &addr, &msg);
+       _ipmi_panic_request_and_wait(intf, &addr, &msg);
 
        if (intf->local_event_generator) {
                /* Request the event receiver from the local MC. */
@@ -5367,7 +5379,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
                msg.data = NULL;
                msg.data_len = 0;
                intf->null_user_handler = event_receiver_fetcher;
-               ipmi_panic_request_and_wait(intf, &addr, &msg);
+               _ipmi_panic_request_and_wait(intf, &addr, &msg);
        }
        intf->null_user_handler = NULL;
 
@@ -5419,7 +5431,7 @@ static void send_panic_events(struct ipmi_smi *intf, char *str)
                memcpy_and_pad(data+5, 11, p, size, '\0');
                p += size;
 
-               ipmi_panic_request_and_wait(intf, &addr, &msg);
+               _ipmi_panic_request_and_wait(intf, &addr, &msg);
        }
 }
 
index 27cd5980bb27d90cec54e89745fe08058de83caf..7da6602eab71ab016ca4c57340597f5af94039fd 100644 (file)
@@ -344,4 +344,14 @@ extern int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data);
 /* Helper function for computing the IPMB checksum of some data. */
 unsigned char ipmb_checksum(unsigned char *data, int size);
 
+/*
+ * For things that must send messages at panic time, like the IPMI watchdog
+ * driver that extends the reset time on a panic, use this to send messages
+ * from panic context.  Note that this puts the driver into a mode that
+ * only works at panic time, so only use it then.
+ */
+void ipmi_panic_request_and_wait(struct ipmi_user *user,
+                                struct ipmi_addr *addr,
+                                struct kernel_ipmi_msg *msg);
+
 #endif /* __LINUX_IPMI_H */