18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Virtio PCI driver - common functionality for all device versions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This module allows virtio devices to be used over a virtual PCI device.
68c2ecf20Sopenharmony_ci * This can be used with QEMU based VMMs like KVM or Xen.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2007
98c2ecf20Sopenharmony_ci * Copyright Red Hat, Inc. 2014
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Authors:
128c2ecf20Sopenharmony_ci *  Anthony Liguori  <aliguori@us.ibm.com>
138c2ecf20Sopenharmony_ci *  Rusty Russell <rusty@rustcorp.com.au>
148c2ecf20Sopenharmony_ci *  Michael S. Tsirkin <mst@redhat.com>
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "virtio_pci_common.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic bool force_legacy = false;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VIRTIO_PCI_LEGACY)
228c2ecf20Sopenharmony_cimodule_param(force_legacy, bool, 0444);
238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_legacy,
248c2ecf20Sopenharmony_ci		 "Force legacy mode for transitional virtio 1 devices");
258c2ecf20Sopenharmony_ci#endif
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* wait for pending irq handlers */
288c2ecf20Sopenharmony_civoid vp_synchronize_vectors(struct virtio_device *vdev)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
318c2ecf20Sopenharmony_ci	int i;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (vp_dev->intx_enabled)
348c2ecf20Sopenharmony_ci		synchronize_irq(vp_dev->pci_dev->irq);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	for (i = 0; i < vp_dev->msix_vectors; ++i)
378c2ecf20Sopenharmony_ci		synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i));
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* the notify function used when creating a virt queue */
418c2ecf20Sopenharmony_cibool vp_notify(struct virtqueue *vq)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	/* we write the queue's selector into the notification register to
448c2ecf20Sopenharmony_ci	 * signal the other end */
458c2ecf20Sopenharmony_ci	iowrite16(vq->index, (void __iomem *)vq->priv);
468c2ecf20Sopenharmony_ci	return true;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Handle a configuration change: Tell driver if it wants to know. */
508c2ecf20Sopenharmony_cistatic irqreturn_t vp_config_changed(int irq, void *opaque)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = opaque;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	virtio_config_changed(&vp_dev->vdev);
558c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Notify all virtqueues on an interrupt. */
598c2ecf20Sopenharmony_cistatic irqreturn_t vp_vring_interrupt(int irq, void *opaque)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = opaque;
628c2ecf20Sopenharmony_ci	struct virtio_pci_vq_info *info;
638c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
648c2ecf20Sopenharmony_ci	unsigned long flags;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vp_dev->lock, flags);
678c2ecf20Sopenharmony_ci	list_for_each_entry(info, &vp_dev->virtqueues, node) {
688c2ecf20Sopenharmony_ci		if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
698c2ecf20Sopenharmony_ci			ret = IRQ_HANDLED;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vp_dev->lock, flags);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return ret;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* A small wrapper to also acknowledge the interrupt when it's handled.
778c2ecf20Sopenharmony_ci * I really need an EIO hook for the vring so I can ack the interrupt once we
788c2ecf20Sopenharmony_ci * know that we'll be handling the IRQ but before we invoke the callback since
798c2ecf20Sopenharmony_ci * the callback may notify the host which results in the host attempting to
808c2ecf20Sopenharmony_ci * raise an interrupt that we would then mask once we acknowledged the
818c2ecf20Sopenharmony_ci * interrupt. */
828c2ecf20Sopenharmony_cistatic irqreturn_t vp_interrupt(int irq, void *opaque)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = opaque;
858c2ecf20Sopenharmony_ci	u8 isr;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* reading the ISR has the effect of also clearing it so it's very
888c2ecf20Sopenharmony_ci	 * important to save off the value. */
898c2ecf20Sopenharmony_ci	isr = ioread8(vp_dev->isr);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* It's definitely not us if the ISR was not high */
928c2ecf20Sopenharmony_ci	if (!isr)
938c2ecf20Sopenharmony_ci		return IRQ_NONE;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* Configuration change?  Tell driver if it wants to know. */
968c2ecf20Sopenharmony_ci	if (isr & VIRTIO_PCI_ISR_CONFIG)
978c2ecf20Sopenharmony_ci		vp_config_changed(irq, opaque);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return vp_vring_interrupt(irq, opaque);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
1038c2ecf20Sopenharmony_ci				   bool per_vq_vectors, struct irq_affinity *desc)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
1068c2ecf20Sopenharmony_ci	const char *name = dev_name(&vp_dev->vdev.dev);
1078c2ecf20Sopenharmony_ci	unsigned flags = PCI_IRQ_MSIX;
1088c2ecf20Sopenharmony_ci	unsigned i, v;
1098c2ecf20Sopenharmony_ci	int err = -ENOMEM;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	vp_dev->msix_vectors = nvectors;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	vp_dev->msix_names = kmalloc_array(nvectors,
1148c2ecf20Sopenharmony_ci					   sizeof(*vp_dev->msix_names),
1158c2ecf20Sopenharmony_ci					   GFP_KERNEL);
1168c2ecf20Sopenharmony_ci	if (!vp_dev->msix_names)
1178c2ecf20Sopenharmony_ci		goto error;
1188c2ecf20Sopenharmony_ci	vp_dev->msix_affinity_masks
1198c2ecf20Sopenharmony_ci		= kcalloc(nvectors, sizeof(*vp_dev->msix_affinity_masks),
1208c2ecf20Sopenharmony_ci			  GFP_KERNEL);
1218c2ecf20Sopenharmony_ci	if (!vp_dev->msix_affinity_masks)
1228c2ecf20Sopenharmony_ci		goto error;
1238c2ecf20Sopenharmony_ci	for (i = 0; i < nvectors; ++i)
1248c2ecf20Sopenharmony_ci		if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i],
1258c2ecf20Sopenharmony_ci					GFP_KERNEL))
1268c2ecf20Sopenharmony_ci			goto error;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (desc) {
1298c2ecf20Sopenharmony_ci		flags |= PCI_IRQ_AFFINITY;
1308c2ecf20Sopenharmony_ci		desc->pre_vectors++; /* virtio config vector */
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	err = pci_alloc_irq_vectors_affinity(vp_dev->pci_dev, nvectors,
1348c2ecf20Sopenharmony_ci					     nvectors, flags, desc);
1358c2ecf20Sopenharmony_ci	if (err < 0)
1368c2ecf20Sopenharmony_ci		goto error;
1378c2ecf20Sopenharmony_ci	vp_dev->msix_enabled = 1;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Set the vector used for configuration */
1408c2ecf20Sopenharmony_ci	v = vp_dev->msix_used_vectors;
1418c2ecf20Sopenharmony_ci	snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
1428c2ecf20Sopenharmony_ci		 "%s-config", name);
1438c2ecf20Sopenharmony_ci	err = request_irq(pci_irq_vector(vp_dev->pci_dev, v),
1448c2ecf20Sopenharmony_ci			  vp_config_changed, 0, vp_dev->msix_names[v],
1458c2ecf20Sopenharmony_ci			  vp_dev);
1468c2ecf20Sopenharmony_ci	if (err)
1478c2ecf20Sopenharmony_ci		goto error;
1488c2ecf20Sopenharmony_ci	++vp_dev->msix_used_vectors;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	v = vp_dev->config_vector(vp_dev, v);
1518c2ecf20Sopenharmony_ci	/* Verify we had enough resources to assign the vector */
1528c2ecf20Sopenharmony_ci	if (v == VIRTIO_MSI_NO_VECTOR) {
1538c2ecf20Sopenharmony_ci		err = -EBUSY;
1548c2ecf20Sopenharmony_ci		goto error;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (!per_vq_vectors) {
1588c2ecf20Sopenharmony_ci		/* Shared vector for all VQs */
1598c2ecf20Sopenharmony_ci		v = vp_dev->msix_used_vectors;
1608c2ecf20Sopenharmony_ci		snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
1618c2ecf20Sopenharmony_ci			 "%s-virtqueues", name);
1628c2ecf20Sopenharmony_ci		err = request_irq(pci_irq_vector(vp_dev->pci_dev, v),
1638c2ecf20Sopenharmony_ci				  vp_vring_interrupt, 0, vp_dev->msix_names[v],
1648c2ecf20Sopenharmony_ci				  vp_dev);
1658c2ecf20Sopenharmony_ci		if (err)
1668c2ecf20Sopenharmony_ci			goto error;
1678c2ecf20Sopenharmony_ci		++vp_dev->msix_used_vectors;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_cierror:
1718c2ecf20Sopenharmony_ci	return err;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned index,
1758c2ecf20Sopenharmony_ci				     void (*callback)(struct virtqueue *vq),
1768c2ecf20Sopenharmony_ci				     const char *name,
1778c2ecf20Sopenharmony_ci				     bool ctx,
1788c2ecf20Sopenharmony_ci				     u16 msix_vec)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
1818c2ecf20Sopenharmony_ci	struct virtio_pci_vq_info *info = kmalloc(sizeof *info, GFP_KERNEL);
1828c2ecf20Sopenharmony_ci	struct virtqueue *vq;
1838c2ecf20Sopenharmony_ci	unsigned long flags;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* fill out our structure that represents an active queue */
1868c2ecf20Sopenharmony_ci	if (!info)
1878c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, ctx,
1908c2ecf20Sopenharmony_ci			      msix_vec);
1918c2ecf20Sopenharmony_ci	if (IS_ERR(vq))
1928c2ecf20Sopenharmony_ci		goto out_info;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	info->vq = vq;
1958c2ecf20Sopenharmony_ci	if (callback) {
1968c2ecf20Sopenharmony_ci		spin_lock_irqsave(&vp_dev->lock, flags);
1978c2ecf20Sopenharmony_ci		list_add(&info->node, &vp_dev->virtqueues);
1988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vp_dev->lock, flags);
1998c2ecf20Sopenharmony_ci	} else {
2008c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&info->node);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	vp_dev->vqs[index] = info;
2048c2ecf20Sopenharmony_ci	return vq;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciout_info:
2078c2ecf20Sopenharmony_ci	kfree(info);
2088c2ecf20Sopenharmony_ci	return vq;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void vp_del_vq(struct virtqueue *vq)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
2148c2ecf20Sopenharmony_ci	struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index];
2158c2ecf20Sopenharmony_ci	unsigned long flags;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vp_dev->lock, flags);
2188c2ecf20Sopenharmony_ci	list_del(&info->node);
2198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vp_dev->lock, flags);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	vp_dev->del_vq(info);
2228c2ecf20Sopenharmony_ci	kfree(info);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/* the config->del_vqs() implementation */
2268c2ecf20Sopenharmony_civoid vp_del_vqs(struct virtio_device *vdev)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
2298c2ecf20Sopenharmony_ci	struct virtqueue *vq, *n;
2308c2ecf20Sopenharmony_ci	int i;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
2338c2ecf20Sopenharmony_ci		if (vp_dev->per_vq_vectors) {
2348c2ecf20Sopenharmony_ci			int v = vp_dev->vqs[vq->index]->msix_vector;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci			if (v != VIRTIO_MSI_NO_VECTOR) {
2378c2ecf20Sopenharmony_ci				int irq = pci_irq_vector(vp_dev->pci_dev, v);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci				irq_set_affinity_hint(irq, NULL);
2408c2ecf20Sopenharmony_ci				free_irq(irq, vq);
2418c2ecf20Sopenharmony_ci			}
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci		vp_del_vq(vq);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci	vp_dev->per_vq_vectors = false;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (vp_dev->intx_enabled) {
2488c2ecf20Sopenharmony_ci		free_irq(vp_dev->pci_dev->irq, vp_dev);
2498c2ecf20Sopenharmony_ci		vp_dev->intx_enabled = 0;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	for (i = 0; i < vp_dev->msix_used_vectors; ++i)
2538c2ecf20Sopenharmony_ci		free_irq(pci_irq_vector(vp_dev->pci_dev, i), vp_dev);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (vp_dev->msix_affinity_masks) {
2568c2ecf20Sopenharmony_ci		for (i = 0; i < vp_dev->msix_vectors; i++)
2578c2ecf20Sopenharmony_ci			free_cpumask_var(vp_dev->msix_affinity_masks[i]);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (vp_dev->msix_enabled) {
2618c2ecf20Sopenharmony_ci		/* Disable the vector used for configuration */
2628c2ecf20Sopenharmony_ci		vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		pci_free_irq_vectors(vp_dev->pci_dev);
2658c2ecf20Sopenharmony_ci		vp_dev->msix_enabled = 0;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	vp_dev->msix_vectors = 0;
2698c2ecf20Sopenharmony_ci	vp_dev->msix_used_vectors = 0;
2708c2ecf20Sopenharmony_ci	kfree(vp_dev->msix_names);
2718c2ecf20Sopenharmony_ci	vp_dev->msix_names = NULL;
2728c2ecf20Sopenharmony_ci	kfree(vp_dev->msix_affinity_masks);
2738c2ecf20Sopenharmony_ci	vp_dev->msix_affinity_masks = NULL;
2748c2ecf20Sopenharmony_ci	kfree(vp_dev->vqs);
2758c2ecf20Sopenharmony_ci	vp_dev->vqs = NULL;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs,
2798c2ecf20Sopenharmony_ci		struct virtqueue *vqs[], vq_callback_t *callbacks[],
2808c2ecf20Sopenharmony_ci		const char * const names[], bool per_vq_vectors,
2818c2ecf20Sopenharmony_ci		const bool *ctx,
2828c2ecf20Sopenharmony_ci		struct irq_affinity *desc)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
2858c2ecf20Sopenharmony_ci	u16 msix_vec;
2868c2ecf20Sopenharmony_ci	int i, err, nvectors, allocated_vectors, queue_idx = 0;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL);
2898c2ecf20Sopenharmony_ci	if (!vp_dev->vqs)
2908c2ecf20Sopenharmony_ci		return -ENOMEM;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (per_vq_vectors) {
2938c2ecf20Sopenharmony_ci		/* Best option: one for change interrupt, one per vq. */
2948c2ecf20Sopenharmony_ci		nvectors = 1;
2958c2ecf20Sopenharmony_ci		for (i = 0; i < nvqs; ++i)
2968c2ecf20Sopenharmony_ci			if (names[i] && callbacks[i])
2978c2ecf20Sopenharmony_ci				++nvectors;
2988c2ecf20Sopenharmony_ci	} else {
2998c2ecf20Sopenharmony_ci		/* Second best: one for change, shared for all vqs. */
3008c2ecf20Sopenharmony_ci		nvectors = 2;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors,
3048c2ecf20Sopenharmony_ci				      per_vq_vectors ? desc : NULL);
3058c2ecf20Sopenharmony_ci	if (err)
3068c2ecf20Sopenharmony_ci		goto error_find;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	vp_dev->per_vq_vectors = per_vq_vectors;
3098c2ecf20Sopenharmony_ci	allocated_vectors = vp_dev->msix_used_vectors;
3108c2ecf20Sopenharmony_ci	for (i = 0; i < nvqs; ++i) {
3118c2ecf20Sopenharmony_ci		if (!names[i]) {
3128c2ecf20Sopenharmony_ci			vqs[i] = NULL;
3138c2ecf20Sopenharmony_ci			continue;
3148c2ecf20Sopenharmony_ci		}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		if (!callbacks[i])
3178c2ecf20Sopenharmony_ci			msix_vec = VIRTIO_MSI_NO_VECTOR;
3188c2ecf20Sopenharmony_ci		else if (vp_dev->per_vq_vectors)
3198c2ecf20Sopenharmony_ci			msix_vec = allocated_vectors++;
3208c2ecf20Sopenharmony_ci		else
3218c2ecf20Sopenharmony_ci			msix_vec = VP_MSIX_VQ_VECTOR;
3228c2ecf20Sopenharmony_ci		vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i],
3238c2ecf20Sopenharmony_ci				     ctx ? ctx[i] : false,
3248c2ecf20Sopenharmony_ci				     msix_vec);
3258c2ecf20Sopenharmony_ci		if (IS_ERR(vqs[i])) {
3268c2ecf20Sopenharmony_ci			err = PTR_ERR(vqs[i]);
3278c2ecf20Sopenharmony_ci			goto error_find;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR)
3318c2ecf20Sopenharmony_ci			continue;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		/* allocate per-vq irq if available and necessary */
3348c2ecf20Sopenharmony_ci		snprintf(vp_dev->msix_names[msix_vec],
3358c2ecf20Sopenharmony_ci			 sizeof *vp_dev->msix_names,
3368c2ecf20Sopenharmony_ci			 "%s-%s",
3378c2ecf20Sopenharmony_ci			 dev_name(&vp_dev->vdev.dev), names[i]);
3388c2ecf20Sopenharmony_ci		err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec),
3398c2ecf20Sopenharmony_ci				  vring_interrupt, 0,
3408c2ecf20Sopenharmony_ci				  vp_dev->msix_names[msix_vec],
3418c2ecf20Sopenharmony_ci				  vqs[i]);
3428c2ecf20Sopenharmony_ci		if (err)
3438c2ecf20Sopenharmony_ci			goto error_find;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cierror_find:
3488c2ecf20Sopenharmony_ci	vp_del_vqs(vdev);
3498c2ecf20Sopenharmony_ci	return err;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int vp_find_vqs_intx(struct virtio_device *vdev, unsigned nvqs,
3538c2ecf20Sopenharmony_ci		struct virtqueue *vqs[], vq_callback_t *callbacks[],
3548c2ecf20Sopenharmony_ci		const char * const names[], const bool *ctx)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
3578c2ecf20Sopenharmony_ci	int i, err, queue_idx = 0;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL);
3608c2ecf20Sopenharmony_ci	if (!vp_dev->vqs)
3618c2ecf20Sopenharmony_ci		return -ENOMEM;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
3648c2ecf20Sopenharmony_ci			dev_name(&vdev->dev), vp_dev);
3658c2ecf20Sopenharmony_ci	if (err)
3668c2ecf20Sopenharmony_ci		goto out_del_vqs;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	vp_dev->intx_enabled = 1;
3698c2ecf20Sopenharmony_ci	vp_dev->per_vq_vectors = false;
3708c2ecf20Sopenharmony_ci	for (i = 0; i < nvqs; ++i) {
3718c2ecf20Sopenharmony_ci		if (!names[i]) {
3728c2ecf20Sopenharmony_ci			vqs[i] = NULL;
3738c2ecf20Sopenharmony_ci			continue;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci		vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i],
3768c2ecf20Sopenharmony_ci				     ctx ? ctx[i] : false,
3778c2ecf20Sopenharmony_ci				     VIRTIO_MSI_NO_VECTOR);
3788c2ecf20Sopenharmony_ci		if (IS_ERR(vqs[i])) {
3798c2ecf20Sopenharmony_ci			err = PTR_ERR(vqs[i]);
3808c2ecf20Sopenharmony_ci			goto out_del_vqs;
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ciout_del_vqs:
3868c2ecf20Sopenharmony_ci	vp_del_vqs(vdev);
3878c2ecf20Sopenharmony_ci	return err;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/* the config->find_vqs() implementation */
3918c2ecf20Sopenharmony_ciint vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
3928c2ecf20Sopenharmony_ci		struct virtqueue *vqs[], vq_callback_t *callbacks[],
3938c2ecf20Sopenharmony_ci		const char * const names[], const bool *ctx,
3948c2ecf20Sopenharmony_ci		struct irq_affinity *desc)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	int err;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* Try MSI-X with one vector per queue. */
3998c2ecf20Sopenharmony_ci	err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true, ctx, desc);
4008c2ecf20Sopenharmony_ci	if (!err)
4018c2ecf20Sopenharmony_ci		return 0;
4028c2ecf20Sopenharmony_ci	/* Fallback: MSI-X with one vector for config, one shared for queues. */
4038c2ecf20Sopenharmony_ci	err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false, ctx, desc);
4048c2ecf20Sopenharmony_ci	if (!err)
4058c2ecf20Sopenharmony_ci		return 0;
4068c2ecf20Sopenharmony_ci	/* Finally fall back to regular interrupts. */
4078c2ecf20Sopenharmony_ci	return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, ctx);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ciconst char *vp_bus_name(struct virtio_device *vdev)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return pci_name(vp_dev->pci_dev);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/* Setup the affinity for a virtqueue:
4188c2ecf20Sopenharmony_ci * - force the affinity for per vq vector
4198c2ecf20Sopenharmony_ci * - OR over all affinities for shared MSI
4208c2ecf20Sopenharmony_ci * - ignore the affinity request if we're using INTX
4218c2ecf20Sopenharmony_ci */
4228c2ecf20Sopenharmony_ciint vp_set_vq_affinity(struct virtqueue *vq, const struct cpumask *cpu_mask)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct virtio_device *vdev = vq->vdev;
4258c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
4268c2ecf20Sopenharmony_ci	struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index];
4278c2ecf20Sopenharmony_ci	struct cpumask *mask;
4288c2ecf20Sopenharmony_ci	unsigned int irq;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (!vq->callback)
4318c2ecf20Sopenharmony_ci		return -EINVAL;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (vp_dev->msix_enabled) {
4348c2ecf20Sopenharmony_ci		mask = vp_dev->msix_affinity_masks[info->msix_vector];
4358c2ecf20Sopenharmony_ci		irq = pci_irq_vector(vp_dev->pci_dev, info->msix_vector);
4368c2ecf20Sopenharmony_ci		if (!cpu_mask)
4378c2ecf20Sopenharmony_ci			irq_set_affinity_hint(irq, NULL);
4388c2ecf20Sopenharmony_ci		else {
4398c2ecf20Sopenharmony_ci			cpumask_copy(mask, cpu_mask);
4408c2ecf20Sopenharmony_ci			irq_set_affinity_hint(irq, mask);
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci	return 0;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciconst struct cpumask *vp_get_vq_affinity(struct virtio_device *vdev, int index)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (!vp_dev->per_vq_vectors ||
4518c2ecf20Sopenharmony_ci	    vp_dev->vqs[index]->msix_vector == VIRTIO_MSI_NO_VECTOR)
4528c2ecf20Sopenharmony_ci		return NULL;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return pci_irq_get_affinity(vp_dev->pci_dev,
4558c2ecf20Sopenharmony_ci				    vp_dev->vqs[index]->msix_vector);
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4598c2ecf20Sopenharmony_cistatic int virtio_pci_freeze(struct device *dev)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
4628c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
4638c2ecf20Sopenharmony_ci	int ret;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	ret = virtio_device_freeze(&vp_dev->vdev);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (!ret)
4688c2ecf20Sopenharmony_ci		pci_disable_device(pci_dev);
4698c2ecf20Sopenharmony_ci	return ret;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int virtio_pci_restore(struct device *dev)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
4758c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
4768c2ecf20Sopenharmony_ci	int ret;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	ret = pci_enable_device(pci_dev);
4798c2ecf20Sopenharmony_ci	if (ret)
4808c2ecf20Sopenharmony_ci		return ret;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	pci_set_master(pci_dev);
4838c2ecf20Sopenharmony_ci	return virtio_device_restore(&vp_dev->vdev);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic const struct dev_pm_ops virtio_pci_pm_ops = {
4878c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(virtio_pci_freeze, virtio_pci_restore)
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_ci#endif
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
4938c2ecf20Sopenharmony_cistatic const struct pci_device_id virtio_pci_id_table[] = {
4948c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) },
4958c2ecf20Sopenharmony_ci	{ 0 }
4968c2ecf20Sopenharmony_ci};
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic void virtio_pci_release_dev(struct device *_d)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct virtio_device *vdev = dev_to_virtio(_d);
5038c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* As struct device is a kobject, it's not safe to
5068c2ecf20Sopenharmony_ci	 * free the memory (including the reference counter itself)
5078c2ecf20Sopenharmony_ci	 * until it's release callback. */
5088c2ecf20Sopenharmony_ci	kfree(vp_dev);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int virtio_pci_probe(struct pci_dev *pci_dev,
5128c2ecf20Sopenharmony_ci			    const struct pci_device_id *id)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev, *reg_dev = NULL;
5158c2ecf20Sopenharmony_ci	int rc;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* allocate our structure and fill it out */
5188c2ecf20Sopenharmony_ci	vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL);
5198c2ecf20Sopenharmony_ci	if (!vp_dev)
5208c2ecf20Sopenharmony_ci		return -ENOMEM;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	pci_set_drvdata(pci_dev, vp_dev);
5238c2ecf20Sopenharmony_ci	vp_dev->vdev.dev.parent = &pci_dev->dev;
5248c2ecf20Sopenharmony_ci	vp_dev->vdev.dev.release = virtio_pci_release_dev;
5258c2ecf20Sopenharmony_ci	vp_dev->pci_dev = pci_dev;
5268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vp_dev->virtqueues);
5278c2ecf20Sopenharmony_ci	spin_lock_init(&vp_dev->lock);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* enable the device */
5308c2ecf20Sopenharmony_ci	rc = pci_enable_device(pci_dev);
5318c2ecf20Sopenharmony_ci	if (rc)
5328c2ecf20Sopenharmony_ci		goto err_enable_device;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (force_legacy) {
5358c2ecf20Sopenharmony_ci		rc = virtio_pci_legacy_probe(vp_dev);
5368c2ecf20Sopenharmony_ci		/* Also try modern mode if we can't map BAR0 (no IO space). */
5378c2ecf20Sopenharmony_ci		if (rc == -ENODEV || rc == -ENOMEM)
5388c2ecf20Sopenharmony_ci			rc = virtio_pci_modern_probe(vp_dev);
5398c2ecf20Sopenharmony_ci		if (rc)
5408c2ecf20Sopenharmony_ci			goto err_probe;
5418c2ecf20Sopenharmony_ci	} else {
5428c2ecf20Sopenharmony_ci		rc = virtio_pci_modern_probe(vp_dev);
5438c2ecf20Sopenharmony_ci		if (rc == -ENODEV)
5448c2ecf20Sopenharmony_ci			rc = virtio_pci_legacy_probe(vp_dev);
5458c2ecf20Sopenharmony_ci		if (rc)
5468c2ecf20Sopenharmony_ci			goto err_probe;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	pci_set_master(pci_dev);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	rc = register_virtio_device(&vp_dev->vdev);
5528c2ecf20Sopenharmony_ci	reg_dev = vp_dev;
5538c2ecf20Sopenharmony_ci	if (rc)
5548c2ecf20Sopenharmony_ci		goto err_register;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cierr_register:
5598c2ecf20Sopenharmony_ci	if (vp_dev->ioaddr)
5608c2ecf20Sopenharmony_ci	     virtio_pci_legacy_remove(vp_dev);
5618c2ecf20Sopenharmony_ci	else
5628c2ecf20Sopenharmony_ci	     virtio_pci_modern_remove(vp_dev);
5638c2ecf20Sopenharmony_cierr_probe:
5648c2ecf20Sopenharmony_ci	pci_disable_device(pci_dev);
5658c2ecf20Sopenharmony_cierr_enable_device:
5668c2ecf20Sopenharmony_ci	if (reg_dev)
5678c2ecf20Sopenharmony_ci		put_device(&vp_dev->vdev.dev);
5688c2ecf20Sopenharmony_ci	else
5698c2ecf20Sopenharmony_ci		kfree(vp_dev);
5708c2ecf20Sopenharmony_ci	return rc;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic void virtio_pci_remove(struct pci_dev *pci_dev)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
5768c2ecf20Sopenharmony_ci	struct device *dev = get_device(&vp_dev->vdev.dev);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/*
5798c2ecf20Sopenharmony_ci	 * Device is marked broken on surprise removal so that virtio upper
5808c2ecf20Sopenharmony_ci	 * layers can abort any ongoing operation.
5818c2ecf20Sopenharmony_ci	 */
5828c2ecf20Sopenharmony_ci	if (!pci_device_is_present(pci_dev))
5838c2ecf20Sopenharmony_ci		virtio_break_device(&vp_dev->vdev);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	pci_disable_sriov(pci_dev);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	unregister_virtio_device(&vp_dev->vdev);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (vp_dev->ioaddr)
5908c2ecf20Sopenharmony_ci		virtio_pci_legacy_remove(vp_dev);
5918c2ecf20Sopenharmony_ci	else
5928c2ecf20Sopenharmony_ci		virtio_pci_modern_remove(vp_dev);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	pci_disable_device(pci_dev);
5958c2ecf20Sopenharmony_ci	put_device(dev);
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic int virtio_pci_sriov_configure(struct pci_dev *pci_dev, int num_vfs)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
6018c2ecf20Sopenharmony_ci	struct virtio_device *vdev = &vp_dev->vdev;
6028c2ecf20Sopenharmony_ci	int ret;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (!(vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_DRIVER_OK))
6058c2ecf20Sopenharmony_ci		return -EBUSY;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (!__virtio_test_bit(vdev, VIRTIO_F_SR_IOV))
6088c2ecf20Sopenharmony_ci		return -EINVAL;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (pci_vfs_assigned(pci_dev))
6118c2ecf20Sopenharmony_ci		return -EPERM;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (num_vfs == 0) {
6148c2ecf20Sopenharmony_ci		pci_disable_sriov(pci_dev);
6158c2ecf20Sopenharmony_ci		return 0;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	ret = pci_enable_sriov(pci_dev, num_vfs);
6198c2ecf20Sopenharmony_ci	if (ret < 0)
6208c2ecf20Sopenharmony_ci		return ret;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return num_vfs;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic struct pci_driver virtio_pci_driver = {
6268c2ecf20Sopenharmony_ci	.name		= "virtio-pci",
6278c2ecf20Sopenharmony_ci	.id_table	= virtio_pci_id_table,
6288c2ecf20Sopenharmony_ci	.probe		= virtio_pci_probe,
6298c2ecf20Sopenharmony_ci	.remove		= virtio_pci_remove,
6308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6318c2ecf20Sopenharmony_ci	.driver.pm	= &virtio_pci_pm_ops,
6328c2ecf20Sopenharmony_ci#endif
6338c2ecf20Sopenharmony_ci	.sriov_configure = virtio_pci_sriov_configure,
6348c2ecf20Sopenharmony_ci};
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cimodule_pci_driver(virtio_pci_driver);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anthony Liguori <aliguori@us.ibm.com>");
6398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("virtio-pci");
6408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6418c2ecf20Sopenharmony_ciMODULE_VERSION("1");
642