Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipmi:msghandler: Export and fix panic messaging capability

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>

+41 -19
+31 -19
drivers/char/ipmi/ipmi_msghandler.c
··· 2284 2284 { 2285 2285 struct ipmi_smi_msg *smi_msg; 2286 2286 struct ipmi_recv_msg *recv_msg; 2287 + int run_to_completion = READ_ONCE(intf->run_to_completion); 2287 2288 int rv = 0; 2288 2289 2289 2290 if (user) { ··· 2318 2317 } 2319 2318 } 2320 2319 2321 - mutex_lock(&intf->users_mutex); 2320 + if (!run_to_completion) 2321 + mutex_lock(&intf->users_mutex); 2322 2322 if (intf->in_shutdown) { 2323 2323 rv = -ENODEV; 2324 2324 goto out_err; ··· 2365 2363 2366 2364 smi_send(intf, intf->handlers, smi_msg, priority); 2367 2365 } 2368 - mutex_unlock(&intf->users_mutex); 2366 + if (!run_to_completion) 2367 + mutex_unlock(&intf->users_mutex); 2369 2368 2370 2369 out: 2371 2370 if (rv && user) ··· 4562 4559 && (msg->data[1] == IPMI_SEND_MSG_CMD) 4563 4560 && (msg->user_data == NULL)) { 4564 4561 4565 - if (intf->in_shutdown) 4562 + if (intf->in_shutdown || intf->run_to_completion) 4566 4563 goto out; 4567 4564 4568 4565 /* ··· 4634 4631 */ 4635 4632 struct ipmi_recv_msg *recv_msg; 4636 4633 4634 + if (intf->run_to_completion) 4635 + goto out; 4636 + 4637 4637 chan = msg->data[2] & 0x0f; 4638 4638 if (chan >= IPMI_MAX_CHANNELS) 4639 4639 /* Invalid channel number */ ··· 4658 4652 } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 4659 4653 && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { 4660 4654 struct ipmi_channel *chans; 4655 + 4656 + if (intf->run_to_completion) 4657 + goto out; 4661 4658 4662 4659 /* It's from the receive queue. */ 4663 4660 chan = msg->rsp[3] & 0xf; ··· 4736 4727 } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) 4737 4728 && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { 4738 4729 /* It's an asynchronous event. */ 4730 + if (intf->run_to_completion) 4731 + goto out; 4732 + 4739 4733 requeue = handle_read_event_rsp(intf, msg); 4740 4734 } else { 4741 4735 /* It's a response from the local BMC. */ ··· 4867 4855 4868 4856 list_del(&msg->link); 4869 4857 4870 - /* 4871 - * I would like for this check (and user->destroyed) 4872 - * to go away, but it's possible that an interface is 4873 - * processing a message that belongs to the user while 4874 - * the user is being deleted. When that response 4875 - * comes back, it could be queued after the user is 4876 - * destroyed. This is simpler than handling it in the 4877 - * interface. 4878 - */ 4879 4858 if (refcount_read(&user->destroyed) == 0) { 4880 4859 ipmi_free_recv_msg(msg); 4881 4860 } else { ··· 5225 5222 /* 5226 5223 * Inside a panic, send a message and wait for a response. 5227 5224 */ 5228 - static void ipmi_panic_request_and_wait(struct ipmi_smi *intf, 5229 - struct ipmi_addr *addr, 5230 - struct kernel_ipmi_msg *msg) 5225 + static void _ipmi_panic_request_and_wait(struct ipmi_smi *intf, 5226 + struct ipmi_addr *addr, 5227 + struct kernel_ipmi_msg *msg) 5231 5228 { 5232 5229 struct ipmi_smi_msg smi_msg; 5233 5230 struct ipmi_recv_msg recv_msg; ··· 5256 5253 while (atomic_read(&panic_done_count) != 0) 5257 5254 ipmi_poll(intf); 5258 5255 } 5256 + 5257 + void ipmi_panic_request_and_wait(struct ipmi_user *user, 5258 + struct ipmi_addr *addr, 5259 + struct kernel_ipmi_msg *msg) 5260 + { 5261 + user->intf->run_to_completion = 1; 5262 + _ipmi_panic_request_and_wait(user->intf, addr, msg); 5263 + } 5264 + EXPORT_SYMBOL(ipmi_panic_request_and_wait); 5259 5265 5260 5266 static void event_receiver_fetcher(struct ipmi_smi *intf, 5261 5267 struct ipmi_recv_msg *msg) ··· 5334 5322 } 5335 5323 5336 5324 /* Send the event announcing the panic. */ 5337 - ipmi_panic_request_and_wait(intf, &addr, &msg); 5325 + _ipmi_panic_request_and_wait(intf, &addr, &msg); 5338 5326 5339 5327 /* 5340 5328 * On every interface, dump a bunch of OEM event holding the ··· 5370 5358 msg.data = NULL; 5371 5359 msg.data_len = 0; 5372 5360 intf->null_user_handler = device_id_fetcher; 5373 - ipmi_panic_request_and_wait(intf, &addr, &msg); 5361 + _ipmi_panic_request_and_wait(intf, &addr, &msg); 5374 5362 5375 5363 if (intf->local_event_generator) { 5376 5364 /* Request the event receiver from the local MC. */ ··· 5379 5367 msg.data = NULL; 5380 5368 msg.data_len = 0; 5381 5369 intf->null_user_handler = event_receiver_fetcher; 5382 - ipmi_panic_request_and_wait(intf, &addr, &msg); 5370 + _ipmi_panic_request_and_wait(intf, &addr, &msg); 5383 5371 } 5384 5372 intf->null_user_handler = NULL; 5385 5373 ··· 5431 5419 memcpy_and_pad(data+5, 11, p, size, '\0'); 5432 5420 p += size; 5433 5421 5434 - ipmi_panic_request_and_wait(intf, &addr, &msg); 5422 + _ipmi_panic_request_and_wait(intf, &addr, &msg); 5435 5423 } 5436 5424 } 5437 5425
+10
include/linux/ipmi.h
··· 344 344 /* Helper function for computing the IPMB checksum of some data. */ 345 345 unsigned char ipmb_checksum(unsigned char *data, int size); 346 346 347 + /* 348 + * For things that must send messages at panic time, like the IPMI watchdog 349 + * driver that extends the reset time on a panic, use this to send messages 350 + * from panic context. Note that this puts the driver into a mode that 351 + * only works at panic time, so only use it then. 352 + */ 353 + void ipmi_panic_request_and_wait(struct ipmi_user *user, 354 + struct ipmi_addr *addr, 355 + struct kernel_ipmi_msg *msg); 356 + 347 357 #endif /* __LINUX_IPMI_H */