/* * DirectConnect provides a common interface for the network devices to achieve the full or partial acceleration services from the underlying packet acceleration engine * Copyright (c) 2017, Intel Corporation. * * 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 #include //#include "directconnect_dp_api.h" #include "directconnect_dp_device.h" #define MAX(x, y) (x > y ? x : y) /*! \brief DC DP device block synchronization */ struct dc_dp_dev_lock { spinlock_t lock; /*!< DC DP lock */ unsigned long flags; /*!< flag */ uint32_t cnt; /*!< lock counter */ }; /* Global variables */ static struct dc_dp_priv_dev_info g_dc_dp_hw_dev[DC_DP_MAX_HW_DEVICE]; static struct dc_dp_priv_dev_info g_dc_dp_sw_dev[DC_DP_MAX_SW_DEVICE]; static struct dc_dp_dev_lock g_dc_dp_dev_lock; static inline int32_t dc_dp_lock_init(struct dc_dp_dev_lock *p_lock) { spin_lock_init(&p_lock->lock); return 0; } static inline void dc_dp_lock_destroy(struct dc_dp_dev_lock *p_lock) { return; } static inline void dc_dp_lock_device_list(struct dc_dp_dev_lock *p_lock) { spin_lock_bh(&p_lock->lock); } static inline void dc_dp_unlock_device_list(struct dc_dp_dev_lock *p_lock) { spin_unlock_bh(&p_lock->lock); } static int32_t __dc_dp_get_device(int32_t port_id, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = -1; int32_t dev_idx; if (port_id >= DC_DP_HW_PORT_RANGE_START && port_id <= DC_DP_HW_PORT_RANGE_END) { dev_idx = port_id; if (g_dc_dp_hw_dev[dev_idx].flags != DC_DP_DEV_FREE) { if (pp_devinfo) *pp_devinfo = &g_dc_dp_hw_dev[dev_idx]; ret = 0; } } else if (port_id >= DC_DP_SW_PORT_RANGE_START && port_id <= DC_DP_SW_PORT_RANGE_END) { dev_idx = port_id - DC_DP_SW_PORT_RANGE_START; if (g_dc_dp_sw_dev[dev_idx].flags != DC_DP_DEV_FREE) { if (pp_devinfo) *pp_devinfo = &g_dc_dp_sw_dev[dev_idx]; ret = 0; } } else { if (pp_devinfo) *pp_devinfo = NULL; } return ret; } static int32_t __dc_dp_find_device(int32_t port_id, struct net_device *dev, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = -1; int32_t dev_idx; if (pp_devinfo) *pp_devinfo = NULL; if (port_id >= DC_DP_HW_PORT_RANGE_START && port_id <= DC_DP_HW_PORT_RANGE_END) { dev_idx = port_id; if ((g_dc_dp_hw_dev[dev_idx].flags != DC_DP_DEV_FREE) && (g_dc_dp_hw_dev[dev_idx].dev == dev) && (g_dc_dp_hw_dev[dev_idx].port_id == port_id)) { if (pp_devinfo) { *pp_devinfo = &g_dc_dp_hw_dev[dev_idx]; ret = 0; } } } else if (port_id >= DC_DP_SW_PORT_RANGE_START && port_id <= DC_DP_SW_PORT_RANGE_END) { dev_idx = port_id - DC_DP_SW_PORT_RANGE_START; if ((g_dc_dp_sw_dev[dev_idx].flags != DC_DP_DEV_FREE) && (g_dc_dp_sw_dev[dev_idx].dev == dev) && (g_dc_dp_sw_dev[dev_idx].port_id == port_id)) { if (pp_devinfo) { *pp_devinfo = &g_dc_dp_sw_dev[dev_idx]; ret = 0; } } } return ret; } static int32_t __dc_dp_alloc_device(int32_t port_id, struct net_device *dev, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = -1; int32_t dev_idx; if (pp_devinfo) *pp_devinfo = NULL; if (port_id >= DC_DP_HW_PORT_RANGE_START && port_id <= DC_DP_HW_PORT_RANGE_END) { dev_idx = port_id; if (g_dc_dp_hw_dev[dev_idx].flags == DC_DP_DEV_FREE) { if (pp_devinfo) { *pp_devinfo = &g_dc_dp_hw_dev[dev_idx]; ret = 0; } } } else if (port_id >= DC_DP_SW_PORT_RANGE_START && port_id <= DC_DP_SW_PORT_RANGE_END) { dev_idx = port_id - DC_DP_SW_PORT_RANGE_START; if (g_dc_dp_sw_dev[dev_idx].flags == DC_DP_DEV_FREE) { if (pp_devinfo) { *pp_devinfo = &g_dc_dp_sw_dev[dev_idx]; ret = 0; } } } return ret; } static int32_t __dc_dp_free_device(int32_t port_id, struct net_device *dev) { int32_t ret = -1; struct dc_dp_priv_dev_info *p_devinfo = NULL; if ( (ret = __dc_dp_find_device(port_id, dev, &p_devinfo)) == 0 ) memset(p_devinfo, 0, sizeof(struct dc_dp_priv_dev_info)); return ret; } static int32_t __dc_dp_alloc_subif(struct dc_dp_priv_dev_info *p_devinfo, struct dp_subif *subif, struct dc_dp_priv_subif_info **pp_subifinfo) { int32_t ret = -1; int32_t subif_id; int32_t start = 0; int32_t end = DC_DP_MAX_SUBIF_PER_DEV; if (pp_subifinfo) *pp_subifinfo = NULL; if (subif && subif->subif >= 0) { start = dc_dp_get_subifidx(subif->subif); end = MAX((start + 1), DC_DP_MAX_SUBIF_PER_DEV); } for (subif_id = start; subif_id < end; subif_id++) { if (p_devinfo->subif_info[subif_id].flags == DC_DP_SUBIF_FREE) { if (pp_subifinfo) { p_devinfo->subif_info[subif_id].subif = ((subif_id & DC_DP_SUBIFID_MASK) << DC_DP_SUBIFID_OFFSET); p_devinfo->subif_info[subif_id].flags = DC_DP_SUBIF_REGISTERED; *pp_subifinfo = &p_devinfo->subif_info[subif_id]; ret = 0; } break; } } return ret; } static int32_t __dc_dp_free_subif(struct dc_dp_priv_subif_info *p_subifinfo) { memset(p_subifinfo, 0, sizeof(struct dc_dp_priv_subif_info)); return 0; } static void dc_dp_free_device_list(void) { int32_t dev_idx; dc_dp_lock_device_list(&g_dc_dp_dev_lock); /* Reset HW device array */ for ( dev_idx = 0; dev_idx < DC_DP_MAX_HW_DEVICE; dev_idx ++ ) memset(&g_dc_dp_hw_dev[dev_idx], 0, sizeof(struct dc_dp_priv_dev_info)); /* Reset SW device array */ for ( dev_idx = 0; dev_idx < DC_DP_MAX_SW_DEVICE; dev_idx ++ ) memset(&g_dc_dp_sw_dev[dev_idx], 0, sizeof(struct dc_dp_priv_dev_info)); dc_dp_unlock_device_list(&g_dc_dp_dev_lock); } /* * #################################### * Exported API * #################################### */ int32_t dc_dp_alloc_device(int32_t port_id, struct net_device *dev, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = -1; struct dc_dp_priv_dev_info *p_devinfo = NULL; dc_dp_lock_device_list(&g_dc_dp_dev_lock); if ( (ret = __dc_dp_alloc_device(port_id, dev, &p_devinfo)) == 0 ) { p_devinfo->port_id = port_id; p_devinfo->dev = dev; p_devinfo->flags = DC_DP_DEV_PORT_ALLOCATED; } dc_dp_unlock_device_list(&g_dc_dp_dev_lock); if (pp_devinfo) *pp_devinfo = p_devinfo; return ret; } void dc_dp_free_device(int32_t port_id, struct net_device *dev) { dc_dp_lock_device_list(&g_dc_dp_dev_lock); __dc_dp_free_device(port_id, dev); dc_dp_unlock_device_list(&g_dc_dp_dev_lock); } int32_t dc_dp_alloc_subif(struct dc_dp_priv_dev_info *p_devinfo, struct net_device *dev, struct dp_subif *subif, struct dc_dp_priv_subif_info **pp_subifinfo) { int32_t ret = -1; dc_dp_lock_device_list(&g_dc_dp_dev_lock); if ( (ret = __dc_dp_alloc_subif(p_devinfo, subif, pp_subifinfo)) == 0 ) { (*pp_subifinfo)->netif = dev; } dc_dp_unlock_device_list(&g_dc_dp_dev_lock); return ret; } void dc_dp_free_subif(struct dc_dp_priv_subif_info *p_subifinfo) { dc_dp_lock_device_list(&g_dc_dp_dev_lock); __dc_dp_free_subif(p_subifinfo); dc_dp_unlock_device_list(&g_dc_dp_dev_lock); } int32_t dc_dp_get_device_by_module_port(struct module *owner, int32_t port_id, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = -1; struct dc_dp_priv_dev_info *p_devinfo = NULL; if ( pp_devinfo ) *pp_devinfo = NULL; dc_dp_lock_device_list(&g_dc_dp_dev_lock); if ( (ret = __dc_dp_get_device(port_id, &p_devinfo)) == 0) { if ( (p_devinfo->owner == owner) && (p_devinfo->port_id == port_id) ) { if ( pp_devinfo ) { *pp_devinfo = p_devinfo; ret = 0; } } } dc_dp_unlock_device_list(&g_dc_dp_dev_lock); return ret; } int32_t dc_dp_get_device_by_port(int32_t port_id, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = -1; struct dc_dp_priv_dev_info *p_devinfo = NULL; if ( pp_devinfo ) *pp_devinfo = NULL; dc_dp_lock_device_list(&g_dc_dp_dev_lock); if ((ret = __dc_dp_get_device(port_id, &p_devinfo)) == 0 ) { if ( pp_devinfo ) *pp_devinfo = p_devinfo; } dc_dp_unlock_device_list(&g_dc_dp_dev_lock); return ret; } int32_t dc_dp_get_device_by_subif_netif(struct net_device *netif, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = DC_DP_FAILURE; struct dc_dp_priv_dev_info *p_devinfo = NULL; int32_t dev_idx; int32_t subif_idx; if (!pp_devinfo) goto out; *pp_devinfo = NULL; dc_dp_lock_device_list(&g_dc_dp_dev_lock); for ( dev_idx = 0; dev_idx < DC_DP_MAX_HW_DEVICE; dev_idx++ ) { p_devinfo = &g_dc_dp_hw_dev[dev_idx]; if (p_devinfo->flags != DC_DP_DEV_SUBIF_REGISTERED) continue; for (subif_idx = 0; subif_idx < DC_DP_MAX_SUBIF_PER_DEV; subif_idx++) { if (p_devinfo->subif_info[subif_idx].flags == DC_DP_SUBIF_FREE) continue; if ( p_devinfo->subif_info[subif_idx].netif == netif ) { *pp_devinfo = p_devinfo; ret = subif_idx; break; } } if (*pp_devinfo) break; } if (NULL == *pp_devinfo) { for ( dev_idx = 0; dev_idx < DC_DP_MAX_SW_DEVICE; dev_idx++ ) { p_devinfo = &g_dc_dp_sw_dev[dev_idx]; if (p_devinfo->flags != DC_DP_DEV_SUBIF_REGISTERED) continue; for (subif_idx = 0; subif_idx < DC_DP_MAX_SUBIF_PER_DEV; subif_idx++) { if (p_devinfo->subif_info[subif_idx].flags == DC_DP_SUBIF_FREE) continue; if ( p_devinfo->subif_info[subif_idx].netif == netif ) { *pp_devinfo = p_devinfo; ret = subif_idx; break; } } if (*pp_devinfo) break; } } dc_dp_unlock_device_list(&g_dc_dp_dev_lock); out: return ret; } int32_t dc_dp_get_device_by_subif_ifname(char *ifname, struct dc_dp_priv_dev_info **pp_devinfo) { int32_t ret = DC_DP_FAILURE; struct dc_dp_priv_dev_info *p_devinfo = NULL; int32_t dev_idx; int32_t subif_idx; if (!pp_devinfo) goto out; *pp_devinfo = NULL; dc_dp_lock_device_list(&g_dc_dp_dev_lock); for ( dev_idx = 0; dev_idx < DC_DP_MAX_HW_DEVICE; dev_idx++ ) { p_devinfo = &g_dc_dp_hw_dev[dev_idx]; if (p_devinfo->flags != DC_DP_DEV_SUBIF_REGISTERED) continue; for (subif_idx = 0; subif_idx < DC_DP_MAX_SUBIF_PER_DEV; subif_idx++) { if (p_devinfo->subif_info[subif_idx].flags == DC_DP_SUBIF_FREE) continue; if ( strncmp(p_devinfo->subif_info[subif_idx].device_name, ifname, IFNAMSIZ) == 0 ) { *pp_devinfo = p_devinfo; ret = subif_idx; break; } } if (*pp_devinfo) break; } if (NULL == *pp_devinfo) { for ( dev_idx = 0; dev_idx < DC_DP_MAX_SW_DEVICE; dev_idx++ ) { p_devinfo = &g_dc_dp_sw_dev[dev_idx]; if (p_devinfo->flags != DC_DP_DEV_SUBIF_REGISTERED) continue; for (subif_idx = 0; subif_idx < DC_DP_MAX_SUBIF_PER_DEV; subif_idx++) { if (p_devinfo->subif_info[subif_idx].flags == DC_DP_SUBIF_FREE) continue; if ( strncmp(p_devinfo->subif_info[subif_idx].device_name, ifname, IFNAMSIZ) == 0 ) { *pp_devinfo = p_devinfo; ret = subif_idx; break; } } if (*pp_devinfo) break; } } dc_dp_unlock_device_list(&g_dc_dp_dev_lock); out: return ret; } int32_t is_hw_port(int32_t port_id) { if (port_id >= DC_DP_HW_PORT_RANGE_START && port_id <= DC_DP_HW_PORT_RANGE_END) return 1; else return 0; } int32_t is_sw_port(int32_t port_id) { if (port_id >= DC_DP_SW_PORT_RANGE_START && port_id <= DC_DP_SW_PORT_RANGE_END) return 1; else return 0; } struct dc_dp_priv_dev_info * dc_dp_get_hw_device_head(void) { return &g_dc_dp_hw_dev[0]; } struct dc_dp_priv_dev_info * dc_dp_get_sw_device_head(void) { return &g_dc_dp_sw_dev[0]; } int32_t dc_dp_device_init(void) { dc_dp_lock_init(&g_dc_dp_dev_lock); dc_dp_free_device_list(); return 0; } void dc_dp_device_exit(void) { dc_dp_free_device_list(); dc_dp_lock_destroy(&g_dc_dp_dev_lock); }