xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
1c2b93d6bSLee Trager // SPDX-License-Identifier: GPL-2.0
2c2b93d6bSLee Trager /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3c2b93d6bSLee Trager 
4c2b93d6bSLee Trager #include <linux/spinlock.h>
5c2b93d6bSLee Trager #include <linux/vmalloc.h>
6c2b93d6bSLee Trager 
7c2b93d6bSLee Trager #include "fbnic.h"
8c2b93d6bSLee Trager #include "fbnic_fw.h"
9c2b93d6bSLee Trager #include "fbnic_fw_log.h"
10c2b93d6bSLee Trager 
fbnic_fw_log_enable(struct fbnic_dev * fbd,bool send_hist)11ecc53b1bSLee Trager void fbnic_fw_log_enable(struct fbnic_dev *fbd, bool send_hist)
12ecc53b1bSLee Trager {
13ecc53b1bSLee Trager 	int err;
14ecc53b1bSLee Trager 
15ecc53b1bSLee Trager 	if (!fbnic_fw_log_ready(fbd))
16ecc53b1bSLee Trager 		return;
17ecc53b1bSLee Trager 
18ecc53b1bSLee Trager 	if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE_HIST)
19ecc53b1bSLee Trager 		send_hist = false;
20ecc53b1bSLee Trager 
21ecc53b1bSLee Trager 	err = fbnic_fw_xmit_send_logs(fbd, true, send_hist);
22ecc53b1bSLee Trager 	if (err && err != -EOPNOTSUPP)
23ecc53b1bSLee Trager 		dev_warn(fbd->dev, "Unable to enable firmware logs: %d\n", err);
24ecc53b1bSLee Trager }
25ecc53b1bSLee Trager 
fbnic_fw_log_disable(struct fbnic_dev * fbd)26ecc53b1bSLee Trager void fbnic_fw_log_disable(struct fbnic_dev *fbd)
27ecc53b1bSLee Trager {
28ecc53b1bSLee Trager 	int err;
29ecc53b1bSLee Trager 
30ecc53b1bSLee Trager 	err = fbnic_fw_xmit_send_logs(fbd, false, false);
31ecc53b1bSLee Trager 	if (err && err != -EOPNOTSUPP)
32ecc53b1bSLee Trager 		dev_warn(fbd->dev, "Unable to disable firmware logs: %d\n",
33ecc53b1bSLee Trager 			 err);
34ecc53b1bSLee Trager }
35ecc53b1bSLee Trager 
fbnic_fw_log_init(struct fbnic_dev * fbd)36c2b93d6bSLee Trager int fbnic_fw_log_init(struct fbnic_dev *fbd)
37c2b93d6bSLee Trager {
38c2b93d6bSLee Trager 	struct fbnic_fw_log *log = &fbd->fw_log;
39c2b93d6bSLee Trager 	void *data;
40c2b93d6bSLee Trager 
41c2b93d6bSLee Trager 	if (WARN_ON_ONCE(fbnic_fw_log_ready(fbd)))
42c2b93d6bSLee Trager 		return -EEXIST;
43c2b93d6bSLee Trager 
44c2b93d6bSLee Trager 	data = vmalloc(FBNIC_FW_LOG_SIZE);
45c2b93d6bSLee Trager 	if (!data)
46c2b93d6bSLee Trager 		return -ENOMEM;
47c2b93d6bSLee Trager 
48c2b93d6bSLee Trager 	spin_lock_init(&fbd->fw_log.lock);
49c2b93d6bSLee Trager 	INIT_LIST_HEAD(&log->entries);
50c2b93d6bSLee Trager 	log->size = FBNIC_FW_LOG_SIZE;
51c2b93d6bSLee Trager 	log->data_start = data;
52c2b93d6bSLee Trager 	log->data_end = data + FBNIC_FW_LOG_SIZE;
53c2b93d6bSLee Trager 
54ecc53b1bSLee Trager 	fbnic_fw_log_enable(fbd, true);
55ecc53b1bSLee Trager 
56c2b93d6bSLee Trager 	return 0;
57c2b93d6bSLee Trager }
58c2b93d6bSLee Trager 
fbnic_fw_log_free(struct fbnic_dev * fbd)59c2b93d6bSLee Trager void fbnic_fw_log_free(struct fbnic_dev *fbd)
60c2b93d6bSLee Trager {
61c2b93d6bSLee Trager 	struct fbnic_fw_log *log = &fbd->fw_log;
62c2b93d6bSLee Trager 
63c2b93d6bSLee Trager 	if (!fbnic_fw_log_ready(fbd))
64c2b93d6bSLee Trager 		return;
65c2b93d6bSLee Trager 
66ecc53b1bSLee Trager 	fbnic_fw_log_disable(fbd);
67c2b93d6bSLee Trager 	INIT_LIST_HEAD(&log->entries);
68c2b93d6bSLee Trager 	log->size = 0;
69c2b93d6bSLee Trager 	vfree(log->data_start);
70c2b93d6bSLee Trager 	log->data_start = NULL;
71c2b93d6bSLee Trager 	log->data_end = NULL;
72c2b93d6bSLee Trager }
73c2b93d6bSLee Trager 
fbnic_fw_log_write(struct fbnic_dev * fbd,u64 index,u32 timestamp,char * msg)74c2b93d6bSLee Trager int fbnic_fw_log_write(struct fbnic_dev *fbd, u64 index, u32 timestamp,
75c2b93d6bSLee Trager 		       char *msg)
76c2b93d6bSLee Trager {
77c2b93d6bSLee Trager 	struct fbnic_fw_log_entry *entry, *head, *tail, *next;
78c2b93d6bSLee Trager 	struct fbnic_fw_log *log = &fbd->fw_log;
79c2b93d6bSLee Trager 	size_t msg_len = strlen(msg) + 1;
80c2b93d6bSLee Trager 	unsigned long flags;
81c2b93d6bSLee Trager 	void *entry_end;
82c2b93d6bSLee Trager 
83c2b93d6bSLee Trager 	if (!fbnic_fw_log_ready(fbd)) {
84c2b93d6bSLee Trager 		dev_err(fbd->dev, "Firmware sent log entry without being requested!\n");
85c2b93d6bSLee Trager 		return -ENOSPC;
86c2b93d6bSLee Trager 	}
87c2b93d6bSLee Trager 
88c2b93d6bSLee Trager 	spin_lock_irqsave(&log->lock, flags);
89c2b93d6bSLee Trager 
90c2b93d6bSLee Trager 	if (list_empty(&log->entries)) {
91c2b93d6bSLee Trager 		entry = log->data_start;
92c2b93d6bSLee Trager 	} else {
93c2b93d6bSLee Trager 		head = list_first_entry(&log->entries, typeof(*head), list);
94*0346000aSJakub Kicinski 		entry_end = head->msg + head->len + 1;
95*0346000aSJakub Kicinski 		entry = PTR_ALIGN(entry_end, 8);
96c2b93d6bSLee Trager 	}
97c2b93d6bSLee Trager 
98*0346000aSJakub Kicinski 	entry_end = entry->msg + msg_len + 1;
99c2b93d6bSLee Trager 
100c2b93d6bSLee Trager 	/* We've reached the end of the buffer, wrap around */
101c2b93d6bSLee Trager 	if (entry_end > log->data_end) {
102c2b93d6bSLee Trager 		entry = log->data_start;
103*0346000aSJakub Kicinski 		entry_end = entry->msg + msg_len + 1;
104c2b93d6bSLee Trager 	}
105c2b93d6bSLee Trager 
106c2b93d6bSLee Trager 	/* Make room for entry by removing from tail. */
107c2b93d6bSLee Trager 	list_for_each_entry_safe_reverse(tail, next, &log->entries, list) {
108c2b93d6bSLee Trager 		if (entry <= tail && entry_end > (void *)tail)
109c2b93d6bSLee Trager 			list_del(&tail->list);
110c2b93d6bSLee Trager 		else
111c2b93d6bSLee Trager 			break;
112c2b93d6bSLee Trager 	}
113c2b93d6bSLee Trager 
114c2b93d6bSLee Trager 	entry->index = index;
115c2b93d6bSLee Trager 	entry->timestamp = timestamp;
116c2b93d6bSLee Trager 	entry->len = msg_len;
117c2b93d6bSLee Trager 	strscpy(entry->msg, msg, entry->len);
118c2b93d6bSLee Trager 	list_add(&entry->list, &log->entries);
119c2b93d6bSLee Trager 
120c2b93d6bSLee Trager 	spin_unlock_irqrestore(&log->lock, flags);
121c2b93d6bSLee Trager 
122c2b93d6bSLee Trager 	return 0;
123c2b93d6bSLee Trager }
124