diff options
author | Rajesh Borundia <rajesh.borundia@qlogic.com> | 2013-04-19 03:01:11 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-04-19 16:02:38 -0400 |
commit | 4000e7a78d12d71e37fcd2366c73fcb02e97fffb (patch) | |
tree | 8408088ddf4c1586356a4435fa1fb3a1baaf3f8c | |
parent | f036e4f44ef04ffd78ffc2f515ebf60ffa543d21 (diff) |
qlcnic: Support MAC address, Tx rate config.
o Add support for MAC address and Tx rate configuration
per VF via iproute2 tool.
o Tx rate change is allowed while the guest is running
and the VF driver is loaded.
o MAC address change is allowed only when VF driver
is not loaded.
Signed-off-by: Manish Chopra <manish.chopra@qlogic.com>
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Rajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
4 files changed, 213 insertions, 3 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 33f154e4c75b..b3ab7a3386e3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | |||
@@ -341,6 +341,11 @@ static const struct net_device_ops qlcnic_netdev_ops = { | |||
341 | #ifdef CONFIG_NET_POLL_CONTROLLER | 341 | #ifdef CONFIG_NET_POLL_CONTROLLER |
342 | .ndo_poll_controller = qlcnic_poll_controller, | 342 | .ndo_poll_controller = qlcnic_poll_controller, |
343 | #endif | 343 | #endif |
344 | #ifdef CONFIG_QLCNIC_SRIOV | ||
345 | .ndo_set_vf_mac = qlcnic_sriov_set_vf_mac, | ||
346 | .ndo_set_vf_tx_rate = qlcnic_sriov_set_vf_tx_rate, | ||
347 | .ndo_get_vf_config = qlcnic_sriov_get_vf_config, | ||
348 | #endif | ||
344 | }; | 349 | }; |
345 | 350 | ||
346 | static const struct net_device_ops qlcnic_netdev_failed_ops = { | 351 | static const struct net_device_ops qlcnic_netdev_failed_ops = { |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index 72db8b135dc7..5f73d42d6689 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | |||
@@ -116,6 +116,8 @@ struct qlcnic_resources { | |||
116 | 116 | ||
117 | struct qlcnic_vport { | 117 | struct qlcnic_vport { |
118 | u16 handle; | 118 | u16 handle; |
119 | u16 max_tx_bw; | ||
120 | u16 min_tx_bw; | ||
119 | u8 mac[6]; | 121 | u8 mac[6]; |
120 | }; | 122 | }; |
121 | 123 | ||
@@ -173,6 +175,8 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *); | |||
173 | void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *); | 175 | void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *); |
174 | int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *, | 176 | int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *, |
175 | struct qlcnic_bc_trans *); | 177 | struct qlcnic_bc_trans *); |
178 | int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *, | ||
179 | struct qlcnic_info *, u16); | ||
176 | 180 | ||
177 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) | 181 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) |
178 | { | 182 | { |
@@ -199,6 +203,10 @@ bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *, | |||
199 | struct qlcnic_vf_info *); | 203 | struct qlcnic_vf_info *); |
200 | void qlcnic_sriov_pf_reset(struct qlcnic_adapter *); | 204 | void qlcnic_sriov_pf_reset(struct qlcnic_adapter *); |
201 | int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *); | 205 | int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *); |
206 | int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *); | ||
207 | int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); | ||
208 | int qlcnic_sriov_get_vf_config(struct net_device *, int , | ||
209 | struct ifla_vf_info *); | ||
202 | #else | 210 | #else |
203 | static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} | 211 | static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} |
204 | static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} | 212 | static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 305c7cd1e56e..b00cba96a82a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | |||
@@ -181,6 +181,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) | |||
181 | goto qlcnic_destroy_async_wq; | 181 | goto qlcnic_destroy_async_wq; |
182 | } | 182 | } |
183 | sriov->vf_info[i].vp = vp; | 183 | sriov->vf_info[i].vp = vp; |
184 | vp->max_tx_bw = MAX_BW; | ||
184 | random_ether_addr(vp->mac); | 185 | random_ether_addr(vp->mac); |
185 | dev_info(&adapter->pdev->dev, | 186 | dev_info(&adapter->pdev->dev, |
186 | "MAC Address %pM is configured for VF %d\n", | 187 | "MAC Address %pM is configured for VF %d\n", |
@@ -378,12 +379,83 @@ static void qlcnic_sriov_vf_cfg_buff_desc(struct qlcnic_adapter *adapter) | |||
378 | adapter->max_rds_rings = MAX_RDS_RINGS; | 379 | adapter->max_rds_rings = MAX_RDS_RINGS; |
379 | } | 380 | } |
380 | 381 | ||
382 | int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *adapter, | ||
383 | struct qlcnic_info *npar_info, u16 vport_id) | ||
384 | { | ||
385 | struct device *dev = &adapter->pdev->dev; | ||
386 | struct qlcnic_cmd_args cmd; | ||
387 | int err; | ||
388 | u32 status; | ||
389 | |||
390 | err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); | ||
391 | if (err) | ||
392 | return err; | ||
393 | |||
394 | cmd.req.arg[1] = vport_id << 16 | 0x1; | ||
395 | err = qlcnic_issue_cmd(adapter, &cmd); | ||
396 | if (err) { | ||
397 | dev_err(&adapter->pdev->dev, | ||
398 | "Failed to get vport info, err=%d\n", err); | ||
399 | qlcnic_free_mbx_args(&cmd); | ||
400 | return err; | ||
401 | } | ||
402 | |||
403 | status = cmd.rsp.arg[2] & 0xffff; | ||
404 | if (status & BIT_0) | ||
405 | npar_info->min_tx_bw = MSW(cmd.rsp.arg[2]); | ||
406 | if (status & BIT_1) | ||
407 | npar_info->max_tx_bw = LSW(cmd.rsp.arg[3]); | ||
408 | if (status & BIT_2) | ||
409 | npar_info->max_tx_ques = MSW(cmd.rsp.arg[3]); | ||
410 | if (status & BIT_3) | ||
411 | npar_info->max_tx_mac_filters = LSW(cmd.rsp.arg[4]); | ||
412 | if (status & BIT_4) | ||
413 | npar_info->max_rx_mcast_mac_filters = MSW(cmd.rsp.arg[4]); | ||
414 | if (status & BIT_5) | ||
415 | npar_info->max_rx_ucast_mac_filters = LSW(cmd.rsp.arg[5]); | ||
416 | if (status & BIT_6) | ||
417 | npar_info->max_rx_ip_addr = MSW(cmd.rsp.arg[5]); | ||
418 | if (status & BIT_7) | ||
419 | npar_info->max_rx_lro_flow = LSW(cmd.rsp.arg[6]); | ||
420 | if (status & BIT_8) | ||
421 | npar_info->max_rx_status_rings = MSW(cmd.rsp.arg[6]); | ||
422 | if (status & BIT_9) | ||
423 | npar_info->max_rx_buf_rings = LSW(cmd.rsp.arg[7]); | ||
424 | |||
425 | npar_info->max_rx_ques = MSW(cmd.rsp.arg[7]); | ||
426 | npar_info->max_tx_vlan_keys = LSW(cmd.rsp.arg[8]); | ||
427 | npar_info->max_local_ipv6_addrs = MSW(cmd.rsp.arg[8]); | ||
428 | npar_info->max_remote_ipv6_addrs = LSW(cmd.rsp.arg[9]); | ||
429 | |||
430 | dev_info(dev, "\n\tmin_tx_bw: %d, max_tx_bw: %d max_tx_ques: %d,\n" | ||
431 | "\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n" | ||
432 | "\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n" | ||
433 | "\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n" | ||
434 | "\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n" | ||
435 | "\tlocal_ipv6_addr: %d, remote_ipv6_addr: %d\n", | ||
436 | npar_info->min_tx_bw, npar_info->max_tx_bw, | ||
437 | npar_info->max_tx_ques, npar_info->max_tx_mac_filters, | ||
438 | npar_info->max_rx_mcast_mac_filters, | ||
439 | npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr, | ||
440 | npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings, | ||
441 | npar_info->max_rx_buf_rings, npar_info->max_rx_ques, | ||
442 | npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs, | ||
443 | npar_info->max_remote_ipv6_addrs); | ||
444 | |||
445 | qlcnic_free_mbx_args(&cmd); | ||
446 | return err; | ||
447 | } | ||
448 | |||
381 | static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) | 449 | static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) |
382 | { | 450 | { |
383 | struct qlcnic_info nic_info; | 451 | struct qlcnic_info nic_info; |
384 | struct qlcnic_hardware_context *ahw = adapter->ahw; | 452 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
385 | int err; | 453 | int err; |
386 | 454 | ||
455 | err = qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, 0); | ||
456 | if (err) | ||
457 | return err; | ||
458 | |||
387 | err = qlcnic_get_nic_info(adapter, &nic_info, ahw->pci_func); | 459 | err = qlcnic_get_nic_info(adapter, &nic_info, ahw->pci_func); |
388 | if (err) | 460 | if (err) |
389 | return -EIO; | 461 | return -EIO; |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index a0135297b1c6..59d385b7ccd7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | |||
@@ -10,6 +10,8 @@ | |||
10 | #include <linux/types.h> | 10 | #include <linux/types.h> |
11 | 11 | ||
12 | #define QLCNIC_SRIOV_VF_MAX_MAC 1 | 12 | #define QLCNIC_SRIOV_VF_MAX_MAC 1 |
13 | #define QLC_VF_MIN_TX_RATE 100 | ||
14 | #define QLC_VF_MAX_TX_RATE 9999 | ||
13 | 15 | ||
14 | static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8); | 16 | static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8); |
15 | 17 | ||
@@ -62,8 +64,9 @@ static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter, | |||
62 | { | 64 | { |
63 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | 65 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; |
64 | struct qlcnic_resources *res = &sriov->ff_max; | 66 | struct qlcnic_resources *res = &sriov->ff_max; |
65 | int ret = -EIO, vpid; | ||
66 | u32 temp, num_vf_macs, num_vfs, max; | 67 | u32 temp, num_vf_macs, num_vfs, max; |
68 | int ret = -EIO, vpid, id; | ||
69 | struct qlcnic_vport *vp; | ||
67 | 70 | ||
68 | vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func); | 71 | vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func); |
69 | if (vpid < 0) | 72 | if (vpid < 0) |
@@ -72,8 +75,6 @@ static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter, | |||
72 | num_vfs = sriov->num_vfs; | 75 | num_vfs = sriov->num_vfs; |
73 | max = num_vfs + 1; | 76 | max = num_vfs + 1; |
74 | info->bit_offsets = 0xffff; | 77 | info->bit_offsets = 0xffff; |
75 | info->min_tx_bw = 0; | ||
76 | info->max_tx_bw = MAX_BW; | ||
77 | info->max_tx_ques = res->num_tx_queues / max; | 78 | info->max_tx_ques = res->num_tx_queues / max; |
78 | info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters; | 79 | info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters; |
79 | num_vf_macs = QLCNIC_SRIOV_VF_MAX_MAC; | 80 | num_vf_macs = QLCNIC_SRIOV_VF_MAX_MAC; |
@@ -83,7 +84,15 @@ static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter, | |||
83 | info->max_rx_ucast_mac_filters = temp; | 84 | info->max_rx_ucast_mac_filters = temp; |
84 | temp = res->num_tx_mac_filters - (num_vfs * num_vf_macs); | 85 | temp = res->num_tx_mac_filters - (num_vfs * num_vf_macs); |
85 | info->max_tx_mac_filters = temp; | 86 | info->max_tx_mac_filters = temp; |
87 | info->min_tx_bw = 0; | ||
88 | info->max_tx_bw = MAX_BW; | ||
86 | } else { | 89 | } else { |
90 | id = qlcnic_sriov_func_to_index(adapter, func); | ||
91 | if (id < 0) | ||
92 | return id; | ||
93 | vp = sriov->vf_info[id].vp; | ||
94 | info->min_tx_bw = vp->min_tx_bw; | ||
95 | info->max_tx_bw = vp->max_tx_bw; | ||
87 | info->max_rx_ucast_mac_filters = num_vf_macs; | 96 | info->max_rx_ucast_mac_filters = num_vf_macs; |
88 | info->max_tx_mac_filters = num_vf_macs; | 97 | info->max_tx_mac_filters = num_vf_macs; |
89 | } | 98 | } |
@@ -1416,3 +1425,119 @@ int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *adapter) | |||
1416 | __func__, ahw->op_mode); | 1425 | __func__, ahw->op_mode); |
1417 | return err; | 1426 | return err; |
1418 | } | 1427 | } |
1428 | |||
1429 | int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) | ||
1430 | { | ||
1431 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
1432 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | ||
1433 | int i, num_vfs = sriov->num_vfs; | ||
1434 | struct qlcnic_vf_info *vf_info; | ||
1435 | u8 *curr_mac; | ||
1436 | |||
1437 | if (!qlcnic_sriov_pf_check(adapter)) | ||
1438 | return -EOPNOTSUPP; | ||
1439 | |||
1440 | if (!is_valid_ether_addr(mac) || vf >= num_vfs) | ||
1441 | return -EINVAL; | ||
1442 | |||
1443 | if (!compare_ether_addr(adapter->mac_addr, mac)) { | ||
1444 | netdev_err(netdev, "MAC address is already in use by the PF\n"); | ||
1445 | return -EINVAL; | ||
1446 | } | ||
1447 | |||
1448 | for (i = 0; i < num_vfs; i++) { | ||
1449 | vf_info = &sriov->vf_info[i]; | ||
1450 | if (!compare_ether_addr(vf_info->vp->mac, mac)) { | ||
1451 | netdev_err(netdev, | ||
1452 | "MAC address is already in use by VF %d\n", | ||
1453 | i); | ||
1454 | return -EINVAL; | ||
1455 | } | ||
1456 | } | ||
1457 | |||
1458 | vf_info = &sriov->vf_info[vf]; | ||
1459 | curr_mac = vf_info->vp->mac; | ||
1460 | |||
1461 | if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { | ||
1462 | netdev_err(netdev, | ||
1463 | "MAC address change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n", | ||
1464 | vf); | ||
1465 | return -EOPNOTSUPP; | ||
1466 | } | ||
1467 | |||
1468 | memcpy(curr_mac, mac, netdev->addr_len); | ||
1469 | netdev_info(netdev, "MAC Address %pM is configured for VF %d\n", | ||
1470 | mac, vf); | ||
1471 | return 0; | ||
1472 | } | ||
1473 | |||
1474 | int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) | ||
1475 | { | ||
1476 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
1477 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | ||
1478 | struct qlcnic_vf_info *vf_info; | ||
1479 | struct qlcnic_info nic_info; | ||
1480 | struct qlcnic_vport *vp; | ||
1481 | u16 vpid; | ||
1482 | |||
1483 | if (!qlcnic_sriov_pf_check(adapter)) | ||
1484 | return -EOPNOTSUPP; | ||
1485 | |||
1486 | if (vf >= sriov->num_vfs) | ||
1487 | return -EINVAL; | ||
1488 | |||
1489 | if (tx_rate >= 10000 || tx_rate < 100) { | ||
1490 | netdev_err(netdev, | ||
1491 | "Invalid Tx rate, allowed range is [%d - %d]", | ||
1492 | QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE); | ||
1493 | return -EINVAL; | ||
1494 | } | ||
1495 | |||
1496 | if (tx_rate == 0) | ||
1497 | tx_rate = 10000; | ||
1498 | |||
1499 | vf_info = &sriov->vf_info[vf]; | ||
1500 | vp = vf_info->vp; | ||
1501 | vpid = vp->handle; | ||
1502 | |||
1503 | if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { | ||
1504 | if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid)) | ||
1505 | return -EIO; | ||
1506 | |||
1507 | nic_info.max_tx_bw = tx_rate / 100; | ||
1508 | nic_info.bit_offsets = BIT_0; | ||
1509 | |||
1510 | if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid)) | ||
1511 | return -EIO; | ||
1512 | } | ||
1513 | |||
1514 | vp->max_tx_bw = tx_rate / 100; | ||
1515 | netdev_info(netdev, | ||
1516 | "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", | ||
1517 | tx_rate, vp->max_tx_bw, vf); | ||
1518 | return 0; | ||
1519 | } | ||
1520 | |||
1521 | int qlcnic_sriov_get_vf_config(struct net_device *netdev, | ||
1522 | int vf, struct ifla_vf_info *ivi) | ||
1523 | { | ||
1524 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
1525 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | ||
1526 | struct qlcnic_vport *vp; | ||
1527 | |||
1528 | if (!qlcnic_sriov_pf_check(adapter)) | ||
1529 | return -EOPNOTSUPP; | ||
1530 | |||
1531 | if (vf >= sriov->num_vfs) | ||
1532 | return -EINVAL; | ||
1533 | |||
1534 | vp = sriov->vf_info[vf].vp; | ||
1535 | memcpy(&ivi->mac, vp->mac, ETH_ALEN); | ||
1536 | if (vp->max_tx_bw == MAX_BW) | ||
1537 | ivi->tx_rate = 0; | ||
1538 | else | ||
1539 | ivi->tx_rate = vp->max_tx_bw * 100; | ||
1540 | |||
1541 | ivi->vf = vf; | ||
1542 | return 0; | ||
1543 | } | ||