Skip to content

Commit c3fc3e0

Browse files
yishaihAlex Williamson
authored andcommitted
virtio-pci: Introduce APIs to execute legacy IO admin commands
Introduce APIs to execute legacy IO admin commands. It includes: io_legacy_read/write for both common and the device configuration, io_legacy_notify_info. In addition, exposing an API to check whether the legacy IO commands are supported. (i.e. virtio_pci_admin_has_legacy_io()). Those APIs will be used by the next patches from this series. Note: Unlike modern drivers which support hardware virtio devices, legacy drivers assume software-based devices: e.g. they don't use proper memory barriers on ARM, use big endian on PPC, etc. X86 drivers are mostly ok though, more or less by chance. For now, only support legacy IO on X86. Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Yishai Hadas <yishaih@nvidia.com> Link: https://lore.kernel.org/r/20231219093247.170936-7-yishaih@nvidia.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
1 parent f51e146 commit c3fc3e0

5 files changed

Lines changed: 281 additions & 0 deletions

File tree

drivers/virtio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
77
obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
88
virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
99
virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
10+
virtio_pci-$(CONFIG_VIRTIO_PCI_ADMIN_LEGACY) += virtio_pci_admin_legacy_io.o
1011
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
1112
obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
1213
obj-$(CONFIG_VIRTIO_VDPA) += virtio_vdpa.o
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved
4+
*/
5+
6+
#include <linux/virtio_pci_admin.h>
7+
#include "virtio_pci_common.h"
8+
9+
/*
10+
* virtio_pci_admin_has_legacy_io - Checks whether the legacy IO
11+
* commands are supported
12+
* @dev: VF pci_dev
13+
*
14+
* Returns true on success.
15+
*/
16+
bool virtio_pci_admin_has_legacy_io(struct pci_dev *pdev)
17+
{
18+
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
19+
struct virtio_pci_device *vp_dev;
20+
21+
if (!virtio_dev)
22+
return false;
23+
24+
if (!virtio_has_feature(virtio_dev, VIRTIO_F_ADMIN_VQ))
25+
return false;
26+
27+
vp_dev = to_vp_device(virtio_dev);
28+
29+
if ((vp_dev->admin_vq.supported_cmds & VIRTIO_LEGACY_ADMIN_CMD_BITMAP) ==
30+
VIRTIO_LEGACY_ADMIN_CMD_BITMAP)
31+
return true;
32+
return false;
33+
}
34+
EXPORT_SYMBOL_GPL(virtio_pci_admin_has_legacy_io);
35+
36+
static int virtio_pci_admin_legacy_io_write(struct pci_dev *pdev, u16 opcode,
37+
u8 offset, u8 size, u8 *buf)
38+
{
39+
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
40+
struct virtio_admin_cmd_legacy_wr_data *data;
41+
struct virtio_admin_cmd cmd = {};
42+
struct scatterlist data_sg;
43+
int vf_id;
44+
int ret;
45+
46+
if (!virtio_dev)
47+
return -ENODEV;
48+
49+
vf_id = pci_iov_vf_id(pdev);
50+
if (vf_id < 0)
51+
return vf_id;
52+
53+
data = kzalloc(sizeof(*data) + size, GFP_KERNEL);
54+
if (!data)
55+
return -ENOMEM;
56+
57+
data->offset = offset;
58+
memcpy(data->registers, buf, size);
59+
sg_init_one(&data_sg, data, sizeof(*data) + size);
60+
cmd.opcode = cpu_to_le16(opcode);
61+
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
62+
cmd.group_member_id = cpu_to_le64(vf_id + 1);
63+
cmd.data_sg = &data_sg;
64+
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
65+
66+
kfree(data);
67+
return ret;
68+
}
69+
70+
/*
71+
* virtio_pci_admin_legacy_io_write_common - Write legacy common configuration
72+
* of a member device
73+
* @dev: VF pci_dev
74+
* @offset: starting byte offset within the common configuration area to write to
75+
* @size: size of the data to write
76+
* @buf: buffer which holds the data
77+
*
78+
* Note: caller must serialize access for the given device.
79+
* Returns 0 on success, or negative on failure.
80+
*/
81+
int virtio_pci_admin_legacy_common_io_write(struct pci_dev *pdev, u8 offset,
82+
u8 size, u8 *buf)
83+
{
84+
return virtio_pci_admin_legacy_io_write(pdev,
85+
VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_WRITE,
86+
offset, size, buf);
87+
}
88+
EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_common_io_write);
89+
90+
/*
91+
* virtio_pci_admin_legacy_io_write_device - Write legacy device configuration
92+
* of a member device
93+
* @dev: VF pci_dev
94+
* @offset: starting byte offset within the device configuration area to write to
95+
* @size: size of the data to write
96+
* @buf: buffer which holds the data
97+
*
98+
* Note: caller must serialize access for the given device.
99+
* Returns 0 on success, or negative on failure.
100+
*/
101+
int virtio_pci_admin_legacy_device_io_write(struct pci_dev *pdev, u8 offset,
102+
u8 size, u8 *buf)
103+
{
104+
return virtio_pci_admin_legacy_io_write(pdev,
105+
VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_WRITE,
106+
offset, size, buf);
107+
}
108+
EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_device_io_write);
109+
110+
static int virtio_pci_admin_legacy_io_read(struct pci_dev *pdev, u16 opcode,
111+
u8 offset, u8 size, u8 *buf)
112+
{
113+
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
114+
struct virtio_admin_cmd_legacy_rd_data *data;
115+
struct scatterlist data_sg, result_sg;
116+
struct virtio_admin_cmd cmd = {};
117+
int vf_id;
118+
int ret;
119+
120+
if (!virtio_dev)
121+
return -ENODEV;
122+
123+
vf_id = pci_iov_vf_id(pdev);
124+
if (vf_id < 0)
125+
return vf_id;
126+
127+
data = kzalloc(sizeof(*data), GFP_KERNEL);
128+
if (!data)
129+
return -ENOMEM;
130+
131+
data->offset = offset;
132+
sg_init_one(&data_sg, data, sizeof(*data));
133+
sg_init_one(&result_sg, buf, size);
134+
cmd.opcode = cpu_to_le16(opcode);
135+
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
136+
cmd.group_member_id = cpu_to_le64(vf_id + 1);
137+
cmd.data_sg = &data_sg;
138+
cmd.result_sg = &result_sg;
139+
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
140+
141+
kfree(data);
142+
return ret;
143+
}
144+
145+
/*
146+
* virtio_pci_admin_legacy_device_io_read - Read legacy device configuration of
147+
* a member device
148+
* @dev: VF pci_dev
149+
* @offset: starting byte offset within the device configuration area to read from
150+
* @size: size of the data to be read
151+
* @buf: buffer to hold the returned data
152+
*
153+
* Note: caller must serialize access for the given device.
154+
* Returns 0 on success, or negative on failure.
155+
*/
156+
int virtio_pci_admin_legacy_device_io_read(struct pci_dev *pdev, u8 offset,
157+
u8 size, u8 *buf)
158+
{
159+
return virtio_pci_admin_legacy_io_read(pdev,
160+
VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ,
161+
offset, size, buf);
162+
}
163+
EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_device_io_read);
164+
165+
/*
166+
* virtio_pci_admin_legacy_common_io_read - Read legacy common configuration of
167+
* a member device
168+
* @dev: VF pci_dev
169+
* @offset: starting byte offset within the common configuration area to read from
170+
* @size: size of the data to be read
171+
* @buf: buffer to hold the returned data
172+
*
173+
* Note: caller must serialize access for the given device.
174+
* Returns 0 on success, or negative on failure.
175+
*/
176+
int virtio_pci_admin_legacy_common_io_read(struct pci_dev *pdev, u8 offset,
177+
u8 size, u8 *buf)
178+
{
179+
return virtio_pci_admin_legacy_io_read(pdev,
180+
VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_READ,
181+
offset, size, buf);
182+
}
183+
EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_common_io_read);
184+
185+
/*
186+
* virtio_pci_admin_legacy_io_notify_info - Read the queue notification
187+
* information for legacy interface
188+
* @dev: VF pci_dev
189+
* @req_bar_flags: requested bar flags
190+
* @bar: on output the BAR number of the owner or member device
191+
* @bar_offset: on output the offset within bar
192+
*
193+
* Returns 0 on success, or negative on failure.
194+
*/
195+
int virtio_pci_admin_legacy_io_notify_info(struct pci_dev *pdev,
196+
u8 req_bar_flags, u8 *bar,
197+
u64 *bar_offset)
198+
{
199+
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
200+
struct virtio_admin_cmd_notify_info_result *result;
201+
struct virtio_admin_cmd cmd = {};
202+
struct scatterlist result_sg;
203+
int vf_id;
204+
int ret;
205+
206+
if (!virtio_dev)
207+
return -ENODEV;
208+
209+
vf_id = pci_iov_vf_id(pdev);
210+
if (vf_id < 0)
211+
return vf_id;
212+
213+
result = kzalloc(sizeof(*result), GFP_KERNEL);
214+
if (!result)
215+
return -ENOMEM;
216+
217+
sg_init_one(&result_sg, result, sizeof(*result));
218+
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO);
219+
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
220+
cmd.group_member_id = cpu_to_le64(vf_id + 1);
221+
cmd.result_sg = &result_sg;
222+
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
223+
if (!ret) {
224+
struct virtio_admin_cmd_notify_info_data *entry;
225+
int i;
226+
227+
ret = -ENOENT;
228+
for (i = 0; i < VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO; i++) {
229+
entry = &result->entries[i];
230+
if (entry->flags == VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_END)
231+
break;
232+
if (entry->flags != req_bar_flags)
233+
continue;
234+
*bar = entry->bar;
235+
*bar_offset = le64_to_cpu(entry->offset);
236+
ret = 0;
237+
break;
238+
}
239+
}
240+
241+
kfree(result);
242+
return ret;
243+
}
244+
EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_io_notify_info);

