xref: /linux/drivers/net/ethernet/cisco/enic/enic_mbox.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
11f0c856bSSatish Kharat // SPDX-License-Identifier: GPL-2.0-only
21f0c856bSSatish Kharat // Copyright 2025 Cisco Systems, Inc.  All rights reserved.
31f0c856bSSatish Kharat 
41f0c856bSSatish Kharat #include <linux/kernel.h>
51f0c856bSSatish Kharat #include <linux/netdevice.h>
61f0c856bSSatish Kharat #include <linux/dma-mapping.h>
71f0c856bSSatish Kharat #include <linux/delay.h>
872b65c94SSatish Kharat #include <linux/completion.h>
91f0c856bSSatish Kharat 
101f0c856bSSatish Kharat #include "vnic_dev.h"
111f0c856bSSatish Kharat #include "vnic_wq.h"
121f0c856bSSatish Kharat #include "vnic_cq.h"
131f0c856bSSatish Kharat #include "enic.h"
141f0c856bSSatish Kharat #include "enic_admin.h"
151f0c856bSSatish Kharat #include "enic_mbox.h"
161f0c856bSSatish Kharat #include "wq_enet_desc.h"
171f0c856bSSatish Kharat 
181f0c856bSSatish Kharat #define ENIC_MBOX_POLL_TIMEOUT_US	5000000
191f0c856bSSatish Kharat #define ENIC_MBOX_POLL_INTERVAL_US	100
201f0c856bSSatish Kharat 
enic_mbox_fill_hdr(struct enic * enic,struct enic_mbox_hdr * hdr,u8 msg_type,u16 dst_vnic_id,u16 msg_len)211f0c856bSSatish Kharat static void enic_mbox_fill_hdr(struct enic *enic, struct enic_mbox_hdr *hdr,
221f0c856bSSatish Kharat 			       u8 msg_type, u16 dst_vnic_id, u16 msg_len)
231f0c856bSSatish Kharat {
241f0c856bSSatish Kharat 	memset(hdr, 0, sizeof(*hdr));
251f0c856bSSatish Kharat 	hdr->dst_vnic_id = cpu_to_le16(dst_vnic_id);
261f0c856bSSatish Kharat 	hdr->msg_type = msg_type;
271f0c856bSSatish Kharat 	hdr->msg_len = cpu_to_le16(msg_len);
281f0c856bSSatish Kharat 	hdr->msg_num = cpu_to_le64(++enic->mbox_msg_num);
291f0c856bSSatish Kharat }
301f0c856bSSatish Kharat 
enic_mbox_send_msg(struct enic * enic,u8 msg_type,u16 dst_vnic_id,void * payload,u16 payload_len)311f0c856bSSatish Kharat int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id,
321f0c856bSSatish Kharat 		       void *payload, u16 payload_len)
331f0c856bSSatish Kharat {
341f0c856bSSatish Kharat 	size_t total_len = sizeof(struct enic_mbox_hdr) + payload_len;
351f0c856bSSatish Kharat 	struct vnic_wq *wq = &enic->admin_wq;
361f0c856bSSatish Kharat 	struct wq_enet_desc *desc;
371f0c856bSSatish Kharat 	unsigned long timeout;
381f0c856bSSatish Kharat 	dma_addr_t dma_addr;
391f0c856bSSatish Kharat 	u16 vlan_tag;
401f0c856bSSatish Kharat 	void *buf;
411f0c856bSSatish Kharat 	int err;
421f0c856bSSatish Kharat 
431f0c856bSSatish Kharat 	/* Reject payloads that cannot fit in a single admin buffer.  Checked
441f0c856bSSatish Kharat 	 * before taking mbox_lock; total_len is computed as size_t so the
451f0c856bSSatish Kharat 	 * sizeof() + payload_len sum cannot wrap.
461f0c856bSSatish Kharat 	 */
471f0c856bSSatish Kharat 	if (payload_len > ENIC_ADMIN_BUF_SIZE - sizeof(struct enic_mbox_hdr))
481f0c856bSSatish Kharat 		return -EINVAL;
491f0c856bSSatish Kharat 
501f0c856bSSatish Kharat 	/* Serialize MBOX sends. The admin channel is a low-frequency
511f0c856bSSatish Kharat 	 * control path; holding the mutex across the poll is acceptable.
521f0c856bSSatish Kharat 	 */
531f0c856bSSatish Kharat 	mutex_lock(&enic->mbox_lock);
541f0c856bSSatish Kharat 
551f0c856bSSatish Kharat 	if (!enic->has_admin_channel || READ_ONCE(enic->mbox_send_disabled)) {
561f0c856bSSatish Kharat 		err = -ENODEV;
571f0c856bSSatish Kharat 		goto unlock;
581f0c856bSSatish Kharat 	}
591f0c856bSSatish Kharat 
601f0c856bSSatish Kharat 	if (vnic_wq_desc_avail(wq) == 0) {
611f0c856bSSatish Kharat 		err = -ENOSPC;
621f0c856bSSatish Kharat 		goto unlock;
631f0c856bSSatish Kharat 	}
641f0c856bSSatish Kharat 
651f0c856bSSatish Kharat 	buf = kmalloc(total_len, GFP_KERNEL);
661f0c856bSSatish Kharat 	if (!buf) {
671f0c856bSSatish Kharat 		err = -ENOMEM;
681f0c856bSSatish Kharat 		goto unlock;
691f0c856bSSatish Kharat 	}
701f0c856bSSatish Kharat 
711f0c856bSSatish Kharat 	enic_mbox_fill_hdr(enic, buf, msg_type, dst_vnic_id, total_len);
721f0c856bSSatish Kharat 	if (payload_len) {
731f0c856bSSatish Kharat 		void *dst = buf + sizeof(struct enic_mbox_hdr);
741f0c856bSSatish Kharat 
751f0c856bSSatish Kharat 		memcpy(dst, payload, payload_len);
761f0c856bSSatish Kharat 	}
771f0c856bSSatish Kharat 
781f0c856bSSatish Kharat 	dma_addr = dma_map_single(&enic->pdev->dev, buf, total_len,
791f0c856bSSatish Kharat 				  DMA_TO_DEVICE);
801f0c856bSSatish Kharat 	if (dma_mapping_error(&enic->pdev->dev, dma_addr)) {
811f0c856bSSatish Kharat 		kfree(buf);
821f0c856bSSatish Kharat 		err = -ENOMEM;
831f0c856bSSatish Kharat 		goto unlock;
841f0c856bSSatish Kharat 	}
851f0c856bSSatish Kharat 
861f0c856bSSatish Kharat 	/* Firmware uses vlan field for routing: 0 = PF, 1-based = VF index */
871f0c856bSSatish Kharat 	if (dst_vnic_id == ENIC_MBOX_DST_PF)
881f0c856bSSatish Kharat 		vlan_tag = 0;
891f0c856bSSatish Kharat 	else
901f0c856bSSatish Kharat 		vlan_tag = dst_vnic_id + 1;
911f0c856bSSatish Kharat 
921f0c856bSSatish Kharat 	desc = vnic_wq_next_desc(wq);
931f0c856bSSatish Kharat 	wq_enet_desc_enc(desc, (u64)dma_addr | VNIC_PADDR_TARGET,
941f0c856bSSatish Kharat 			 total_len,
951f0c856bSSatish Kharat 			 0, 0, 0,       /* mss, hdr_len, offload_mode */
961f0c856bSSatish Kharat 			 1, 1,          /* eop, cq_entry */
971f0c856bSSatish Kharat 			 0,             /* fcoe_encap */
981f0c856bSSatish Kharat 			 1, vlan_tag,   /* vlan_tag_insert, vlan_tag */
991f0c856bSSatish Kharat 			 0);            /* loopback */
1001f0c856bSSatish Kharat 	vnic_wq_post(wq, buf, dma_addr, total_len,
1011f0c856bSSatish Kharat 		     1, 1,              /* sop, eop */
1021f0c856bSSatish Kharat 		     1, 1,              /* desc_skip_cnt, cq_entry */
1031f0c856bSSatish Kharat 		     0, 0);             /* compressed_send, wrid */
1041f0c856bSSatish Kharat 	vnic_wq_doorbell(wq);
1051f0c856bSSatish Kharat 
1061f0c856bSSatish Kharat 	timeout = jiffies + usecs_to_jiffies(ENIC_MBOX_POLL_TIMEOUT_US);
1071f0c856bSSatish Kharat 	err = -ETIMEDOUT;
1081f0c856bSSatish Kharat 	while (time_before(jiffies, timeout)) {
1091f0c856bSSatish Kharat 		if (enic_admin_wq_cq_service(enic)) {
1101f0c856bSSatish Kharat 			err = 0;
1111f0c856bSSatish Kharat 			break;
1121f0c856bSSatish Kharat 		}
1131f0c856bSSatish Kharat 		usleep_range(ENIC_MBOX_POLL_INTERVAL_US,
1141f0c856bSSatish Kharat 			     ENIC_MBOX_POLL_INTERVAL_US + 50);
1151f0c856bSSatish Kharat 	}
1161f0c856bSSatish Kharat 	/* Final check in case completion arrived during the last sleep */
1171f0c856bSSatish Kharat 	if (err && enic_admin_wq_cq_service(enic))
1181f0c856bSSatish Kharat 		err = 0;
1191f0c856bSSatish Kharat 
1201f0c856bSSatish Kharat 	if (!err) {
1211f0c856bSSatish Kharat 		wq->to_clean = wq->to_clean->next;
1221f0c856bSSatish Kharat 		wq->ring.desc_avail++;
1231f0c856bSSatish Kharat 		dma_unmap_single(&enic->pdev->dev, dma_addr, total_len,
1241f0c856bSSatish Kharat 				 DMA_TO_DEVICE);
1251f0c856bSSatish Kharat 		kfree(buf);
1261f0c856bSSatish Kharat 	} else {
1271f0c856bSSatish Kharat 		netdev_err(enic->netdev,
1281f0c856bSSatish Kharat 			   "MBOX send timed out (type %u dst %u), disabling channel\n",
1291f0c856bSSatish Kharat 			   msg_type, dst_vnic_id);
1301f0c856bSSatish Kharat 		/*
1311f0c856bSSatish Kharat 		 * The WQ descriptor is still live in hardware. Do not unmap
1321f0c856bSSatish Kharat 		 * or free the buffer: the device may still DMA from dma_addr.
1331f0c856bSSatish Kharat 		 * Mark the channel unusable so no further sends are attempted.
1341f0c856bSSatish Kharat 		 */
1351f0c856bSSatish Kharat 		WRITE_ONCE(enic->mbox_send_disabled, true);
1361f0c856bSSatish Kharat 	}
1371f0c856bSSatish Kharat 
1381f0c856bSSatish Kharat 	netdev_dbg(enic->netdev,
1391f0c856bSSatish Kharat 		   "MBOX send msg_type %u dst %u vlan %u err %d\n",
1401f0c856bSSatish Kharat 		   msg_type, dst_vnic_id, vlan_tag, err);
1411f0c856bSSatish Kharat unlock:
1421f0c856bSSatish Kharat 	mutex_unlock(&enic->mbox_lock);
1431f0c856bSSatish Kharat 	return err;
1441f0c856bSSatish Kharat }
1451f0c856bSSatish Kharat 
enic_mbox_wait_reply(struct enic * enic,unsigned long timeout_ms)14672b65c94SSatish Kharat static int enic_mbox_wait_reply(struct enic *enic, unsigned long timeout_ms)
14772b65c94SSatish Kharat {
14872b65c94SSatish Kharat 	unsigned long left;
14972b65c94SSatish Kharat 
15072b65c94SSatish Kharat 	left = wait_for_completion_timeout(&enic->mbox_comp,
15172b65c94SSatish Kharat 					   msecs_to_jiffies(timeout_ms));
15272b65c94SSatish Kharat 
15372b65c94SSatish Kharat 	return left ? 0 : -ETIMEDOUT;
15472b65c94SSatish Kharat }
15572b65c94SSatish Kharat 
enic_mbox_send_link_state(struct enic * enic,u16 vf_id,u32 link_state)15606bdfb16SSatish Kharat int enic_mbox_send_link_state(struct enic *enic, u16 vf_id, u32 link_state)
15706bdfb16SSatish Kharat {
15806bdfb16SSatish Kharat 	struct enic_mbox_pf_link_state_notif_msg notif = {};
15906bdfb16SSatish Kharat 
16006bdfb16SSatish Kharat 	if (!enic->vf_state || vf_id >= enic->num_vfs ||
16106bdfb16SSatish Kharat 	    !enic->vf_state[vf_id].registered) {
16206bdfb16SSatish Kharat 		netdev_dbg(enic->netdev,
16306bdfb16SSatish Kharat 			   "MBOX: skip link state to unregistered VF %u\n",
16406bdfb16SSatish Kharat 			   vf_id);
16506bdfb16SSatish Kharat 		return 0;
16606bdfb16SSatish Kharat 	}
16706bdfb16SSatish Kharat 
16806bdfb16SSatish Kharat 	notif.link_state = cpu_to_le32(link_state);
16906bdfb16SSatish Kharat 	return enic_mbox_send_msg(enic, ENIC_MBOX_PF_LINK_STATE_NOTIF, vf_id,
17006bdfb16SSatish Kharat 				  &notif, sizeof(notif));
17106bdfb16SSatish Kharat }
17206bdfb16SSatish Kharat 
enic_mbox_pf_handle_capability(struct enic * enic,void * msg,u16 vf_id,u64 msg_num)17306bdfb16SSatish Kharat static int enic_mbox_pf_handle_capability(struct enic *enic, void *msg,
17406bdfb16SSatish Kharat 					  u16 vf_id, u64 msg_num)
17506bdfb16SSatish Kharat {
17606bdfb16SSatish Kharat 	struct enic_mbox_vf_capability_reply_msg reply = {};
17706bdfb16SSatish Kharat 
17806bdfb16SSatish Kharat 	reply.reply.ret_major = cpu_to_le16(0);
17906bdfb16SSatish Kharat 	reply.version = cpu_to_le32(ENIC_MBOX_CAP_VERSION_1);
18006bdfb16SSatish Kharat 
18106bdfb16SSatish Kharat 	return enic_mbox_send_msg(enic, ENIC_MBOX_VF_CAPABILITY_REPLY, vf_id,
18206bdfb16SSatish Kharat 				  &reply, sizeof(reply));
18306bdfb16SSatish Kharat }
18406bdfb16SSatish Kharat 
enic_mbox_pf_handle_register(struct enic * enic,void * msg,u16 vf_id,u64 msg_num)18506bdfb16SSatish Kharat static int enic_mbox_pf_handle_register(struct enic *enic, void *msg,
18606bdfb16SSatish Kharat 					u16 vf_id, u64 msg_num)
18706bdfb16SSatish Kharat {
18806bdfb16SSatish Kharat 	struct enic_mbox_vf_register_reply_msg reply = {};
18906bdfb16SSatish Kharat 	u32 link_state;
19006bdfb16SSatish Kharat 	int err;
19106bdfb16SSatish Kharat 
19206bdfb16SSatish Kharat 	if (!enic->vf_state || vf_id >= enic->num_vfs) {
19306bdfb16SSatish Kharat 		if (net_ratelimit())
19406bdfb16SSatish Kharat 			netdev_warn(enic->netdev,
19506bdfb16SSatish Kharat 				    "MBOX: register from invalid VF %u\n",
19606bdfb16SSatish Kharat 				    vf_id);
19706bdfb16SSatish Kharat 		return -EINVAL;
19806bdfb16SSatish Kharat 	}
19906bdfb16SSatish Kharat 
20006bdfb16SSatish Kharat 	/* VF re-registering (e.g. guest reboot without clean unregister):
20106bdfb16SSatish Kharat 	 * mark the previous registration inactive before accepting the new one.
20206bdfb16SSatish Kharat 	 */
20306bdfb16SSatish Kharat 	if (enic->vf_state[vf_id].registered) {
20406bdfb16SSatish Kharat 		netdev_dbg(enic->netdev,
20506bdfb16SSatish Kharat 			   "MBOX: VF %u re-register, cleaning previous state\n",
20606bdfb16SSatish Kharat 			   vf_id);
20706bdfb16SSatish Kharat 		enic->vf_state[vf_id].registered = false;
20806bdfb16SSatish Kharat 	}
20906bdfb16SSatish Kharat 
21006bdfb16SSatish Kharat 	reply.reply.ret_major = cpu_to_le16(0);
21106bdfb16SSatish Kharat 	err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_REGISTER_REPLY, vf_id,
21206bdfb16SSatish Kharat 				 &reply, sizeof(reply));
21306bdfb16SSatish Kharat 	if (err)
21406bdfb16SSatish Kharat 		return err;
21506bdfb16SSatish Kharat 
21606bdfb16SSatish Kharat 	enic->vf_state[vf_id].registered = true;
21706bdfb16SSatish Kharat 	if (net_ratelimit())
21806bdfb16SSatish Kharat 		netdev_info(enic->netdev, "VF %u registered via MBOX\n", vf_id);
21906bdfb16SSatish Kharat 
22006bdfb16SSatish Kharat 	link_state = netif_carrier_ok(enic->netdev) ?
22106bdfb16SSatish Kharat 		ENIC_MBOX_LINK_STATE_ENABLE :
22206bdfb16SSatish Kharat 		ENIC_MBOX_LINK_STATE_DISABLE;
22306bdfb16SSatish Kharat 	err = enic_mbox_send_link_state(enic, vf_id, link_state);
22406bdfb16SSatish Kharat 	if (err && net_ratelimit())
22506bdfb16SSatish Kharat 		netdev_warn(enic->netdev,
22606bdfb16SSatish Kharat 			    "VF %u: failed to send initial link state: %d\n",
22706bdfb16SSatish Kharat 			    vf_id, err);
22806bdfb16SSatish Kharat 	/* Registration succeeded; initial link state notification attempted
22906bdfb16SSatish Kharat 	 * above.  Subsequent link state changes are sent from the PF
23006bdfb16SSatish Kharat 	 * when enic_link_check() detects carrier changes.
23106bdfb16SSatish Kharat 	 */
23206bdfb16SSatish Kharat 	return 0;
23306bdfb16SSatish Kharat }
23406bdfb16SSatish Kharat 
enic_mbox_pf_handle_unregister(struct enic * enic,void * msg,u16 vf_id,u64 msg_num)23506bdfb16SSatish Kharat static int enic_mbox_pf_handle_unregister(struct enic *enic, void *msg,
23606bdfb16SSatish Kharat 					  u16 vf_id, u64 msg_num)
23706bdfb16SSatish Kharat {
23806bdfb16SSatish Kharat 	struct enic_mbox_vf_register_reply_msg reply = {};
23906bdfb16SSatish Kharat 	int err;
24006bdfb16SSatish Kharat 
24106bdfb16SSatish Kharat 	if (!enic->vf_state || vf_id >= enic->num_vfs) {
24206bdfb16SSatish Kharat 		if (net_ratelimit())
24306bdfb16SSatish Kharat 			netdev_warn(enic->netdev,
24406bdfb16SSatish Kharat 				    "MBOX: unregister from invalid VF %u\n",
24506bdfb16SSatish Kharat 				    vf_id);
24606bdfb16SSatish Kharat 		return -EINVAL;
24706bdfb16SSatish Kharat 	}
24806bdfb16SSatish Kharat 
24906bdfb16SSatish Kharat 	/* VF is unloading; clear local state regardless of whether
25006bdfb16SSatish Kharat 	 * the reply is successfully delivered to avoid the PF treating
25106bdfb16SSatish Kharat 	 * a dead VF as still registered.
25206bdfb16SSatish Kharat 	 */
25306bdfb16SSatish Kharat 	enic->vf_state[vf_id].registered = false;
25406bdfb16SSatish Kharat 
25506bdfb16SSatish Kharat 	reply.reply.ret_major = cpu_to_le16(0);
25606bdfb16SSatish Kharat 	err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_UNREGISTER_REPLY, vf_id,
25706bdfb16SSatish Kharat 				 &reply, sizeof(reply));
25806bdfb16SSatish Kharat 
25906bdfb16SSatish Kharat 	if (net_ratelimit())
26006bdfb16SSatish Kharat 		netdev_info(enic->netdev,
26106bdfb16SSatish Kharat 			    "VF %u unregistered via MBOX\n", vf_id);
26206bdfb16SSatish Kharat 
26306bdfb16SSatish Kharat 	return err;
26406bdfb16SSatish Kharat }
26506bdfb16SSatish Kharat 
enic_mbox_pf_process_msg(struct enic * enic,struct enic_mbox_hdr * hdr,void * payload)26606bdfb16SSatish Kharat static void enic_mbox_pf_process_msg(struct enic *enic,
26706bdfb16SSatish Kharat 				     struct enic_mbox_hdr *hdr, void *payload)
26806bdfb16SSatish Kharat {
26906bdfb16SSatish Kharat 	u16 vf_id = le16_to_cpu(hdr->src_vnic_id);
27006bdfb16SSatish Kharat 	u16 msg_len = le16_to_cpu(hdr->msg_len);
27106bdfb16SSatish Kharat 	int err = 0;
27206bdfb16SSatish Kharat 
27306bdfb16SSatish Kharat 	if (!enic->vf_state) {
27406bdfb16SSatish Kharat 		netdev_dbg(enic->netdev,
27506bdfb16SSatish Kharat 			   "MBOX: PF received msg but SRIOV not active\n");
27606bdfb16SSatish Kharat 		return;
27706bdfb16SSatish Kharat 	}
27806bdfb16SSatish Kharat 
27906bdfb16SSatish Kharat 	if (vf_id >= enic->num_vfs) {
28006bdfb16SSatish Kharat 		if (net_ratelimit())
28106bdfb16SSatish Kharat 			netdev_warn(enic->netdev,
28206bdfb16SSatish Kharat 				    "MBOX: PF received msg from invalid VF %u\n",
28306bdfb16SSatish Kharat 				    vf_id);
28406bdfb16SSatish Kharat 		return;
28506bdfb16SSatish Kharat 	}
28606bdfb16SSatish Kharat 
28706bdfb16SSatish Kharat 	switch (hdr->msg_type) {
28806bdfb16SSatish Kharat 	case ENIC_MBOX_VF_CAPABILITY_REQUEST:
28906bdfb16SSatish Kharat 		err = enic_mbox_pf_handle_capability(enic, payload, vf_id,
29006bdfb16SSatish Kharat 						     le64_to_cpu(hdr->msg_num));
29106bdfb16SSatish Kharat 		break;
29206bdfb16SSatish Kharat 	case ENIC_MBOX_VF_REGISTER_REQUEST:
29306bdfb16SSatish Kharat 		err = enic_mbox_pf_handle_register(enic, payload, vf_id,
29406bdfb16SSatish Kharat 						   le64_to_cpu(hdr->msg_num));
29506bdfb16SSatish Kharat 		break;
29606bdfb16SSatish Kharat 	case ENIC_MBOX_VF_UNREGISTER_REQUEST:
29706bdfb16SSatish Kharat 		err = enic_mbox_pf_handle_unregister(enic, payload, vf_id,
29806bdfb16SSatish Kharat 						     le64_to_cpu(hdr->msg_num));
29906bdfb16SSatish Kharat 		break;
30006bdfb16SSatish Kharat 	case ENIC_MBOX_PF_LINK_STATE_ACK: {
30106bdfb16SSatish Kharat 		struct enic_mbox_pf_link_state_ack_msg *ack = payload;
30206bdfb16SSatish Kharat 
30306bdfb16SSatish Kharat 		if (msg_len < sizeof(*hdr) + sizeof(*ack))
30406bdfb16SSatish Kharat 			break;
30506bdfb16SSatish Kharat 		if (le16_to_cpu(ack->ack.ret_major) && net_ratelimit())
30606bdfb16SSatish Kharat 			netdev_warn(enic->netdev,
30706bdfb16SSatish Kharat 				    "MBOX: VF %u link state ACK error %u/%u\n",
30806bdfb16SSatish Kharat 				    vf_id,
30906bdfb16SSatish Kharat 				    le16_to_cpu(ack->ack.ret_major),
31006bdfb16SSatish Kharat 				    le16_to_cpu(ack->ack.ret_minor));
31106bdfb16SSatish Kharat 		break;
31206bdfb16SSatish Kharat 	}
31306bdfb16SSatish Kharat 	default:
31406bdfb16SSatish Kharat 		netdev_dbg(enic->netdev,
31506bdfb16SSatish Kharat 			   "MBOX: PF unhandled msg type %u from VF %u\n",
31606bdfb16SSatish Kharat 			   hdr->msg_type, vf_id);
31706bdfb16SSatish Kharat 		err = -EOPNOTSUPP;
31806bdfb16SSatish Kharat 		break;
31906bdfb16SSatish Kharat 	}
32006bdfb16SSatish Kharat 
32106bdfb16SSatish Kharat 	if (err && net_ratelimit())
32206bdfb16SSatish Kharat 		netdev_warn(enic->netdev,
32306bdfb16SSatish Kharat 			    "MBOX: PF handler for msg type %u from VF %u failed: %d\n",
32406bdfb16SSatish Kharat 			    hdr->msg_type, vf_id, err);
32506bdfb16SSatish Kharat }
32606bdfb16SSatish Kharat 
enic_mbox_vf_handle_capability_reply(struct enic * enic,void * payload)32772b65c94SSatish Kharat static void enic_mbox_vf_handle_capability_reply(struct enic *enic,
32872b65c94SSatish Kharat 						 void *payload)
32972b65c94SSatish Kharat {
33072b65c94SSatish Kharat 	struct enic_mbox_vf_capability_reply_msg *reply = payload;
33172b65c94SSatish Kharat 
33272b65c94SSatish Kharat 	if (READ_ONCE(enic->mbox_expected_reply) != ENIC_MBOX_VF_CAPABILITY_REPLY) {
33372b65c94SSatish Kharat 		netdev_warn(enic->netdev,
33472b65c94SSatish Kharat 			    "MBOX: stale capability reply (expected %u), drop\n",
33572b65c94SSatish Kharat 			    READ_ONCE(enic->mbox_expected_reply));
33672b65c94SSatish Kharat 		return;
33772b65c94SSatish Kharat 	}
33872b65c94SSatish Kharat 
33972b65c94SSatish Kharat 	if (le16_to_cpu(reply->reply.ret_major) == 0)
34072b65c94SSatish Kharat 		enic->pf_cap_version = le32_to_cpu(reply->version);
34172b65c94SSatish Kharat 	else
34272b65c94SSatish Kharat 		netdev_warn(enic->netdev,
34372b65c94SSatish Kharat 			    "MBOX: PF rejected capability request: %u/%u\n",
34472b65c94SSatish Kharat 			    le16_to_cpu(reply->reply.ret_major),
34572b65c94SSatish Kharat 			    le16_to_cpu(reply->reply.ret_minor));
34672b65c94SSatish Kharat 	complete(&enic->mbox_comp);
34772b65c94SSatish Kharat }
34872b65c94SSatish Kharat 
enic_mbox_vf_handle_register_reply(struct enic * enic,void * payload)34972b65c94SSatish Kharat static void enic_mbox_vf_handle_register_reply(struct enic *enic,
35072b65c94SSatish Kharat 					       void *payload)
35172b65c94SSatish Kharat {
35272b65c94SSatish Kharat 	struct enic_mbox_vf_register_reply_msg *reply = payload;
35372b65c94SSatish Kharat 
35472b65c94SSatish Kharat 	if (READ_ONCE(enic->mbox_expected_reply) != ENIC_MBOX_VF_REGISTER_REPLY) {
35572b65c94SSatish Kharat 		netdev_warn(enic->netdev,
35672b65c94SSatish Kharat 			    "MBOX: stale register reply (expected %u), drop\n",
35772b65c94SSatish Kharat 			    READ_ONCE(enic->mbox_expected_reply));
35872b65c94SSatish Kharat 		return;
35972b65c94SSatish Kharat 	}
36072b65c94SSatish Kharat 
36172b65c94SSatish Kharat 	if (le16_to_cpu(reply->reply.ret_major)) {
36272b65c94SSatish Kharat 		netdev_warn(enic->netdev,
36372b65c94SSatish Kharat 			    "MBOX: VF register rejected by PF: %u/%u\n",
36472b65c94SSatish Kharat 			    le16_to_cpu(reply->reply.ret_major),
36572b65c94SSatish Kharat 			    le16_to_cpu(reply->reply.ret_minor));
36672b65c94SSatish Kharat 	} else {
36772b65c94SSatish Kharat 		enic->vf_registered = true;
36872b65c94SSatish Kharat 	}
36972b65c94SSatish Kharat 	complete(&enic->mbox_comp);
37072b65c94SSatish Kharat }
37172b65c94SSatish Kharat 
enic_mbox_vf_handle_unregister_reply(struct enic * enic,void * payload)37272b65c94SSatish Kharat static void enic_mbox_vf_handle_unregister_reply(struct enic *enic,
37372b65c94SSatish Kharat 						 void *payload)
37472b65c94SSatish Kharat {
37572b65c94SSatish Kharat 	struct enic_mbox_vf_register_reply_msg *reply = payload;
37672b65c94SSatish Kharat 
37772b65c94SSatish Kharat 	if (READ_ONCE(enic->mbox_expected_reply) != ENIC_MBOX_VF_UNREGISTER_REPLY) {
37872b65c94SSatish Kharat 		netdev_warn(enic->netdev,
37972b65c94SSatish Kharat 			    "MBOX: stale unregister reply (expected %u), drop\n",
38072b65c94SSatish Kharat 			    READ_ONCE(enic->mbox_expected_reply));
38172b65c94SSatish Kharat 		return;
38272b65c94SSatish Kharat 	}
38372b65c94SSatish Kharat 
38472b65c94SSatish Kharat 	if (le16_to_cpu(reply->reply.ret_major)) {
38572b65c94SSatish Kharat 		netdev_warn(enic->netdev,
38672b65c94SSatish Kharat 			    "MBOX: VF unregister rejected by PF: %u/%u\n",
38772b65c94SSatish Kharat 			    le16_to_cpu(reply->reply.ret_major),
38872b65c94SSatish Kharat 			    le16_to_cpu(reply->reply.ret_minor));
38972b65c94SSatish Kharat 	} else {
39072b65c94SSatish Kharat 		enic->vf_registered = false;
39172b65c94SSatish Kharat 	}
39272b65c94SSatish Kharat 	complete(&enic->mbox_comp);
39372b65c94SSatish Kharat }
39472b65c94SSatish Kharat 
enic_mbox_vf_handle_link_state(struct enic * enic,void * payload)39572b65c94SSatish Kharat static void enic_mbox_vf_handle_link_state(struct enic *enic, void *payload)
39672b65c94SSatish Kharat {
39772b65c94SSatish Kharat 	struct enic_mbox_pf_link_state_notif_msg *notif = payload;
39872b65c94SSatish Kharat 	struct enic_mbox_pf_link_state_ack_msg ack = {};
39972b65c94SSatish Kharat 	int err;
40072b65c94SSatish Kharat 
40172b65c94SSatish Kharat 	switch (le32_to_cpu(notif->link_state)) {
40272b65c94SSatish Kharat 	case ENIC_MBOX_LINK_STATE_ENABLE:
40372b65c94SSatish Kharat 		if (!netif_carrier_ok(enic->netdev))
40472b65c94SSatish Kharat 			netif_carrier_on(enic->netdev);
40572b65c94SSatish Kharat 		netdev_dbg(enic->netdev, "MBOX: link state -> UP\n");
40672b65c94SSatish Kharat 		break;
40772b65c94SSatish Kharat 	case ENIC_MBOX_LINK_STATE_DISABLE:
40872b65c94SSatish Kharat 		if (netif_carrier_ok(enic->netdev))
40972b65c94SSatish Kharat 			netif_carrier_off(enic->netdev);
41072b65c94SSatish Kharat 		netdev_dbg(enic->netdev, "MBOX: link state -> DOWN\n");
41172b65c94SSatish Kharat 		break;
41272b65c94SSatish Kharat 	default:
41372b65c94SSatish Kharat 		netdev_warn(enic->netdev, "MBOX: unknown link state %u\n",
41472b65c94SSatish Kharat 			    le32_to_cpu(notif->link_state));
41572b65c94SSatish Kharat 		ack.ack.ret_major = cpu_to_le16(ENIC_MBOX_ERR_GENERIC);
41672b65c94SSatish Kharat 		break;
41772b65c94SSatish Kharat 	}
41872b65c94SSatish Kharat 
41972b65c94SSatish Kharat 	err = enic_mbox_send_msg(enic, ENIC_MBOX_PF_LINK_STATE_ACK,
42072b65c94SSatish Kharat 				 ENIC_MBOX_DST_PF, &ack, sizeof(ack));
42172b65c94SSatish Kharat 	if (err && net_ratelimit())
42272b65c94SSatish Kharat 		netdev_warn(enic->netdev,
42372b65c94SSatish Kharat 			    "MBOX: failed to send link state ACK: %d\n", err);
42472b65c94SSatish Kharat }
42572b65c94SSatish Kharat 
enic_mbox_vf_payload_ok(struct enic * enic,u8 msg_type,u16 payload_len,size_t min_len)42672b65c94SSatish Kharat static bool enic_mbox_vf_payload_ok(struct enic *enic, u8 msg_type,
42772b65c94SSatish Kharat 				    u16 payload_len, size_t min_len)
42872b65c94SSatish Kharat {
42972b65c94SSatish Kharat 	if (payload_len < min_len) {
43072b65c94SSatish Kharat 		netdev_warn(enic->netdev,
43172b65c94SSatish Kharat 			    "MBOX: short payload for type %u (%u < %zu)\n",
43272b65c94SSatish Kharat 			    msg_type, payload_len, min_len);
43372b65c94SSatish Kharat 		return false;
43472b65c94SSatish Kharat 	}
43572b65c94SSatish Kharat 	return true;
43672b65c94SSatish Kharat }
43772b65c94SSatish Kharat 
enic_mbox_vf_process_msg(struct enic * enic,struct enic_mbox_hdr * hdr,void * payload,u16 payload_len)43872b65c94SSatish Kharat static void enic_mbox_vf_process_msg(struct enic *enic,
43972b65c94SSatish Kharat 				     struct enic_mbox_hdr *hdr, void *payload,
44072b65c94SSatish Kharat 				     u16 payload_len)
44172b65c94SSatish Kharat {
44272b65c94SSatish Kharat 	switch (hdr->msg_type) {
44372b65c94SSatish Kharat 	case ENIC_MBOX_VF_CAPABILITY_REPLY: {
44472b65c94SSatish Kharat 		size_t exp = sizeof(struct enic_mbox_vf_capability_reply_msg);
44572b65c94SSatish Kharat 
44672b65c94SSatish Kharat 		if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type,
44772b65c94SSatish Kharat 					     payload_len, exp))
44872b65c94SSatish Kharat 			return;
44972b65c94SSatish Kharat 		enic_mbox_vf_handle_capability_reply(enic, payload);
45072b65c94SSatish Kharat 		break;
45172b65c94SSatish Kharat 	}
45272b65c94SSatish Kharat 	case ENIC_MBOX_VF_REGISTER_REPLY: {
45372b65c94SSatish Kharat 		size_t exp = sizeof(struct enic_mbox_vf_register_reply_msg);
45472b65c94SSatish Kharat 
45572b65c94SSatish Kharat 		if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type,
45672b65c94SSatish Kharat 					     payload_len, exp))
45772b65c94SSatish Kharat 			return;
45872b65c94SSatish Kharat 		enic_mbox_vf_handle_register_reply(enic, payload);
45972b65c94SSatish Kharat 		break;
46072b65c94SSatish Kharat 	}
46172b65c94SSatish Kharat 	case ENIC_MBOX_VF_UNREGISTER_REPLY: {
46272b65c94SSatish Kharat 		size_t exp = sizeof(struct enic_mbox_vf_register_reply_msg);
46372b65c94SSatish Kharat 
46472b65c94SSatish Kharat 		if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type,
46572b65c94SSatish Kharat 					     payload_len, exp))
46672b65c94SSatish Kharat 			return;
46772b65c94SSatish Kharat 		enic_mbox_vf_handle_unregister_reply(enic, payload);
46872b65c94SSatish Kharat 		break;
46972b65c94SSatish Kharat 	}
47072b65c94SSatish Kharat 	case ENIC_MBOX_PF_LINK_STATE_NOTIF: {
47172b65c94SSatish Kharat 		size_t exp = sizeof(struct enic_mbox_pf_link_state_notif_msg);
47272b65c94SSatish Kharat 
47372b65c94SSatish Kharat 		if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type,
47472b65c94SSatish Kharat 					     payload_len, exp))
47572b65c94SSatish Kharat 			return;
47672b65c94SSatish Kharat 		enic_mbox_vf_handle_link_state(enic, payload);
47772b65c94SSatish Kharat 		break;
47872b65c94SSatish Kharat 	}
47972b65c94SSatish Kharat 	default:
48072b65c94SSatish Kharat 		netdev_dbg(enic->netdev,
48172b65c94SSatish Kharat 			   "MBOX: VF unhandled msg type %u\n",
48272b65c94SSatish Kharat 			   hdr->msg_type);
48372b65c94SSatish Kharat 		break;
48472b65c94SSatish Kharat 	}
48572b65c94SSatish Kharat }
48672b65c94SSatish Kharat 
enic_mbox_recv_handler(struct enic * enic,void * buf,unsigned int len)4871f0c856bSSatish Kharat static void enic_mbox_recv_handler(struct enic *enic, void *buf,
4881f0c856bSSatish Kharat 				   unsigned int len)
4891f0c856bSSatish Kharat {
4901f0c856bSSatish Kharat 	struct enic_mbox_hdr *hdr = buf;
49106bdfb16SSatish Kharat 	void *payload;
49206bdfb16SSatish Kharat 	u16 msg_len;
4931f0c856bSSatish Kharat 
4941f0c856bSSatish Kharat 	if (len < sizeof(*hdr)) {
4951f0c856bSSatish Kharat 		if (net_ratelimit())
4961f0c856bSSatish Kharat 			netdev_warn(enic->netdev,
4971f0c856bSSatish Kharat 				    "MBOX: truncated message (len %u < %zu)\n",
4981f0c856bSSatish Kharat 				    len, sizeof(*hdr));
4991f0c856bSSatish Kharat 		return;
5001f0c856bSSatish Kharat 	}
5011f0c856bSSatish Kharat 
5021f0c856bSSatish Kharat 	if (hdr->msg_type >= ENIC_MBOX_MAX) {
5031f0c856bSSatish Kharat 		if (net_ratelimit())
5041f0c856bSSatish Kharat 			netdev_warn(enic->netdev,
5051f0c856bSSatish Kharat 				    "MBOX: unknown msg type %u\n",
5061f0c856bSSatish Kharat 				    hdr->msg_type);
5071f0c856bSSatish Kharat 		return;
5081f0c856bSSatish Kharat 	}
5091f0c856bSSatish Kharat 
51006bdfb16SSatish Kharat 	msg_len = le16_to_cpu(hdr->msg_len);
51106bdfb16SSatish Kharat 	if (msg_len < sizeof(*hdr) || msg_len > len) {
51206bdfb16SSatish Kharat 		if (net_ratelimit())
51306bdfb16SSatish Kharat 			netdev_warn(enic->netdev,
51406bdfb16SSatish Kharat 				    "MBOX: invalid msg_len %u (buf len %u)\n",
51506bdfb16SSatish Kharat 				    msg_len, len);
51606bdfb16SSatish Kharat 		return;
51706bdfb16SSatish Kharat 	}
51806bdfb16SSatish Kharat 
5191f0c856bSSatish Kharat 	netdev_dbg(enic->netdev,
5201f0c856bSSatish Kharat 		   "MBOX recv: type %u from vnic %u len %u\n",
52106bdfb16SSatish Kharat 		   hdr->msg_type, le16_to_cpu(hdr->src_vnic_id), msg_len);
52206bdfb16SSatish Kharat 
52306bdfb16SSatish Kharat 	payload = buf + sizeof(*hdr);
52406bdfb16SSatish Kharat 
52572b65c94SSatish Kharat 	if (enic->vf_state) {
52606bdfb16SSatish Kharat 		enic_mbox_pf_process_msg(enic, hdr, payload);
52772b65c94SSatish Kharat 	} else if (le16_to_cpu(hdr->src_vnic_id) == ENIC_MBOX_DST_PF) {
52872b65c94SSatish Kharat 		/* src_vnic_id was overwritten from the hardware-verified CQ
52972b65c94SSatish Kharat 		 * VLAN sender field, so a VF only accepts messages that the
53072b65c94SSatish Kharat 		 * adapter attributes to the PF.  Its sole admin-channel peer is
53172b65c94SSatish Kharat 		 * the PF; drop anything else as a spoofed notification.
53272b65c94SSatish Kharat 		 */
53372b65c94SSatish Kharat 		enic_mbox_vf_process_msg(enic, hdr, payload,
53472b65c94SSatish Kharat 					 msg_len - (u16)sizeof(*hdr));
53572b65c94SSatish Kharat 	} else if (net_ratelimit()) {
53672b65c94SSatish Kharat 		netdev_warn(enic->netdev,
53772b65c94SSatish Kharat 			    "MBOX: VF dropping non-PF message from vnic %u\n",
53872b65c94SSatish Kharat 			    le16_to_cpu(hdr->src_vnic_id));
53972b65c94SSatish Kharat 	}
54072b65c94SSatish Kharat }
54172b65c94SSatish Kharat 
enic_mbox_vf_capability_check(struct enic * enic)54272b65c94SSatish Kharat int enic_mbox_vf_capability_check(struct enic *enic)
54372b65c94SSatish Kharat {
54472b65c94SSatish Kharat 	struct enic_mbox_vf_capability_msg req = {};
54572b65c94SSatish Kharat 	int err;
54672b65c94SSatish Kharat 
54772b65c94SSatish Kharat 	enic->pf_cap_version = 0;
54872b65c94SSatish Kharat 	reinit_completion(&enic->mbox_comp);
54972b65c94SSatish Kharat 	WRITE_ONCE(enic->mbox_expected_reply, ENIC_MBOX_VF_CAPABILITY_REPLY);
55072b65c94SSatish Kharat 	req.version = cpu_to_le32(ENIC_MBOX_CAP_VERSION_1);
55172b65c94SSatish Kharat 
55272b65c94SSatish Kharat 	err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_CAPABILITY_REQUEST,
55372b65c94SSatish Kharat 				 ENIC_MBOX_DST_PF, &req, sizeof(req));
55472b65c94SSatish Kharat 	if (err) {
55572b65c94SSatish Kharat 		WRITE_ONCE(enic->mbox_expected_reply, 0);
55672b65c94SSatish Kharat 		return err;
55772b65c94SSatish Kharat 	}
55872b65c94SSatish Kharat 
55972b65c94SSatish Kharat 	err = enic_mbox_wait_reply(enic, 3000);
56072b65c94SSatish Kharat 	WRITE_ONCE(enic->mbox_expected_reply, 0);
56172b65c94SSatish Kharat 	if (err) {
56272b65c94SSatish Kharat 		netdev_warn(enic->netdev,
56372b65c94SSatish Kharat 			    "MBOX: no capability reply from PF\n");
56472b65c94SSatish Kharat 		return err;
56572b65c94SSatish Kharat 	}
56672b65c94SSatish Kharat 
56772b65c94SSatish Kharat 	if (enic->pf_cap_version < ENIC_MBOX_CAP_VERSION_1) {
56872b65c94SSatish Kharat 		netdev_warn(enic->netdev,
56972b65c94SSatish Kharat 			    "MBOX: PF rejected capability request or reported unsupported version %u\n",
57072b65c94SSatish Kharat 			    enic->pf_cap_version);
57172b65c94SSatish Kharat 		return -EOPNOTSUPP;
57272b65c94SSatish Kharat 	}
57372b65c94SSatish Kharat 
57472b65c94SSatish Kharat 	return 0;
57572b65c94SSatish Kharat }
57672b65c94SSatish Kharat 
enic_mbox_vf_register(struct enic * enic)57772b65c94SSatish Kharat int enic_mbox_vf_register(struct enic *enic)
57872b65c94SSatish Kharat {
57972b65c94SSatish Kharat 	int err;
58072b65c94SSatish Kharat 
58172b65c94SSatish Kharat 	enic->vf_registered = false;
58272b65c94SSatish Kharat 	reinit_completion(&enic->mbox_comp);
58372b65c94SSatish Kharat 	WRITE_ONCE(enic->mbox_expected_reply, ENIC_MBOX_VF_REGISTER_REPLY);
58472b65c94SSatish Kharat 
58572b65c94SSatish Kharat 	err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_REGISTER_REQUEST,
58672b65c94SSatish Kharat 				 ENIC_MBOX_DST_PF, NULL, 0);
58772b65c94SSatish Kharat 	if (err) {
58872b65c94SSatish Kharat 		WRITE_ONCE(enic->mbox_expected_reply, 0);
58972b65c94SSatish Kharat 		return err;
59072b65c94SSatish Kharat 	}
59172b65c94SSatish Kharat 
59272b65c94SSatish Kharat 	err = enic_mbox_wait_reply(enic, 3000);
59372b65c94SSatish Kharat 	WRITE_ONCE(enic->mbox_expected_reply, 0);
59472b65c94SSatish Kharat 	if (err) {
59572b65c94SSatish Kharat 		netdev_warn(enic->netdev,
59672b65c94SSatish Kharat 			    "MBOX: VF registration with PF timed out\n");
59772b65c94SSatish Kharat 		return err;
59872b65c94SSatish Kharat 	}
59972b65c94SSatish Kharat 
60072b65c94SSatish Kharat 	if (!enic->vf_registered)
60172b65c94SSatish Kharat 		return -ENODEV;
60272b65c94SSatish Kharat 
60372b65c94SSatish Kharat 	return 0;
60472b65c94SSatish Kharat }
60572b65c94SSatish Kharat 
enic_mbox_vf_unregister(struct enic * enic)60672b65c94SSatish Kharat int enic_mbox_vf_unregister(struct enic *enic)
60772b65c94SSatish Kharat {
60872b65c94SSatish Kharat 	int err;
60972b65c94SSatish Kharat 
61072b65c94SSatish Kharat 	if (!enic->vf_registered)
61172b65c94SSatish Kharat 		return 0;
61272b65c94SSatish Kharat 
61372b65c94SSatish Kharat 	reinit_completion(&enic->mbox_comp);
61472b65c94SSatish Kharat 	WRITE_ONCE(enic->mbox_expected_reply, ENIC_MBOX_VF_UNREGISTER_REPLY);
61572b65c94SSatish Kharat 
61672b65c94SSatish Kharat 	err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_UNREGISTER_REQUEST,
61772b65c94SSatish Kharat 				 ENIC_MBOX_DST_PF, NULL, 0);
61872b65c94SSatish Kharat 	if (err) {
61972b65c94SSatish Kharat 		WRITE_ONCE(enic->mbox_expected_reply, 0);
62072b65c94SSatish Kharat 		return err;
62172b65c94SSatish Kharat 	}
62272b65c94SSatish Kharat 
62372b65c94SSatish Kharat 	err = enic_mbox_wait_reply(enic, 3000);
62472b65c94SSatish Kharat 	WRITE_ONCE(enic->mbox_expected_reply, 0);
62572b65c94SSatish Kharat 	if (err)
62672b65c94SSatish Kharat 		return err;
62772b65c94SSatish Kharat 	if (enic->vf_registered)
62872b65c94SSatish Kharat 		return -EACCES;
62972b65c94SSatish Kharat 	return 0;
6301f0c856bSSatish Kharat }
6311f0c856bSSatish Kharat 
enic_mbox_init(struct enic * enic)6321f0c856bSSatish Kharat void enic_mbox_init(struct enic *enic)
6331f0c856bSSatish Kharat {
634*4ff204ceSSatish Kharat 	/* mbox_lock and mbox_comp must be initialized exactly once per
635*4ff204ceSSatish Kharat 	 * device lifetime; the PF sriov_configure path can re-enter this
636*4ff204ceSSatish Kharat 	 * on each enable cycle where these primitives are already set up.
637*4ff204ceSSatish Kharat 	 */
638*4ff204ceSSatish Kharat 	if (!enic->mbox_initialized) {
6391f0c856bSSatish Kharat 		mutex_init(&enic->mbox_lock);
64072b65c94SSatish Kharat 		init_completion(&enic->mbox_comp);
641*4ff204ceSSatish Kharat 		enic->mbox_initialized = true;
642*4ff204ceSSatish Kharat 	} else {
643*4ff204ceSSatish Kharat 		reinit_completion(&enic->mbox_comp);
644*4ff204ceSSatish Kharat 	}
645*4ff204ceSSatish Kharat 	enic->mbox_msg_num = 0;
6461f0c856bSSatish Kharat 	enic->admin_rq_handler = enic_mbox_recv_handler;
6471f0c856bSSatish Kharat }
648