xref: /kvmtool/virtio/pci.c (revision 1c47ce695a0c853fcca0dccf558c7f2d62d23715)
136f5dc91SSasha Levin #include "kvm/virtio-pci.h"
236f5dc91SSasha Levin 
336f5dc91SSasha Levin #include "kvm/ioport.h"
436f5dc91SSasha Levin #include "kvm/kvm.h"
536f5dc91SSasha Levin #include "kvm/virtio-pci-dev.h"
636f5dc91SSasha Levin #include "kvm/irq.h"
736f5dc91SSasha Levin #include "kvm/virtio.h"
81599d724SSasha Levin #include "kvm/ioeventfd.h"
9*1c47ce69SSasha Levin #include "kvm/virtio-trans.h"
1036f5dc91SSasha Levin 
1136f5dc91SSasha Levin #include <linux/virtio_pci.h>
1236f5dc91SSasha Levin #include <string.h>
1336f5dc91SSasha Levin 
14*1c47ce69SSasha Levin struct virtio_trans_ops *virtio_pci__get_trans_ops(void)
15*1c47ce69SSasha Levin {
16*1c47ce69SSasha Levin 	static struct virtio_trans_ops virtio_pci_trans = (struct virtio_trans_ops) {
17*1c47ce69SSasha Levin 		.signal_vq	= virtio_pci__signal_vq,
18*1c47ce69SSasha Levin 		.signal_config	= virtio_pci__signal_config,
19*1c47ce69SSasha Levin 		.init		= virtio_pci__init,
20*1c47ce69SSasha Levin 	};
21*1c47ce69SSasha Levin 	return &virtio_pci_trans;
22*1c47ce69SSasha Levin };
23*1c47ce69SSasha Levin 
241599d724SSasha Levin static void virtio_pci__ioevent_callback(struct kvm *kvm, void *param)
251599d724SSasha Levin {
261599d724SSasha Levin 	struct virtio_pci_ioevent_param *ioeventfd = param;
27*1c47ce69SSasha Levin 	struct virtio_pci *vpci = ioeventfd->vtrans->virtio;
281599d724SSasha Levin 
29*1c47ce69SSasha Levin 	ioeventfd->vtrans->virtio_ops->notify_vq(kvm, vpci->dev, ioeventfd->vq);
301599d724SSasha Levin }
311599d724SSasha Levin 
32*1c47ce69SSasha Levin static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_trans *vtrans, u32 vq)
331599d724SSasha Levin {
341599d724SSasha Levin 	struct ioevent ioevent;
35*1c47ce69SSasha Levin 	struct virtio_pci *vpci = vtrans->virtio;
361599d724SSasha Levin 
371599d724SSasha Levin 	vpci->ioeventfds[vq] = (struct virtio_pci_ioevent_param) {
38*1c47ce69SSasha Levin 		.vtrans		= vtrans,
391599d724SSasha Levin 		.vq		= vq,
401599d724SSasha Levin 	};
411599d724SSasha Levin 
421599d724SSasha Levin 	ioevent = (struct ioevent) {
431599d724SSasha Levin 		.io_addr	= vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
441599d724SSasha Levin 		.io_len		= sizeof(u16),
451599d724SSasha Levin 		.fn		= virtio_pci__ioevent_callback,
461599d724SSasha Levin 		.fn_ptr		= &vpci->ioeventfds[vq],
471599d724SSasha Levin 		.datamatch	= vq,
481599d724SSasha Levin 		.fn_kvm		= kvm,
491599d724SSasha Levin 		.fd		= eventfd(0, 0),
501599d724SSasha Levin 	};
511599d724SSasha Levin 
521599d724SSasha Levin 	ioeventfd__add_event(&ioevent);
531599d724SSasha Levin 
541599d724SSasha Levin 	return 0;
551599d724SSasha Levin }
561599d724SSasha Levin 
5706f48103SSasha Levin static inline bool virtio_pci__msix_enabled(struct virtio_pci *vpci)
5806f48103SSasha Levin {
5906f48103SSasha Levin 	return vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_ENABLE;
6006f48103SSasha Levin }
6106f48103SSasha Levin 
62*1c47ce69SSasha Levin static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_trans *vtrans, u16 port,
6336f5dc91SSasha Levin 					void *data, int size, int offset)
6436f5dc91SSasha Levin {
6536f5dc91SSasha Levin 	u32 config_offset;
66*1c47ce69SSasha Levin 	struct virtio_pci *vpci = vtrans->virtio;
6706f48103SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20,
6806f48103SSasha Levin 							virtio_pci__msix_enabled(vpci),
6936f5dc91SSasha Levin 							0, &config_offset);
7036f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
7136f5dc91SSasha Levin 		switch (offset) {
7236f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
7336f5dc91SSasha Levin 			ioport__write16(data, vpci->config_vector);
7436f5dc91SSasha Levin 			break;
7536f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR:
7636f5dc91SSasha Levin 			ioport__write16(data, vpci->vq_vector[vpci->queue_selector]);
7736f5dc91SSasha Levin 			break;
7836f5dc91SSasha Levin 		};
7936f5dc91SSasha Levin 
8036f5dc91SSasha Levin 		return true;
8136f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
8236f5dc91SSasha Levin 		u8 cfg;
8336f5dc91SSasha Levin 
84*1c47ce69SSasha Levin 		cfg = vtrans->virtio_ops->get_config(kvm, vpci->dev, config_offset);
8536f5dc91SSasha Levin 		ioport__write8(data, cfg);
8636f5dc91SSasha Levin 		return true;
8736f5dc91SSasha Levin 	}
8836f5dc91SSasha Levin 
8936f5dc91SSasha Levin 	return false;
9036f5dc91SSasha Levin }
9136f5dc91SSasha Levin 
9236f5dc91SSasha Levin static bool virtio_pci__io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
9336f5dc91SSasha Levin {
9436f5dc91SSasha Levin 	unsigned long offset;
9536f5dc91SSasha Levin 	bool ret = true;
96*1c47ce69SSasha Levin 	struct virtio_trans *vtrans;
9736f5dc91SSasha Levin 	struct virtio_pci *vpci;
9836f5dc91SSasha Levin 	u32 val;
9936f5dc91SSasha Levin 
100*1c47ce69SSasha Levin 	vtrans = ioport->priv;
101*1c47ce69SSasha Levin 	vpci = vtrans->virtio;
10236f5dc91SSasha Levin 	offset = port - vpci->base_addr;
10336f5dc91SSasha Levin 
10436f5dc91SSasha Levin 	switch (offset) {
10536f5dc91SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
106*1c47ce69SSasha Levin 		val = vtrans->virtio_ops->get_host_features(kvm, vpci->dev);
10736f5dc91SSasha Levin 		ioport__write32(data, val);
10836f5dc91SSasha Levin 		break;
10936f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
110*1c47ce69SSasha Levin 		val = vtrans->virtio_ops->get_pfn_vq(kvm, vpci->dev, vpci->queue_selector);
11136f5dc91SSasha Levin 		ioport__write32(data, val);
11236f5dc91SSasha Levin 		break;
11336f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
114*1c47ce69SSasha Levin 		val = vtrans->virtio_ops->get_size_vq(kvm, vpci->dev, vpci->queue_selector);
11536f5dc91SSasha Levin 		ioport__write32(data, val);
11636f5dc91SSasha Levin 		break;
11736f5dc91SSasha Levin 		break;
11836f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
11936f5dc91SSasha Levin 		ioport__write8(data, vpci->status);
12036f5dc91SSasha Levin 		break;
12136f5dc91SSasha Levin 	case VIRTIO_PCI_ISR:
12236f5dc91SSasha Levin 		ioport__write8(data, vpci->isr);
12336f5dc91SSasha Levin 		kvm__irq_line(kvm, vpci->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
12436f5dc91SSasha Levin 		vpci->isr = VIRTIO_IRQ_LOW;
12536f5dc91SSasha Levin 		break;
12636f5dc91SSasha Levin 	default:
127*1c47ce69SSasha Levin 		ret = virtio_pci__specific_io_in(kvm, vtrans, port, data, size, offset);
12836f5dc91SSasha Levin 		break;
12936f5dc91SSasha Levin 	};
13036f5dc91SSasha Levin 
13136f5dc91SSasha Levin 	return ret;
13236f5dc91SSasha Levin }
13336f5dc91SSasha Levin 
134*1c47ce69SSasha Levin static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_trans *vtrans, u16 port,
13536f5dc91SSasha Levin 					void *data, int size, int offset)
13636f5dc91SSasha Levin {
137*1c47ce69SSasha Levin 	struct virtio_pci *vpci = vtrans->virtio;
13836f5dc91SSasha Levin 	u32 config_offset, gsi, vec;
13906f48103SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20, virtio_pci__msix_enabled(vpci),
14036f5dc91SSasha Levin 							0, &config_offset);
14136f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
14236f5dc91SSasha Levin 		switch (offset) {
14336f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
14436f5dc91SSasha Levin 			vec = vpci->config_vector = ioport__read16(data);
14536f5dc91SSasha Levin 
1461de74957SSasha Levin 			gsi = irq__add_msix_route(kvm, &vpci->msix_table[vec].msg);
14736f5dc91SSasha Levin 
14836f5dc91SSasha Levin 			vpci->config_gsi = gsi;
14936f5dc91SSasha Levin 			break;
15036f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR: {
15136f5dc91SSasha Levin 			vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);
15236f5dc91SSasha Levin 
1531de74957SSasha Levin 			gsi = irq__add_msix_route(kvm, &vpci->msix_table[vec].msg);
15436f5dc91SSasha Levin 			vpci->gsis[vpci->queue_selector] = gsi;
15536f5dc91SSasha Levin 			break;
15636f5dc91SSasha Levin 		}
15736f5dc91SSasha Levin 		};
15836f5dc91SSasha Levin 
15936f5dc91SSasha Levin 		return true;
16036f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
161*1c47ce69SSasha Levin 		vtrans->virtio_ops->set_config(kvm, vpci->dev, *(u8 *)data, config_offset);
16236f5dc91SSasha Levin 
16336f5dc91SSasha Levin 		return true;
16436f5dc91SSasha Levin 	}
16536f5dc91SSasha Levin 
16636f5dc91SSasha Levin 	return false;
16736f5dc91SSasha Levin }
16836f5dc91SSasha Levin 
16936f5dc91SSasha Levin static bool virtio_pci__io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
17036f5dc91SSasha Levin {
17136f5dc91SSasha Levin 	unsigned long offset;
17236f5dc91SSasha Levin 	bool ret = true;
173*1c47ce69SSasha Levin 	struct virtio_trans *vtrans;
17436f5dc91SSasha Levin 	struct virtio_pci *vpci;
17536f5dc91SSasha Levin 	u32 val;
17636f5dc91SSasha Levin 
177*1c47ce69SSasha Levin 	vtrans = ioport->priv;
178*1c47ce69SSasha Levin 	vpci = vtrans->virtio;
17936f5dc91SSasha Levin 	offset = port - vpci->base_addr;
18036f5dc91SSasha Levin 
18136f5dc91SSasha Levin 	switch (offset) {
18236f5dc91SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
18336f5dc91SSasha Levin 		val = ioport__read32(data);
184*1c47ce69SSasha Levin 		vtrans->virtio_ops->set_guest_features(kvm, vpci, val);
18536f5dc91SSasha Levin 		break;
18636f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
18736f5dc91SSasha Levin 		val = ioport__read32(data);
188*1c47ce69SSasha Levin 		virtio_pci__init_ioeventfd(kvm, vtrans, vpci->queue_selector);
189*1c47ce69SSasha Levin 		vtrans->virtio_ops->init_vq(kvm, vpci->dev, vpci->queue_selector, val);
19036f5dc91SSasha Levin 		break;
19136f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
19236f5dc91SSasha Levin 		vpci->queue_selector	= ioport__read16(data);
19336f5dc91SSasha Levin 		break;
19436f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
19536f5dc91SSasha Levin 		val			= ioport__read16(data);
196*1c47ce69SSasha Levin 		vtrans->virtio_ops->notify_vq(kvm, vpci->dev, val);
19736f5dc91SSasha Levin 		break;
19836f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
19936f5dc91SSasha Levin 		vpci->status		= ioport__read8(data);
20036f5dc91SSasha Levin 		break;
20136f5dc91SSasha Levin 	default:
202*1c47ce69SSasha Levin 		ret = virtio_pci__specific_io_out(kvm, vtrans, port, data, size, offset);
20336f5dc91SSasha Levin 		break;
20436f5dc91SSasha Levin 	};
20536f5dc91SSasha Levin 
20636f5dc91SSasha Levin 	return ret;
20736f5dc91SSasha Levin }
20836f5dc91SSasha Levin 
20936f5dc91SSasha Levin static struct ioport_operations virtio_pci__io_ops = {
21036f5dc91SSasha Levin 	.io_in	= virtio_pci__io_in,
21136f5dc91SSasha Levin 	.io_out	= virtio_pci__io_out,
21236f5dc91SSasha Levin };
21336f5dc91SSasha Levin 
21406f48103SSasha Levin static void callback_mmio_table(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
21536f5dc91SSasha Levin {
21636f5dc91SSasha Levin 	struct virtio_pci *vpci = ptr;
21706f48103SSasha Levin 	void *table = &vpci->msix_table;
21836f5dc91SSasha Levin 
21936f5dc91SSasha Levin 	if (is_write)
22036f5dc91SSasha Levin 		memcpy(table + addr - vpci->msix_io_block, data, len);
22136f5dc91SSasha Levin 	else
22236f5dc91SSasha Levin 		memcpy(data, table + addr - vpci->msix_io_block, len);
22336f5dc91SSasha Levin }
22436f5dc91SSasha Levin 
22506f48103SSasha Levin static void callback_mmio_pba(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
22606f48103SSasha Levin {
22706f48103SSasha Levin 	struct virtio_pci *vpci = ptr;
22806f48103SSasha Levin 	void *pba = &vpci->msix_pba;
22906f48103SSasha Levin 
23006f48103SSasha Levin 	if (is_write)
23106f48103SSasha Levin 		memcpy(pba + addr - vpci->msix_pba_block, data, len);
23206f48103SSasha Levin 	else
23306f48103SSasha Levin 		memcpy(data, pba + addr - vpci->msix_pba_block, len);
23406f48103SSasha Levin }
23506f48103SSasha Levin 
236*1c47ce69SSasha Levin int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_trans *vtrans, u32 vq)
23736f5dc91SSasha Levin {
238*1c47ce69SSasha Levin 	struct virtio_pci *vpci = vtrans->virtio;
23906f48103SSasha Levin 	int tbl = vpci->vq_vector[vq];
24036f5dc91SSasha Levin 
24106f48103SSasha Levin 	if (virtio_pci__msix_enabled(vpci)) {
24206f48103SSasha Levin 		if (vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_MASKALL ||
24306f48103SSasha Levin 			vpci->msix_table[tbl].ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
24406f48103SSasha Levin 
24506f48103SSasha Levin 			vpci->msix_pba |= 1 << tbl;
24606f48103SSasha Levin 			return 0;
24706f48103SSasha Levin 		}
24806f48103SSasha Levin 
24906f48103SSasha Levin 		kvm__irq_trigger(kvm, vpci->gsis[vq]);
25006f48103SSasha Levin 	} else {
251a36eca7bSSasha Levin 		vpci->isr = VIRTIO_IRQ_HIGH;
25206f48103SSasha Levin 		kvm__irq_trigger(kvm, vpci->pci_hdr.irq_line);
25306f48103SSasha Levin 	}
25436f5dc91SSasha Levin 	return 0;
25536f5dc91SSasha Levin }
25636f5dc91SSasha Levin 
257*1c47ce69SSasha Levin int virtio_pci__signal_config(struct kvm *kvm, struct virtio_trans *vtrans)
25836f5dc91SSasha Levin {
259*1c47ce69SSasha Levin 	struct virtio_pci *vpci = vtrans->virtio;
26006f48103SSasha Levin 	int tbl = vpci->config_vector;
26106f48103SSasha Levin 
26206f48103SSasha Levin 	if (virtio_pci__msix_enabled(vpci)) {
26306f48103SSasha Levin 		if (vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_MASKALL ||
26406f48103SSasha Levin 			vpci->msix_table[tbl].ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
26506f48103SSasha Levin 
26606f48103SSasha Levin 			vpci->msix_pba |= 1 << tbl;
26706f48103SSasha Levin 			return 0;
26806f48103SSasha Levin 		}
26906f48103SSasha Levin 
27006f48103SSasha Levin 		kvm__irq_trigger(kvm, vpci->config_gsi);
27106f48103SSasha Levin 	} else {
27206f48103SSasha Levin 		vpci->isr = VIRTIO_PCI_ISR_CONFIG;
27306f48103SSasha Levin 		kvm__irq_trigger(kvm, vpci->pci_hdr.irq_line);
27406f48103SSasha Levin 	}
27536f5dc91SSasha Levin 
27636f5dc91SSasha Levin 	return 0;
27736f5dc91SSasha Levin }
27836f5dc91SSasha Levin 
279*1c47ce69SSasha Levin int virtio_pci__init(struct kvm *kvm, struct virtio_trans *vtrans, void *dev,
280507e02d8SAsias He 			int device_id, int subsys_id, int class)
28136f5dc91SSasha Levin {
282*1c47ce69SSasha Levin 	struct virtio_pci *vpci = vtrans->virtio;
28336f5dc91SSasha Levin 	u8 pin, line, ndev;
28436f5dc91SSasha Levin 
28536f5dc91SSasha Levin 	vpci->dev = dev;
28695d13a52SSasha Levin 	vpci->msix_io_block = pci_get_io_space_block(PCI_IO_SIZE);
28795d13a52SSasha Levin 	vpci->msix_pba_block = pci_get_io_space_block(PCI_IO_SIZE);
28836f5dc91SSasha Levin 
289*1c47ce69SSasha Levin 	vpci->base_addr = ioport__register(IOPORT_EMPTY, &virtio_pci__io_ops, IOPORT_SIZE, vtrans);
29006f48103SSasha Levin 	kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, callback_mmio_table, vpci);
29106f48103SSasha Levin 	kvm__register_mmio(kvm, vpci->msix_pba_block, 0x100, callback_mmio_pba, vpci);
29236f5dc91SSasha Levin 
29336f5dc91SSasha Levin 	vpci->pci_hdr = (struct pci_device_header) {
29436f5dc91SSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
29536f5dc91SSasha Levin 		.device_id		= device_id,
29636f5dc91SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
29736f5dc91SSasha Levin 		.revision_id		= 0,
298507e02d8SAsias He 		.class			= class,
29936f5dc91SSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
30036f5dc91SSasha Levin 		.subsys_id		= subsys_id,
30136f5dc91SSasha Levin 		.bar[0]			= vpci->base_addr | PCI_BASE_ADDRESS_SPACE_IO,
30206f48103SSasha Levin 		.bar[1]			= vpci->msix_io_block | PCI_BASE_ADDRESS_SPACE_MEMORY
30306f48103SSasha Levin 					| PCI_BASE_ADDRESS_MEM_TYPE_64,
30406f48103SSasha Levin 		.bar[3]			= vpci->msix_pba_block | PCI_BASE_ADDRESS_SPACE_MEMORY
30506f48103SSasha Levin 					| PCI_BASE_ADDRESS_MEM_TYPE_64,
30636f5dc91SSasha Levin 		.status			= PCI_STATUS_CAP_LIST,
30736f5dc91SSasha Levin 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
30836f5dc91SSasha Levin 	};
30936f5dc91SSasha Levin 
31036f5dc91SSasha Levin 	vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
31136f5dc91SSasha Levin 	vpci->pci_hdr.msix.next = 0;
31214bba8a0SAsias He 	/*
31314bba8a0SAsias He 	 * We at most have VIRTIO_PCI_MAX_VQ entries for virt queue,
31414bba8a0SAsias He 	 * VIRTIO_PCI_MAX_CONFIG entries for config.
31514bba8a0SAsias He 	 *
31614bba8a0SAsias He 	 * To quote the PCI spec:
31714bba8a0SAsias He 	 *
31814bba8a0SAsias He 	 * System software reads this field to determine the
31914bba8a0SAsias He 	 * MSI-X Table Size N, which is encoded as N-1.
32014bba8a0SAsias He 	 * For example, a returned value of "00000000011"
32114bba8a0SAsias He 	 * indicates a table size of 4.
32214bba8a0SAsias He 	 */
32314bba8a0SAsias He 	vpci->pci_hdr.msix.ctrl = (VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1);
32406f48103SSasha Levin 
32506f48103SSasha Levin 	/*
32606f48103SSasha Levin 	 * Both table and PBA could be mapped on the same BAR, but for now
32706f48103SSasha Levin 	 * we're not in short of BARs
32806f48103SSasha Levin 	 */
32936f5dc91SSasha Levin 	vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
33006f48103SSasha Levin 	vpci->pci_hdr.msix.pba_offset = 3; /* Use BAR 3 */
33136f5dc91SSasha Levin 	vpci->config_vector = 0;
33236f5dc91SSasha Levin 
333d6d239beSSasha Levin 	if (irq__register_device(subsys_id, &ndev, &pin, &line) < 0)
33436f5dc91SSasha Levin 		return -1;
33536f5dc91SSasha Levin 
33636f5dc91SSasha Levin 	vpci->pci_hdr.irq_pin	= pin;
33736f5dc91SSasha Levin 	vpci->pci_hdr.irq_line	= line;
33836f5dc91SSasha Levin 	pci__register(&vpci->pci_hdr, ndev);
33936f5dc91SSasha Levin 
34036f5dc91SSasha Levin 	return 0;
34136f5dc91SSasha Levin }
342