drivers/virtio/virtio_pci_common.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,17 @@ static struct pci_driver virtio_pci_driver = {
645645
.sriov_configure = virtio_pci_sriov_configure,
646646
};
647647

648+
struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev)
649+
{
650+
struct virtio_pci_device *pf_vp_dev;
651+
652+
pf_vp_dev = pci_iov_get_pf_drvdata(pdev, &virtio_pci_driver);
653+
if (IS_ERR(pf_vp_dev))
654+
return NULL;
655+
656+
return &pf_vp_dev->vdev;
657+
}
658+
648659
module_pci_driver(virtio_pci_driver);
649660

650661
MODULE_AUTHOR("Anthony Liguori <aliguori@us.ibm.com>");

drivers/virtio/virtio_pci_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ static inline void virtio_pci_legacy_remove(struct virtio_pci_device *vp_dev)
156156
int virtio_pci_modern_probe(struct virtio_pci_device *);
157157
void virtio_pci_modern_remove(struct virtio_pci_device *);
158158

159+
struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev);
160+
159161
#define VIRTIO_LEGACY_ADMIN_CMD_BITMAP \
160162
(BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_WRITE) | \
161163
BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_READ) | \

include/linux/virtio_pci_admin.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_VIRTIO_PCI_ADMIN_H
3+
#define _LINUX_VIRTIO_PCI_ADMIN_H
4+
5+
#include <linux/types.h>
6+
#include <linux/pci.h>
7+
8+
#ifdef CONFIG_VIRTIO_PCI_ADMIN_LEGACY
9+
bool virtio_pci_admin_has_legacy_io(struct pci_dev *pdev);
10+
int virtio_pci_admin_legacy_common_io_write(struct pci_dev *pdev, u8 offset,
11+
u8 size, u8 *buf);
12+
int virtio_pci_admin_legacy_common_io_read(struct pci_dev *pdev, u8 offset,
13+
u8 size, u8 *buf);
14+
int virtio_pci_admin_legacy_device_io_write(struct pci_dev *pdev, u8 offset,
15+
u8 size, u8 *buf);
16+
int virtio_pci_admin_legacy_device_io_read(struct pci_dev *pdev, u8 offset,
17+
u8 size, u8 *buf);
18+
int virtio_pci_admin_legacy_io_notify_info(struct pci_dev *pdev,
19+
u8 req_bar_flags, u8 *bar,
20+
u64 *bar_offset);
21+
#endif
22+
23+
#endif /* _LINUX_VIRTIO_PCI_ADMIN_H */

0 commit comments

Comments
 (0)