cregit-Linux how code gets into the kernel

Release 4.16 drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c

/*
 * Huawei HiNIC PCI Express Linux driver
 * Copyright(c) 2017 Huawei Technologies Co., Ltd
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <asm/barrier.h>

#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
#include "hinic_hw_api_cmd.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hw_dev.h"


#define SYNC_MSG_ID_MASK                0x1FF


#define SYNC_MSG_ID(pf_to_mgmt)         ((pf_to_mgmt)->sync_msg_id)


#define SYNC_MSG_ID_INC(pf_to_mgmt)     (SYNC_MSG_ID(pf_to_mgmt) = \
                                        ((SYNC_MSG_ID(pf_to_mgmt) + 1) & \
                                         SYNC_MSG_ID_MASK))


#define MSG_SZ_IS_VALID(in_size)        ((in_size) <= MAX_MSG_LEN)


#define MGMT_MSG_LEN_MIN                20

#define MGMT_MSG_LEN_STEP               16

#define MGMT_MSG_RSVD_FOR_DEV           8


#define SEGMENT_LEN                     48


#define MAX_PF_MGMT_BUF_SIZE            2048

/* Data should be SEG LEN size aligned */

#define MAX_MSG_LEN                     2016


#define MSG_NOT_RESP                    0xFFFF


#define MGMT_MSG_TIMEOUT                1000


#define mgmt_to_pfhwdev(pf_mgmt)        \
		container_of(pf_mgmt, struct hinic_pfhwdev, pf_to_mgmt)


enum msg_segment_type {
	
NOT_LAST_SEGMENT = 0,
	
LAST_SEGMENT     = 1,
};


enum mgmt_direction_type {
	
MGMT_DIRECT_SEND = 0,
	
MGMT_RESP        = 1,
};


enum msg_ack_type {
	
MSG_ACK         = 0,
	
MSG_NO_ACK      = 1,
};

/**
 * hinic_register_mgmt_msg_cb - register msg handler for a msg from a module
 * @pf_to_mgmt: PF to MGMT channel
 * @mod: module in the chip that this handler will handle its messages
 * @handle: private data for the callback
 * @callback: the handler that will handle messages
 **/

