@@ -585,6 +585,66 @@ kni_ioctl_free(struct net *net, uint32_t ioctl_num,
return ret;
}
+static int
+kni_ioctl_linkstat(struct net *net, uint32_t ioctl_num,
+ unsigned long ioctl_param)
+{
+ struct kni_net *knet = net_generic(net, kni_net_id);
+ int ret = -EINVAL;
+ struct kni_dev *dev, *n;
+ struct rte_kni_link_info link_info;
+ struct net_device *netdev;
+ uint16_t link;
+
+ if (_IOC_SIZE(ioctl_num) > sizeof(link_info))
+ return -EINVAL;
+
+ ret = copy_from_user(&link_info, (void *)ioctl_param,
+ sizeof(link_info));
+ if (ret) {
+ pr_err("copy_from_user in kni_ioctl_release");
+ return -EIO;
+ }
+
+ /* Release the network device according to its name */
+ if (strlen(link_info.name) == 0)
+ return ret;
+
+ down_read(&knet->kni_list_lock);
+ list_for_each_entry_safe(dev, n, &knet->kni_list_head, list) {
+ if (strncmp(dev->name, link_info.name, RTE_KNI_NAMESIZE) != 0)
+ continue;
+
+ netdev = dev->net_dev;
+ if (netdev == NULL) {
+ up_read(&knet->kni_list_lock);
+ return ret;
+ }
+
+ link = link_info.link_status;
+
+ if (!netif_carrier_ok(netdev) && link) {
+ pr_info("%s NIC Link is Up %d Mbps %s.\n",
+ netdev->name,
+ link_info.link_speed,
+ link_info.link_duplex ==
+ RTE_KNI_LINK_FULL_DUPLEX ?
+ "Full Duplex" : "Half Duplex");
+ netif_carrier_on(netdev);
+ } else if (netif_carrier_ok(netdev) && !link) {
+ pr_info("%s NIC Link is Down.\n",
+ netdev->name);
+ netif_carrier_off(netdev);
+ }
+
+ ret = 0;
+ break;
+ }
+ up_read(&knet->kni_list_lock);
+
+ return ret;
+}
+
static int
kni_ioctl(struct inode *inode, uint32_t ioctl_num, unsigned long ioctl_param)
{
@@ -609,6 +669,9 @@ kni_ioctl(struct inode *inode, uint32_t ioctl_num, unsigned long ioctl_param)
case _IOC_NR(RTE_KNI_IOCTL_FREE):
ret = kni_ioctl_free(net, ioctl_num, ioctl_param);
break;
+ case _IOC_NR(RTE_KNI_IOCTL_LINKSTAT):
+ ret = kni_ioctl_linkstat(net, ioctl_num, ioctl_param);
+ break;
default:
pr_debug("IOCTL default\n");
break;
@@ -134,6 +134,7 @@ kni_net_open(struct net_device *dev)
struct kni_dev *kni = netdev_priv(dev);
netif_start_queue(dev);
+ netif_carrier_off(dev);
memset(&req, 0, sizeof(req));
req.req_id = RTE_KNI_REQ_CFG_NETWORK_IF;
@@ -153,6 +154,7 @@ kni_net_release(struct net_device *dev)
struct kni_dev *kni = netdev_priv(dev);
netif_stop_queue(dev); /* can't transmit any more */
+ netif_carrier_off(dev);
memset(&req, 0, sizeof(req));
req.req_id = RTE_KNI_REQ_CFG_NETWORK_IF;
@@ -124,11 +124,30 @@ struct rte_kni_device_info {
char mac_addr[6];
};
+
+struct rte_kni_link_info {
+ char name[RTE_KNI_NAMESIZE]; /**< Network device name for KNI */
+ uint32_t link_speed; /**< ETH_SPEED_NUM_ */
+
+#define RTE_KNI_LINK_HALF_DUPLEX 0 /**< Half-duplex connection. */
+#define RTE_KNI_LINK_FULL_DUPLEX 1 /**< Full-duplex connection. */
+ uint16_t link_duplex : 1; /**< RTE_KNI_LINK_[HALF/FULL]_DUPLEX */
+
+#define RTE_KNI_LINK_FIXED 0 /**< No autonegotiation. */
+#define RTE_KNI_LINK_AUTONEG 1 /**< Autonegotiated. */
+ uint16_t link_autoneg : 1; /**< RTE_KNI_LINK_[AUTONEG/FIXED] */
+
+#define RTE_KNI_LINK_DOWN 0 /**< Link is down. */
+#define RTE_KNI_LINK_UP 1 /**< Link is up. */
+ uint16_t link_status : 1; /**< RTE_KNI_LINK_[DOWN/UP] */
+};
+
#define KNI_DEVICE "kni"
#define RTE_KNI_IOCTL_TEST _IOWR(0, 1, int)
#define RTE_KNI_IOCTL_CREATE _IOWR(0, 2, struct rte_kni_device_info)
#define RTE_KNI_IOCTL_RELEASE _IOWR(0, 3, struct rte_kni_device_info)
#define RTE_KNI_IOCTL_FREE _IOWR(0, 4, struct rte_kni_device_info)
+#define RTE_KNI_IOCTL_LINKSTAT _IOWR(0, 5, struct rte_kni_link_info)
#endif /* _RTE_KNI_COMMON_H_ */
@@ -13,7 +13,6 @@
#include <rte_spinlock.h>
#include <rte_string_fns.h>
-#include <rte_ethdev.h>
#include <rte_malloc.h>
#include <rte_log.h>
#include <rte_kni.h>
@@ -817,6 +816,42 @@ rte_kni_unregister_handlers(struct rte_kni *kni)
return 0;
}
+
+int
+rte_kni_update_link(struct rte_kni *kni, struct rte_eth_link *link)
+{
+ struct rte_kni_link_info link_info;
+
+ if (kni == NULL || !kni->in_use || link == NULL)
+ return -1;
+
+ snprintf(link_info.name, sizeof(link_info.name), "%s", kni->name);
+
+ link_info.link_speed = link->link_speed;
+ if (link->link_duplex == ETH_LINK_FULL_DUPLEX)
+ link_info.link_duplex = RTE_KNI_LINK_FULL_DUPLEX;
+ else
+ link_info.link_duplex = RTE_KNI_LINK_FULL_DUPLEX;
+
+ if (link->link_autoneg == ETH_LINK_FIXED)
+ link_info.link_autoneg = RTE_KNI_LINK_FIXED;
+ else
+ link_info.link_autoneg = RTE_KNI_LINK_AUTONEG;
+
+ if (link->link_status == ETH_LINK_UP)
+ link_info.link_status = RTE_KNI_LINK_UP;
+ else
+ link_info.link_status = RTE_KNI_LINK_DOWN;
+
+ if (ioctl(kni_fd, RTE_KNI_IOCTL_LINKSTAT, &link_info) < 0) {
+ RTE_LOG(ERR, KNI,
+ "Failed to update kni link info for dev '%s'.\n",
+ kni->name);
+ return -1;
+ }
+ return 0;
+}
+
void
rte_kni_close(void)
{
@@ -21,6 +21,7 @@
#include <rte_memory.h>
#include <rte_mempool.h>
#include <rte_ether.h>
+#include <rte_ethdev.h>
#include <exec-env/rte_kni_common.h>
@@ -255,6 +256,21 @@ int rte_kni_register_handlers(struct rte_kni *kni, struct rte_kni_ops *ops);
*/
int rte_kni_unregister_handlers(struct rte_kni *kni);
+/**
+ * Update link status info for KNI port.
+ *
+ * Update the linkup/linkdown status of a KNI interface in the kernel.
+ *
+ * @param kni
+ * pointer to struct rte_kni.
+ *
+ * @return
+ * On success: 0
+ * On failure: -1
+ */
+int __rte_experimental
+rte_kni_update_link(struct rte_kni *kni, struct rte_eth_link *link);
+
/**
* Close KNI device.
*/