OpenCloudOS-Kernel/drivers/thirdparty/hinic/ossl_knl_linux.c

498 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/pci_regs.h>
#include "ossl_knl_linux.h"
#define OSSL_MINUTE_BASE (60)
#if (KERNEL_VERSION(2, 6, 39) > LINUX_VERSION_CODE)
#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6, 0)))
#ifdef HAVE_NETDEV_SELECT_QUEUE
#include <net/ip.h>
#include <linux/pkt_sched.h>
#endif /* HAVE_NETDEV_SELECT_QUEUE */
#endif /* !(RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,0)) */
#endif /* < 2.6.39 */
#if (KERNEL_VERSION(3, 4, 0) > LINUX_VERSION_CODE)
void _kc_skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page,
int off, int size, unsigned int truesize)
{
skb_fill_page_desc(skb, i, page, off, size);
skb->len += size;
skb->data_len += size;
skb->truesize += truesize;
}
#endif /* < 3.4.0 */
#if (KERNEL_VERSION(3, 8, 0) > LINUX_VERSION_CODE)
/*
* pci_sriov_get_totalvfs -- get total VFs supported on this device
* @dev: the PCI PF device
*
* For a PCIe device with SRIOV support, return the PCIe
* SRIOV capability value of TotalVFs. Otherwise 0.
*/
int pci_sriov_get_totalvfs(struct pci_dev *dev)
{
int sriov_cap_pos;
u16 total_vfs = 0;
if (dev->is_virtfn)
return 0;
sriov_cap_pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
pci_read_config_word(dev, sriov_cap_pos + PCI_SRIOV_TOTAL_VF,
&total_vfs);
return total_vfs;
}
#endif
#if (KERNEL_VERSION(3, 10, 0) > LINUX_VERSION_CODE)
/*
* pci_vfs_assigned - returns number of VFs are assigned to a guest
* @dev: the PCI device
*
* Returns number of VFs belonging to this device that are assigned to a guest.
* If device is not a physical function returns -ENODEV.
*/
int pci_vfs_assigned(struct pci_dev *dev)
{
unsigned int vfs_assigned = 0;
#ifdef HAVE_PCI_DEV_FLAGS_ASSIGNED
struct pci_dev *vfdev;
unsigned short dev_id = 0;
int sriov_cap_pos;
/* only search if we are a PF. */
if (dev->is_virtfn)
return 0;
/* determine the device ID for the VFs, the vendor ID will be the
* same as the PF so there is no need to check for that one.
*/
sriov_cap_pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
pci_read_config_word(dev, sriov_cap_pos + PCI_SRIOV_VF_DID, &dev_id);
/* loop through all the VFs to see if we own any that are assigned. */
vfdev = pci_get_device(dev->vendor, dev_id, NULL);
while (vfdev) {
/* It is considered assigned if it is a virtual function with
* our dev as the physical function and the assigned bit is set.
*/
if (vfdev->is_virtfn && vfdev->physfn == dev &&
vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED)
vfs_assigned++;
vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
}
#endif /* HAVE_PCI_DEV_FLAGS_ASSIGNED */
return vfs_assigned;
}
#endif /* 3.10.0 */
#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
int __kc_dma_set_mask_and_coherent(struct device *dev, u64 mask)
{
int err = dma_set_mask(dev, mask);
if (!err)
/*
* coherent mask for the same size will always succeed if
* dma_set_mask does. However we store the error anyways, due
* to some kernels which use gcc's warn_unused_result on their
* definition of dma_set_coherent_mask.
*/
err = dma_set_coherent_mask(dev, mask);
return err;
}
void __kc_netdev_rss_key_fill(void *buffer, size_t len)
{
/* Set of random keys generated using kernel random number generator */
static const u8 seed[NETDEV_RSS_KEY_LEN] = {0xE6, 0xFA, 0x35, 0x62,
0x95, 0x12, 0x3E, 0xA3, 0xFB, 0x46, 0xC1, 0x5F,
0xB1, 0x43, 0x82, 0x5B, 0x6A, 0x49, 0x50, 0x95,
0xCD, 0xAB, 0xD8, 0x11, 0x8F, 0xC5, 0xBD, 0xBC,
0x6A, 0x4A, 0xB2, 0xD4, 0x1F, 0xFE, 0xBC, 0x41,
0xBF, 0xAC, 0xB2, 0x9A, 0x8F, 0x70, 0xE9, 0x2A,
0xD7, 0xB2, 0x80, 0xB6, 0x5B, 0xAA, 0x9D, 0x20};
BUG_ON(len > NETDEV_RSS_KEY_LEN);
memcpy(buffer, seed, len);
}
#endif /* 3.13.0 */
#if (KERNEL_VERSION(3, 14, 0) > LINUX_VERSION_CODE)
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
int minvec, int maxvec)
{
int nvec = maxvec;
int rc;
if (maxvec < minvec)
return -ERANGE;
do {
rc = pci_enable_msix(dev, entries, nvec);
if (rc < 0) {
return rc;
} else if (rc > 0) {
if (rc < minvec)
return -ENOSPC;
nvec = rc;
}
} while (rc);
return nvec;
}
#endif
#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
#ifdef HAVE_SET_RX_MODE
#ifdef NETDEV_HW_ADDR_T_UNICAST
int __kc_hw_addr_sync_dev(struct netdev_hw_addr_list *list,
struct net_device *dev,
int (*sync)(struct net_device *,
const unsigned char *),
int (*unsync)(struct net_device *,
const unsigned char *))
{
struct netdev_hw_addr *tmp;
struct netdev_hw_addr *ha;
int err;
/* first go through and flush out any stale entries. */
list_for_each_entry_safe(ha, tmp, &list->list, list) {
#if (KERNEL_VERSION(3, 10, 0) > LINUX_VERSION_CODE)
if (!ha->synced || ha->refcount != 1)
#else
if (!ha->sync_cnt || ha->refcount != 1)
#endif
continue;
if (unsync && unsync(dev, ha->addr))
continue;
list_del_rcu(&ha->list);
kfree_rcu(ha, rcu_head);
list->count--;
}
/* go through and sync new entries to the list. */
list_for_each_entry_safe(ha, tmp, &list->list, list) {
#if (KERNEL_VERSION(3, 10, 0) > LINUX_VERSION_CODE)
if (ha->synced)
#else
if (ha->sync_cnt)
#endif
continue;
err = sync(dev, ha->addr);
if (err)
return err;
#if (KERNEL_VERSION(3, 10, 0) > LINUX_VERSION_CODE)
ha->synced = true;
#else
ha->sync_cnt++;
#endif
ha->refcount++;
}
return 0;
}
void __kc_hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
struct net_device *dev,
int (*unsync)(struct net_device *,
const unsigned char *))
{
struct netdev_hw_addr *tmp;
struct netdev_hw_addr *ha;
list_for_each_entry_safe(ha, tmp, &list->list, list) {
#if (KERNEL_VERSION(3, 10, 0) > LINUX_VERSION_CODE)
if (!ha->synced)
#else
if (!ha->sync_cnt)
#endif
continue;
if (unsync && unsync(dev, ha->addr))
continue;
#if (KERNEL_VERSION(3, 10, 0) > LINUX_VERSION_CODE)
ha->synced = false;
#else
ha->sync_cnt--;
#endif
if (--ha->refcount)
continue;
list_del_rcu(&ha->list);
kfree_rcu(ha, rcu_head);
list->count--;
}
}
#endif /* NETDEV_HW_ADDR_T_UNICAST */
#ifndef NETDEV_HW_ADDR_T_MULTICAST
int __kc_dev_addr_sync_dev(struct dev_addr_list **list, int *count,
struct net_device *dev,
int (*sync)(struct net_device *,
const unsigned char *),
int (*unsync)(struct net_device *,
const unsigned char *))
{
struct dev_addr_list **next = list;
struct dev_addr_list *da;
int err;
/* first go through and flush out any stale entries. */
while ((da = *next)) {
if (da->da_synced && da->da_users == 1) {
if (!unsync || !unsync(dev, da->da_addr)) {
*next = da->next;
kfree(da);
(*count)--;
continue;
}
}
next = &da->next;
}
/* go through and sync new entries to the list. */
for (da = *list; da; da = da->next) {
if (da->da_synced)
continue;
err = sync(dev, da->da_addr);
if (err)
return err;
da->da_synced++;
da->da_users++;
}
return 0;
}
void __kc_dev_addr_unsync_dev(struct dev_addr_list **list, int *count,
struct net_device *dev,
int (*unsync)(struct net_device *,
const unsigned char *))
{
struct dev_addr_list *da;
while ((da = *list) != NULL) {
if (da->da_synced) {
if (!unsync || !unsync(dev, da->da_addr)) {
da->da_synced--;
if (--da->da_users == 0) {
*list = da->next;
kfree(da);
(*count)--;
continue;
}
}
}
list = &da->next;
}
}
#endif /* NETDEV_HW_ADDR_T_MULTICAST */
#endif /* HAVE_SET_RX_MODE */
void *__kc_devm_kmemdup(struct device *dev, const void *src, size_t len,
unsigned int gfp)
{
void *p;
p = devm_kzalloc(dev, len, gfp);
if (p)
memcpy(p, src, len);
return p;
}
#endif /* 3.16.0 */
#if (KERNEL_VERSION(3, 19, 0) > LINUX_VERSION_CODE)
#ifdef HAVE_NET_GET_RANDOM_ONCE
static u8 __kc_netdev_rss_key[NETDEV_RSS_KEY_LEN];
void __kc_netdev_rss_key_fill(void *buffer, size_t len)
{
BUG_ON(len > sizeof(__kc_netdev_rss_key));
net_get_random_once(__kc_netdev_rss_key, sizeof(__kc_netdev_rss_key));
memcpy(buffer, __kc_netdev_rss_key, len);
}
#endif
#endif
#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE)
unsigned int cpumask_local_spread(unsigned int i, int node)
{
int cpu;
/* Wrap: we always want a cpu. */
i %= num_online_cpus();
if (node == -1) {
for_each_cpu(cpu, cpu_online_mask)
if (i-- == 0)
return cpu;
} else {
/* NUMA first. */
for_each_cpu_and(cpu, cpumask_of_node(node), cpu_online_mask)
if (i-- == 0)
return cpu;
for_each_cpu(cpu, cpu_online_mask) {
/* Skip NUMA nodes, done above. */
if (cpumask_test_cpu(cpu, cpumask_of_node(node)))
continue;
if (i-- == 0)
return cpu;
}
}
BUG();
}
#endif
struct file *file_creat(const char *file_name)
{
return filp_open(file_name, O_CREAT | O_RDWR | O_APPEND, 0);
}
struct file *file_open(const char *file_name)
{
return filp_open(file_name, O_RDONLY, 0);
}
void file_close(struct file *file_handle)
{
(void)filp_close(file_handle, NULL);
}
u32 get_file_size(struct file *file_handle)
{
struct inode *file_inode;
#if (KERNEL_VERSION(3, 19, 0) > LINUX_VERSION_CODE)
file_inode = file_handle->f_dentry->d_inode;
#else
file_inode = file_handle->f_inode;
#endif
return (u32)(file_inode->i_size);
}
void set_file_position(struct file *file_handle, u32 position)
{
file_handle->f_pos = position;
}
int file_read(struct file *file_handle, char *log_buffer,
u32 rd_length, u32 *file_pos)
{
return (int)file_handle->f_op->read(file_handle, log_buffer,
rd_length, &file_handle->f_pos);
}
u32 file_write(struct file *file_handle, char *log_buffer, u32 wr_length)
{
return (u32)file_handle->f_op->write(file_handle, log_buffer,
wr_length, &file_handle->f_pos);
}
static int _linux_thread_func(void *thread)
{
struct sdk_thread_info *info = (struct sdk_thread_info *)thread;
while (!kthread_should_stop())
info->thread_fn(info->data);
return 0;
}
int creat_thread(struct sdk_thread_info *thread_info)
{
thread_info->thread_obj = kthread_run(_linux_thread_func,
thread_info, thread_info->name);
if (!thread_info->thread_obj)
return -EFAULT;
return 0;
}
void stop_thread(struct sdk_thread_info *thread_info)
{
if (thread_info->thread_obj)
(void)kthread_stop(thread_info->thread_obj);
}
void utctime_to_localtime(u64 utctime, u64 *localtime)
{
*localtime = utctime - sys_tz.tz_minuteswest * OSSL_MINUTE_BASE;
}
#ifndef HAVE_TIMER_SETUP
void initialize_timer(void *adapter_hdl, struct timer_list *timer)
{
if (!adapter_hdl || !timer)
return;
init_timer(timer);
}
#endif
void add_to_timer(struct timer_list *timer, long period)
{
if (!timer)
return;
add_timer(timer);
}
void stop_timer(struct timer_list *timer)
{
}
void delete_timer(struct timer_list *timer)
{
if (!timer)
return;
del_timer_sync(timer);
}
int local_atoi(const char *name)
{
int val = 0;
for (;; name++) {
switch (*name) {
case '0' ... '9':
val = 10 * val + (*name - '0');
break;
default:
return val;
}
}
}