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