diff options
author | Rajesh Borundia <rajesh.borundia@qlogic.com> | 2013-04-19 03:01:09 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-04-19 16:02:38 -0400 |
commit | 97d8105cf3fb1eb84351ff4b69287ef7d25a4422 (patch) | |
tree | 8afb96c29213064151a887d1d1a31cd2bc8b7651 /drivers/net/ethernet/qlogic/qlcnic | |
parent | f80bc8fe6d44f1f0ebd90d4e698189c5b9ad25e7 (diff) |
qlcnic: VF FLR implementation.
o FLR from Hypervisor - When hypervisor issues a VF FLR request,
adapter notifies the parent PF driver of the FLR request for PF
driver to perform any cleanup on behalf of that VF.
o FLR from VF Driver - VF driver may initiate a VF FLR request,
if VF state needs to be cleaned up before a re-initialization.
VF re-initialization during kdump is an example.
o PF driver cleans up all resources allocated on behalf of a VF,
on VF FLR notifications from the adapter or from the VF driver.
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>
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic')
4 files changed, 320 insertions, 18 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index d132765f92af..33f154e4c75b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | |||
@@ -2112,6 +2112,7 @@ static int __qlcnic_shutdown(struct pci_dev *pdev) | |||
2112 | if (netif_running(netdev)) | 2112 | if (netif_running(netdev)) |
2113 | qlcnic_down(adapter, netdev); | 2113 | qlcnic_down(adapter, netdev); |
2114 | 2114 | ||
2115 | qlcnic_sriov_cleanup(adapter); | ||
2115 | if (qlcnic_82xx_check(adapter)) | 2116 | if (qlcnic_82xx_check(adapter)) |
2116 | qlcnic_clr_all_drv_state(adapter, 0); | 2117 | qlcnic_clr_all_drv_state(adapter, 0); |
2117 | 2118 | ||
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index b476ebac2439..7fda5d4625b0 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | |||
@@ -91,6 +91,8 @@ enum qlcnic_vf_state { | |||
91 | QLC_BC_VF_RECV, | 91 | QLC_BC_VF_RECV, |
92 | QLC_BC_VF_CHANNEL, | 92 | QLC_BC_VF_CHANNEL, |
93 | QLC_BC_VF_STATE, | 93 | QLC_BC_VF_STATE, |
94 | QLC_BC_VF_FLR, | ||
95 | QLC_BC_VF_SOFT_FLR, | ||
94 | }; | 96 | }; |
95 | 97 | ||
96 | struct qlcnic_resources { | 98 | struct qlcnic_resources { |
@@ -124,9 +126,11 @@ struct qlcnic_vf_info { | |||
124 | unsigned long state; | 126 | unsigned long state; |
125 | struct completion ch_free_cmpl; | 127 | struct completion ch_free_cmpl; |
126 | struct work_struct trans_work; | 128 | struct work_struct trans_work; |
129 | struct work_struct flr_work; | ||
127 | /* It synchronizes commands sent from VF */ | 130 | /* It synchronizes commands sent from VF */ |
128 | struct mutex send_cmd_lock; | 131 | struct mutex send_cmd_lock; |
129 | struct qlcnic_bc_trans *send_cmd; | 132 | struct qlcnic_bc_trans *send_cmd; |
133 | struct qlcnic_bc_trans *flr_trans; | ||
130 | struct qlcnic_trans_list rcv_act; | 134 | struct qlcnic_trans_list rcv_act; |
131 | struct qlcnic_trans_list rcv_pend; | 135 | struct qlcnic_trans_list rcv_pend; |
132 | struct qlcnic_adapter *adapter; | 136 | struct qlcnic_adapter *adapter; |
@@ -143,6 +147,7 @@ struct qlcnic_back_channel { | |||
143 | u16 trans_counter; | 147 | u16 trans_counter; |
144 | struct workqueue_struct *bc_trans_wq; | 148 | struct workqueue_struct *bc_trans_wq; |
145 | struct workqueue_struct *bc_async_wq; | 149 | struct workqueue_struct *bc_async_wq; |
150 | struct workqueue_struct *bc_flr_wq; | ||
146 | struct list_head async_list; | 151 | struct list_head async_list; |
147 | }; | 152 | }; |
148 | 153 | ||
@@ -165,6 +170,9 @@ int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8); | |||
165 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); | 170 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); |
166 | int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); | 171 | int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); |
167 | void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *); | 172 | void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *); |
173 | void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *); | ||
174 | int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *, | ||
175 | struct qlcnic_bc_trans *); | ||
168 | 176 | ||
169 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) | 177 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) |
170 | { | 178 | { |
@@ -185,6 +193,10 @@ void qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *, u32 *); | |||
185 | void qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *, u32 *); | 193 | void qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *, u32 *); |
186 | void qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *, u32 *); | 194 | void qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *, u32 *); |
187 | void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *, u32 *); | 195 | void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *, u32 *); |
196 | void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *, struct qlcnic_vf_info *); | ||
197 | bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *, | ||
198 | struct qlcnic_bc_trans *, | ||
199 | struct qlcnic_vf_info *); | ||
188 | #else | 200 | #else |
189 | static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} | 201 | static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} |
190 | static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} | 202 | static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} |
@@ -209,6 +221,12 @@ qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *adapter, u32 *int_id) | |||
209 | static inline void | 221 | static inline void |
210 | qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *adapter, u32 *int_id) | 222 | qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *adapter, u32 *int_id) |
211 | {} | 223 | {} |
224 | static inline void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *sriov, | ||
225 | struct qlcnic_vf_info *vf) {} | ||
226 | static inline bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *adapter, | ||
227 | struct qlcnic_bc_trans *trans, | ||
228 | struct qlcnic_vf_info *vf) | ||
229 | { return false; } | ||
212 | #endif | 230 | #endif |
213 | 231 | ||
214 | #endif | 232 | #endif |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 14e9ebd3b73a..2346b16b7869 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | |||
@@ -18,12 +18,14 @@ | |||
18 | 18 | ||
19 | #define QLC_BC_MSG 0 | 19 | #define QLC_BC_MSG 0 |
20 | #define QLC_BC_CFREE 1 | 20 | #define QLC_BC_CFREE 1 |
21 | #define QLC_BC_FLR 2 | ||
21 | #define QLC_BC_HDR_SZ 16 | 22 | #define QLC_BC_HDR_SZ 16 |
22 | #define QLC_BC_PAYLOAD_SZ (1024 - QLC_BC_HDR_SZ) | 23 | #define QLC_BC_PAYLOAD_SZ (1024 - QLC_BC_HDR_SZ) |
23 | 24 | ||
24 | #define QLC_DEFAULT_RCV_DESCRIPTORS_SRIOV_VF 2048 | 25 | #define QLC_DEFAULT_RCV_DESCRIPTORS_SRIOV_VF 2048 |
25 | #define QLC_DEFAULT_JUMBO_RCV_DESCRIPTORS_SRIOV_VF 512 | 26 | #define QLC_DEFAULT_JUMBO_RCV_DESCRIPTORS_SRIOV_VF 512 |
26 | 27 | ||
28 | static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *); | ||
27 | static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *, | 29 | static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *, |
28 | struct qlcnic_cmd_args *); | 30 | struct qlcnic_cmd_args *); |
29 | 31 | ||
@@ -84,6 +86,11 @@ static inline bool qlcnic_sriov_channel_free_check(u32 val) | |||
84 | return (val & (1 << QLC_BC_CFREE)) ? true : false; | 86 | return (val & (1 << QLC_BC_CFREE)) ? true : false; |
85 | } | 87 | } |
86 | 88 | ||
89 | static inline bool qlcnic_sriov_flr_check(u32 val) | ||
90 | { | ||
91 | return (val & (1 << QLC_BC_FLR)) ? true : false; | ||
92 | } | ||
93 | |||
87 | static inline u8 qlcnic_sriov_target_func_id(u32 val) | 94 | static inline u8 qlcnic_sriov_target_func_id(u32 val) |
88 | { | 95 | { |
89 | return (val >> 4) & 0xff; | 96 | return (val >> 4) & 0xff; |
@@ -192,10 +199,33 @@ qlcnic_free_sriov: | |||
192 | return err; | 199 | return err; |
193 | } | 200 | } |
194 | 201 | ||
202 | void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *t_list) | ||
203 | { | ||
204 | struct qlcnic_bc_trans *trans; | ||
205 | struct qlcnic_cmd_args cmd; | ||
206 | unsigned long flags; | ||
207 | |||
208 | spin_lock_irqsave(&t_list->lock, flags); | ||
209 | |||
210 | while (!list_empty(&t_list->wait_list)) { | ||
211 | trans = list_first_entry(&t_list->wait_list, | ||
212 | struct qlcnic_bc_trans, list); | ||
213 | list_del(&trans->list); | ||
214 | t_list->count--; | ||
215 | cmd.req.arg = (u32 *)trans->req_pay; | ||
216 | cmd.rsp.arg = (u32 *)trans->rsp_pay; | ||
217 | qlcnic_free_mbx_args(&cmd); | ||
218 | qlcnic_sriov_cleanup_transaction(trans); | ||
219 | } | ||
220 | |||
221 | spin_unlock_irqrestore(&t_list->lock, flags); | ||
222 | } | ||
223 | |||
195 | void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) | 224 | void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) |
196 | { | 225 | { |
197 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | 226 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; |
198 | struct qlcnic_back_channel *bc = &sriov->bc; | 227 | struct qlcnic_back_channel *bc = &sriov->bc; |
228 | struct qlcnic_vf_info *vf; | ||
199 | int i; | 229 | int i; |
200 | 230 | ||
201 | if (!qlcnic_sriov_enable_check(adapter)) | 231 | if (!qlcnic_sriov_enable_check(adapter)) |
@@ -203,6 +233,14 @@ void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) | |||
203 | 233 | ||
204 | qlcnic_sriov_cleanup_async_list(bc); | 234 | qlcnic_sriov_cleanup_async_list(bc); |
205 | destroy_workqueue(bc->bc_async_wq); | 235 | destroy_workqueue(bc->bc_async_wq); |
236 | |||
237 | for (i = 0; i < sriov->num_vfs; i++) { | ||
238 | vf = &sriov->vf_info[i]; | ||
239 | qlcnic_sriov_cleanup_list(&vf->rcv_pend); | ||
240 | cancel_work_sync(&vf->trans_work); | ||
241 | qlcnic_sriov_cleanup_list(&vf->rcv_act); | ||
242 | } | ||
243 | |||
206 | destroy_workqueue(bc->bc_trans_wq); | 244 | destroy_workqueue(bc->bc_trans_wq); |
207 | 245 | ||
208 | for (i = 0; i < sriov->num_vfs; i++) | 246 | for (i = 0; i < sriov->num_vfs; i++) |
@@ -651,6 +689,9 @@ static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov, | |||
651 | struct qlcnic_vf_info *vf, | 689 | struct qlcnic_vf_info *vf, |
652 | work_func_t func) | 690 | work_func_t func) |
653 | { | 691 | { |
692 | if (test_bit(QLC_BC_VF_FLR, &vf->state)) | ||
693 | return; | ||
694 | |||
654 | INIT_WORK(&vf->trans_work, func); | 695 | INIT_WORK(&vf->trans_work, func); |
655 | queue_work(sriov->bc.bc_trans_wq, &vf->trans_work); | 696 | queue_work(sriov->bc.bc_trans_wq, &vf->trans_work); |
656 | } | 697 | } |
@@ -768,10 +809,13 @@ static int qlcnic_sriov_issue_bc_post(struct qlcnic_bc_trans *trans, u8 type) | |||
768 | static int __qlcnic_sriov_send_bc_msg(struct qlcnic_bc_trans *trans, | 809 | static int __qlcnic_sriov_send_bc_msg(struct qlcnic_bc_trans *trans, |
769 | struct qlcnic_vf_info *vf, u8 type) | 810 | struct qlcnic_vf_info *vf, u8 type) |
770 | { | 811 | { |
771 | int err; | ||
772 | bool flag = true; | 812 | bool flag = true; |
813 | int err = -EIO; | ||
773 | 814 | ||
774 | while (flag) { | 815 | while (flag) { |
816 | if (test_bit(QLC_BC_VF_FLR, &vf->state)) | ||
817 | trans->trans_state = QLC_ABORT; | ||
818 | |||
775 | switch (trans->trans_state) { | 819 | switch (trans->trans_state) { |
776 | case QLC_INIT: | 820 | case QLC_INIT: |
777 | trans->trans_state = QLC_WAIT_FOR_CHANNEL_FREE; | 821 | trans->trans_state = QLC_WAIT_FOR_CHANNEL_FREE; |
@@ -853,6 +897,9 @@ static void qlcnic_sriov_process_bc_cmd(struct work_struct *work) | |||
853 | struct qlcnic_cmd_args cmd; | 897 | struct qlcnic_cmd_args cmd; |
854 | u8 req; | 898 | u8 req; |
855 | 899 | ||
900 | if (test_bit(QLC_BC_VF_FLR, &vf->state)) | ||
901 | return; | ||
902 | |||
856 | trans = list_first_entry(&vf->rcv_act.wait_list, | 903 | trans = list_first_entry(&vf->rcv_act.wait_list, |
857 | struct qlcnic_bc_trans, list); | 904 | struct qlcnic_bc_trans, list); |
858 | adapter = vf->adapter; | 905 | adapter = vf->adapter; |
@@ -906,18 +953,30 @@ clear_send: | |||
906 | clear_bit(QLC_BC_VF_SEND, &vf->state); | 953 | clear_bit(QLC_BC_VF_SEND, &vf->state); |
907 | } | 954 | } |
908 | 955 | ||
909 | static int qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov, | 956 | int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov, |
910 | struct qlcnic_vf_info *vf, | 957 | struct qlcnic_vf_info *vf, |
911 | struct qlcnic_bc_trans *trans) | 958 | struct qlcnic_bc_trans *trans) |
912 | { | 959 | { |
913 | struct qlcnic_trans_list *t_list = &vf->rcv_act; | 960 | struct qlcnic_trans_list *t_list = &vf->rcv_act; |
914 | 961 | ||
915 | spin_lock(&t_list->lock); | ||
916 | t_list->count++; | 962 | t_list->count++; |
917 | list_add_tail(&trans->list, &t_list->wait_list); | 963 | list_add_tail(&trans->list, &t_list->wait_list); |
918 | if (t_list->count == 1) | 964 | if (t_list->count == 1) |
919 | qlcnic_sriov_schedule_bc_cmd(sriov, vf, | 965 | qlcnic_sriov_schedule_bc_cmd(sriov, vf, |
920 | qlcnic_sriov_process_bc_cmd); | 966 | qlcnic_sriov_process_bc_cmd); |
967 | return 0; | ||
968 | } | ||
969 | |||
970 | static int qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov, | ||
971 | struct qlcnic_vf_info *vf, | ||
972 | struct qlcnic_bc_trans *trans) | ||
973 | { | ||
974 | struct qlcnic_trans_list *t_list = &vf->rcv_act; | ||
975 | |||
976 | spin_lock(&t_list->lock); | ||
977 | |||
978 | __qlcnic_sriov_add_act_list(sriov, vf, trans); | ||
979 | |||
921 | spin_unlock(&t_list->lock); | 980 | spin_unlock(&t_list->lock); |
922 | return 0; | 981 | return 0; |
923 | } | 982 | } |
@@ -1019,6 +1078,10 @@ static void qlcnic_sriov_handle_bc_cmd(struct qlcnic_sriov *sriov, | |||
1019 | trans->vf = vf; | 1078 | trans->vf = vf; |
1020 | trans->trans_id = hdr->seq_id; | 1079 | trans->trans_id = hdr->seq_id; |
1021 | trans->curr_req_frag++; | 1080 | trans->curr_req_frag++; |
1081 | |||
1082 | if (qlcnic_sriov_soft_flr_check(adapter, trans, vf)) | ||
1083 | return; | ||
1084 | |||
1022 | if (trans->curr_req_frag == trans->req_hdr->num_frags) { | 1085 | if (trans->curr_req_frag == trans->req_hdr->num_frags) { |
1023 | if (qlcnic_sriov_add_act_list(sriov, vf, trans)) { | 1086 | if (qlcnic_sriov_add_act_list(sriov, vf, trans)) { |
1024 | qlcnic_free_mbx_args(&cmd); | 1087 | qlcnic_free_mbx_args(&cmd); |
@@ -1053,6 +1116,18 @@ static void qlcnic_sriov_handle_msg_event(struct qlcnic_sriov *sriov, | |||
1053 | } | 1116 | } |
1054 | } | 1117 | } |
1055 | 1118 | ||
1119 | static void qlcnic_sriov_handle_flr_event(struct qlcnic_sriov *sriov, | ||
1120 | struct qlcnic_vf_info *vf) | ||
1121 | { | ||
1122 | struct qlcnic_adapter *adapter = vf->adapter; | ||
1123 | |||
1124 | if (qlcnic_sriov_pf_check(adapter)) | ||
1125 | qlcnic_sriov_pf_handle_flr(sriov, vf); | ||
1126 | else | ||
1127 | dev_err(&adapter->pdev->dev, | ||
1128 | "Invalid event to VF. VF should not get FLR event\n"); | ||
1129 | } | ||
1130 | |||
1056 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *adapter, u32 event) | 1131 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *adapter, u32 event) |
1057 | { | 1132 | { |
1058 | struct qlcnic_vf_info *vf; | 1133 | struct qlcnic_vf_info *vf; |
@@ -1073,6 +1148,11 @@ void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *adapter, u32 event) | |||
1073 | if (qlcnic_sriov_channel_free_check(event)) | 1148 | if (qlcnic_sriov_channel_free_check(event)) |
1074 | complete(&vf->ch_free_cmpl); | 1149 | complete(&vf->ch_free_cmpl); |
1075 | 1150 | ||
1151 | if (qlcnic_sriov_flr_check(event)) { | ||
1152 | qlcnic_sriov_handle_flr_event(sriov, vf); | ||
1153 | return; | ||
1154 | } | ||
1155 | |||
1076 | if (qlcnic_sriov_bc_msg_check(event)) | 1156 | if (qlcnic_sriov_bc_msg_check(event)) |
1077 | qlcnic_sriov_handle_msg_event(sriov, vf); | 1157 | qlcnic_sriov_handle_msg_event(sriov, vf); |
1078 | } | 1158 | } |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 3a86e1682456..50cdd510421a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | |||
@@ -303,6 +303,33 @@ static int qlcnic_sriov_pf_cfg_eswitch(struct qlcnic_adapter *adapter, | |||
303 | return err; | 303 | return err; |
304 | } | 304 | } |
305 | 305 | ||
306 | static void qlcnic_sriov_pf_del_flr_queue(struct qlcnic_adapter *adapter) | ||
307 | { | ||
308 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | ||
309 | struct qlcnic_back_channel *bc = &sriov->bc; | ||
310 | int i; | ||
311 | |||
312 | for (i = 0; i < sriov->num_vfs; i++) | ||
313 | cancel_work_sync(&sriov->vf_info[i].flr_work); | ||
314 | |||
315 | destroy_workqueue(bc->bc_flr_wq); | ||
316 | } | ||
317 | |||
318 | static int qlcnic_sriov_pf_create_flr_queue(struct qlcnic_adapter *adapter) | ||
319 | { | ||
320 | struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc; | ||
321 | struct workqueue_struct *wq; | ||
322 | |||
323 | wq = create_singlethread_workqueue("qlcnic-flr"); | ||
324 | if (wq == NULL) { | ||
325 | dev_err(&adapter->pdev->dev, "Cannot create FLR workqueue\n"); | ||
326 | return -ENOMEM; | ||
327 | } | ||
328 | |||
329 | bc->bc_flr_wq = wq; | ||
330 | return 0; | ||
331 | } | ||
332 | |||
306 | void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) | 333 | void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) |
307 | { | 334 | { |
308 | u8 func = adapter->ahw->pci_func; | 335 | u8 func = adapter->ahw->pci_func; |
@@ -310,6 +337,7 @@ void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) | |||
310 | if (!qlcnic_sriov_enable_check(adapter)) | 337 | if (!qlcnic_sriov_enable_check(adapter)) |
311 | return; | 338 | return; |
312 | 339 | ||
340 | qlcnic_sriov_pf_del_flr_queue(adapter); | ||
313 | qlcnic_sriov_cfg_bc_intr(adapter, 0); | 341 | qlcnic_sriov_cfg_bc_intr(adapter, 0); |
314 | qlcnic_sriov_pf_config_vport(adapter, 0, func); | 342 | qlcnic_sriov_pf_config_vport(adapter, 0, func); |
315 | qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); | 343 | qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); |
@@ -367,7 +395,7 @@ static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter) | |||
367 | 395 | ||
368 | err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1); | 396 | err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1); |
369 | if (err) | 397 | if (err) |
370 | goto clear_sriov_enable; | 398 | return err; |
371 | 399 | ||
372 | err = qlcnic_sriov_pf_config_vport(adapter, 1, func); | 400 | err = qlcnic_sriov_pf_config_vport(adapter, 1, func); |
373 | if (err) | 401 | if (err) |
@@ -402,10 +430,6 @@ delete_vport: | |||
402 | disable_eswitch: | 430 | disable_eswitch: |
403 | qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); | 431 | qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); |
404 | 432 | ||
405 | clear_sriov_enable: | ||
406 | __qlcnic_sriov_cleanup(adapter); | ||
407 | adapter->ahw->op_mode = QLCNIC_MGMT_FUNC; | ||
408 | clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); | ||
409 | return err; | 433 | return err; |
410 | } | 434 | } |
411 | 435 | ||
@@ -431,17 +455,31 @@ static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, | |||
431 | set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); | 455 | set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); |
432 | adapter->ahw->op_mode = QLCNIC_SRIOV_PF_FUNC; | 456 | adapter->ahw->op_mode = QLCNIC_SRIOV_PF_FUNC; |
433 | 457 | ||
434 | if (qlcnic_sriov_init(adapter, num_vfs)) { | 458 | err = qlcnic_sriov_init(adapter, num_vfs); |
435 | clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); | 459 | if (err) |
436 | adapter->ahw->op_mode = QLCNIC_MGMT_FUNC; | 460 | goto clear_op_mode; |
437 | return -EIO; | ||
438 | } | ||
439 | 461 | ||
440 | if (qlcnic_sriov_pf_init(adapter)) | 462 | err = qlcnic_sriov_pf_create_flr_queue(adapter); |
441 | return -EIO; | 463 | if (err) |
464 | goto sriov_cleanup; | ||
465 | |||
466 | err = qlcnic_sriov_pf_init(adapter); | ||
467 | if (err) | ||
468 | goto del_flr_queue; | ||
442 | 469 | ||
443 | err = qlcnic_sriov_pf_enable(adapter, num_vfs); | 470 | err = qlcnic_sriov_pf_enable(adapter, num_vfs); |
444 | return err; | 471 | return err; |
472 | |||
473 | del_flr_queue: | ||
474 | qlcnic_sriov_pf_del_flr_queue(adapter); | ||
475 | |||
476 | sriov_cleanup: | ||
477 | __qlcnic_sriov_cleanup(adapter); | ||
478 | |||
479 | clear_op_mode: | ||
480 | clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); | ||
481 | adapter->ahw->op_mode = QLCNIC_MGMT_FUNC; | ||
482 | return err; | ||
445 | } | 483 | } |
446 | 484 | ||
447 | static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs) | 485 | static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs) |
@@ -463,12 +501,15 @@ static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs) | |||
463 | netdev_info(netdev, "Failed to enable SR-IOV on port %d\n", | 501 | netdev_info(netdev, "Failed to enable SR-IOV on port %d\n", |
464 | adapter->portnum); | 502 | adapter->portnum); |
465 | 503 | ||
504 | err = -EIO; | ||
466 | if (qlcnic_83xx_configure_opmode(adapter)) | 505 | if (qlcnic_83xx_configure_opmode(adapter)) |
467 | goto error; | 506 | goto error; |
468 | } else { | 507 | } else { |
469 | netdev_info(adapter->netdev, | 508 | netdev_info(netdev, |
470 | "SR-IOV is enabled successfully on port %d\n", | 509 | "SR-IOV is enabled successfully on port %d\n", |
471 | adapter->portnum); | 510 | adapter->portnum); |
511 | /* Return number of vfs enabled */ | ||
512 | err = num_vfs; | ||
472 | } | 513 | } |
473 | if (netif_running(netdev)) | 514 | if (netif_running(netdev)) |
474 | __qlcnic_up(adapter, netdev); | 515 | __qlcnic_up(adapter, netdev); |
@@ -1173,3 +1214,165 @@ void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *adapter, | |||
1173 | adapter->ahw->pci_func); | 1214 | adapter->ahw->pci_func); |
1174 | *int_id |= (vpid << 16) | BIT_31; | 1215 | *int_id |= (vpid << 16) | BIT_31; |
1175 | } | 1216 | } |
1217 | |||
1218 | static void qlcnic_sriov_del_rx_ctx(struct qlcnic_adapter *adapter, | ||
1219 | struct qlcnic_vf_info *vf) | ||
1220 | { | ||
1221 | struct qlcnic_cmd_args cmd; | ||
1222 | int vpid; | ||
1223 | |||
1224 | if (!vf->rx_ctx_id) | ||
1225 | return; | ||
1226 | |||
1227 | if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX)) | ||
1228 | return; | ||
1229 | |||
1230 | vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func); | ||
1231 | if (vpid >= 0) { | ||
1232 | cmd.req.arg[1] = vf->rx_ctx_id | (vpid & 0xffff) << 16; | ||
1233 | if (qlcnic_issue_cmd(adapter, &cmd)) | ||
1234 | dev_err(&adapter->pdev->dev, | ||
1235 | "Failed to delete Tx ctx in firmware for func 0x%x\n", | ||
1236 | vf->pci_func); | ||
1237 | else | ||
1238 | vf->rx_ctx_id = 0; | ||
1239 | } | ||
1240 | |||
1241 | qlcnic_free_mbx_args(&cmd); | ||
1242 | } | ||
1243 | |||
1244 | static void qlcnic_sriov_del_tx_ctx(struct qlcnic_adapter *adapter, | ||
1245 | struct qlcnic_vf_info *vf) | ||
1246 | { | ||
1247 | struct qlcnic_cmd_args cmd; | ||
1248 | int vpid; | ||
1249 | |||
1250 | if (!vf->tx_ctx_id) | ||
1251 | return; | ||
1252 | |||
1253 | if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX)) | ||
1254 | return; | ||
1255 | |||
1256 | vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func); | ||
1257 | if (vpid >= 0) { | ||
1258 | cmd.req.arg[1] |= vf->tx_ctx_id | (vpid & 0xffff) << 16; | ||
1259 | if (qlcnic_issue_cmd(adapter, &cmd)) | ||
1260 | dev_err(&adapter->pdev->dev, | ||
1261 | "Failed to delete Tx ctx in firmware for func 0x%x\n", | ||
1262 | vf->pci_func); | ||
1263 | else | ||
1264 | vf->tx_ctx_id = 0; | ||
1265 | } | ||
1266 | |||
1267 | qlcnic_free_mbx_args(&cmd); | ||
1268 | } | ||
1269 | |||
1270 | static int qlcnic_sriov_add_act_list_irqsave(struct qlcnic_sriov *sriov, | ||
1271 | struct qlcnic_vf_info *vf, | ||
1272 | struct qlcnic_bc_trans *trans) | ||
1273 | { | ||
1274 | struct qlcnic_trans_list *t_list = &vf->rcv_act; | ||
1275 | unsigned long flag; | ||
1276 | |||
1277 | spin_lock_irqsave(&t_list->lock, flag); | ||
1278 | |||
1279 | __qlcnic_sriov_add_act_list(sriov, vf, trans); | ||
1280 | |||
1281 | spin_unlock_irqrestore(&t_list->lock, flag); | ||
1282 | return 0; | ||
1283 | } | ||
1284 | |||
1285 | static void __qlcnic_sriov_process_flr(struct qlcnic_vf_info *vf) | ||
1286 | { | ||
1287 | struct qlcnic_adapter *adapter = vf->adapter; | ||
1288 | |||
1289 | qlcnic_sriov_cleanup_list(&vf->rcv_pend); | ||
1290 | cancel_work_sync(&vf->trans_work); | ||
1291 | qlcnic_sriov_cleanup_list(&vf->rcv_act); | ||
1292 | |||
1293 | if (test_bit(QLC_BC_VF_SOFT_FLR, &vf->state)) { | ||
1294 | qlcnic_sriov_del_tx_ctx(adapter, vf); | ||
1295 | qlcnic_sriov_del_rx_ctx(adapter, vf); | ||
1296 | } | ||
1297 | |||
1298 | qlcnic_sriov_pf_config_vport(adapter, 0, vf->pci_func); | ||
1299 | |||
1300 | clear_bit(QLC_BC_VF_FLR, &vf->state); | ||
1301 | if (test_bit(QLC_BC_VF_SOFT_FLR, &vf->state)) { | ||
1302 | qlcnic_sriov_add_act_list_irqsave(adapter->ahw->sriov, vf, | ||
1303 | vf->flr_trans); | ||
1304 | clear_bit(QLC_BC_VF_SOFT_FLR, &vf->state); | ||
1305 | vf->flr_trans = NULL; | ||
1306 | } | ||
1307 | } | ||
1308 | |||
1309 | static void qlcnic_sriov_pf_process_flr(struct work_struct *work) | ||
1310 | { | ||
1311 | struct qlcnic_vf_info *vf; | ||
1312 | |||
1313 | vf = container_of(work, struct qlcnic_vf_info, flr_work); | ||
1314 | __qlcnic_sriov_process_flr(vf); | ||
1315 | return; | ||
1316 | } | ||
1317 | |||
1318 | static void qlcnic_sriov_schedule_flr(struct qlcnic_sriov *sriov, | ||
1319 | struct qlcnic_vf_info *vf, | ||
1320 | work_func_t func) | ||
1321 | { | ||
1322 | if (test_bit(__QLCNIC_RESETTING, &vf->adapter->state)) | ||
1323 | return; | ||
1324 | |||
1325 | INIT_WORK(&vf->flr_work, func); | ||
1326 | queue_work(sriov->bc.bc_flr_wq, &vf->flr_work); | ||
1327 | } | ||
1328 | |||
1329 | static void qlcnic_sriov_handle_soft_flr(struct qlcnic_adapter *adapter, | ||
1330 | struct qlcnic_bc_trans *trans, | ||
1331 | struct qlcnic_vf_info *vf) | ||
1332 | { | ||
1333 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | ||
1334 | |||
1335 | set_bit(QLC_BC_VF_FLR, &vf->state); | ||
1336 | clear_bit(QLC_BC_VF_STATE, &vf->state); | ||
1337 | set_bit(QLC_BC_VF_SOFT_FLR, &vf->state); | ||
1338 | vf->flr_trans = trans; | ||
1339 | qlcnic_sriov_schedule_flr(sriov, vf, qlcnic_sriov_pf_process_flr); | ||
1340 | netdev_info(adapter->netdev, "Software FLR for PCI func %d\n", | ||
1341 | vf->pci_func); | ||
1342 | } | ||
1343 | |||
1344 | bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *adapter, | ||
1345 | struct qlcnic_bc_trans *trans, | ||
1346 | struct qlcnic_vf_info *vf) | ||
1347 | { | ||
1348 | struct qlcnic_bc_hdr *hdr = trans->req_hdr; | ||
1349 | |||
1350 | if ((hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) && | ||
1351 | (hdr->op_type == QLC_BC_CMD) && | ||
1352 | test_bit(QLC_BC_VF_STATE, &vf->state)) { | ||
1353 | qlcnic_sriov_handle_soft_flr(adapter, trans, vf); | ||
1354 | return true; | ||
1355 | } | ||
1356 | |||
1357 | return false; | ||
1358 | } | ||
1359 | |||
1360 | void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *sriov, | ||
1361 | struct qlcnic_vf_info *vf) | ||
1362 | { | ||
1363 | struct net_device *dev = vf->adapter->netdev; | ||
1364 | |||
1365 | if (!test_and_clear_bit(QLC_BC_VF_STATE, &vf->state)) { | ||
1366 | clear_bit(QLC_BC_VF_FLR, &vf->state); | ||
1367 | return; | ||
1368 | } | ||
1369 | |||
1370 | if (test_and_set_bit(QLC_BC_VF_FLR, &vf->state)) { | ||
1371 | netdev_info(dev, "FLR for PCI func %d in progress\n", | ||
1372 | vf->pci_func); | ||
1373 | return; | ||
1374 | } | ||
1375 | |||
1376 | qlcnic_sriov_schedule_flr(sriov, vf, qlcnic_sriov_pf_process_flr); | ||
1377 | netdev_info(dev, "FLR received for PCI func %d\n", vf->pci_func); | ||
1378 | } | ||