File Index
Files and directories
Sign in
FILE

mcdi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/cdx/controller/mcdi.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Management-Controller-to-Driver Interface
  4 *
  5 * Copyright 2008-2013 Solarflare Communications Inc.
  6 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
  7 */
  8#include <linux/delay.h>
  9#include <linux/slab.h>
 10#include <linux/io.h>
 11#include <linux/spinlock.h>
 12#include <linux/netdevice.h>
 13#include <linux/etherdevice.h>
 14#include <linux/ethtool.h>
 15#include <linux/if_vlan.h>
 16#include <linux/timer.h>
 17#include <linux/list.h>
 18#include <linux/pci.h>
 19#include <linux/device.h>
 20#include <linux/rwsem.h>
 21#include <linux/vmalloc.h>
 22#include <net/netevent.h>
 23#include <linux/log2.h>
 24#include <linux/net_tstamp.h>
 25#include <linux/wait.h>
 26#include <linux/cdx/bitfield.h>
 27
 28#include <linux/cdx/mcdi.h>
 29#include "mcdid.h"
 30
 31static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd);
 32static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx);
 33static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
 34				       struct cdx_mcdi_cmd *cmd,
 35				       unsigned int *handle);
 36static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
 37				    bool allow_retry);
 38static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
 39					struct cdx_mcdi_cmd *cmd);
 40static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
 41				  struct cdx_mcdi_cmd *cmd,
 42				  struct cdx_dword *outbuf,
 43				  int len,
 44				  struct list_head *cleanup_list);
 45static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
 46				 struct cdx_mcdi_cmd *cmd,
 47				 struct list_head *cleanup_list);
 48static void cdx_mcdi_cmd_work(struct work_struct *context);
 49static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list);
 50static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
 51				    size_t inlen, int raw, int arg, int err_no);
 52
 53static bool cdx_cmd_cancelled(struct cdx_mcdi_cmd *cmd)
 54{
 55	return cmd->state == MCDI_STATE_RUNNING_CANCELLED;
 56}
 57
 58static void cdx_mcdi_cmd_release(struct kref *ref)
 59{
 60	kfree(container_of(ref, struct cdx_mcdi_cmd, ref));
 61}
 62
 63static unsigned int cdx_mcdi_cmd_handle(struct cdx_mcdi_cmd *cmd)
 64{
 65	return cmd->handle;
 66}
 67
 68static void _cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
 69				 struct cdx_mcdi_cmd *cmd,
 70				 struct list_head *cleanup_list)
 71{
 72	/* if cancelled, the completers have already been called */
 73	if (cdx_cmd_cancelled(cmd))
 74		return;
 75
 76	if (cmd->completer) {
 77		list_add_tail(&cmd->cleanup_list, cleanup_list);
 78		++mcdi->outstanding_cleanups;
 79		kref_get(&cmd->ref);
 80	}
 81}
 82
 83static void cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
 84				struct cdx_mcdi_cmd *cmd,
 85				struct list_head *cleanup_list)
 86{
 87	list_del(&cmd->list);
 88	_cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
 89	cmd->state = MCDI_STATE_FINISHED;
 90	kref_put(&cmd->ref, cdx_mcdi_cmd_release);
 91	if (list_empty(&mcdi->cmd_list))
 92		wake_up(&mcdi->cmd_complete_wq);
 93}
 94
 95static unsigned long cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
 96{
 97	if (!cdx->mcdi_ops->mcdi_rpc_timeout)
 98		return MCDI_RPC_TIMEOUT;
 99	else
100		return cdx->mcdi_ops->mcdi_rpc_timeout(cdx, cmd);
101}
102
103/**
104 * cdx_mcdi_init - Initialize MCDI (Management Controller Driver Interface) state
105 * @cdx:	Handle to the CDX MCDI structure
106 *
107 * This function allocates and initializes internal MCDI structures and resources
108 * for the CDX device, including the workqueue, locking primitives, and command
109 * tracking mechanisms. It sets the initial operating mode and prepares the device
110 * for MCDI operations.
111 *
112 * Return:
113 * * 0        - on success
114 * * -ENOMEM  - if memory allocation or workqueue creation fails
115 */
116int cdx_mcdi_init(struct cdx_mcdi *cdx)
117{
118	struct cdx_mcdi_iface *mcdi;
119	int rc = -ENOMEM;
120
121	cdx->mcdi = kzalloc(sizeof(*cdx->mcdi), GFP_KERNEL);
122	if (!cdx->mcdi)
123		goto fail;
124
125	mcdi = cdx_mcdi_if(cdx);
126	mcdi->cdx = cdx;
127
128	mcdi->workqueue = alloc_ordered_workqueue("mcdi_wq", 0);
129	if (!mcdi->workqueue)
130		goto fail2;
131	mutex_init(&mcdi->iface_lock);
132	mcdi->mode = MCDI_MODE_EVENTS;
133	INIT_LIST_HEAD(&mcdi->cmd_list);
134	init_waitqueue_head(&mcdi->cmd_complete_wq);
135
136	mcdi->new_epoch = true;
137
138	return 0;
139fail2:
140	kfree(cdx->mcdi);
141	cdx->mcdi = NULL;
142fail:
143	return rc;
144}
145EXPORT_SYMBOL_GPL(cdx_mcdi_init);
146
147/**
148 * cdx_mcdi_finish - Cleanup MCDI (Management Controller Driver Interface) state
149 * @cdx:	Handle to the CDX MCDI structure
150 *
151 * This function is responsible for cleaning up the MCDI (Management Controller Driver Interface)
152 * resources associated with a cdx_mcdi structure. Also destroys the mcdi workqueue.
153 *
154 */
155void cdx_mcdi_finish(struct cdx_mcdi *cdx)
156{
157	struct cdx_mcdi_iface *mcdi;
158
159	mcdi = cdx_mcdi_if(cdx);
160	if (!mcdi)
161		return;
162
163	cdx_mcdi_wait_for_cleanup(cdx);
164
165	destroy_workqueue(mcdi->workqueue);
166	kfree(cdx->mcdi);
167	cdx->mcdi = NULL;
168}
169EXPORT_SYMBOL_GPL(cdx_mcdi_finish);
170
171static bool cdx_mcdi_flushed(struct cdx_mcdi_iface *mcdi, bool ignore_cleanups)
172{
173	bool flushed;
174
175	mutex_lock(&mcdi->iface_lock);
176	flushed = list_empty(&mcdi->cmd_list) &&
177		  (ignore_cleanups || !mcdi->outstanding_cleanups);
178	mutex_unlock(&mcdi->iface_lock);
179	return flushed;
180}
181
182/* Wait for outstanding MCDI commands to complete. */
183static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx)
184{
185	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
186
187	if (!mcdi)
188		return;
189
190	wait_event(mcdi->cmd_complete_wq,
191		   cdx_mcdi_flushed(mcdi, false));
192}
193
194int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
195				 unsigned int timeout_jiffies)
196{
197	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
198	DEFINE_WAIT_FUNC(wait, woken_wake_function);
199	int rc = 0;
200
201	if (!mcdi)
202		return -EINVAL;
203
204	flush_workqueue(mcdi->workqueue);
205
206	add_wait_queue(&mcdi->cmd_complete_wq, &wait);
207
208	while (!cdx_mcdi_flushed(mcdi, true)) {
209		rc = wait_woken(&wait, TASK_IDLE, timeout_jiffies);
210		if (rc)
211			continue;
212		break;
213	}
214
215	remove_wait_queue(&mcdi->cmd_complete_wq, &wait);
216
217	if (rc > 0)
218		rc = 0;
219	else if (rc == 0)
220		rc = -ETIMEDOUT;
221
222	return rc;
223}
224
225static u8 cdx_mcdi_payload_csum(const struct cdx_dword *hdr, size_t hdr_len,
226				const struct cdx_dword *sdu, size_t sdu_len)
227{
228	u8 *p = (u8 *)hdr;
229	u8 csum = 0;
230	int i;
231
232	for (i = 0; i < hdr_len; i++)
233		csum += p[i];
234
235	p = (u8 *)sdu;
236	for (i = 0; i < sdu_len; i++)
237		csum += p[i];
238
239	return ~csum & 0xff;
240}
241
242static void cdx_mcdi_send_request(struct cdx_mcdi *cdx,
243				  struct cdx_mcdi_cmd *cmd)
244{
245	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
246	const struct cdx_dword *inbuf = cmd->inbuf;
247	size_t inlen = cmd->inlen;
248	struct cdx_dword hdr[2];
249	size_t hdr_len;
250	bool not_epoch;
251	u32 xflags;
252
253	if (!mcdi)
254		return;
255
256	mcdi->prev_seq = cmd->seq;
257	mcdi->seq_held_by[cmd->seq] = cmd;
258	mcdi->db_held_by = cmd;
259	cmd->started = jiffies;
260
261	not_epoch = !mcdi->new_epoch;
262	xflags = 0;
263
264	/* MCDI v2 */
265	WARN_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2);
266	CDX_POPULATE_DWORD_7(hdr[0],
267			     MCDI_HEADER_RESPONSE, 0,
268			     MCDI_HEADER_RESYNC, 1,
269			     MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
270			     MCDI_HEADER_DATALEN, 0,
271			     MCDI_HEADER_SEQ, cmd->seq,
272			     MCDI_HEADER_XFLAGS, xflags,
273			     MCDI_HEADER_NOT_EPOCH, not_epoch);
274	CDX_POPULATE_DWORD_3(hdr[1],
275			     MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd->cmd,
276			     MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen,
277			     MC_CMD_V2_EXTN_IN_MESSAGE_TYPE,
278			     MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM);
279	hdr_len = 8;
280
281	hdr[0].cdx_u32 |= (__force __le32)(cdx_mcdi_payload_csum(hdr, hdr_len, inbuf, inlen) <<
282			 MCDI_HEADER_XFLAGS_LBN);
283
284	print_hex_dump_debug("MCDI REQ HEADER: ", DUMP_PREFIX_NONE, 32, 4, hdr, hdr_len, false);
285	print_hex_dump_debug("MCDI REQ PAYLOAD: ", DUMP_PREFIX_NONE, 32, 4, inbuf, inlen, false);
286
287	cdx->mcdi_ops->mcdi_request(cdx, hdr, hdr_len, inbuf, inlen);
288
289	mcdi->new_epoch = false;
290}
291
292static int cdx_mcdi_errno(struct cdx_mcdi *cdx, unsigned int mcdi_err)
293{
294	switch (mcdi_err) {
295	case 0:
296	case MC_CMD_ERR_QUEUE_FULL:
297		return mcdi_err;
298	case MC_CMD_ERR_EPERM:
299		return -EPERM;
300	case MC_CMD_ERR_ENOENT:
301		return -ENOENT;
302	case MC_CMD_ERR_EINTR:
303		return -EINTR;
304	case MC_CMD_ERR_EAGAIN:
305		return -EAGAIN;
306	case MC_CMD_ERR_EACCES:
307		return -EACCES;
308	case MC_CMD_ERR_EBUSY:
309		return -EBUSY;
310	case MC_CMD_ERR_EINVAL:
311		return -EINVAL;
312	case MC_CMD_ERR_ERANGE:
313		return -ERANGE;
314	case MC_CMD_ERR_EDEADLK:
315		return -EDEADLK;
316	case MC_CMD_ERR_ENOSYS:
317		return -EOPNOTSUPP;
318	case MC_CMD_ERR_ETIME:
319		return -ETIME;
320	case MC_CMD_ERR_EALREADY:
321		return -EALREADY;
322	case MC_CMD_ERR_ENOSPC:
323		return -ENOSPC;
324	case MC_CMD_ERR_ENOMEM:
325		return -ENOMEM;
326	case MC_CMD_ERR_ENOTSUP:
327		return -EOPNOTSUPP;
328	case MC_CMD_ERR_ALLOC_FAIL:
329		return -ENOBUFS;
330	case MC_CMD_ERR_MAC_EXIST:
331		return -EADDRINUSE;
332	case MC_CMD_ERR_NO_EVB_PORT:
333		return -EAGAIN;
334	default:
335		return -EPROTO;
336	}
337}
338
339static void cdx_mcdi_process_cleanup_list(struct cdx_mcdi *cdx,
340					  struct list_head *cleanup_list)
341{
342	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
343	unsigned int cleanups = 0;
344
345	if (!mcdi)
346		return;
347
348	while (!list_empty(cleanup_list)) {
349		struct cdx_mcdi_cmd *cmd =
350			list_first_entry(cleanup_list,
351					 struct cdx_mcdi_cmd, cleanup_list);
352		cmd->completer(cdx, cmd->cookie, cmd->rc,
353			       cmd->outbuf, cmd->outlen);
354		list_del(&cmd->cleanup_list);
355		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
356		++cleanups;
357	}
358
359	if (cleanups) {
360		bool all_done;
361
362		mutex_lock(&mcdi->iface_lock);
363		CDX_WARN_ON_PARANOID(cleanups > mcdi->outstanding_cleanups);
364		all_done = (mcdi->outstanding_cleanups -= cleanups) == 0;
365		mutex_unlock(&mcdi->iface_lock);
366		if (all_done)
367			wake_up(&mcdi->cmd_complete_wq);
368	}
369}
370
371static void _cdx_mcdi_cancel_cmd(struct cdx_mcdi_iface *mcdi,
372				 unsigned int handle,
373				 struct list_head *cleanup_list)
374{
375	struct cdx_mcdi_cmd *cmd;
376
377	list_for_each_entry(cmd, &mcdi->cmd_list, list)
378		if (cdx_mcdi_cmd_handle(cmd) == handle) {
379			switch (cmd->state) {
380			case MCDI_STATE_QUEUED:
381			case MCDI_STATE_RETRY:
382				pr_debug("command %#x inlen %zu cancelled in queue\n",
383					 cmd->cmd, cmd->inlen);
384				/* if not yet running, properly cancel it */
385				cmd->rc = -EPIPE;
386				cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
387				break;
388			case MCDI_STATE_RUNNING:
389			case MCDI_STATE_RUNNING_CANCELLED:
390			case MCDI_STATE_FINISHED:
391			default:
392				/* invalid state? */
393				WARN_ON(1);
394			}
395			break;
396		}
397}
398
399static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd)
400{
401	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
402	LIST_HEAD(cleanup_list);
403
404	if (!mcdi)
405		return;
406
407	mutex_lock(&mcdi->iface_lock);
408	cdx_mcdi_timeout_cmd(mcdi, cmd, &cleanup_list);
409	mutex_unlock(&mcdi->iface_lock);
410	cdx_mcdi_process_cleanup_list(cdx, &cleanup_list);
411}
412
413struct cdx_mcdi_blocking_data {
414	struct kref ref;
415	bool done;
416	wait_queue_head_t wq;
417	int rc;
418	struct cdx_dword *outbuf;
419	size_t outlen;
420	size_t outlen_actual;
421};
422
423static void cdx_mcdi_blocking_data_release(struct kref *ref)
424{
425	kfree(container_of(ref, struct cdx_mcdi_blocking_data, ref));
426}
427
428static void cdx_mcdi_rpc_completer(struct cdx_mcdi *cdx, unsigned long cookie,
429				   int rc, struct cdx_dword *outbuf,
430				   size_t outlen_actual)
431{
432	struct cdx_mcdi_blocking_data *wait_data =
433		(struct cdx_mcdi_blocking_data *)cookie;
434
435	wait_data->rc = rc;
436	memcpy(wait_data->outbuf, outbuf,
437	       min(outlen_actual, wait_data->outlen));
438	wait_data->outlen_actual = outlen_actual;
439	/* memory barrier */
440	smp_wmb();
441	wait_data->done = true;
442	wake_up(&wait_data->wq);
443	kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
444}
445
446static int cdx_mcdi_rpc_sync(struct cdx_mcdi *cdx, unsigned int cmd,
447			     const struct cdx_dword *inbuf, size_t inlen,
448			     struct cdx_dword *outbuf, size_t outlen,
449			     size_t *outlen_actual, bool quiet)
450{
451	struct cdx_mcdi_blocking_data *wait_data;
452	struct cdx_mcdi_cmd *cmd_item;
453	unsigned int handle;
454	int rc;
455
456	if (outlen_actual)
457		*outlen_actual = 0;
458
459	wait_data = kmalloc(sizeof(*wait_data), GFP_KERNEL);
460	if (!wait_data)
461		return -ENOMEM;
462
463	cmd_item = kmalloc(sizeof(*cmd_item), GFP_KERNEL);
464	if (!cmd_item) {
465		kfree(wait_data);
466		return -ENOMEM;
467	}
468
469	kref_init(&wait_data->ref);
470	wait_data->done = false;
471	init_waitqueue_head(&wait_data->wq);
472	wait_data->outbuf = outbuf;
473	wait_data->outlen = outlen;
474
475	kref_init(&cmd_item->ref);
476	cmd_item->quiet = quiet;
477	cmd_item->cookie = (unsigned long)wait_data;
478	cmd_item->completer = &cdx_mcdi_rpc_completer;
479	cmd_item->cmd = cmd;
480	cmd_item->inlen = inlen;
481	cmd_item->inbuf = inbuf;
482
483	/* Claim an extra reference for the completer to put. */
484	kref_get(&wait_data->ref);
485	rc = cdx_mcdi_rpc_async_internal(cdx, cmd_item, &handle);
486	if (rc) {
487		kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
488		goto out;
489	}
490
491	if (!wait_event_timeout(wait_data->wq, wait_data->done,
492				cdx_mcdi_rpc_timeout(cdx, cmd)) &&
493	    !wait_data->done) {
494		pr_err("MC command 0x%x inlen %zu timed out (sync)\n",
495		       cmd, inlen);
496
497		cdx_mcdi_cancel_cmd(cdx, cmd_item);
498
499		wait_data->rc = -ETIMEDOUT;
500		wait_data->outlen_actual = 0;
501	}
502
503	if (outlen_actual)
504		*outlen_actual = wait_data->outlen_actual;
505	rc = wait_data->rc;
506
507out:
508	kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
509
510	return rc;
511}
512
513static bool cdx_mcdi_get_seq(struct cdx_mcdi_iface *mcdi, unsigned char *seq)
514{
515	*seq = mcdi->prev_seq;
516	do {
517		*seq = (*seq + 1) % ARRAY_SIZE(mcdi->seq_held_by);
518	} while (mcdi->seq_held_by[*seq] && *seq != mcdi->prev_seq);
519	return !mcdi->seq_held_by[*seq];
520}
521
522static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
523				       struct cdx_mcdi_cmd *cmd,
524				       unsigned int *handle)
525{
526	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
527	LIST_HEAD(cleanup_list);
528
529	if (!mcdi) {
530		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
531		return -ENETDOWN;
532	}
533
534	if (mcdi->mode == MCDI_MODE_FAIL) {
535		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
536		return -ENETDOWN;
537	}
538
539	cmd->mcdi = mcdi;
540	INIT_WORK(&cmd->work, cdx_mcdi_cmd_work);
541	INIT_LIST_HEAD(&cmd->list);
542	INIT_LIST_HEAD(&cmd->cleanup_list);
543	cmd->rc = 0;
544	cmd->outbuf = NULL;
545	cmd->outlen = 0;
546
547	queue_work(mcdi->workqueue, &cmd->work);
548	return 0;
549}
550
551static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
552					struct cdx_mcdi_cmd *cmd)
553{
554	struct cdx_mcdi *cdx = mcdi->cdx;
555	u8 seq;
556
557	if (!mcdi->db_held_by &&
558	    cdx_mcdi_get_seq(mcdi, &seq)) {
559		cmd->seq = seq;
560		cmd->reboot_seen = false;
561		cdx_mcdi_send_request(cdx, cmd);
562		cmd->state = MCDI_STATE_RUNNING;
563	} else {
564		cmd->state = MCDI_STATE_QUEUED;
565	}
566}
567
568/* try to advance other commands */
569static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
570				    bool allow_retry)
571{
572	struct cdx_mcdi_cmd *cmd, *tmp;
573
574	list_for_each_entry_safe(cmd, tmp, &mcdi->cmd_list, list)
575		if (cmd->state == MCDI_STATE_QUEUED ||
576		    (cmd->state == MCDI_STATE_RETRY && allow_retry))
577			cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
578}
579
580/**
581 * cdx_mcdi_process_cmd - Process an incoming MCDI response
582 * @cdx:	Handle to the CDX MCDI structure
583 * @outbuf:	Pointer to the response buffer received from the management controller
584 * @len:	Length of the response buffer in bytes
585 *
586 * This function handles a response from the management controller. It locates the
587 * corresponding command using the sequence number embedded in the header,
588 * completes the command if it is still pending, and initiates any necessary cleanup.
589 *
590 * The function assumes that the response buffer is well-formed and at least one
591 * dword in size.
592 */
593void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len)
594{
595	struct cdx_mcdi_iface *mcdi;
596	struct cdx_mcdi_cmd *cmd;
597	LIST_HEAD(cleanup_list);
598	unsigned int respseq;
599
600	if (!len || !outbuf) {
601		pr_err("Got empty MC response\n");
602		return;
603	}
604
605	mcdi = cdx_mcdi_if(cdx);
606	if (!mcdi)
607		return;
608
609	respseq = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_SEQ);
610
611	mutex_lock(&mcdi->iface_lock);
612	cmd = mcdi->seq_held_by[respseq];
613
614	if (cmd) {
615		if (cmd->state == MCDI_STATE_FINISHED) {
616			mutex_unlock(&mcdi->iface_lock);
617			kref_put(&cmd->ref, cdx_mcdi_cmd_release);
618			return;
619		}
620
621		cdx_mcdi_complete_cmd(mcdi, cmd, outbuf, len, &cleanup_list);
622	} else {
623		pr_err("MC response unexpected for seq : %0X\n", respseq);
624	}
625
626	mutex_unlock(&mcdi->iface_lock);
627
628	cdx_mcdi_process_cleanup_list(mcdi->cdx, &cleanup_list);
629}
630EXPORT_SYMBOL_GPL(cdx_mcdi_process_cmd);
631
632static void cdx_mcdi_cmd_work(struct work_struct *context)
633{
634	struct cdx_mcdi_cmd *cmd =
635		container_of(context, struct cdx_mcdi_cmd, work);
636	struct cdx_mcdi_iface *mcdi = cmd->mcdi;
637
638	mutex_lock(&mcdi->iface_lock);
639
640	cmd->handle = mcdi->prev_handle++;
641	list_add_tail(&cmd->list, &mcdi->cmd_list);
642	cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
643
644	mutex_unlock(&mcdi->iface_lock);
645}
646
647/*
648 * Returns true if the MCDI module is finished with the command.
649 * (examples of false would be if the command was proxied, or it was
650 * rejected by the MC due to lack of resources and requeued).
651 */
652static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
653				  struct cdx_mcdi_cmd *cmd,
654				  struct cdx_dword *outbuf,
655				  int len,
656				  struct list_head *cleanup_list)
657{
658	size_t resp_hdr_len, resp_data_len;
659	struct cdx_mcdi *cdx = mcdi->cdx;
660	unsigned int respcmd, error;
661	bool completed = false;
662	int rc;
663
664	/* ensure the command can't go away before this function returns */
665	kref_get(&cmd->ref);
666
667	respcmd = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_CODE);
668	error = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_ERROR);
669
670	if (respcmd != MC_CMD_V2_EXTN) {
671		resp_hdr_len = 4;
672		resp_data_len = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_DATALEN);
673	} else {
674		resp_data_len = 0;
675		resp_hdr_len = 8;
676		if (len >= 8)
677			resp_data_len =
678				CDX_DWORD_FIELD(outbuf[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
679	}
680
681	if ((resp_hdr_len + resp_data_len) > len) {
682		pr_warn("Incomplete MCDI response received %d. Expected %zu\n",
683			len, (resp_hdr_len + resp_data_len));
684		resp_data_len = 0;
685	}
686
687	print_hex_dump_debug("MCDI RESP HEADER: ", DUMP_PREFIX_NONE, 32, 4,
688			     outbuf, resp_hdr_len, false);
689	print_hex_dump_debug("MCDI RESP PAYLOAD: ", DUMP_PREFIX_NONE, 32, 4,
690			     outbuf + (resp_hdr_len / 4), resp_data_len, false);
691
692	if (error && resp_data_len == 0) {
693		/* MC rebooted during command */
694		rc = -EIO;
695	} else {
696		if (WARN_ON_ONCE(error && resp_data_len < 4))
697			resp_data_len = 4;
698		if (error) {
699			rc = CDX_DWORD_FIELD(outbuf[resp_hdr_len / 4], CDX_DWORD);
700			if (!cmd->quiet) {
701				int err_arg = 0;
702
703				if (resp_data_len >= MC_CMD_ERR_ARG_OFST + 4) {
704					int offset = (resp_hdr_len + MC_CMD_ERR_ARG_OFST) / 4;
705
706					err_arg = CDX_DWORD_VAL(outbuf[offset]);
707				}
708
709				_cdx_mcdi_display_error(cdx, cmd->cmd,
710							cmd->inlen, rc, err_arg,
711							cdx_mcdi_errno(cdx, rc));
712			}
713			rc = cdx_mcdi_errno(cdx, rc);
714		} else {
715			rc = 0;
716		}
717	}
718
719	/* free doorbell */
720	if (mcdi->db_held_by == cmd)
721		mcdi->db_held_by = NULL;
722
723	if (cdx_cmd_cancelled(cmd)) {
724		list_del(&cmd->list);
725		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
726		completed = true;
727	} else if (rc == MC_CMD_ERR_QUEUE_FULL) {
728		cmd->state = MCDI_STATE_RETRY;
729	} else {
730		cmd->rc = rc;
731		cmd->outbuf = outbuf + DIV_ROUND_UP(resp_hdr_len, 4);
732		cmd->outlen = resp_data_len;
733		cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
734		completed = true;
735	}
736
737	/* free sequence number and buffer */
738	mcdi->seq_held_by[cmd->seq] = NULL;
739
740	cdx_mcdi_start_or_queue(mcdi, rc != MC_CMD_ERR_QUEUE_FULL);
741
742	/* wake up anyone waiting for flush */
743	wake_up(&mcdi->cmd_complete_wq);
744
745	kref_put(&cmd->ref, cdx_mcdi_cmd_release);
746
747	return completed;
748}
749
750static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
751				 struct cdx_mcdi_cmd *cmd,
752				 struct list_head *cleanup_list)
753{
754	struct cdx_mcdi *cdx = mcdi->cdx;
755
756	pr_err("MC command 0x%x inlen %zu state %d timed out after %u ms\n",
757	       cmd->cmd, cmd->inlen, cmd->state,
758	       jiffies_to_msecs(jiffies - cmd->started));
759
760	cmd->rc = -ETIMEDOUT;
761	cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
762
763	cdx_mcdi_mode_fail(cdx, cleanup_list);
764}
765
766/**
767 * cdx_mcdi_rpc - Issue an MCDI command and wait for completion
768 * @cdx: NIC through which to issue the command
769 * @cmd: Command type number
770 * @inbuf: Command parameters
771 * @inlen: Length of command parameters, in bytes. Must be a multiple
772 *	of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
773 * @outbuf: Response buffer. May be %NULL if @outlen is 0.
774 * @outlen: Length of response buffer, in bytes. If the actual
775 *	response is longer than @outlen & ~3, it will be truncated
776 *	to that length.
777 * @outlen_actual: Pointer through which to return the actual response
778 *	length. May be %NULL if this is not needed.
779 *
780 * This function may sleep and therefore must be called in process
781 * context.
782 *
783 * Return: A negative error code, or zero if successful. The error
784 *	code may come from the MCDI response or may indicate a failure
785 *	to communicate with the MC. In the former case, the response
786 *	will still be copied to @outbuf and *@outlen_actual will be
787 *	set accordingly. In the latter case, *@outlen_actual will be
788 *	set to zero.
789 */
790int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd,
791		 const struct cdx_dword *inbuf, size_t inlen,
792		 struct cdx_dword *outbuf, size_t outlen,
793		 size_t *outlen_actual)
794{
795	return cdx_mcdi_rpc_sync(cdx, cmd, inbuf, inlen, outbuf, outlen,
796				 outlen_actual, false);
797}
798EXPORT_SYMBOL_GPL(cdx_mcdi_rpc);
799
800/**
801 * cdx_mcdi_rpc_async - Schedule an MCDI command to run asynchronously
802 * @cdx: NIC through which to issue the command
803 * @cmd: Command type number
804 * @inbuf: Command parameters
805 * @inlen: Length of command parameters, in bytes
806 * @complete: Function to be called on completion or cancellation.
807 * @cookie: Arbitrary value to be passed to @complete.
808 *
809 * This function does not sleep and therefore may be called in atomic
810 * context.  It will fail if event queues are disabled or if MCDI
811 * event completions have been disabled due to an error.
812 *
813 * If it succeeds, the @complete function will be called exactly once
814 * in process context, when one of the following occurs:
815 * (a) the completion event is received (in process context)
816 * (b) event queues are disabled (in the process that disables them)
817 */
818int
819cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
820		   const struct cdx_dword *inbuf, size_t inlen,
821		   cdx_mcdi_async_completer *complete, unsigned long cookie)
822{
823	struct cdx_mcdi_cmd *cmd_item =
824		kmalloc(sizeof(struct cdx_mcdi_cmd) + inlen, GFP_ATOMIC);
825
826	if (!cmd_item)
827		return -ENOMEM;
828
829	kref_init(&cmd_item->ref);
830	cmd_item->quiet = true;
831	cmd_item->cookie = cookie;
832	cmd_item->completer = complete;
833	cmd_item->cmd = cmd;
834	cmd_item->inlen = inlen;
835	/* inbuf is probably not valid after return, so take a copy */
836	cmd_item->inbuf = (struct cdx_dword *)(cmd_item + 1);
837	memcpy(cmd_item + 1, inbuf, inlen);
838
839	return cdx_mcdi_rpc_async_internal(cdx, cmd_item, NULL);
840}
841
842static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
843				    size_t inlen, int raw, int arg, int err_no)
844{
845	pr_err("MC command 0x%x inlen %d failed err_no=%d (raw=%d) arg=%d\n",
846	       cmd, (int)inlen, err_no, raw, arg);
847}
848
849/*
850 * Set MCDI mode to fail to prevent any new commands, then cancel any
851 * outstanding commands.
852 * Caller must hold the mcdi iface_lock.
853 */
854static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list)
855{
856	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
857
858	if (!mcdi)
859		return;
860
861	mcdi->mode = MCDI_MODE_FAIL;
862
863	while (!list_empty(&mcdi->cmd_list)) {
864		struct cdx_mcdi_cmd *cmd;
865
866		cmd = list_first_entry(&mcdi->cmd_list, struct cdx_mcdi_cmd,
867				       list);
868		_cdx_mcdi_cancel_cmd(mcdi, cdx_mcdi_cmd_handle(cmd), cleanup_list);
869	}
870}
~/ Read-only file preview.