void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, void *handle, void (*callback)(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size)) { struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; mgmt_cb->cb = callback; mgmt_cb->handle = handle; mgmt_cb->state = HINIC_MGMT_CB_ENABLED; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk78100.00%1100.00%
Total78100.00%1100.00%

/** * hinic_unregister_mgmt_msg_cb - unregister msg handler for a msg from a module * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that this handler handles its messages **/
void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod) { struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; mgmt_cb->state &= ~HINIC_MGMT_CB_ENABLED; while (mgmt_cb->state & HINIC_MGMT_CB_RUNNING) schedule(); mgmt_cb->cb = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk51100.00%1100.00%
Total51100.00%1100.00%

/** * prepare_header - prepare the header of the message * @pf_to_mgmt: PF to MGMT channel * @msg_len: the length of the message * @mod: module in the chip that will get the message * @ack_type: ask for response * @direction: the direction of the message * @cmd: command of the message * @msg_id: message id * * Return the prepared header value **/
static u64 prepare_header(struct hinic_pf_to_mgmt *pf_to_mgmt, u16 msg_len, enum hinic_mod_type mod, enum msg_ack_type ack_type, enum mgmt_direction_type direction, u16 cmd, u16 msg_id) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; return HINIC_MSG_HEADER_SET(msg_len, MSG_LEN) | HINIC_MSG_HEADER_SET(mod, MODULE) | HINIC_MSG_HEADER_SET(SEGMENT_LEN, SEG_LEN) | HINIC_MSG_HEADER_SET(ack_type, NO_ACK) | HINIC_MSG_HEADER_SET(0, ASYNC_MGMT_TO_PF) | HINIC_MSG_HEADER_SET(0, SEQID) | HINIC_MSG_HEADER_SET(LAST_SEGMENT, LAST) | HINIC_MSG_HEADER_SET(direction, DIRECTION) | HINIC_MSG_HEADER_SET(cmd, CMD) | HINIC_MSG_HEADER_SET(HINIC_HWIF_PCI_INTF(hwif), PCI_INTF) | HINIC_MSG_HEADER_SET(HINIC_HWIF_PF_IDX(hwif), PF_IDX) | HINIC_MSG_HEADER_SET(msg_id, MSG_ID); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk132100.00%2100.00%
Total132100.00%2100.00%

/** * prepare_mgmt_cmd - prepare the mgmt command * @mgmt_cmd: pointer to the command to prepare * @header: pointer of the header for the message * @msg: the data of the message * @msg_len: the length of the message **/
static void prepare_mgmt_cmd(u8 *mgmt_cmd, u64 *header, u8 *msg, u16 msg_len) { memset(mgmt_cmd, 0, MGMT_MSG_RSVD_FOR_DEV); mgmt_cmd += MGMT_MSG_RSVD_FOR_DEV; memcpy(mgmt_cmd, header, sizeof(*header)); mgmt_cmd += sizeof(*header); memcpy(mgmt_cmd, msg, msg_len); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk64100.00%2100.00%
Total64100.00%2100.00%

/** * mgmt_msg_len - calculate the total message length * @msg_data_len: the length of the message data * * Return the total message length **/
static u16 mgmt_msg_len(u16 msg_data_len) { /* RSVD + HEADER_SIZE + DATA_LEN */ u16 msg_len = MGMT_MSG_RSVD_FOR_DEV + sizeof(u64) + msg_data_len; if (msg_len > MGMT_MSG_LEN_MIN) msg_len = MGMT_MSG_LEN_MIN + ALIGN((msg_len - MGMT_MSG_LEN_MIN), MGMT_MSG_LEN_STEP); else msg_len = MGMT_MSG_LEN_MIN; return msg_len; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk51100.00%2100.00%
Total51100.00%2100.00%

/** * send_msg_to_mgmt - send message to mgmt by API CMD * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @data: the msg data * @data_len: the msg data length * @ack_type: ask for response * @direction: the direction of the original message * @resp_msg_id: msg id to response for * * Return 0 - Success, negative - Failure **/
static int send_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, u8 *data, u16 data_len, enum msg_ack_type ack_type, enum mgmt_direction_type direction, u16 resp_msg_id) { struct hinic_api_cmd_chain *chain; u64 header; u16 msg_id; msg_id = SYNC_MSG_ID(pf_to_mgmt); if (direction == MGMT_RESP) { header = prepare_header(pf_to_mgmt, data_len, mod, ack_type, direction, cmd, resp_msg_id); } else { SYNC_MSG_ID_INC(pf_to_mgmt); header = prepare_header(pf_to_mgmt, data_len, mod, ack_type, direction, cmd, msg_id); } prepare_mgmt_cmd(pf_to_mgmt->sync_msg_buf, &header, data, data_len); chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_WRITE_TO_MGMT_CPU]; return hinic_api_cmd_write(chain, HINIC_NODE_ID_MGMT, pf_to_mgmt->sync_msg_buf, mgmt_msg_len(data_len)); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk148100.00%1100.00%
Total148100.00%1100.00%

/** * msg_to_mgmt_sync - send sync message to mgmt * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @buf_in: the msg data * @in_size: the msg data length * @buf_out: response * @out_size: response length * @direction: the direction of the original message * @resp_msg_id: msg id to response for * * Return 0 - Success, negative - Failure **/
static int msg_to_mgmt_sync(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, u8 *buf_in, u16 in_size, u8 *buf_out, u16 *out_size, enum mgmt_direction_type direction, u16 resp_msg_id) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; struct hinic_recv_msg *recv_msg; struct completion *recv_done; u16 msg_id; int err; /* Lock the sync_msg_buf */ down(&pf_to_mgmt->sync_msg_lock); recv_msg = &pf_to_mgmt->recv_resp_msg_from_mgmt; recv_done = &recv_msg->recv_done; if (resp_msg_id == MSG_NOT_RESP) msg_id = SYNC_MSG_ID(pf_to_mgmt); else msg_id = resp_msg_id; init_completion(recv_done); err = send_msg_to_mgmt(pf_to_mgmt, mod, cmd, buf_in, in_size, MSG_ACK, direction, resp_msg_id); if (err) { dev_err(&pdev->dev, "Failed to send sync msg to mgmt\n"); goto unlock_sync_msg; } if (!wait_for_completion_timeout(recv_done, MGMT_MSG_TIMEOUT)) { dev_err(&pdev->dev, "MGMT timeout, MSG id = %d\n", msg_id); err = -ETIMEDOUT; goto unlock_sync_msg; } smp_rmb(); /* verify reading after completion */ if (recv_msg->msg_id != msg_id) { dev_err(&pdev->dev, "incorrect MSG for id = %d\n", msg_id); err = -EFAULT; goto unlock_sync_msg; } if ((buf_out) && (recv_msg->msg_len <= MAX_PF_MGMT_BUF_SIZE)) { memcpy(buf_out, recv_msg->msg, recv_msg->msg_len); *out_size = recv_msg->msg_len; } unlock_sync_msg: up(&pf_to_mgmt->sync_msg_lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk275100.00%1100.00%
Total275100.00%1100.00%

/** * msg_to_mgmt_async - send message to mgmt without response * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @buf_in: the msg data * @in_size: the msg data length * @direction: the direction of the original message * @resp_msg_id: msg id to response for * * Return 0 - Success, negative - Failure **/
static int msg_to_mgmt_async(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, u8 *buf_in, u16 in_size, enum mgmt_direction_type direction, u16 resp_msg_id) { int err; /* Lock the sync_msg_buf */ down(&pf_to_mgmt->sync_msg_lock); err = send_msg_to_mgmt(pf_to_mgmt, mod, cmd, buf_in, in_size, MSG_NO_ACK, direction, resp_msg_id); up(&pf_to_mgmt->sync_msg_lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk76100.00%1100.00%
Total76100.00%1100.00%

/** * hinic_msg_to_mgmt - send message to mgmt * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @buf_in: the msg data * @in_size: the msg data length * @buf_out: response * @out_size: returned response length * @sync: sync msg or async msg * * Return 0 - Success, negative - Failure **/
int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, enum hinic_mgmt_msg_type sync) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; if (sync != HINIC_MGMT_MSG_SYNC) { dev_err(&pdev->dev, "Invalid MGMT msg type\n"); return -EINVAL; } if (!MSG_SZ_IS_VALID(in_size)) { dev_err(&pdev->dev, "Invalid MGMT msg buffer size\n"); return -EINVAL; } return msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size, buf_out, out_size, MGMT_DIRECT_SEND, MSG_NOT_RESP); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk122100.00%1100.00%
Total122100.00%1100.00%

/** * mgmt_recv_msg_handler - handler for message from mgmt cpu * @pf_to_mgmt: PF to MGMT channel * @recv_msg: received message details **/
static void mgmt_recv_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, struct hinic_recv_msg *recv_msg) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; u8 *buf_out = recv_msg->buf_out; struct hinic_mgmt_cb *mgmt_cb; unsigned long cb_state; u16 out_size = 0; if (recv_msg->mod >= HINIC_MOD_MAX) { dev_err(&pdev->dev, "Unknown MGMT MSG module = %d\n", recv_msg->mod); return; } mgmt_cb = &pf_to_mgmt->mgmt_cb[recv_msg->mod]; cb_state = cmpxchg(&mgmt_cb->state, HINIC_MGMT_CB_ENABLED, HINIC_MGMT_CB_ENABLED | HINIC_MGMT_CB_RUNNING); if ((cb_state == HINIC_MGMT_CB_ENABLED) && (mgmt_cb->cb)) mgmt_cb->cb(mgmt_cb->handle, recv_msg->cmd, recv_msg->msg, recv_msg->msg_len, buf_out, &out_size); else dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n", recv_msg->mod); mgmt_cb->state &= ~HINIC_MGMT_CB_RUNNING; if (!recv_msg->async_mgmt_to_pf) /* MGMT sent sync msg, send the response */ msg_to_mgmt_async(pf_to_mgmt, recv_msg->mod, recv_msg->cmd, buf_out, out_size, MGMT_RESP, recv_msg->msg_id); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk202100.00%2100.00%
Total202100.00%2100.00%

/** * mgmt_resp_msg_handler - handler for a response message from mgmt cpu * @pf_to_mgmt: PF to MGMT channel * @recv_msg: received message details **/
static void mgmt_resp_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, struct hinic_recv_msg *recv_msg) { wmb(); /* verify writing all, before reading */ complete(&recv_msg->recv_done); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk28100.00%1100.00%
Total28100.00%1100.00%

/** * recv_mgmt_msg_handler - handler for a message from mgmt cpu * @pf_to_mgmt: PF to MGMT channel * @header: the header of the message * @recv_msg: received message details **/
static void recv_mgmt_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, u64 *header, struct hinic_recv_msg *recv_msg) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; int seq_id, seg_len; u8 *msg_body; seq_id = HINIC_MSG_HEADER_GET(*header, SEQID); seg_len = HINIC_MSG_HEADER_GET(*header, SEG_LEN); if (seq_id >= (MAX_MSG_LEN / SEGMENT_LEN)) { dev_err(&pdev->dev, "recv big mgmt msg\n"); return; } msg_body = (u8 *)header + sizeof(*header); memcpy(recv_msg->msg + seq_id * SEGMENT_LEN, msg_body, seg_len); if (!HINIC_MSG_HEADER_GET(*header, LAST)) return; recv_msg->cmd = HINIC_MSG_HEADER_GET(*header, CMD); recv_msg->mod = HINIC_MSG_HEADER_GET(*header, MODULE); recv_msg->async_mgmt_to_pf = HINIC_MSG_HEADER_GET(*header, ASYNC_MGMT_TO_PF); recv_msg->msg_len = HINIC_MSG_HEADER_GET(*header, MSG_LEN); recv_msg->msg_id = HINIC_MSG_HEADER_GET(*header, MSG_ID); if (HINIC_MSG_HEADER_GET(*header, DIRECTION) == MGMT_RESP) mgmt_resp_msg_handler(pf_to_mgmt, recv_msg); else mgmt_recv_msg_handler(pf_to_mgmt, recv_msg); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk218100.00%1100.00%
Total218100.00%1100.00%

/** * mgmt_msg_aeqe_handler - handler for a mgmt message event * @handle: PF to MGMT channel * @data: the header of the message * @size: unused **/
static void mgmt_msg_aeqe_handler(void *handle, void *data, u8 size) { struct hinic_pf_to_mgmt *pf_to_mgmt = handle; struct hinic_recv_msg *recv_msg; u64 *header = (u64 *)data; recv_msg = HINIC_MSG_HEADER_GET(*header, DIRECTION) == MGMT_DIRECT_SEND ? &pf_to_mgmt->recv_msg_from_mgmt : &pf_to_mgmt->recv_resp_msg_from_mgmt; recv_mgmt_msg_handler(pf_to_mgmt, header, recv_msg); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk70100.00%1100.00%
Total70100.00%1100.00%

/** * alloc_recv_msg - allocate receive message memory * @pf_to_mgmt: PF to MGMT channel * @recv_msg: pointer that will hold the allocated data * * Return 0 - Success, negative - Failure **/
static int alloc_recv_msg(struct hinic_pf_to_mgmt *pf_to_mgmt, struct hinic_recv_msg *recv_msg) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; recv_msg->msg = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!recv_msg->msg) return -ENOMEM; recv_msg->buf_out = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!recv_msg->buf_out) return -ENOMEM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk91100.00%1100.00%
Total91100.00%1100.00%

/** * alloc_msg_buf - allocate all the message buffers of PF to MGMT channel * @pf_to_mgmt: PF to MGMT channel * * Return 0 - Success, negative - Failure **/
static int alloc_msg_buf(struct hinic_pf_to_mgmt *pf_to_mgmt) { struct hinic_hwif *hwif = pf_to_mgmt->hwif; struct pci_dev *pdev = hwif->pdev; int err; err = alloc_recv_msg(pf_to_mgmt, &pf_to_mgmt->recv_msg_from_mgmt); if (err) { dev_err(&pdev->dev, "Failed to allocate recv msg\n"); return err; } err = alloc_recv_msg(pf_to_mgmt, &pf_to_mgmt->recv_resp_msg_from_mgmt); if (err) { dev_err(&pdev->dev, "Failed to allocate resp recv msg\n"); return err; } pf_to_mgmt->sync_msg_buf = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!pf_to_mgmt->sync_msg_buf) return -ENOMEM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk124100.00%1100.00%
Total124100.00%1100.00%

/** * hinic_pf_to_mgmt_init - initialize PF to MGMT channel * @pf_to_mgmt: PF to MGMT channel * @hwif: HW interface the PF to MGMT will use for accessing HW * * Return 0 - Success, negative - Failure **/
int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, struct hinic_hwif *hwif) { struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); struct hinic_hwdev *hwdev = &pfhwdev->hwdev; struct pci_dev *pdev = hwif->pdev; int err; pf_to_mgmt->hwif = hwif; sema_init(&pf_to_mgmt->sync_msg_lock, 1); pf_to_mgmt->sync_msg_id = 0; err = alloc_msg_buf(pf_to_mgmt); if (err) { dev_err(&pdev->dev, "Failed to allocate msg buffers\n"); return err; } err = hinic_api_cmd_init(pf_to_mgmt->cmd_chain, hwif); if (err) { dev_err(&pdev->dev, "Failed to initialize cmd chains\n"); return err; } hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU, pf_to_mgmt, mgmt_msg_aeqe_handler); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk142100.00%3100.00%
Total142100.00%3100.00%

/** * hinic_pf_to_mgmt_free - free PF to MGMT channel * @pf_to_mgmt: PF to MGMT channel **/
void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt) { struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); struct hinic_hwdev *hwdev = &pfhwdev->hwdev; hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU); hinic_api_cmd_free(pf_to_mgmt->cmd_chain); }

Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk47100.00%2100.00%
Total47100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Aviad Krawczyk2081100.00%4100.00%
Total2081100.00%4100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.