* Re: [PATCH v3] Add virtio-input driver.
From: Dmitry Torokhov @ 2015-03-24 17:28 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: virtio-dev, open list:ABI/API, open list, virtualization,
David Herrmann
In-Reply-To: <20150324180532-mutt-send-email-mst@redhat.com>
On Tue, Mar 24, 2015 at 06:22:36PM +0100, Michael S. Tsirkin wrote:
> On Tue, Mar 24, 2015 at 09:23:37AM -0700, Dmitry Torokhov wrote:
> > On Tue, Mar 24, 2015 at 11:36:31AM +0100, Michael S. Tsirkin wrote:
> > > On Tue, Mar 24, 2015 at 08:32:01AM +0100, Gerd Hoffmann wrote:
> > > > virtio-input is basically evdev-events-over-virtio, so this driver isn't
> > > > much more than reading configuration from config space and forwarding
> > > > incoming events to the linux input layer.
> > > >
> > > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> > >
> > > Looks pretty neat overall. I think I still see some
> > > small issues, but it's getting there.
> > >
> > > > ---
> > > > MAINTAINERS | 6 +
> > > > drivers/virtio/Kconfig | 10 ++
> > > > drivers/virtio/Makefile | 1 +
> > > > drivers/virtio/virtio_input.c | 313 ++++++++++++++++++++++++++++++++++++++
> > > > include/uapi/linux/Kbuild | 1 +
> > > > include/uapi/linux/virtio_ids.h | 1 +
> > > > include/uapi/linux/virtio_input.h | 76 +++++++++
> > > > 7 files changed, 408 insertions(+)
> > > > create mode 100644 drivers/virtio/virtio_input.c
> > > > create mode 100644 include/uapi/linux/virtio_input.h
> > > >
> > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > index 358eb01..6f233dd 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -10442,6 +10442,12 @@ S: Maintained
> > > > F: drivers/vhost/
> > > > F: include/uapi/linux/vhost.h
> > > >
> > > > +VIRTIO INPUT DRIVER
> > > > +M: Gerd Hoffmann <kraxel@redhat.com>
> > > > +S: Maintained
> > > > +F: drivers/virtio/virtio_input.c
> > > > +F: include/uapi/linux/virtio_input.h
> > > > +
> > > > VIA RHINE NETWORK DRIVER
> > > > M: Roger Luethi <rl@hellgate.ch>
> > > > S: Maintained
> > > > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> > > > index b546da5..cab9f3f 100644
> > > > --- a/drivers/virtio/Kconfig
> > > > +++ b/drivers/virtio/Kconfig
> > > > @@ -48,6 +48,16 @@ config VIRTIO_BALLOON
> > > >
> > > > If unsure, say M.
> > > >
> > > > +config VIRTIO_INPUT
> > > > + tristate "Virtio input driver"
> > > > + depends on VIRTIO
> > > > + depends on INPUT
> > > > + ---help---
> > > > + This driver supports virtio input devices such as
> > > > + keyboards, mice and tablets.
> > > > +
> > > > + If unsure, say M.
> > > > +
> > > > config VIRTIO_MMIO
> > > > tristate "Platform bus driver for memory mapped virtio devices"
> > > > depends on HAS_IOMEM
> > > > diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> > > > index d85565b..41e30e3 100644
> > > > --- a/drivers/virtio/Makefile
> > > > +++ b/drivers/virtio/Makefile
> > > > @@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
> > > > virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
> > > > virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
> > > > obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
> > > > +obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
> > > > diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
> > > > new file mode 100644
> > > > index 0000000..cf112b2
> > > > --- /dev/null
> > > > +++ b/drivers/virtio/virtio_input.c
> > > > @@ -0,0 +1,313 @@
> > > > +#include <linux/module.h>
> > > > +#include <linux/virtio.h>
> > > > +#include <linux/input.h>
> > > > +
> > > > +#include <uapi/linux/virtio_ids.h>
> > > > +#include <uapi/linux/virtio_input.h>
> > > > +
> > > > +struct virtio_input {
> > > > + struct virtio_device *vdev;
> > > > + struct input_dev *idev;
> > > > + char name[64];
> > > > + char serial[64];
> > > > + char phys[64];
> > > > + struct virtqueue *evt, *sts;
> > > > + struct virtio_input_event evts[64];
> > > > + spinlock_t lock;
> > > > +};
> > > > +
> > > > +static void virtinput_queue_evtbuf(struct virtio_input *vi,
> > > > + struct virtio_input_event *evtbuf)
> > > > +{
> > > > + struct scatterlist sg[1];
> > > > +
> > > > + sg_init_one(sg, evtbuf, sizeof(*evtbuf));
> > > > + virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
> > > > +}
> > > > +
> > > > +static void virtinput_recv_events(struct virtqueue *vq)
> > > > +{
> > > > + struct virtio_input *vi = vq->vdev->priv;
> > > > + struct virtio_input_event *event;
> > > > + unsigned long flags;
> > > > + unsigned int len;
> > > > +
> > > > + spin_lock_irqsave(&vi->lock, flags);
> > > > + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
> > > > + input_event(vi->idev,
> > > > + le16_to_cpu(event->type),
> > > > + le16_to_cpu(event->code),
> > > > + le32_to_cpu(event->value));
> > >
> > > What happens if input layer gets an
> > > unexpected event code or value?
> > > Or does something prevent it?
> > >
> > >
> > >
> > > > + virtinput_queue_evtbuf(vi, event);
> > > > + }
> > > > + virtqueue_kick(vq);
> > > > + spin_unlock_irqrestore(&vi->lock, flags);
> > > > +}
> > > > +
> > > > +static int virtinput_send_status(struct virtio_input *vi,
> > > > + u16 type, u16 code, s32 value)
> > > > +{
> > > > + struct virtio_input_event *stsbuf;
> > > > + struct scatterlist sg[1];
> > > > + unsigned long flags;
> > > > + int rc;
> > > > +
> > > > + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
> > > > + if (!stsbuf)
> > > > + return -ENOMEM;
> > > > +
> > > > + stsbuf->type = cpu_to_le16(type);
> > > > + stsbuf->code = cpu_to_le16(code);
> > > > + stsbuf->value = cpu_to_le32(value);
> > > > + sg_init_one(sg, stsbuf, sizeof(*stsbuf));
> > > > +
> > > > + spin_lock_irqsave(&vi->lock, flags);
> > > > + rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
> > > > + virtqueue_kick(vi->sts);
> > > > + spin_unlock_irqrestore(&vi->lock, flags);
> >
> > I think locking is wrong here. This is basically input_dev->event()
> > which is called with input_dev->event_lock spinlock held, and it is
> > taking vi->lock. OTOH virtinput_recv_events() takes vi->lock and then
> > calls input_event(), which will try taking input_dev->event_lock. It is
> > bound to deadlock at some point.
> >
> > I guess the easiest way would be to drop vi->lock() after fetching
> > virtio event and before calling input_event().
>
> Or just always use event_lock for event vq, leave vq->lock for
> status vq only.
>
> > > > +
> > > > + if (rc != 0)
> > > > + kfree(stsbuf);
> > > > + return rc;
> > >
> > > This means that caller will get errors if it happens to call
> > > send_status at a rate that's faster than host's consumption of them.
> > > To me this looks wrong.
> > > Poking at input layer, it seems to simply discard errors.
> > > Is it always safe to discard status updates?
> > > If yes, some kind of comment to clarify the logic would
> > > make sense IMHO.
> > >
> > >
> > >
> > > > +}
> > > > +
> > > > +static void virtinput_recv_status(struct virtqueue *vq)
> > > > +{
> > > > + struct virtio_input *vi = vq->vdev->priv;
> > > > + struct virtio_input_event *stsbuf;
> > > > + unsigned long flags;
> > > > + unsigned int len;
> > > > +
> > > > + spin_lock_irqsave(&vi->lock, flags);
> > > > + while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
> > > > + kfree(stsbuf);
> > > > + spin_unlock_irqrestore(&vi->lock, flags);
> > > > +}
> > > > +
> > > > +static int virtinput_status(struct input_dev *idev, unsigned int type,
> > > > + unsigned int code, int value)
> > > > +{
> > > > + struct virtio_input *vi = input_get_drvdata(idev);
> > > > +
> > > > + return virtinput_send_status(vi, type, code, value);
> > > > +}
> > > > +
> > > > +static size_t virtinput_cfg_select(struct virtio_input *vi,
> > > > + u8 select, u8 subsel)
> > > > +{
> > > > + u8 size;
> > > > +
> > > > + virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select);
> > > > + virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config, size, &size);
> > > > + return size;
> > > > +}
> > > > +
> > > > +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
> > > > + unsigned long *bits, unsigned int bitcount)
> > > > +{
> > > > + unsigned int bit;
> > > > + size_t bytes;
> > > > + u8 *virtio_bits;
> > > > +
> > > > + bytes = virtinput_cfg_select(vi, select, subsel);
> > > > + if (!bytes)
> > > > + return;
> > >
> > > How about limiting bytes to sizeof struct virtio_input_config->u?
> > >
> > > > + if (bitcount > bytes*8)
> > > > + bitcount = bytes*8;
> > >
> > > Space around * pls.
> > >
> > > > +
> > > > + /*
> > > > + * Bitmap in virtio config space is a simple stream of bytes,
> > > > + * with the first byte carrying bits 0-7, second bits 8-15 and
> > > > + * so on.
> > > > + */
> > > > + virtio_bits = kzalloc(bytes, GFP_KERNEL);
> > > > + if (!virtio_bits)
> > > > + return;
> > > > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > > > + virtio_bits, bytes);
> > > > + for (bit = 0; bit < bitcount; bit++) {
> > > > + if (virtio_bits[bit / 8] & (1 << (bit % 8)))
> > > > + __set_bit(bit, bits);
> > > > + }
> > > > + kfree(virtio_bits);
> > > > +
> > > > + if (select == VIRTIO_INPUT_CFG_EV_BITS)
> > > > + __set_bit(subsel, vi->idev->evbit);
> > > > +}
> > > > +
> > > > +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> > > > +{
> > > > + u32 mi, ma, re, fu, fl;
> > > > +
> > > > + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> > > > + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> > > > + input_abs_set_res(vi->idev, abs, re);
> > > > +}
> > > > +
> > > > +static int virtinput_init_vqs(struct virtio_input *vi)
> > > > +{
> > > > + struct virtqueue *vqs[2];
> > > > + vq_callback_t *cbs[] = { virtinput_recv_events,
> > > > + virtinput_recv_status };
> > > > + static const char * names[] = { "events", "status" };
> > >
> > > No space between * and names expected
> > >
> > > > + int i, err, size;
> > > > +
> > > > + err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names);
> > > > + if (err)
> > > > + return err;
> > > > + vi->evt = vqs[0];
> > > > + vi->sts = vqs[1];
> > > > +
> > > > + size = virtqueue_get_vring_size(vi->evt);
> > > > + if (size > ARRAY_SIZE(vi->evts))
> > > > + size = ARRAY_SIZE(vi->evts);
> > > > + for (i = 0; i < size; i++)
> > > > + virtinput_queue_evtbuf(vi, &vi->evts[i]);
> > > > + virtqueue_kick(vi->evt);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int virtinput_probe(struct virtio_device *vdev)
> > > > +{
> > > > + struct virtio_input *vi;
> > > > + size_t size;
> > > > + int abs, err;
> > > > +
> > > > + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
> > > > + return -ENODEV;
> > > > +
> > > > + vi = kzalloc(sizeof(*vi), GFP_KERNEL);
> > > > + if (!vi)
> > > > + return -ENOMEM;
> > > > +
> > > > + vdev->priv = vi;
> > > > + vi->vdev = vdev;
> > > > + spin_lock_init(&vi->lock);
> > > > +
> > > > + err = virtinput_init_vqs(vi);
> > > > + if (err)
> > > > + goto err_init_vq;
> > > > +
> > > > + vi->idev = input_allocate_device();
> > > > + if (!vi->idev) {
> > > > + err = -ENOMEM;
> > > > + goto err_input_alloc;
> > > > + }
> > > > + input_set_drvdata(vi->idev, vi);
> > > > +
> > > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
> > > > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > > > + vi->name, min(size, sizeof(vi->name)));
> > > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);
> > > > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > > > + vi->serial, min(size, sizeof(vi->serial)));
> > > > + snprintf(vi->phys, sizeof(vi->phys),
> > > > + "virtio%d/input0", vdev->index);
> > > > + vi->idev->name = vi->name;
> > > > + vi->idev->phys = vi->phys;
> > > > + vi->idev->uniq = vi->serial;
> > > > +
> > > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
> > > > + if (size >= 8) {
> > >
> > > What does 8 mean here? Should be sizeof virtio_input_devids?
> > >
> > > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > > + u.ids.bustype, &vi->idev->id.bustype);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > > + u.ids.vendor, &vi->idev->id.vendor);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > > + u.ids.product, &vi->idev->id.product);
> > > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > > + u.ids.version, &vi->idev->id.version);
> > > > + } else {
> > > > + vi->idev->id.bustype = BUS_VIRTUAL;
> > > > + }
> > > > +
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,
> > > > + vi->idev->propbit, INPUT_PROP_CNT);
> > > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);
> > > > + if (size)
> > > > + __set_bit(EV_REP, vi->idev->evbit);
> > > > +
> > > > + vi->idev->dev.parent = &vdev->dev;
> > > > + vi->idev->event = virtinput_status;
> > > > +
> > > > + /* device -> kernel */
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,
> > > > + vi->idev->keybit, KEY_CNT);
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,
> > > > + vi->idev->relbit, REL_CNT);
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,
> > > > + vi->idev->absbit, ABS_CNT);
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,
> > > > + vi->idev->mscbit, MSC_CNT);
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,
> > > > + vi->idev->swbit, SW_CNT);
> > > > +
> > > > + /* kernel -> device */
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,
> > > > + vi->idev->ledbit, LED_CNT);
> > > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
> > > > + vi->idev->sndbit, SND_CNT);
> > > > +
> > > > + if (test_bit(EV_ABS, vi->idev->evbit)) {
> > > > + for (abs = 0; abs < ABS_CNT; abs++) {
> > > > + if (!test_bit(abs, vi->idev->absbit))
> > > > + continue;
> > > > + virtinput_cfg_abs(vi, abs);
> > > > + }
> > > > + }
> > > > + virtio_device_ready(vdev);
> > > > +
> > > At this point you can already get interrupts.
> > > This will cause events to be forwarded.
> > > I'm guessing this is ok since you called
> > > input_allocate_device, but worth checking,
> > > and maybe adding a comment.
> >
> > Yes, it is OK to send events though yet unregistered input device, as
> > long as it was allocated with input_allocate_device().
> >
> > >
> > > > + err = input_register_device(vi->idev);
> > > > + if (err)
> > > > + goto err_input_register;
> > > > +
> > > > + return 0;
> > > > +
> > > > +err_input_register:
> > >
> > > > + input_free_device(vi->idev);
> > >
> > > At this point you can already get interrupts
> > > since you called virtio_device_ready, and
> > > getting events from a freed device likely won't
> > > DTRT.
> >
> > Right. I guess you want to mark the virtio device ready only after
> > registering input device.
>
> No that's broken since you can get events after this
> point, and you won't be able to forward them.
Who cares? What makes these events needed compared to ones sent 1 ms
earlier before we had input device registered?
But I guess if you can call virtio_device_ready/virtio_device_broken
several times then the best option is putting them into input_dev->open
and input_dev->close callbacks.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2 1/7] eeprom: Add a simple EEPROM framework for eeprom providers
From: Mark Brown @ 2015-03-24 17:23 UTC (permalink / raw)
To: Srinivas Kandagatla
Cc: linux-arm-kernel, Maxime Ripard, Rob Herring, Pawel Moll,
Kumar Gala, linux-api, linux-kernel, devicetree, Stephen Boyd,
Arnd Bergmann, Greg Kroah-Hartman, linux-arm-msm
In-Reply-To: <55112BD6.2070600@linaro.org>
[-- Attachment #1: Type: text/plain, Size: 700 bytes --]
On Tue, Mar 24, 2015 at 09:18:14AM +0000, Srinivas Kandagatla wrote:
> I did try your suggestion, by which I could remove the regmap from config.
> One thing I did not like was eeprom-core getting size/stride info directly
> from providers and regmap from regmap apis. I was wondering if we could take
> a step further and introduce new regmap helpers like
> regmap_get_size(regmap)
This is already there.
> regmap_get_stride(regmap)
> Which would be give eeprom-core the size and stride info, doing this way
> would cut down regmap related things from eeprom_config structure to minimal
> and also the source of information would come from just regmap apis.
Documentation/SubmittingPatches...
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* Re: [PATCH v3] Add virtio-input driver.
From: Michael S. Tsirkin @ 2015-03-24 17:22 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: virtio-dev, open list:ABI/API, open list, virtualization,
David Herrmann
In-Reply-To: <20150324162337.GB34117@dtor-ws>
On Tue, Mar 24, 2015 at 09:23:37AM -0700, Dmitry Torokhov wrote:
> On Tue, Mar 24, 2015 at 11:36:31AM +0100, Michael S. Tsirkin wrote:
> > On Tue, Mar 24, 2015 at 08:32:01AM +0100, Gerd Hoffmann wrote:
> > > virtio-input is basically evdev-events-over-virtio, so this driver isn't
> > > much more than reading configuration from config space and forwarding
> > > incoming events to the linux input layer.
> > >
> > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> >
> > Looks pretty neat overall. I think I still see some
> > small issues, but it's getting there.
> >
> > > ---
> > > MAINTAINERS | 6 +
> > > drivers/virtio/Kconfig | 10 ++
> > > drivers/virtio/Makefile | 1 +
> > > drivers/virtio/virtio_input.c | 313 ++++++++++++++++++++++++++++++++++++++
> > > include/uapi/linux/Kbuild | 1 +
> > > include/uapi/linux/virtio_ids.h | 1 +
> > > include/uapi/linux/virtio_input.h | 76 +++++++++
> > > 7 files changed, 408 insertions(+)
> > > create mode 100644 drivers/virtio/virtio_input.c
> > > create mode 100644 include/uapi/linux/virtio_input.h
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 358eb01..6f233dd 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -10442,6 +10442,12 @@ S: Maintained
> > > F: drivers/vhost/
> > > F: include/uapi/linux/vhost.h
> > >
> > > +VIRTIO INPUT DRIVER
> > > +M: Gerd Hoffmann <kraxel@redhat.com>
> > > +S: Maintained
> > > +F: drivers/virtio/virtio_input.c
> > > +F: include/uapi/linux/virtio_input.h
> > > +
> > > VIA RHINE NETWORK DRIVER
> > > M: Roger Luethi <rl@hellgate.ch>
> > > S: Maintained
> > > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> > > index b546da5..cab9f3f 100644
> > > --- a/drivers/virtio/Kconfig
> > > +++ b/drivers/virtio/Kconfig
> > > @@ -48,6 +48,16 @@ config VIRTIO_BALLOON
> > >
> > > If unsure, say M.
> > >
> > > +config VIRTIO_INPUT
> > > + tristate "Virtio input driver"
> > > + depends on VIRTIO
> > > + depends on INPUT
> > > + ---help---
> > > + This driver supports virtio input devices such as
> > > + keyboards, mice and tablets.
> > > +
> > > + If unsure, say M.
> > > +
> > > config VIRTIO_MMIO
> > > tristate "Platform bus driver for memory mapped virtio devices"
> > > depends on HAS_IOMEM
> > > diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> > > index d85565b..41e30e3 100644
> > > --- a/drivers/virtio/Makefile
> > > +++ b/drivers/virtio/Makefile
> > > @@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
> > > virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
> > > virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
> > > obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
> > > +obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
> > > diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
> > > new file mode 100644
> > > index 0000000..cf112b2
> > > --- /dev/null
> > > +++ b/drivers/virtio/virtio_input.c
> > > @@ -0,0 +1,313 @@
> > > +#include <linux/module.h>
> > > +#include <linux/virtio.h>
> > > +#include <linux/input.h>
> > > +
> > > +#include <uapi/linux/virtio_ids.h>
> > > +#include <uapi/linux/virtio_input.h>
> > > +
> > > +struct virtio_input {
> > > + struct virtio_device *vdev;
> > > + struct input_dev *idev;
> > > + char name[64];
> > > + char serial[64];
> > > + char phys[64];
> > > + struct virtqueue *evt, *sts;
> > > + struct virtio_input_event evts[64];
> > > + spinlock_t lock;
> > > +};
> > > +
> > > +static void virtinput_queue_evtbuf(struct virtio_input *vi,
> > > + struct virtio_input_event *evtbuf)
> > > +{
> > > + struct scatterlist sg[1];
> > > +
> > > + sg_init_one(sg, evtbuf, sizeof(*evtbuf));
> > > + virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
> > > +}
> > > +
> > > +static void virtinput_recv_events(struct virtqueue *vq)
> > > +{
> > > + struct virtio_input *vi = vq->vdev->priv;
> > > + struct virtio_input_event *event;
> > > + unsigned long flags;
> > > + unsigned int len;
> > > +
> > > + spin_lock_irqsave(&vi->lock, flags);
> > > + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
> > > + input_event(vi->idev,
> > > + le16_to_cpu(event->type),
> > > + le16_to_cpu(event->code),
> > > + le32_to_cpu(event->value));
> >
> > What happens if input layer gets an
> > unexpected event code or value?
> > Or does something prevent it?
> >
> >
> >
> > > + virtinput_queue_evtbuf(vi, event);
> > > + }
> > > + virtqueue_kick(vq);
> > > + spin_unlock_irqrestore(&vi->lock, flags);
> > > +}
> > > +
> > > +static int virtinput_send_status(struct virtio_input *vi,
> > > + u16 type, u16 code, s32 value)
> > > +{
> > > + struct virtio_input_event *stsbuf;
> > > + struct scatterlist sg[1];
> > > + unsigned long flags;
> > > + int rc;
> > > +
> > > + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
> > > + if (!stsbuf)
> > > + return -ENOMEM;
> > > +
> > > + stsbuf->type = cpu_to_le16(type);
> > > + stsbuf->code = cpu_to_le16(code);
> > > + stsbuf->value = cpu_to_le32(value);
> > > + sg_init_one(sg, stsbuf, sizeof(*stsbuf));
> > > +
> > > + spin_lock_irqsave(&vi->lock, flags);
> > > + rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
> > > + virtqueue_kick(vi->sts);
> > > + spin_unlock_irqrestore(&vi->lock, flags);
>
> I think locking is wrong here. This is basically input_dev->event()
> which is called with input_dev->event_lock spinlock held, and it is
> taking vi->lock. OTOH virtinput_recv_events() takes vi->lock and then
> calls input_event(), which will try taking input_dev->event_lock. It is
> bound to deadlock at some point.
>
> I guess the easiest way would be to drop vi->lock() after fetching
> virtio event and before calling input_event().
Or just always use event_lock for event vq, leave vq->lock for
status vq only.
> > > +
> > > + if (rc != 0)
> > > + kfree(stsbuf);
> > > + return rc;
> >
> > This means that caller will get errors if it happens to call
> > send_status at a rate that's faster than host's consumption of them.
> > To me this looks wrong.
> > Poking at input layer, it seems to simply discard errors.
> > Is it always safe to discard status updates?
> > If yes, some kind of comment to clarify the logic would
> > make sense IMHO.
> >
> >
> >
> > > +}
> > > +
> > > +static void virtinput_recv_status(struct virtqueue *vq)
> > > +{
> > > + struct virtio_input *vi = vq->vdev->priv;
> > > + struct virtio_input_event *stsbuf;
> > > + unsigned long flags;
> > > + unsigned int len;
> > > +
> > > + spin_lock_irqsave(&vi->lock, flags);
> > > + while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
> > > + kfree(stsbuf);
> > > + spin_unlock_irqrestore(&vi->lock, flags);
> > > +}
> > > +
> > > +static int virtinput_status(struct input_dev *idev, unsigned int type,
> > > + unsigned int code, int value)
> > > +{
> > > + struct virtio_input *vi = input_get_drvdata(idev);
> > > +
> > > + return virtinput_send_status(vi, type, code, value);
> > > +}
> > > +
> > > +static size_t virtinput_cfg_select(struct virtio_input *vi,
> > > + u8 select, u8 subsel)
> > > +{
> > > + u8 size;
> > > +
> > > + virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select);
> > > + virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel);
> > > + virtio_cread(vi->vdev, struct virtio_input_config, size, &size);
> > > + return size;
> > > +}
> > > +
> > > +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
> > > + unsigned long *bits, unsigned int bitcount)
> > > +{
> > > + unsigned int bit;
> > > + size_t bytes;
> > > + u8 *virtio_bits;
> > > +
> > > + bytes = virtinput_cfg_select(vi, select, subsel);
> > > + if (!bytes)
> > > + return;
> >
> > How about limiting bytes to sizeof struct virtio_input_config->u?
> >
> > > + if (bitcount > bytes*8)
> > > + bitcount = bytes*8;
> >
> > Space around * pls.
> >
> > > +
> > > + /*
> > > + * Bitmap in virtio config space is a simple stream of bytes,
> > > + * with the first byte carrying bits 0-7, second bits 8-15 and
> > > + * so on.
> > > + */
> > > + virtio_bits = kzalloc(bytes, GFP_KERNEL);
> > > + if (!virtio_bits)
> > > + return;
> > > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > > + virtio_bits, bytes);
> > > + for (bit = 0; bit < bitcount; bit++) {
> > > + if (virtio_bits[bit / 8] & (1 << (bit % 8)))
> > > + __set_bit(bit, bits);
> > > + }
> > > + kfree(virtio_bits);
> > > +
> > > + if (select == VIRTIO_INPUT_CFG_EV_BITS)
> > > + __set_bit(subsel, vi->idev->evbit);
> > > +}
> > > +
> > > +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> > > +{
> > > + u32 mi, ma, re, fu, fl;
> > > +
> > > + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> > > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> > > + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> > > + input_abs_set_res(vi->idev, abs, re);
> > > +}
> > > +
> > > +static int virtinput_init_vqs(struct virtio_input *vi)
> > > +{
> > > + struct virtqueue *vqs[2];
> > > + vq_callback_t *cbs[] = { virtinput_recv_events,
> > > + virtinput_recv_status };
> > > + static const char * names[] = { "events", "status" };
> >
> > No space between * and names expected
> >
> > > + int i, err, size;
> > > +
> > > + err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names);
> > > + if (err)
> > > + return err;
> > > + vi->evt = vqs[0];
> > > + vi->sts = vqs[1];
> > > +
> > > + size = virtqueue_get_vring_size(vi->evt);
> > > + if (size > ARRAY_SIZE(vi->evts))
> > > + size = ARRAY_SIZE(vi->evts);
> > > + for (i = 0; i < size; i++)
> > > + virtinput_queue_evtbuf(vi, &vi->evts[i]);
> > > + virtqueue_kick(vi->evt);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int virtinput_probe(struct virtio_device *vdev)
> > > +{
> > > + struct virtio_input *vi;
> > > + size_t size;
> > > + int abs, err;
> > > +
> > > + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
> > > + return -ENODEV;
> > > +
> > > + vi = kzalloc(sizeof(*vi), GFP_KERNEL);
> > > + if (!vi)
> > > + return -ENOMEM;
> > > +
> > > + vdev->priv = vi;
> > > + vi->vdev = vdev;
> > > + spin_lock_init(&vi->lock);
> > > +
> > > + err = virtinput_init_vqs(vi);
> > > + if (err)
> > > + goto err_init_vq;
> > > +
> > > + vi->idev = input_allocate_device();
> > > + if (!vi->idev) {
> > > + err = -ENOMEM;
> > > + goto err_input_alloc;
> > > + }
> > > + input_set_drvdata(vi->idev, vi);
> > > +
> > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
> > > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > > + vi->name, min(size, sizeof(vi->name)));
> > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);
> > > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > > + vi->serial, min(size, sizeof(vi->serial)));
> > > + snprintf(vi->phys, sizeof(vi->phys),
> > > + "virtio%d/input0", vdev->index);
> > > + vi->idev->name = vi->name;
> > > + vi->idev->phys = vi->phys;
> > > + vi->idev->uniq = vi->serial;
> > > +
> > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
> > > + if (size >= 8) {
> >
> > What does 8 mean here? Should be sizeof virtio_input_devids?
> >
> > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > + u.ids.bustype, &vi->idev->id.bustype);
> > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > + u.ids.vendor, &vi->idev->id.vendor);
> > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > + u.ids.product, &vi->idev->id.product);
> > > + virtio_cread(vi->vdev, struct virtio_input_config,
> > > + u.ids.version, &vi->idev->id.version);
> > > + } else {
> > > + vi->idev->id.bustype = BUS_VIRTUAL;
> > > + }
> > > +
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,
> > > + vi->idev->propbit, INPUT_PROP_CNT);
> > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);
> > > + if (size)
> > > + __set_bit(EV_REP, vi->idev->evbit);
> > > +
> > > + vi->idev->dev.parent = &vdev->dev;
> > > + vi->idev->event = virtinput_status;
> > > +
> > > + /* device -> kernel */
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,
> > > + vi->idev->keybit, KEY_CNT);
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,
> > > + vi->idev->relbit, REL_CNT);
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,
> > > + vi->idev->absbit, ABS_CNT);
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,
> > > + vi->idev->mscbit, MSC_CNT);
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,
> > > + vi->idev->swbit, SW_CNT);
> > > +
> > > + /* kernel -> device */
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,
> > > + vi->idev->ledbit, LED_CNT);
> > > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
> > > + vi->idev->sndbit, SND_CNT);
> > > +
> > > + if (test_bit(EV_ABS, vi->idev->evbit)) {
> > > + for (abs = 0; abs < ABS_CNT; abs++) {
> > > + if (!test_bit(abs, vi->idev->absbit))
> > > + continue;
> > > + virtinput_cfg_abs(vi, abs);
> > > + }
> > > + }
> > > + virtio_device_ready(vdev);
> > > +
> > At this point you can already get interrupts.
> > This will cause events to be forwarded.
> > I'm guessing this is ok since you called
> > input_allocate_device, but worth checking,
> > and maybe adding a comment.
>
> Yes, it is OK to send events though yet unregistered input device, as
> long as it was allocated with input_allocate_device().
>
> >
> > > + err = input_register_device(vi->idev);
> > > + if (err)
> > > + goto err_input_register;
> > > +
> > > + return 0;
> > > +
> > > +err_input_register:
> >
> > > + input_free_device(vi->idev);
> >
> > At this point you can already get interrupts
> > since you called virtio_device_ready, and
> > getting events from a freed device likely won't
> > DTRT.
>
> Right. I guess you want to mark the virtio device ready only after
> registering input device.
No that's broken since you can get events after this
point, and you won't be able to forward them.
> >
> > > +err_input_alloc:
> > > + vdev->config->del_vqs(vdev);
> > > +err_init_vq:
> > > + kfree(vi);
> > > + return err;
> > > +}
> > > +
> > > +static void virtinput_remove(struct virtio_device *vdev)
> > > +{
> > > + struct virtio_input *vi = vdev->priv;
> > > +
> > > + input_unregister_device(vi->idev);
> >
> > same thing here, you might get an event at this point.
> > You need to somehow block new events
> > being sent to device while keeping
> > device around.
> >
> > Since you already do everything under a spinlock,
> > it's probably easiest to add a flag discarding
> > recv events. You can then check it in virtinput_recv_events
> > before calling input_event.
>
> Instead of checking the flag is it possible to pause virio device? Maybe
> virtio_break_device()?
>
> >
> >
> >
> > > + vdev->config->del_vqs(vdev);
> > > + kfree(vi);
> > > +}
> > > +
> > > +static unsigned int features[] = {
> > > +};
> > > +static struct virtio_device_id id_table[] = {
> > > + { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },
> > > + { 0 },
> > > +};
> > > +
> > > +static struct virtio_driver virtio_input_driver = {
> > > + .driver.name = KBUILD_MODNAME,
> > > + .driver.owner = THIS_MODULE,
> > > + .feature_table = features,
> > > + .feature_table_size = ARRAY_SIZE(features),
> > > + .id_table = id_table,
> > > + .probe = virtinput_probe,
> > > + .remove = virtinput_remove,
> >
> > I note this driver doesn't seem to handle hybernation,
> > that's probably a bug?
> >
> >
> > > +};
> > > +
> > > +module_virtio_driver(virtio_input_driver);
> > > +MODULE_DEVICE_TABLE(virtio, id_table);
> > > +
> > > +MODULE_LICENSE("GPL");
> > > +MODULE_DESCRIPTION("Virtio input device driver");
> > > +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
> > > diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> > > index 68ceb97..04b829e 100644
> > > --- a/include/uapi/linux/Kbuild
> > > +++ b/include/uapi/linux/Kbuild
> > > @@ -430,6 +430,7 @@ header-y += virtio_blk.h
> > > header-y += virtio_config.h
> > > header-y += virtio_console.h
> > > header-y += virtio_ids.h
> > > +header-y += virtio_input.h
> > > header-y += virtio_net.h
> > > header-y += virtio_pci.h
> > > header-y += virtio_ring.h
> > > diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> > > index 284fc3a..5f60aa4 100644
> > > --- a/include/uapi/linux/virtio_ids.h
> > > +++ b/include/uapi/linux/virtio_ids.h
> > > @@ -39,5 +39,6 @@
> > > #define VIRTIO_ID_9P 9 /* 9p virtio console */
> > > #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
> > > #define VIRTIO_ID_CAIF 12 /* Virtio caif */
> > > +#define VIRTIO_ID_INPUT 18 /* virtio input */
> > >
> > > #endif /* _LINUX_VIRTIO_IDS_H */
> > > diff --git a/include/uapi/linux/virtio_input.h b/include/uapi/linux/virtio_input.h
> > > new file mode 100644
> > > index 0000000..7fceabd
> > > --- /dev/null
> > > +++ b/include/uapi/linux/virtio_input.h
> > > @@ -0,0 +1,76 @@
> > > +#ifndef _LINUX_VIRTIO_INPUT_H
> > > +#define _LINUX_VIRTIO_INPUT_H
> > > +/* This header is BSD licensed so anyone can use the definitions to implement
> > > + * compatible drivers/servers.
> > > + *
> > > + * Redistribution and use in source and binary forms, with or without
> > > + * modification, are permitted provided that the following conditions
> > > + * are met:
> > > + * 1. Redistributions of source code must retain the above copyright
> > > + * notice, this list of conditions and the following disclaimer.
> > > + * 2. Redistributions in binary form must reproduce the above copyright
> > > + * notice, this list of conditions and the following disclaimer in the
> > > + * documentation and/or other materials provided with the distribution.
> > > + * 3. Neither the name of IBM nor the names of its contributors
> > > + * may be used to endorse or promote products derived from this software
> > > + * without specific prior written permission.
> > > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> > > + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> > > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> > > + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
> > > + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> > > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> > > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> > > + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> > > + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> > > + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> > > + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > > + * SUCH DAMAGE. */
> > > +#include <linux/virtio_ids.h>
> > > +#include <linux/virtio_config.h>
> > > +
> > > +enum virtio_input_config_select {
> > > + VIRTIO_INPUT_CFG_UNSET = 0x00,
> > > + VIRTIO_INPUT_CFG_ID_NAME = 0x01,
> > > + VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
> > > + VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
> > > + VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
> > > + VIRTIO_INPUT_CFG_EV_BITS = 0x11,
> > > + VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
> > > +};
> > > +
> > > +struct virtio_input_absinfo {
> > > + __virtio32 min;
> > > + __virtio32 max;
> > > + __virtio32 fuzz;
> > > + __virtio32 flat;
> > > + __virtio32 res;
> > > +};
> > > +
> > > +struct virtio_input_devids {
> > > + __virtio16 bustype;
> > > + __virtio16 vendor;
> > > + __virtio16 product;
> > > + __virtio16 version;
> > > +};
> > > +
> >
> > this padding bt two spaces looks weird.
> >
> > > +struct virtio_input_config {
> > > + __u8 select;
> > > + __u8 subsel;
> > > + __u8 size;
> > > + __u8 reserved;
> > > + union {
> > > + char string[128];
> > > + __u8 bitmap[128];
> >
> > I note that neither string nor bitmap are used by
> > driver. What are they in aid of?
>
> Also, what happens if we need more than 1024 bits to pass bitmap data?
> We might get there with keyboards.
>
> >
> > > + struct virtio_input_absinfo abs;
> > > + struct virtio_input_devids ids;
> > > + } u;
> > > +};
> > > +
> > > +struct virtio_input_event {
> > > + __le16 type;
> > > + __le16 code;
> > > + __le32 value;
> > > +};
> > > +
> > > +#endif /* _LINUX_VIRTIO_INPUT_H */
> > > --
> > > 1.8.3.1
>
> Thanks.
>
> --
> Dmitry
^ permalink raw reply
* Re: [PATCH v3 10/15] serial: stm32-usart: Add STM32 USART Driver
From: Maxime Coquelin @ 2015-03-24 17:21 UTC (permalink / raw)
To: Peter Hurley
Cc: Andy Shevchenko, Uwe Kleine-König, Andreas Färber,
Geert Uytterhoeven, Rob Herring, Philipp Zabel, Linus Walleij,
Arnd Bergmann, Stefan Agner, Peter Meerwald, Paul Bolle,
Jonathan Corbet, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, Russell King, Daniel Lezcano, Thomas Gleixner,
Greg Kroah-Hartman, Jiri Slaby, Andrew Morton
In-Reply-To: <CALszF6DX2pz_US6uqQwBjmZhfgjw5PP1UAy9H+c-_Kgkk_Z72w@mail.gmail.com>
Hi Peter,
2015-03-19 18:35 GMT+01:00 Maxime Coquelin <mcoquelin.stm32@gmail.com>:
> 2015-03-19 15:58 GMT+01:00 Peter Hurley <peter@hurleysoftware.com>:
>> On 03/19/2015 09:55 AM, Maxime Coquelin wrote:
>>>>>>> +static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
>>>>>>> + struct ktermios *old)
>> [...]
>>>>>>> + usardiv = (port->uartclk * 25) / (baud * 4);
>>>>>>> + mantissa = (usardiv / 100) << USART_BRR_DIV_M_SHIFT;
>>>>>>> + fraction = DIV_ROUND_CLOSEST((usardiv % 100) * 16, 100);
>>>>>>> + if (fraction & ~USART_BRR_DIV_F_MASK) {
>>>>>>> + fraction = 0;
>>>>>>> + mantissa += (1 << USART_BRR_DIV_M_SHIFT);
>>>>>>> + }
>> [...]
>>> Really, I would prefer keeping this fractional divider as it is
>>> implemented today.
>>
>> You have to admit that's basically an unintelligible mess;
>> how would anyone ever be able to refactor and replace that with a
>> common divider implementation?
>>
>> At the very least, please comment on the formula and format.
>
> Ok, I will refactor the implementation, and comment it.
The implementation was indeed a mess.
I found some time to refactor the code, and also added support for 8
times oversampling (16 by default).
It will allow to achieve higher speeds, with the side effect of being
less tolerant to clock deviations.
What do you think about the code below?
usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
/*
* The USART supports 16 or 8 times oversampling.
* By default we prefer 16 times oversampling, so that the receiver
* has a better tolerance to clock deviations.
* 8 times oversampling is only used to achieve higher speeds.
*/
if (usartdiv < 16) {
oversampling = 8;
stm32_set_bits(port, USART_CR1, USART_CR1_OVER8);
} else {
oversampling = 16;
stm32_clr_bits(port, USART_CR1, USART_CR1_OVER8);
}
mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT;
fraction = usartdiv % oversampling;
writel_relaxed(mantissa | fraction, port->membase + USART_BRR)
Thanks,
Maxime
>
> Regards,
> Maxime
>
>>
>> Regards,
>> Peter Hurley
^ permalink raw reply
* Re: [PATCH 13/14] kdbus: add walk-through user space example
From: David Herrmann @ 2015-03-24 17:15 UTC (permalink / raw)
To: Jiri Slaby
Cc: Greg Kroah-Hartman, Arnd Bergmann, Eric W. Biederman,
One Thousand Gnomes, Tom Gundersen, Jiri Kosina, Andy Lutomirski,
Linux API, linux-kernel, Daniel Mack, Djalal Harouni
In-Reply-To: <551194C8.4070707-AlSwsSmVLrQ@public.gmane.org>
Hi
On Tue, Mar 24, 2015 at 5:46 PM, Jiri Slaby <jslaby-AlSwsSmVLrQ@public.gmane.org> wrote:
> On 03/09/2015, 02:09 PM, Greg Kroah-Hartman wrote:
>> --- /dev/null
>> +++ b/samples/kdbus/Makefile
>> @@ -0,0 +1,10 @@
>> +# kbuild trick to avoid linker error. Can be omitted if a module is built.
>> +obj- := dummy.o
>> +
>> +hostprogs-y += kdbus-workers
>> +
>> +always := $(hostprogs-y)
>
> Errr, no. Not only it causes build failures (even with KDBUS=n), it
> definitely should not be built for everyone.
It's only built if CONFIG_SAMPLES is set, right?
What build-failures does it cause? linux/kdbus.h is not optional based
on CONFIG_KDBUS, so the samples should build just fine. Can you tell
me what kind of errors you get? The kbuild-robots didn't report
anything so far.
> And why is it a host prog? It's a sample prog for the kernel I am
> building, i.e. for the destination arch, like all the other samples.
It's modeled after the other user-space examples in ./samples/, which
all use hostprogs (see samples/{bpf,hidraw,seccomp,uhid}/Makefile). I
have no idea how to build programs that run on the target
architecture. Documentation/kbuild/makefiles.txt doesn't list it,
which is, I guess, the reason why everyone used hostprogs so far. And
given that autotools calls the target architecture "--host", I
actually thought this is what hostprogs does.. apparently that's not
the case, sorry.
Thanks
David
^ permalink raw reply
* Re: [PATCH] Add virtio gpu driver.
From: Michael S. Tsirkin @ 2015-03-24 17:04 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, open list:ABI/API, Rusty Russell, open list,
open list:DRM DRIVERS, open list:VIRTIO CORE, NET..., Dave Airlie
In-Reply-To: <1427213239-8775-1-git-send-email-kraxel@redhat.com>
On Tue, Mar 24, 2015 at 05:07:18PM +0100, Gerd Hoffmann wrote:
> From: Dave Airlie <airlied@gmail.com>
>
> This patch adds a kms driver for the virtio gpu. The xorg modesetting
> driver can handle the device just fine, the framebuffer for fbcon is
> there too.
>
> Qemu patches for the host side are under review currently.
>
> The pci version of the device comes in two variants: with and without
> vga compatibility. The former has a extra memory bar for the vga
> framebuffer, the later is a pure virtio device. The only concern for
> this driver is that in the virtio-vga case we have to kick out the
> firmware framebuffer.
>
> Initial revision has only 2d support, 3d (virgl) support requires
> some more work on the qemu side and will be added later.
>
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
I did a quick scan of the code, below are mostly cosmetic
issues.
> ---
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/virtio/Kconfig | 11 +
> drivers/gpu/drm/virtio/Makefile | 9 +
> drivers/gpu/drm/virtio/virtgpu_debugfs.c | 64 ++++
> drivers/gpu/drm/virtio/virtgpu_display.c | 527 ++++++++++++++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 68 ++++
> drivers/gpu/drm/virtio/virtgpu_drv.c | 132 ++++++++
> drivers/gpu/drm/virtio/virtgpu_drv.h | 326 +++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_fb.c | 415 ++++++++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_fence.c | 95 ++++++
> drivers/gpu/drm/virtio/virtgpu_gem.c | 120 +++++++
> drivers/gpu/drm/virtio/virtgpu_kms.c | 125 +++++++
> drivers/gpu/drm/virtio/virtgpu_object.c | 174 ++++++++++
> drivers/gpu/drm/virtio/virtgpu_ttm.c | 451 ++++++++++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_vq.c | 540 +++++++++++++++++++++++++++++++
> drivers/virtio/virtio_pci_common.c | 2 +-
> include/drm/drmP.h | 1 +
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/virtio_gpu.h | 203 ++++++++++++
> include/uapi/linux/virtio_ids.h | 2 +-
> 21 files changed, 3267 insertions(+), 2 deletions(-)
> create mode 100644 drivers/gpu/drm/virtio/Kconfig
> create mode 100644 drivers/gpu/drm/virtio/Makefile
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_debugfs.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_display.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.h
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_fb.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_fence.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_gem.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_kms.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_object.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_ttm.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_vq.c
> create mode 100644 include/uapi/linux/virtio_gpu.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 151a050..f2388ea 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -197,6 +197,8 @@ source "drivers/gpu/drm/qxl/Kconfig"
>
> source "drivers/gpu/drm/bochs/Kconfig"
>
> +source "drivers/gpu/drm/virtio/Kconfig"
> +
> source "drivers/gpu/drm/msm/Kconfig"
>
> source "drivers/gpu/drm/tegra/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 2c239b9..083d443 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/
> obj-$(CONFIG_DRM_TILCDC) += tilcdc/
> obj-$(CONFIG_DRM_QXL) += qxl/
> obj-$(CONFIG_DRM_BOCHS) += bochs/
> +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
> obj-$(CONFIG_DRM_MSM) += msm/
> obj-$(CONFIG_DRM_TEGRA) += tegra/
> obj-$(CONFIG_DRM_STI) += sti/
> diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig
> new file mode 100644
> index 0000000..55868e2
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/Kconfig
> @@ -0,0 +1,11 @@
> +config DRM_VIRTIO_GPU
> + tristate "QEMU Virtio GPU"
I think it should be "Virtio GPU driver".
> + depends on DRM && VIRTIO
> + select FB_SYS_FILLRECT
> + select FB_SYS_COPYAREA
> + select FB_SYS_IMAGEBLIT
> + select DRM_KMS_HELPER
> + select DRM_KMS_FB_HELPER
> + select DRM_TTM
> + help
> + QEMU based virtio GPU.
How about:
This is the virtual GPU driver for virtio. It can be used with
lguest or QEMU based VMMs (like KVM or Xen). Say Y or M.
> diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
> new file mode 100644
> index 0000000..57d59ee
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Makefile for the drm device driver. This driver provides support for the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +ccflags-y := -Iinclude/drm
> +
> +virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o
> +
> +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
Are cflags hacks and long makefile lines the norm for drm?
If yes this is fine.
> diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
> new file mode 100644
> index 0000000..dbc497d
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
> @@ -0,0 +1,64 @@
> +/*
> + * Copyright (C) 2009 Red Hat
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial
> + * portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
> + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +#include <linux/debugfs.h>
> +
> +#include "drmP.h"
> +#include "virtgpu_drv.h"
> +
> +static int
> +virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data)
> +{
> + struct drm_info_node *node = (struct drm_info_node *) m->private;
> + struct virtio_gpu_device *vgdev = node->minor->dev->dev_private;
> +
> + seq_printf(m, "fence %ld %lld\n",
> + atomic64_read(&vgdev->fence_drv.last_seq),
> + vgdev->fence_drv.sync_seq);
> + return 0;
> +}
> +
> +static struct drm_info_list virtio_gpu_debugfs_list[] = {
> + { "irq_fence", virtio_gpu_debugfs_irq_info, 0, NULL },
> +};
> +
> +#define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list)
> +
> +int
> +virtio_gpu_debugfs_init(struct drm_minor *minor)
> +{
> + drm_debugfs_create_files(virtio_gpu_debugfs_list,
> + VIRTIO_GPU_DEBUGFS_ENTRIES,
> + minor->debugfs_root, minor);
> + return 0;
> +}
> +
> +void
> +virtio_gpu_debugfs_takedown(struct drm_minor *minor)
> +{
> + drm_debugfs_remove_files(virtio_gpu_debugfs_list,
> + VIRTIO_GPU_DEBUGFS_ENTRIES,
> + minor);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
> new file mode 100644
> index 0000000..578a02c
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_display.c
> @@ -0,0 +1,527 @@
> +/*
> + * Copyright 2013 Red Hat Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors: Dave Airlie
> + * Alon Levy
> + */
> +
> +#include "virtgpu_drv.h"
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#define XRES_MIN 320
> +#define YRES_MIN 200
> +
> +#define XRES_DEF 1024
> +#define YRES_DEF 768
> +
> +#define XRES_MAX 8192
> +#define YRES_MAX 8192
> +
> +static void virtio_gpu_crtc_gamma_set(struct drm_crtc *crtc,
> + u16 *red, u16 *green, u16 *blue,
> + uint32_t start, uint32_t size)
> +{
> + /* TODO */
> +}
> +
> +static void
> +virtio_gpu_hide_cursor(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_output *output)
> +{
> + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
> + output->cursor.resource_id = 0;
> + virtio_gpu_cursor_ping(vgdev, output);
> +}
> +
> +static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc,
> + struct drm_file *file_priv,
> + uint32_t handle,
> + uint32_t width,
> + uint32_t height,
> + int32_t hot_x, int32_t hot_y)
> +{
> + struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
> + struct virtio_gpu_output *output =
> + container_of(crtc, struct virtio_gpu_output, crtc);
> + struct drm_gem_object *gobj = NULL;
> + struct virtio_gpu_object *qobj = NULL;
> + struct virtio_gpu_fence *fence = NULL;
> + int ret = 0;
> +
> + if (handle == 0) {
> + virtio_gpu_hide_cursor(vgdev, output);
> + return 0;
> + }
> +
> + /* lookup the cursor */
> + gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
> + if (gobj == NULL)
> + return -ENOENT;
> +
> + qobj = gem_to_virtio_gpu_obj(gobj);
> +
> + if (!qobj->hw_res_handle) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ret = virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0,
> + cpu_to_le32(64),
> + cpu_to_le32(64),
> + 0, 0, &fence);
> + if (!ret) {
> + reservation_object_add_excl_fence(qobj->tbo.resv,
> + &fence->f);
> + virtio_gpu_object_wait(qobj, false);
> + }
> +
> + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
> + output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle);
> + output->cursor.hot_x = cpu_to_le32(hot_x);
> + output->cursor.hot_y = cpu_to_le32(hot_y);
> + virtio_gpu_cursor_ping(vgdev, output);
> +out:
> + drm_gem_object_unreference_unlocked(gobj);
> + return ret;
> +}
> +
> +static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc,
> + int x, int y)
> +{
> + struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
> + struct virtio_gpu_output *output =
> + container_of(crtc, struct virtio_gpu_output, crtc);
> +
> + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR);
> + output->cursor.pos.x = cpu_to_le32(x);
> + output->cursor.pos.y = cpu_to_le32(y);
> + virtio_gpu_cursor_ping(vgdev, output);
> + return 0;
> +}
> +
> +static int virtio_gpu_crtc_page_flip(struct drm_crtc *crtc,
> + struct drm_framebuffer *fb,
> + struct drm_pending_vblank_event *event,
> + uint32_t flags)
> +{
> + return -EINVAL;
> +}
> +
> +
> +static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
> + .cursor_set2 = virtio_gpu_crtc_cursor_set,
> + .cursor_move = virtio_gpu_crtc_cursor_move,
> + .gamma_set = virtio_gpu_crtc_gamma_set,
> + .set_config = drm_crtc_helper_set_config,
> + .page_flip = virtio_gpu_crtc_page_flip,
> + .destroy = drm_crtc_cleanup,
> +};
> +
> +static void virtio_gpu_user_framebuffer_destroy(struct drm_framebuffer *fb)
> +{
> + struct virtio_gpu_framebuffer *virtio_gpu_fb
> + = to_virtio_gpu_framebuffer(fb);
> +
> + if (virtio_gpu_fb->obj)
> + drm_gem_object_unreference_unlocked(virtio_gpu_fb->obj);
> + drm_framebuffer_cleanup(fb);
> + kfree(virtio_gpu_fb);
> +}
> +
> +static int
> +virtio_gpu_framebuffer_surface_dirty(struct drm_framebuffer *fb,
> + struct drm_file *file_priv,
> + unsigned flags, unsigned color,
> + struct drm_clip_rect *clips,
> + unsigned num_clips)
> +{
> + struct virtio_gpu_framebuffer *virtio_gpu_fb
> + = to_virtio_gpu_framebuffer(fb);
> +
> + return virtio_gpu_surface_dirty(virtio_gpu_fb, clips, num_clips);
> +}
> +
> +static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
> + .destroy = virtio_gpu_user_framebuffer_destroy,
> + .dirty = virtio_gpu_framebuffer_surface_dirty,
> +};
> +
> +int
> +virtio_gpu_framebuffer_init(struct drm_device *dev,
> + struct virtio_gpu_framebuffer *vgfb,
> + struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object *obj)
> +{
> + int ret;
> + struct virtio_gpu_object *bo;
> + vgfb->obj = obj;
> +
> + bo = gem_to_virtio_gpu_obj(obj);
> +
> + ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs);
> + if (ret) {
> + vgfb->obj = NULL;
> + return ret;
> + }
> + drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd);
> +
> + spin_lock_init(&vgfb->dirty_lock);
> + vgfb->x1 = vgfb->y1 = INT_MAX;
> + vgfb->x2 = vgfb->y2 = 0;
> + return 0;
> +}
> +
> +static void virtio_gpu_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +}
> +
> +static bool virtio_gpu_crtc_mode_fixup(struct drm_crtc *crtc,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + return true;
> +}
> +
> +static int virtio_gpu_crtc_mode_set(struct drm_crtc *crtc,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode,
> + int x, int y,
> + struct drm_framebuffer *old_fb)
> +{
> + struct drm_device *dev = crtc->dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct virtio_gpu_framebuffer *vgfb;
> + struct virtio_gpu_object *bo, *old_bo = NULL;
> + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
> +
> + if (!crtc->primary->fb) {
> + DRM_DEBUG_KMS("No FB bound\n");
> + return 0;
> + }
> +
> + if (old_fb) {
> + vgfb = to_virtio_gpu_framebuffer(old_fb);
> + old_bo = gem_to_virtio_gpu_obj(vgfb->obj);
> + }
> + vgfb = to_virtio_gpu_framebuffer(crtc->primary->fb);
> + bo = gem_to_virtio_gpu_obj(vgfb->obj);
> + DRM_DEBUG("+%d+%d (%d,%d) => (%d,%d)\n",
> + x, y,
> + mode->hdisplay, mode->vdisplay,
> + adjusted_mode->hdisplay,
> + adjusted_mode->vdisplay);
> +
> + virtio_gpu_cmd_set_scanout(vgdev, output->index, bo->hw_res_handle,
> + mode->hdisplay, mode->vdisplay, x, y);
> +
> + return 0;
> +}
> +
> +static void virtio_gpu_crtc_prepare(struct drm_crtc *crtc)
> +{
> + DRM_DEBUG("current: %dx%d+%d+%d (%d).\n",
> + crtc->mode.hdisplay, crtc->mode.vdisplay,
> + crtc->x, crtc->y, crtc->enabled);
> +}
> +
> +static void virtio_gpu_crtc_commit(struct drm_crtc *crtc)
> +{
> + DRM_DEBUG("\n");
> +}
> +
> +static void virtio_gpu_crtc_load_lut(struct drm_crtc *crtc)
> +{
> +}
> +
> +static void virtio_gpu_crtc_disable(struct drm_crtc *crtc)
> +{
> + struct drm_device *dev = crtc->dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
> +
> + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0);
> +}
> +
> +static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
> + .disable = virtio_gpu_crtc_disable,
> + .dpms = virtio_gpu_crtc_dpms,
> + .mode_fixup = virtio_gpu_crtc_mode_fixup,
> + .mode_set = virtio_gpu_crtc_mode_set,
> + .prepare = virtio_gpu_crtc_prepare,
> + .commit = virtio_gpu_crtc_commit,
> + .load_lut = virtio_gpu_crtc_load_lut,
> +};
> +
> +static void virtio_gpu_enc_dpms(struct drm_encoder *encoder, int mode)
> +{
> +}
> +
> +static bool virtio_gpu_enc_mode_fixup(struct drm_encoder *encoder,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + return true;
> +}
> +
> +static void virtio_gpu_enc_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void virtio_gpu_enc_commit(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static int virtio_gpu_conn_get_modes(struct drm_connector *connector)
> +{
> + struct virtio_gpu_output *output =
> + drm_connector_to_virtio_gpu_output(connector);
> + struct drm_display_mode *mode = NULL;
> + int count, width, height;
> +
> + width = le32_to_cpu(output->info.r.width);
> + height = le32_to_cpu(output->info.r.height);
> + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
> +
> + if (width == 0 || height == 0) {
> + width = XRES_DEF;
> + height = YRES_DEF;
> + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
> + } else {
> + DRM_DEBUG("add mode: %dx%d\n", width, height);
> + mode = drm_cvt_mode(connector->dev, width, height, 60,
> + false, false, false);
> + mode->type |= DRM_MODE_TYPE_PREFERRED;
> + drm_mode_probed_add(connector, mode);
> + count++;
> + }
> +
> + return count;
> +}
> +
> +static int virtio_gpu_conn_mode_valid(struct drm_connector *connector,
> + struct drm_display_mode *mode)
> +{
> + struct virtio_gpu_output *output =
> + drm_connector_to_virtio_gpu_output(connector);
> + int width, height;
> +
> + width = le32_to_cpu(output->info.r.width);
> + height = le32_to_cpu(output->info.r.height);
> +
> + if (!(mode->type & DRM_MODE_TYPE_PREFERRED))
> + return MODE_OK;
> + if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF)
> + return MODE_OK;
> + if (mode->hdisplay <= width && mode->hdisplay >= width - 16 &&
> + mode->vdisplay <= height && mode->vdisplay >= height - 16)
> + return MODE_OK;
> +
> + DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay);
> + return MODE_BAD;
> +}
> +
> +static struct drm_encoder*
> +virtio_gpu_best_encoder(struct drm_connector *connector)
> +{
> + struct virtio_gpu_output *virtio_gpu_output =
> + drm_connector_to_virtio_gpu_output(connector);
> +
> + return &virtio_gpu_output->enc;
> +}
> +
> +
> +static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
> + .dpms = virtio_gpu_enc_dpms,
> + .mode_fixup = virtio_gpu_enc_mode_fixup,
> + .prepare = virtio_gpu_enc_prepare,
> + .mode_set = virtio_gpu_enc_mode_set,
> + .commit = virtio_gpu_enc_commit,
> +};
> +
> +static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
> + .get_modes = virtio_gpu_conn_get_modes,
> + .mode_valid = virtio_gpu_conn_mode_valid,
> + .best_encoder = virtio_gpu_best_encoder,
> +};
> +
> +static void virtio_gpu_conn_save(struct drm_connector *connector)
> +{
> + DRM_DEBUG("\n");
> +}
> +
> +static void virtio_gpu_conn_restore(struct drm_connector *connector)
> +{
> + DRM_DEBUG("\n");
> +}
> +
> +static enum drm_connector_status virtio_gpu_conn_detect(
> + struct drm_connector *connector,
> + bool force)
> +{
> + struct virtio_gpu_output *output =
> + drm_connector_to_virtio_gpu_output(connector);
> +
> + if (output->info.enabled)
> + return connector_status_connected;
> + else
> + return connector_status_disconnected;
> +}
> +
> +static int virtio_gpu_conn_set_property(struct drm_connector *connector,
> + struct drm_property *property,
> + uint64_t value)
> +{
> + DRM_DEBUG("\n");
> + return 0;
> +}
> +
> +static void virtio_gpu_conn_destroy(struct drm_connector *connector)
> +{
> + struct virtio_gpu_output *virtio_gpu_output =
> + drm_connector_to_virtio_gpu_output(connector);
> +
> + drm_connector_unregister(connector);
> + drm_connector_cleanup(connector);
> + kfree(virtio_gpu_output);
> +}
> +
> +static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
> + .dpms = drm_helper_connector_dpms,
> + .save = virtio_gpu_conn_save,
> + .restore = virtio_gpu_conn_restore,
> + .detect = virtio_gpu_conn_detect,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .set_property = virtio_gpu_conn_set_property,
> + .destroy = virtio_gpu_conn_destroy,
> +};
> +
> +static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
> + .destroy = drm_encoder_cleanup,
> +};
> +
> +static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
> +{
> + struct drm_device *dev = vgdev->ddev;
> + struct virtio_gpu_output *output = vgdev->outputs + index;
> + struct drm_connector *connector = &output->conn;
> + struct drm_encoder *encoder = &output->enc;
> + struct drm_crtc *crtc = &output->crtc;
> +
> + output->index = index;
> + if (index == 0) {
> + output->info.enabled = cpu_to_le32(true);
> + output->info.r.width = cpu_to_le32(XRES_DEF);
> + output->info.r.height = cpu_to_le32(YRES_DEF);
> + }
> +
> + drm_crtc_init(dev, crtc, &virtio_gpu_crtc_funcs);
> + drm_mode_crtc_set_gamma_size(crtc, 256);
> + drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
> +
> + drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
> + DRM_MODE_CONNECTOR_VIRTUAL);
> + connector->polled = DRM_CONNECTOR_POLL_HPD;
> + drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
> + DRM_MODE_ENCODER_VIRTUAL);
> +
> + encoder->possible_crtcs = 1 << index;
> + drm_mode_connector_attach_encoder(connector, encoder);
> + drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
> + drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
> + drm_connector_register(connector);
> + return 0;
> +}
> +
> +static struct drm_framebuffer *
> +virtio_gpu_user_framebuffer_create(struct drm_device *dev,
> + struct drm_file *file_priv,
> + struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> + struct drm_gem_object *obj = NULL;
> + struct virtio_gpu_framebuffer *virtio_gpu_fb;
> + int ret;
> +
> + /* lookup object associated with res handle */
> + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> + if (!obj)
> + return ERR_PTR(-EINVAL);
> +
> + virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL);
> + if (virtio_gpu_fb == NULL)
> + return ERR_PTR(-ENOMEM);
> +
> + ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj);
> + if (ret) {
> + kfree(virtio_gpu_fb);
> + if (obj)
> + drm_gem_object_unreference_unlocked(obj);
> + return NULL;
> + }
> +
> + return &virtio_gpu_fb->base;
> +}
> +
> +static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
> + .fb_create = virtio_gpu_user_framebuffer_create,
> +};
> +
> +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
> +{
> + int i;
> + int ret;
> +
> + drm_mode_config_init(vgdev->ddev);
> + vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs;
> +
> + /* modes will be validated against the framebuffer size */
> + vgdev->ddev->mode_config.min_width = XRES_MIN;
> + vgdev->ddev->mode_config.min_height = YRES_MIN;
> + vgdev->ddev->mode_config.max_width = XRES_MAX;
> + vgdev->ddev->mode_config.max_height = YRES_MAX;
> +
> + for (i = 0 ; i < vgdev->num_scanouts; ++i)
> + vgdev_output_init(vgdev, i);
> +
> + /* primary surface must be created by this point, to allow
> + * issuing command queue commands and having them read by
> + * spice server. */
> + ret = virtio_gpu_fbdev_init(vgdev);
> + if (ret)
> + return ret;
> +
> + ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts);
> +
> + drm_kms_helper_poll_init(vgdev->ddev);
> + return ret;
> +}
> +
> +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
> +{
> + virtio_gpu_fbdev_fini(vgdev);
> + drm_mode_config_cleanup(vgdev->ddev);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> new file mode 100644
> index 0000000..e4b50af
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> @@ -0,0 +1,68 @@
> +#include <linux/pci.h>
> +
> +#include "virtgpu_drv.h"
> +
> +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master)
> +{
> + struct pci_dev *pdev = dev->pdev;
> +
> + if (pdev) {
> + return drm_pci_set_busid(dev, master);
> + }
> + return 0;
> +}
> +
> +static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev)
> +{
> + struct apertures_struct *ap;
> + bool primary;
> + ap = alloc_apertures(1);
> + if (!ap)
> + return;
> +
> + ap->ranges[0].base = pci_resource_start(pci_dev, 2);
> + ap->ranges[0].size = pci_resource_len(pci_dev, 2);
> +
> + primary = pci_dev->resource[PCI_ROM_RESOURCE].flags
> + & IORESOURCE_ROM_SHADOW;
> +
> + remove_conflicting_framebuffers(ap, "virtiodrmfb", primary);
> +
> + kfree(ap);
> +}
> +
> +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
> +{
> + struct drm_device *dev;
> + int ret;
> +
> + dev = drm_dev_alloc(driver, &vdev->dev);
> + if (!dev)
> + return -ENOMEM;
> + dev->virtdev = vdev;
> + vdev->priv = dev;
> +
> + if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
> + struct pci_dev *pdev = to_pci_dev(vdev->dev.parent);
> + bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
> + DRM_INFO("pci: %s detected\n",
> + vga ? "virtio-vga" : "virtio-gpu-pci");
> + dev->pdev = pdev;
> + if (vga)
> + virtio_pci_kick_out_firmware_fb(pdev);
> + }
> +
> + ret = drm_dev_register(dev, 0);
> + if (ret)
> + goto err_free;
> +
> + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
> + driver->major, driver->minor, driver->patchlevel,
> + driver->date, dev->primary->index);
> +
> + return 0;
> +
> +err_free:
> + drm_dev_unref(dev);
> + return ret;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
> new file mode 100644
> index 0000000..3662e86
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
> @@ -0,0 +1,132 @@
> +/*
> + * 2011 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Dave Airlie <airlied@redhat.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/console.h>
> +#include <linux/pci.h>
> +#include "drmP.h"
> +#include "drm/drm.h"
> +
> +#include "virtgpu_drv.h"
> +static struct drm_driver driver;
> +
> +static int virtio_gpu_modeset = -1;
> +
> +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
> +module_param_named(modeset, virtio_gpu_modeset, int, 0400);
> +
> +static int virtio_gpu_probe(struct virtio_device *vdev)
> +{
> +#ifdef CONFIG_VGA_CONSOLE
> + if (vgacon_text_force() && virtio_gpu_modeset == -1)
> + return -EINVAL;
> +#endif
> +
> + if (virtio_gpu_modeset == 0)
> + return -EINVAL;
> +
> + return drm_virtio_init(&driver, vdev);
> +}
> +
> +static void virtio_gpu_remove(struct virtio_device *vdev)
> +{
> + struct drm_device *dev = vdev->priv;
> + drm_put_dev(dev);
> +}
> +
> +static void virtio_gpu_config_changed(struct virtio_device *vdev)
> +{
> + struct drm_device *dev = vdev->priv;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> +
> + schedule_work(&vgdev->config_changed_work);
> +}
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static unsigned int features[] = {
> +};
> +static struct virtio_driver virtio_gpu_driver = {
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .probe = virtio_gpu_probe,
> + .remove = virtio_gpu_remove,
> + .config_changed = virtio_gpu_config_changed
> +};
> +
> +module_virtio_driver(virtio_gpu_driver);
> +
> +MODULE_DEVICE_TABLE(virtio, id_table);
> +MODULE_DESCRIPTION("Virtio GPU driver");
> +MODULE_LICENSE("GPL");
> +
> +static const struct file_operations virtio_gpu_driver_fops = {
> + .owner = THIS_MODULE,
> + .open = drm_open,
> + .mmap = virtio_gpu_mmap,
> + .poll = drm_poll,
> + .read = drm_read,
> + .unlocked_ioctl = drm_ioctl,
> + .release = drm_release,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = drm_compat_ioctl,
> +#endif
> + .llseek = noop_llseek,
> +};
> +
> +
> +static struct drm_driver driver = {
> + .driver_features = DRIVER_MODESET | DRIVER_GEM,
> + .set_busid = drm_virtio_set_busid,
> + .load = virtio_gpu_driver_load,
> + .unload = virtio_gpu_driver_unload,
> +
> + .dumb_create = virtio_gpu_mode_dumb_create,
> + .dumb_map_offset = virtio_gpu_mode_dumb_mmap,
> + .dumb_destroy = virtio_gpu_mode_dumb_destroy,
> +
> +#if defined(CONFIG_DEBUG_FS)
> + .debugfs_init = virtio_gpu_debugfs_init,
> + .debugfs_cleanup = virtio_gpu_debugfs_takedown,
> +#endif
> +
> + .gem_free_object = virtio_gpu_gem_free_object,
> + .fops = &virtio_gpu_driver_fops,
> +
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> + .patchlevel = DRIVER_PATCHLEVEL,
> +};
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
> new file mode 100644
> index 0000000..6082ec3
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
> @@ -0,0 +1,326 @@
> +/*
> + * Copyright (C) 2012 Red Hat
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#ifndef VIRTIO_DRV_H
> +#define VIRTIO_DRV_H
> +
> +#include <linux/virtio.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_gpu.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <ttm/ttm_bo_api.h>
> +#include <ttm/ttm_bo_driver.h>
> +#include <ttm/ttm_placement.h>
> +#include <ttm/ttm_module.h>
> +
> +#define DRIVER_NAME "virtio_gpu"
> +#define DRIVER_DESC "virtio GPU"
> +#define DRIVER_DATE "0"
> +
> +#define DRIVER_MAJOR 0
> +#define DRIVER_MINOR 0
> +#define DRIVER_PATCHLEVEL 1
> +
> +/* virtgpu_drm_bus.c */
> +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master);
> +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev);
> +
> +struct virtio_gpu_object {
> + struct drm_gem_object gem_base;
> + uint32_t hw_res_handle;
> +
> + struct sg_table *pages;
> + void *vmap;
> + bool dumb;
> + struct ttm_place placement_code;
> + struct ttm_placement placement;
> + struct ttm_buffer_object tbo;
> + struct ttm_bo_kmap_obj kmap;
> +};
> +#define gem_to_virtio_gpu_obj(gobj) \
> + container_of((gobj), struct virtio_gpu_object, gem_base)
> +
> +struct virtio_gpu_vbuffer;
> +struct virtio_gpu_device;
> +
> +typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf);
> +
> +struct virtio_gpu_fence_driver {
> + atomic64_t last_seq;
> + uint64_t sync_seq;
> + struct list_head fences;
> + spinlock_t lock;
> +};
> +
> +struct virtio_gpu_fence {
> + struct fence f;
> + struct virtio_gpu_fence_driver *drv;
> + struct list_head node;
> + uint64_t seq;
> +};
> +#define to_virtio_fence(x) \
> + container_of(x, struct virtio_gpu_fence, f)
> +
> +struct virtio_gpu_vbuffer {
> + char *buf;
> + int size;
> + bool debug_dump_sglists;
> +
> + void *data_buf;
> + uint32_t data_size;
> +
> + char *resp_buf;
> + int resp_size;
> +
> + virtio_gpu_resp_cb resp_cb;
> +
> + struct list_head destroy_list;
> +};
> +
> +struct virtio_gpu_output {
> + int index;
> + struct drm_crtc crtc;
> + struct drm_connector conn;
> + struct drm_encoder enc;
> + struct virtio_gpu_display_one info;
> + struct virtio_gpu_update_cursor cursor;
> + int cur_x;
> + int cur_y;
> +};
> +#define drm_crtc_to_virtio_gpu_output(x) \
> + container_of(x, struct virtio_gpu_output, crtc)
> +#define drm_connector_to_virtio_gpu_output(x) \
> + container_of(x, struct virtio_gpu_output, conn)
> +#define drm_encoder_to_virtio_gpu_output(x) \
> + container_of(x, struct virtio_gpu_output, enc)
> +
> +struct virtio_gpu_framebuffer {
> + struct drm_framebuffer base;
> + struct drm_gem_object *obj;
> + int x1, y1, x2, y2; /* dirty rect */
> + spinlock_t dirty_lock;
> + uint32_t hw_res_handle;
> +};
> +#define to_virtio_gpu_framebuffer(x) \
> + container_of(x, struct virtio_gpu_framebuffer, base)
> +
> +struct virtio_gpu_mman {
> + struct ttm_bo_global_ref bo_global_ref;
> + struct drm_global_reference mem_global_ref;
> + bool mem_global_referenced;
> + struct ttm_bo_device bdev;
> +};
> +
> +struct virtio_gpu_fbdev;
> +
> +struct virtio_gpu_queue {
> + struct virtqueue *vq;
> + spinlock_t qlock;
> + wait_queue_head_t ack_queue;
> + struct work_struct dequeue_work;
> +};
> +
> +struct virtio_gpu_device {
> + struct device *dev;
> + struct drm_device *ddev;
> +
> + struct virtio_device *vdev;
> +
> + struct virtio_gpu_mman mman;
> +
> + /* pointer to fbdev info structure */
> + struct virtio_gpu_fbdev *vgfbdev;
> + struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS];
> + uint32_t num_scanouts;
> +
> + struct virtio_gpu_queue ctrlq;
> + struct virtio_gpu_queue cursorq;
> +
> + struct idr resource_idr;
> + spinlock_t resource_idr_lock;
> +
> + wait_queue_head_t resp_wq;
> + /* current display info */
> + spinlock_t display_info_lock;
> +
> + struct virtio_gpu_fence_driver fence_drv;
> +
> + struct idr ctx_id_idr;
> + spinlock_t ctx_id_idr_lock;
> +
> + struct work_struct config_changed_work;
> +};
> +
> +struct virtio_gpu_fpriv {
> + uint32_t ctx_id;
> +};
> +
> +/* virtio_ioctl.c */
> +#define DRM_VIRTIO_NUM_IOCTLS 10
> +extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
> +
> +/* virtio_kms.c */
> +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
> +int virtio_gpu_driver_unload(struct drm_device *dev);
> +
> +/* virtio_gem.c */
> +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj);
> +int virtio_gpu_gem_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_gem_fini(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_gem_create(struct drm_file *file,
> + struct drm_device *dev,
> + uint64_t size,
> + struct drm_gem_object **obj_p,
> + uint32_t *handle_p);
> +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
> + size_t size, bool kernel,
> + bool pinned);
> +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev,
> + struct drm_mode_create_dumb *args);
> +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle);
> +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle, uint64_t *offset_p);
> +
> +/* virtio_fb */
> +#define VIRTIO_GPUFB_CONN_LIMIT 1
> +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb,
> + struct drm_clip_rect *clips,
> + unsigned num_clips);
> +/* virtio vg */
> +int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
> + uint32_t *resid);
> +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id);
> +int virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t format,
> + uint32_t width,
> + uint32_t height);
> +int virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id);
> +int virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id, uint64_t offset,
> + __le32 width, __le32 height,
> + __le32 x, __le32 y,
> + struct virtio_gpu_fence **fence);
> +int virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t x, uint32_t y,
> + uint32_t width, uint32_t height);
> +int virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
> + uint32_t scanout_id, uint32_t resource_id,
> + uint32_t width, uint32_t height,
> + uint32_t x, uint32_t y);
> +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_object *obj,
> + uint32_t resource_id,
> + struct virtio_gpu_fence **fence);
> +int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_output *output);
> +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id);
> +void virtio_gpu_ctrl_ack(struct virtqueue *vq);
> +void virtio_gpu_cursor_ack(struct virtqueue *vq);
> +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
> +void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
> +
> +/* virtio_gpu_display.c */
> +int virtio_gpu_framebuffer_init(struct drm_device *dev,
> + struct virtio_gpu_framebuffer *vgfb,
> + struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object *obj);
> +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev);
> +
> +/* virtio_gpu_ttm.c */
> +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev);
> +bool virtio_gpu_ttm_bo_is_virtio_gpu_object(struct ttm_buffer_object *bo);
> +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +/* virtio_gpu_fence.c */
> +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_ctrl_hdr *cmd_hdr,
> + struct virtio_gpu_fence **fence);
> +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
> + u64 last_seq);
> +
> +/* virtio_gpu_object */
> +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
> + unsigned long size, bool kernel, bool pinned,
> + struct virtio_gpu_object **bo_ptr);
> +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr);
> +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
> + struct virtio_gpu_object *bo);
> +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
> +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
> +
> +static inline struct virtio_gpu_object*
> +virtio_gpu_object_ref(struct virtio_gpu_object *bo)
> +{
> + ttm_bo_reference(&bo->tbo);
> + return bo;
> +}
> +
> +static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo)
> +{
> + struct ttm_buffer_object *tbo;
> +
> + if ((*bo) == NULL)
> + return;
> + tbo = &((*bo)->tbo);
> + ttm_bo_unref(&tbo);
> + if (tbo == NULL)
> + *bo = NULL;
> +}
> +
> +static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo)
> +{
> + return drm_vma_node_offset_addr(&bo->tbo.vma_node);
> +}
> +
> +static inline int virtio_gpu_object_reserve(struct virtio_gpu_object *bo,
> + bool no_wait)
> +{
> + int r;
> +
> + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
> + if (unlikely(r != 0)) {
> + if (r != -ERESTARTSYS) {
> + struct virtio_gpu_device *qdev =
> + bo->gem_base.dev->dev_private;
> + dev_err(qdev->dev, "%p reserve failed\n", bo);
> + }
> + return r;
> + }
> + return 0;
> +}
> +
> +static inline void virtio_gpu_object_unreserve(struct virtio_gpu_object *bo)
> +{
> + ttm_bo_unreserve(&bo->tbo);
> +}
> +
> +/* virgl debufs */
> +int virtio_gpu_debugfs_init(struct drm_minor *minor);
> +void virtio_gpu_debugfs_takedown(struct drm_minor *minor);
> +
> +#endif
> diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c
> new file mode 100644
> index 0000000..1d79457
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c
> @@ -0,0 +1,415 @@
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include "virtgpu_drv.h"
> +
> +#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60)
> +
> +struct virtio_gpu_fbdev {
> + struct drm_fb_helper helper;
> + struct virtio_gpu_framebuffer vgfb;
> + struct list_head fbdev_list;
> + struct virtio_gpu_device *vgdev;
> + struct delayed_work work;
> +};
> +#define DL_ALIGN_UP(x, a) ALIGN(x, a)
> +#define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a)
does not work if x < a.
also spaces around - missing.
I would say just open-code x / sizeof long * sizeof long
below and drop both these macros.
> +
> +static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb,
> + bool store, int x, int y,
> + int width, int height)
> +{
> + struct drm_device *dev = fb->base.dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + bool store_for_later = false;
> + int aligned_x;
> + int bpp = (fb->base.bits_per_pixel / 8);
don't put () around the whole expression.
> + int x2, y2;
> + unsigned long flags;
> + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj);
> +
> + aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
> + width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
missing spaces around - again
> + x = aligned_x;
> +
> + if ((width <= 0) ||
> + (x + width > fb->base.width) ||
> + (y + height > fb->base.height)) {
you don't really need () around < > if using it with ||.
> + DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n",
> + width, height, x, y,
> + fb->base.width, fb->base.height);
> + return -EINVAL;
> + }
> +
> + /* if we are in atomic just store the info
> + can't test inside spin lock */
should use a different style for multiline comments.
> + if (in_atomic() || store)
> + store_for_later = true;
in_atomic users are suspect, this needs better comments. What are you
trying to test for here? it's usually best just split up code,
or pass a flag from callers that know in which context
they are invoked, but maybe it's justified here.
> +
> + x2 = x + width - 1;
> + y2 = y + height - 1;
> +
> + spin_lock_irqsave(&fb->dirty_lock, flags);
> +
> + if (fb->y1 < y)
> + y = fb->y1;
> + if (fb->y2 > y2)
> + y2 = fb->y2;
> + if (fb->x1 < x)
> + x = fb->x1;
> + if (fb->x2 > x2)
> + x2 = fb->x2;
> +
> + if (store_for_later) {
> + fb->x1 = x;
> + fb->x2 = x2;
> + fb->y1 = y;
> + fb->y2 = y2;
> + spin_unlock_irqrestore(&fb->dirty_lock, flags);
> + return 0;
> + }
> +
> + fb->x1 = fb->y1 = INT_MAX;
> + fb->x2 = fb->y2 = 0;
> +
> + spin_unlock_irqrestore(&fb->dirty_lock, flags);
> +
> + {
> + uint32_t offset;
> + uint32_t w = x2 - x + 1;
> + uint32_t h = y2 - y + 1;
> +
> + offset = (y * fb->base.pitches[0]) + x * bpp;
> +
> + virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle,
> + offset,
> + cpu_to_le32(w),
> + cpu_to_le32(h),
> + cpu_to_le32(x),
> + cpu_to_le32(y),
> + NULL);
> +
> + }
> + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
> + x, y, x2 - x + 1, y2 - y + 1);
> + return 0;
> +}
> +
> +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb,
> + struct drm_clip_rect *clips,
> + unsigned num_clips)
> +{
> + struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private;
> + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->obj);
> + struct drm_clip_rect norect;
> + struct drm_clip_rect *clips_ptr;
> + int left, right, top, bottom;
> + int i;
> + int inc = 1;
> + if (!num_clips) {
> + num_clips = 1;
> + clips = &norect;
> + norect.x1 = norect.y1 = 0;
> + norect.x2 = vgfb->base.width;
> + norect.y2 = vgfb->base.height;
> + }
> + left = clips->x1;
> + right = clips->x2;
> + top = clips->y1;
> + bottom = clips->y2;
> +
> + /* skip the first clip rect */
> + for (i = 1, clips_ptr = clips + inc;
> + i < num_clips; i++, clips_ptr += inc) {
> + left = min_t(int, left, (int)clips_ptr->x1);
> + right = max_t(int, right, (int)clips_ptr->x2);
> + top = min_t(int, top, (int)clips_ptr->y1);
> + bottom = max_t(int, bottom, (int)clips_ptr->y2);
> + }
> +
> + if (obj->dumb)
> + return virtio_gpu_dirty_update(vgfb, false, left, top,
> + right - left, bottom - top);
> +
> + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
> + left, top, right - left, bottom - top);
> + return 0;
> +}
> +
> +static void virtio_gpu_fb_dirty_work(struct work_struct *work)
> +{
> + struct delayed_work *delayed_work = to_delayed_work(work);
> + struct virtio_gpu_fbdev *vfbdev =
> + container_of(delayed_work, struct virtio_gpu_fbdev, work);
> + struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb;
> +
> + virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1,
> + vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1);
> +}
> +
> +static void virtio_gpu_3d_fillrect(struct fb_info *info,
> + const struct fb_fillrect *rect)
> +{
> + struct virtio_gpu_fbdev *vfbdev = info->par;
> + sys_fillrect(info, rect);
> + virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy,
> + rect->width, rect->height);
> + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
> +}
> +
> +static void virtio_gpu_3d_copyarea(struct fb_info *info,
> + const struct fb_copyarea *area)
> +{
> + struct virtio_gpu_fbdev *vfbdev = info->par;
> + sys_copyarea(info, area);
> + virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy,
> + area->width, area->height);
> + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
> +}
> +
> +static void virtio_gpu_3d_imageblit(struct fb_info *info,
> + const struct fb_image *image)
> +{
> + struct virtio_gpu_fbdev *vfbdev = info->par;
> + sys_imageblit(info, image);
> + virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy,
> + image->width, image->height);
> + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
> +}
> +
> +static struct fb_ops virtio_gpufb_ops = {
> + .owner = THIS_MODULE,
> + .fb_check_var = drm_fb_helper_check_var,
> + .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */
> + .fb_fillrect = virtio_gpu_3d_fillrect,
> + .fb_copyarea = virtio_gpu_3d_copyarea,
> + .fb_imageblit = virtio_gpu_3d_imageblit,
> + .fb_pan_display = drm_fb_helper_pan_display,
> + .fb_blank = drm_fb_helper_blank,
> + .fb_setcmap = drm_fb_helper_setcmap,
> + .fb_debug_enter = drm_fb_helper_debug_enter,
> + .fb_debug_leave = drm_fb_helper_debug_leave,
> +};
> +
> +static int virtio_gpu_vmap_fb(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_object *obj)
> +{
> + return virtio_gpu_object_kmap(obj, NULL);
> +}
> +
> +static int virtio_gpufb_create(struct drm_fb_helper *helper,
> + struct drm_fb_helper_surface_size *sizes)
> +{
> + struct virtio_gpu_fbdev *vfbdev =
> + container_of(helper, struct virtio_gpu_fbdev, helper);
> + struct drm_device *dev = helper->dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct fb_info *info;
> + struct drm_framebuffer *fb;
> + struct drm_mode_fb_cmd2 mode_cmd = {};
> + struct virtio_gpu_object *obj;
> + struct device *device = vgdev->dev;
> + uint32_t resid, format, size;
> + int ret;
> +
> + if (sizes->surface_bpp == 24)
> + sizes->surface_bpp = 32;
> + mode_cmd.width = sizes->surface_width;
> + mode_cmd.height = sizes->surface_height;
> + mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
> + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> + sizes->surface_depth);
> +
> + switch (mode_cmd.pixel_format) {
> +#ifdef __BIG_ENDIAN
> + case DRM_FORMAT_XRGB8888:
> + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_ARGB8888:
> + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_BGRX8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
> + break;
> + case DRM_FORMAT_BGRA8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
> + break;
> + case DRM_FORMAT_RGBX8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
> + break;
> + case DRM_FORMAT_RGBA8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM;
> + break;
> + case DRM_FORMAT_XBGR8888:
> + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM;
> + break;
> + case DRM_FORMAT_ABGR8888:
> + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
> + break;
> +#else
> + case DRM_FORMAT_XRGB8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
> + break;
> + case DRM_FORMAT_ARGB8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
> + break;
> + case DRM_FORMAT_BGRX8888:
> + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_BGRA8888:
> + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_RGBX8888:
> + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM;
> + break;
> + case DRM_FORMAT_RGBA8888:
> + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
> + break;
> + case DRM_FORMAT_XBGR8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
> + break;
> + case DRM_FORMAT_ABGR8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM;
> + break;
> +#endif
> + default:
> + format = 0;
> + break;
> + }
> + if (format == 0) {
> + ret = -EINVAL;
> + DRM_ERROR("failed to find virtio gpu format for %d\n",
> + mode_cmd.pixel_format);
> + goto fail;
> + }
> +
> + size = mode_cmd.pitches[0] * mode_cmd.height;
> + obj = virtio_gpu_alloc_object(dev, size, false, true);
> + if (!obj) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + ret = virtio_gpu_resource_id_get(vgdev, &resid);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_cmd_create_resource(vgdev, resid, format,
> + mode_cmd.width, mode_cmd.height);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_vmap_fb(vgdev, obj);
> + if (ret) {
> + DRM_ERROR("failed to vmap fb %d\n", ret);
> + goto fail;
> + }
> +
> + /* attach the object to the resource */
> + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
> + if (ret)
> + goto fail;
> +
> + info = framebuffer_alloc(0, device);
> + if (!info) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + info->par = helper;
> +
> + ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb,
> + &mode_cmd, &obj->gem_base);
> + if (ret)
> + goto fail;
> +
> + fb = &vfbdev->vgfb.base;
> +
> + vfbdev->helper.fb = fb;
> + vfbdev->helper.fbdev = info;
> +
> + strcpy(info->fix.id, "virtiodrmfb");
> + info->flags = FBINFO_DEFAULT;
> + info->fbops = &virtio_gpufb_ops;
> + info->pixmap.flags = FB_PIXMAP_SYSTEM;
> + ret = fb_alloc_cmap(&info->cmap, 256, 0);
> + if (ret) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + info->screen_base = obj->vmap;
> + info->screen_size = obj->gem_base.size;
> + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
> + drm_fb_helper_fill_var(info, &vfbdev->helper,
> + sizes->fb_width, sizes->fb_height);
> +
> + info->fix.mmio_start = 0;
> + info->fix.mmio_len = 0;
> +
> + return 0;
> +fail:
> +
Seem too simple. shouldn't this cleanup whatever it allocated?
> + return -EINVAL;
> +}
> +
> +static int virtio_gpu_fbdev_destroy(struct drm_device *dev,
> + struct virtio_gpu_fbdev *vgfbdev)
> +{
> + struct fb_info *info;
> + struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb;
> +
> + if (vgfbdev->helper.fbdev) {
> + info = vgfbdev->helper.fbdev;
> +
> + unregister_framebuffer(info);
> + framebuffer_release(info);
> + }
> + if (vgfb->obj)
> + vgfb->obj = NULL;
> + drm_fb_helper_fini(&vgfbdev->helper);
> + drm_framebuffer_cleanup(&vgfb->base);
> +
> + return 0;
> +}
> +static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = {
> + .fb_probe = virtio_gpufb_create,
> +};
> +
> +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev)
> +{
> + struct virtio_gpu_fbdev *vgfbdev;
> + int bpp_sel = 32; /* TODO: parameter from somewhere? */
> + int ret;
> +
> + vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL);
> + if (!vgfbdev)
> + return -ENOMEM;
> +
> + vgfbdev->vgdev = vgdev;
> + vgdev->vgfbdev = vgfbdev;
> + INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work);
> +
> + drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper,
> + &virtio_gpu_fb_helper_funcs);
> + ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper,
> + vgdev->num_scanouts,
> + VIRTIO_GPUFB_CONN_LIMIT);
> + if (ret) {
> + kfree(vgfbdev);
> + return ret;
> + }
> +
> + drm_fb_helper_single_add_all_connectors(&vgfbdev->helper);
> + drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel);
> + return 0;
> +}
> +
> +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev)
> +{
> + if (!vgdev->vgfbdev)
> + return;
> +
> + virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev);
> + kfree(vgdev->vgfbdev);
> + vgdev->vgfbdev = NULL;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c
> new file mode 100644
> index 0000000..552aa49
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c
> @@ -0,0 +1,95 @@
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +
> +static const char *virtio_get_driver_name(struct fence *f)
> +{
> + return "virtio_gpu";
> +}
> +
> +static const char *virtio_get_timeline_name(struct fence *f)
> +{
> + return "controlq";
> +}
> +
> +static bool virtio_enable_signaling(struct fence *f)
> +{
> + return true;
> +}
> +
> +static bool virtio_signaled(struct fence *f)
> +{
> + struct virtio_gpu_fence *fence = to_virtio_fence(f);
> +
> + if (atomic64_read(&fence->drv->last_seq) >= fence->seq) {
> + return true;
> + }
drop {}
> + return false;
> +}
> +
> +static void virtio_fence_value_str(struct fence *f, char *str, int size)
> +{
> + struct virtio_gpu_fence *fence = to_virtio_fence(f);
> +
> + snprintf(str, size, "%llu", fence->seq);
> +}
> +
> +static void virtio_timeline_value_str(struct fence *f, char *str, int size)
> +{
> + struct virtio_gpu_fence *fence = to_virtio_fence(f);
> +
> + snprintf(str, size, "%lu", atomic64_read(&fence->drv->last_seq));
> +}
> +
> +static const struct fence_ops virtio_fence_ops = {
> + .get_driver_name = virtio_get_driver_name,
> + .get_timeline_name = virtio_get_timeline_name,
> + .enable_signaling = virtio_enable_signaling,
> + .signaled = virtio_signaled,
> + .wait = fence_default_wait,
> + .fence_value_str = virtio_fence_value_str,
> + .timeline_value_str = virtio_timeline_value_str,
> +};
> +
> +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_ctrl_hdr *cmd_hdr,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
> + unsigned long irq_flags;
> +
> + *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL);
> + if ((*fence) == NULL)
> + return -ENOMEM;
> +
> + spin_lock_irqsave(&drv->lock, irq_flags);
> + (*fence)->drv = drv;
> + (*fence)->seq = ++drv->sync_seq;
> + fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock,
> + 0, (*fence)->seq);
> + fence_get(&(*fence)->f);
> + list_add_tail(&(*fence)->node, &drv->fences);
> + spin_unlock_irqrestore(&drv->lock, irq_flags);
> +
> + cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE);
> + cmd_hdr->fence_id = cpu_to_le64((*fence)->seq);
> + return 0;
> +}
> +
> +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev,
> + u64 last_seq)
> +{
> + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
> + struct virtio_gpu_fence *fence, *tmp;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&drv->lock, irq_flags);
> + atomic64_set(&vgdev->fence_drv.last_seq, last_seq);
> + list_for_each_entry_safe(fence, tmp, &drv->fences, node) {
> + if (last_seq < fence->seq)
> + continue;
> + fence_signal_locked(&fence->f);
> + list_del(&fence->node);
> + fence_put(&fence->f);
> + }
> + spin_unlock_irqrestore(&drv->lock, irq_flags);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
> new file mode 100644
> index 0000000..8bc0a24
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
> @@ -0,0 +1,120 @@
> +
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +
> +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj)
> +{
> + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj);
> +
> + if (obj)
> + virtio_gpu_object_unref(&obj);
> +}
> +
> +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
> + size_t size, bool kernel,
> + bool pinned)
> +{
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct virtio_gpu_object *obj;
> + int ret;
> +
> + ret = virtio_gpu_object_create(vgdev, size, kernel, pinned, &obj);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return obj;
> +}
> +
> +int virtio_gpu_gem_create(struct drm_file *file,
> + struct drm_device *dev,
> + uint64_t size,
> + struct drm_gem_object **obj_p,
> + uint32_t *handle_p)
> +{
> + struct virtio_gpu_object *obj;
> + int ret;
> + u32 handle;
> +
> + obj = virtio_gpu_alloc_object(dev, size, false, false);
> + if (IS_ERR(obj))
> + return PTR_ERR(obj);
> +
> + ret = drm_gem_handle_create(file, &obj->gem_base, &handle);
> + if (ret) {
> + drm_gem_object_release(&obj->gem_base);
> + return ret;
> + }
> +
> + *obj_p = &obj->gem_base;
> +
> + /* drop reference from allocate - handle holds it now */
> + drm_gem_object_unreference_unlocked(&obj->gem_base);
> +
> + *handle_p = handle;
> + return 0;
> +}
> +
> +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev,
> + struct drm_mode_create_dumb *args)
> +{
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct drm_gem_object *gobj;
> + struct virtio_gpu_object *obj;
> + int ret;
> + uint32_t pitch;
> + uint32_t resid;
> +
> + pitch = args->width * ((args->bpp + 1) / 8);
> + args->size = pitch * args->height;
> + args->size = ALIGN(args->size, PAGE_SIZE);
> +
> + ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj,
> + &args->handle);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_resource_id_get(vgdev, &resid);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_cmd_create_resource(vgdev, resid,
> + 2, args->width, args->height);
> + if (ret)
> + goto fail;
> +
> + /* attach the object to the resource */
> + obj = gem_to_virtio_gpu_obj(gobj);
> + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
> + if (ret)
> + goto fail;
> +
> + obj->dumb = true;
> + args->pitch = pitch;
> + return ret;
> +fail:
> + return ret;
> +}
> +
> +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle)
> +{
> + return drm_gem_handle_delete(file_priv, handle);
> +}
> +
> +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle, uint64_t *offset_p)
> +{
> + struct drm_gem_object *gobj;
> + struct virtio_gpu_object *obj;
> + BUG_ON(!offset_p);
> + gobj = drm_gem_object_lookup(dev, file_priv, handle);
> + if (gobj == NULL)
> + return -ENOENT;
> + obj = gem_to_virtio_gpu_obj(gobj);
> + *offset_p = virtio_gpu_object_mmap_offset(obj);
> + drm_gem_object_unreference_unlocked(gobj);
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
> new file mode 100644
> index 0000000..45c4beb
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -0,0 +1,125 @@
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +
> +static void virtio_gpu_config_changed_work_func(struct work_struct *work)
> +{
> + struct virtio_gpu_device *vgdev =
> + container_of(work, struct virtio_gpu_device,
> + config_changed_work);
> + u32 events_read, events_clear = 0;
> +
> + /* read the config space */
> + virtio_cread(vgdev->vdev, struct virtio_gpu_config,
> + events_read, &events_read);
> + if (events_read & VIRTIO_GPU_EVENT_DISPLAY) {
> + virtio_gpu_cmd_get_display_info(vgdev);
> + drm_helper_hpd_irq_event(vgdev->ddev);
> + events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
> + }
> + virtio_cwrite(vgdev->vdev, struct virtio_gpu_config,
> + events_clear, &events_clear);
> +}
> +
> +static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
> + void (*work_func)(struct work_struct *work))
> +{
> + spin_lock_init(&vgvq->qlock);
> + init_waitqueue_head(&vgvq->ack_queue);
> + INIT_WORK(&vgvq->dequeue_work, work_func);
Generally, you must flush wqs on cleanup path,
since qork might be pending.
Maybe it's not needed here, if so needs a comment.
> +}
> +
> +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
> +{
> + static vq_callback_t *callbacks[] = {
> + virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack
> + };
> + static const char *names[] = { "control", "cursor" };
> +
> + struct virtio_gpu_device *vgdev;
> + /* this will expand later */
> + struct virtqueue *vqs[2];
> + u32 num_scanouts;
> + int ret;
> +
> + if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
> + return -ENODEV;
> +
> + vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
> + if (!vgdev)
> + return -ENOMEM;
> +
> + vgdev->ddev = dev;
> + dev->dev_private = vgdev;
> + vgdev->vdev = dev->virtdev;
> + vgdev->dev = dev->dev;
> +
> + spin_lock_init(&vgdev->display_info_lock);
> + spin_lock_init(&vgdev->ctx_id_idr_lock);
> + idr_init(&vgdev->ctx_id_idr);
> + spin_lock_init(&vgdev->resource_idr_lock);
> + idr_init(&vgdev->resource_idr);
> + init_waitqueue_head(&vgdev->resp_wq);
> + virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func);
> + virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func);
> +
> + spin_lock_init(&vgdev->fence_drv.lock);
> + INIT_LIST_HEAD(&vgdev->fence_drv.fences);
> + INIT_WORK(&vgdev->config_changed_work,
> + virtio_gpu_config_changed_work_func);
> +
> + ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs,
> + callbacks, names);
> + if (ret) {
> + DRM_ERROR("failed to find virt queues\n");
> + goto err_vqs;
> + }
> + vgdev->ctrlq.vq = vqs[0];
> + vgdev->cursorq.vq = vqs[1];
> +
> + ret = virtio_gpu_ttm_init(vgdev);
> + if (ret) {
> + DRM_ERROR("failed to init ttm %d\n", ret);
> + goto err_ttm;
> + }
> +
> + /* get display info */
> + virtio_cread(vgdev->vdev, struct virtio_gpu_config,
> + num_scanouts, &num_scanouts);
> + vgdev->num_scanouts = min_t(uint32_t, num_scanouts,
> + VIRTIO_GPU_MAX_SCANOUTS);
> + if (!vgdev->num_scanouts) {
> + DRM_ERROR("num_scanouts is zero\n");
> + ret = -EINVAL;
> + goto err_scanouts;
> + }
> +
> + ret = virtio_gpu_modeset_init(vgdev);
> + if (ret)
> + goto err_modeset;
> +
> + virtio_device_ready(vgdev->vdev);
> + virtio_gpu_cmd_get_display_info(vgdev);
> + return 0;
> +
> +err_modeset:
> +err_scanouts:
> + virtio_gpu_ttm_fini(vgdev);
> +err_ttm:
> + vgdev->vdev->config->del_vqs(vgdev->vdev);
> +err_vqs:
> + kfree(vgdev);
> + return ret;
> +}
> +
> +int virtio_gpu_driver_unload(struct drm_device *dev)
> +{
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> +
Is below safe to do while device might be sending
us interrupts (before del_vqs)? I didn't check, but looks
suspicious.
OTOH you must also be careful not to do del_vqs
if your code might kick.
> + virtio_gpu_modeset_fini(vgdev);
> + virtio_gpu_ttm_fini(vgdev);
> + vgdev->vdev->config->del_vqs(vgdev->vdev);
> + kfree(vgdev);
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
> new file mode 100644
> index 0000000..0d98ae4
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_object.c
> @@ -0,0 +1,174 @@
> +#include "virtgpu_drv.h"
> +
> +static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo)
> +{
> + struct virtio_gpu_object *bo;
> + struct virtio_gpu_device *vgdev;
> +
> + bo = container_of(tbo, struct virtio_gpu_object, tbo);
> + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
> +
> + if (bo->hw_res_handle)
> + virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle);
> + if (bo->pages)
> + virtio_gpu_object_free_sg_table(bo);
> + drm_gem_object_release(&bo->gem_base);
> + kfree(bo);
> +}
> +
> +bool virtio_gpu_ttm_bo_is_virtio_gpu_object(struct ttm_buffer_object *bo)
> +{
> + if (bo->destroy == &virtio_gpu_ttm_bo_destroy)
> + return true;
> + return false;
> +}
this function seems unused.
> +
> +static void virtio_gpu_init_ttm_placement(struct virtio_gpu_object *vgbo,
> + bool pinned)
> +{
> + u32 c = 1;
> + u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
> +
> + vgbo->placement.placement = &vgbo->placement_code;
> + vgbo->placement.busy_placement = &vgbo->placement_code;
> + vgbo->placement_code.fpfn = 0;
> + vgbo->placement_code.lpfn = 0;
> + vgbo->placement_code.flags =
> + TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT | pflag;
> + vgbo->placement.num_placement = c;
> + vgbo->placement.num_busy_placement = c;
> +
> +}
> +
> +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
> + unsigned long size, bool kernel, bool pinned,
> + struct virtio_gpu_object **bo_ptr)
> +{
> + struct virtio_gpu_object *bo;
> + enum ttm_bo_type type;
> + size_t acc_size;
> + int r;
> +
> + if (kernel)
> + type = ttm_bo_type_kernel;
> + else
> + type = ttm_bo_type_device;
> + *bo_ptr = NULL;
> +
> + acc_size = ttm_bo_dma_acc_size(&vgdev->mman.bdev, size,
> + sizeof(struct virtio_gpu_object));
> +
> + bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL);
> + if (bo == NULL)
> + return -ENOMEM;
> + size = roundup(size, PAGE_SIZE);
> + r = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size);
> + if (unlikely(r)) {
> + kfree(bo);
> + return r;
> + }
> + bo->dumb = false;
> +
> + virtio_gpu_init_ttm_placement(bo, pinned);
> + r = ttm_bo_init(&vgdev->mman.bdev, &bo->tbo, size, type,
> + &bo->placement, 0, !kernel, NULL, acc_size,
> + NULL, NULL, &virtio_gpu_ttm_bo_destroy);
> + if (unlikely(r != 0)) {
> + if (r != -ERESTARTSYS)
That's unusual in kernel.
What's this test in aid of? Needs a comment.
> + dev_err(vgdev->dev,
> + "object_init %d failed for (%lu)\n", r,
> + size);
> + return r;
> + }
> + *bo_ptr = bo;
> + return 0;
> +}
> +
> +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr)
> +{
> + bool is_iomem;
> + int r;
> +
> + if (bo->vmap) {
> + if (ptr)
> + *ptr = bo->vmap;
> + return 0;
> + }
> + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap);
> + if (r)
> + return r;
> + bo->vmap = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem);
> + if (ptr)
> + *ptr = bo->vmap;
> + return 0;
> +}
> +
> +#if 0
is this code useful?
> +void virtio_gpu_object_force_delete(struct virtio_gpu_device *vgdev)
> +{
> + struct virtio_gpu_object *bo, *n;
> +
> +
two emoty lines
> + dev_err(vgdev->dev, "Userspace still has active objects !\n");
> + list_for_each_entry_safe(bo, n, &vgdev->gem.objects, list) {
> + mutex_lock(&vgdev->ddev->struct_mutex);
> + dev_err(vgdev->dev, "%p %p %lu %lu force free\n",
> + &bo->gem_base, bo, (unsigned long)bo->gem_base.size,
> + *((unsigned long *)&bo->gem_base.refcount));
> + spin_lock(&vgdev->gem.lock);
> + list_del_init(&bo->list);
> + spin_unlock(&vgdev->gem.lock);
> + /* this should unref the ttm bo */
> + drm_gem_object_unreference(&bo->gem_base);
> + mutex_unlock(&vgdev->ddev->struct_mutex);
> + }
> +}
> +#endif
> +
> +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
> + struct virtio_gpu_object *bo)
> +{
> + int ret;
> + struct page **pages = bo->tbo.ttm->pages;
> + int nr_pages = bo->tbo.num_pages;
> +
> + /* wtf swapping */
> + if (bo->pages)
> + return 0;
> +
> + if (bo->tbo.ttm->state == tt_unpopulated)
> + bo->tbo.ttm->bdev->driver->ttm_tt_populate(bo->tbo.ttm);
> + bo->pages = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
> + if (!bo->pages)
> + goto out;
> +
> + ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0,
> + nr_pages << PAGE_SHIFT, GFP_KERNEL);
> + if (ret)
> + goto out;
> + return 0;
> +out:
> + kfree(bo->pages);
> + bo->pages = NULL;
> + return -ENOMEM;
> +}
> +
> +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo)
> +{
> + sg_free_table(bo->pages);
> + kfree(bo->pages);
> + bo->pages = NULL;
> +}
> +
> +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait)
> +{
> + int r;
> +
> + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
> + if (unlikely(r != 0))
> + return r;
> + r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
> + ttm_bo_unreserve(&bo->tbo);
> + return r;
> +}
> +
> diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c
> new file mode 100644
> index 0000000..a6f22e0
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c
> @@ -0,0 +1,451 @@
> +/*
> + * Copyright 2013 Red Hat Inc.
It's 2015 isn't it?
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors: Dave Airlie
> + * Alon Levy
> + */
> +
> +#include <ttm/ttm_bo_api.h>
> +#include <ttm/ttm_bo_driver.h>
> +#include <ttm/ttm_placement.h>
> +#include <ttm/ttm_page_alloc.h>
> +#include <ttm/ttm_module.h>
> +#include <drm/drmP.h>
> +#include <drm/drm.h>
> +#include "virtgpu_drv.h"
> +
> +#include <linux/delay.h>
> +
> +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
> +
> +static struct
> +virtio_gpu_device *virtio_gpu_get_vgdev(struct ttm_bo_device *bdev)
> +{
> + struct virtio_gpu_mman *mman;
> + struct virtio_gpu_device *vgdev;
> +
> + mman = container_of(bdev, struct virtio_gpu_mman, bdev);
> + vgdev = container_of(mman, struct virtio_gpu_device, mman);
> + return vgdev;
> +}
> +
> +static int virtio_gpu_ttm_mem_global_init(struct drm_global_reference *ref)
> +{
> + return ttm_mem_global_init(ref->object);
> +}
> +
> +static void virtio_gpu_ttm_mem_global_release(struct drm_global_reference *ref)
> +{
> + ttm_mem_global_release(ref->object);
> +}
> +
> +static int virtio_gpu_ttm_global_init(struct virtio_gpu_device *vgdev)
> +{
> + struct drm_global_reference *global_ref;
> + int r;
> +
> + vgdev->mman.mem_global_referenced = false;
> + global_ref = &vgdev->mman.mem_global_ref;
> + global_ref->global_type = DRM_GLOBAL_TTM_MEM;
> + global_ref->size = sizeof(struct ttm_mem_global);
> + global_ref->init = &virtio_gpu_ttm_mem_global_init;
> + global_ref->release = &virtio_gpu_ttm_mem_global_release;
> +
> + r = drm_global_item_ref(global_ref);
> + if (r != 0) {
> + DRM_ERROR("Failed setting up TTM memory accounting "
> + "subsystem.\n");
> + return r;
> + }
> +
> + vgdev->mman.bo_global_ref.mem_glob =
> + vgdev->mman.mem_global_ref.object;
> + global_ref = &vgdev->mman.bo_global_ref.ref;
> + global_ref->global_type = DRM_GLOBAL_TTM_BO;
> + global_ref->size = sizeof(struct ttm_bo_global);
> + global_ref->init = &ttm_bo_global_init;
> + global_ref->release = &ttm_bo_global_release;
> + r = drm_global_item_ref(global_ref);
> + if (r != 0) {
> + DRM_ERROR("Failed setting up TTM BO subsystem.\n");
> + drm_global_item_unref(&vgdev->mman.mem_global_ref);
> + return r;
> + }
> +
> + vgdev->mman.mem_global_referenced = true;
> + return 0;
> +}
> +
> +static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev)
> +{
> + if (vgdev->mman.mem_global_referenced) {
> + drm_global_item_unref(&vgdev->mman.bo_global_ref.ref);
> + drm_global_item_unref(&vgdev->mman.mem_global_ref);
> + vgdev->mman.mem_global_referenced = false;
> + }
> +}
> +
> +static struct vm_operations_struct virtio_gpu_ttm_vm_ops;
> +static const struct vm_operations_struct *ttm_vm_ops;
What do the globals do? generally it's best to avoid
globals.
> +
> +static int virtio_gpu_ttm_fault(struct vm_area_struct *vma,
> + struct vm_fault *vmf)
> +{
> + struct ttm_buffer_object *bo;
> + struct virtio_gpu_device *vgdev;
> + int r;
> +
> + bo = (struct ttm_buffer_object *)vma->vm_private_data;
> + if (bo == NULL)
> + return VM_FAULT_NOPAGE;
> + vgdev = virtio_gpu_get_vgdev(bo->bdev);
> + r = ttm_vm_ops->fault(vma, vmf);
> + return r;
> +}
> +
> +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + struct drm_file *file_priv;
> + struct virtio_gpu_device *vgdev;
> + int r;
> +
> + file_priv = filp->private_data;
> + vgdev = file_priv->minor->dev->dev_private;
> + if (vgdev == NULL) {
> + DRM_ERROR(
> + "filp->private_data->minor->dev->dev_private == NULL\n");
> + return -EINVAL;
> + }
> + r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev);
> + if (unlikely(r != 0))
> + return r;
> + if (unlikely(ttm_vm_ops == NULL)) {
> + ttm_vm_ops = vma->vm_ops;
> + virtio_gpu_ttm_vm_ops = *ttm_vm_ops;
> + virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault;
These globals are operated without any locks.
Seems racy.
> + }
> + vma->vm_ops = &virtio_gpu_ttm_vm_ops;
> + return 0;
> +}
> +
> +static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev,
> + uint32_t flags)
> +{
> + return 0;
> +}
> +
> +static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
> + struct ttm_buffer_object *bo,
> + const struct ttm_place *place,
> + struct ttm_mem_reg *mem)
> +{
> + mem->mm_node = (void *)1;
> + return 0;
> +}
> +
> +static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man,
> + struct ttm_mem_reg *mem)
> +{
> + mem->mm_node = (void *)NULL;
> + return;
> +}
> +
> +static int ttm_bo_man_init(struct ttm_mem_type_manager *man,
> + unsigned long p_size)
> +{
> + return 0;
> +}
> +
> +static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man)
> +{
> + return 0;
> +}
> +
> +static void ttm_bo_man_debug(struct ttm_mem_type_manager *man,
> + const char *prefix)
> +{
> +}
> +
> +static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = {
> + ttm_bo_man_init,
> + ttm_bo_man_takedown,
> + ttm_bo_man_get_node,
> + ttm_bo_man_put_node,
> + ttm_bo_man_debug
> +};
> +
> +static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
> + struct ttm_mem_type_manager *man)
> +{
> + struct virtio_gpu_device *vgdev;
> +
> + vgdev = virtio_gpu_get_vgdev(bdev);
> +
> + switch (type) {
> + case TTM_PL_SYSTEM:
> + /* System memory */
> + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
> + man->available_caching = TTM_PL_MASK_CACHING;
> + man->default_caching = TTM_PL_FLAG_CACHED;
> + break;
> + case TTM_PL_TT:
> + man->func = &virtio_gpu_bo_manager_func;
> + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
> + man->available_caching = TTM_PL_MASK_CACHING;
> + man->default_caching = TTM_PL_FLAG_CACHED;
> + break;
> + default:
> + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static void virtio_gpu_evict_flags(struct ttm_buffer_object *bo,
> + struct ttm_placement *placement)
> +{
> + static struct ttm_place placements = {
> + .fpfn = 0,
> + .lpfn = 0,
> + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM,
> + };
> +
> + placement->placement = &placements;
> + placement->busy_placement = &placements;
> + placement->num_placement = 1;
> + placement->num_busy_placement = 1;
> + return;
> +}
> +
> +static int virtio_gpu_verify_access(struct ttm_buffer_object *bo,
> + struct file *filp)
> +{
> + return 0;
> +}
> +
> +static int virtio_gpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
> + struct ttm_mem_reg *mem)
> +{
> + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
> +
> + mem->bus.addr = NULL;
> + mem->bus.offset = 0;
> + mem->bus.size = mem->num_pages << PAGE_SHIFT;
> + mem->bus.base = 0;
> + mem->bus.is_iomem = false;
> + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
> + return -EINVAL;
> + switch (mem->mem_type) {
> + case TTM_PL_SYSTEM:
> + case TTM_PL_TT:
> + /* system memory */
> + return 0;
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static void virtio_gpu_ttm_io_mem_free(struct ttm_bo_device *bdev,
> + struct ttm_mem_reg *mem)
> +{
> +}
> +
> +/*
> + * TTM backend functions.
> + */
> +struct virtio_gpu_ttm_tt {
> + struct ttm_dma_tt ttm;
> + struct virtio_gpu_device *vgdev;
> + u64 offset;
> +};
> +
> +static int virtio_gpu_ttm_backend_bind(struct ttm_tt *ttm,
> + struct ttm_mem_reg *bo_mem)
> +{
> + struct virtio_gpu_ttm_tt *gtt = (void *)ttm;
> +
> + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
> + if (!ttm->num_pages) {
> + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
> + ttm->num_pages, bo_mem, ttm);
> + }
single line within {}, pls drop {}/
> + /* Not implemented */
What isn't?
> + return 0;
> +}
> +
> +static int virtio_gpu_ttm_backend_unbind(struct ttm_tt *ttm)
> +{
> + /* Not implemented */
> + return 0;
comment does not seem helpful.
0 means failure here?
> +}
> +
> +static void virtio_gpu_ttm_backend_destroy(struct ttm_tt *ttm)
> +{
> + struct virtio_gpu_ttm_tt *gtt = (void *)ttm;
> +
> + ttm_dma_tt_fini(>t->ttm);
> + kfree(gtt);
> +}
> +
> +static struct ttm_backend_func virtio_gpu_backend_func = {
> + .bind = &virtio_gpu_ttm_backend_bind,
> + .unbind = &virtio_gpu_ttm_backend_unbind,
> + .destroy = &virtio_gpu_ttm_backend_destroy,
> +};
> +
> +static int virtio_gpu_ttm_tt_populate(struct ttm_tt *ttm)
> +{
> + if (ttm->state != tt_unpopulated)
> + return 0;
> +
> + return ttm_pool_populate(ttm);
> +}
> +
> +static void virtio_gpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
> +{
> + ttm_pool_unpopulate(ttm);
> +}
> +
> +static struct ttm_tt *virtio_gpu_ttm_tt_create(struct ttm_bo_device *bdev,
> + unsigned long size,
> + uint32_t page_flags,
> + struct page *dummy_read_page)
> +{
> + struct virtio_gpu_device *vgdev;
> + struct virtio_gpu_ttm_tt *gtt;
> +
> + vgdev = virtio_gpu_get_vgdev(bdev);
> + gtt = kzalloc(sizeof(struct virtio_gpu_ttm_tt), GFP_KERNEL);
> + if (gtt == NULL)
> + return NULL;
> + gtt->ttm.ttm.func = &virtio_gpu_backend_func;
> + gtt->vgdev = vgdev;
> + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags,
> + dummy_read_page)) {
> + kfree(gtt);
> + return NULL;
> + }
> + return >t->ttm.ttm;
> +}
> +
> +static void virtio_gpu_move_null(struct ttm_buffer_object *bo,
> + struct ttm_mem_reg *new_mem)
> +{
> + struct ttm_mem_reg *old_mem = &bo->mem;
> +
> + BUG_ON(old_mem->mm_node != NULL);
> + *old_mem = *new_mem;
> + new_mem->mm_node = NULL;
> +}
> +
> +static int virtio_gpu_bo_move(struct ttm_buffer_object *bo,
> + bool evict, bool interruptible,
> + bool no_wait_gpu,
> + struct ttm_mem_reg *new_mem)
> +{
> + virtio_gpu_move_null(bo, new_mem);
> + return 0;
> +}
> +
> +static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo,
> + struct ttm_mem_reg *new_mem)
> +{
> + struct virtio_gpu_object *bo;
> + struct virtio_gpu_device *vgdev;
> +
> + bo = container_of(tbo, struct virtio_gpu_object, tbo);
> + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
> +
> + if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) {
> + if (bo->hw_res_handle)
> + virtio_gpu_cmd_resource_inval_backing(vgdev,
> + bo->hw_res_handle);
> +
> + } else if (new_mem->placement & TTM_PL_FLAG_TT) {
> + if (bo->hw_res_handle) {
> + virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle,
> + NULL);
> + }
> + }
> +
> + return;
return here is useless.
> +}
> +
> +static void virtio_gpu_bo_swap_notify(struct ttm_buffer_object *tbo)
> +{
> + struct virtio_gpu_object *bo;
> + struct virtio_gpu_device *vgdev;
> +
> + bo = container_of(tbo, struct virtio_gpu_object, tbo);
> + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
> +
> + if (bo->pages)
> + virtio_gpu_object_free_sg_table(bo);
> +}
> +
> +static struct ttm_bo_driver virtio_gpu_bo_driver = {
> + .ttm_tt_create = &virtio_gpu_ttm_tt_create,
> + .ttm_tt_populate = &virtio_gpu_ttm_tt_populate,
> + .ttm_tt_unpopulate = &virtio_gpu_ttm_tt_unpopulate,
> + .invalidate_caches = &virtio_gpu_invalidate_caches,
> + .init_mem_type = &virtio_gpu_init_mem_type,
> + .evict_flags = &virtio_gpu_evict_flags,
> + .move = &virtio_gpu_bo_move,
> + .verify_access = &virtio_gpu_verify_access,
> + .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve,
> + .io_mem_free = &virtio_gpu_ttm_io_mem_free,
> + .move_notify = &virtio_gpu_bo_move_notify,
> + .swap_notify = &virtio_gpu_bo_swap_notify,
> +};
> +
> +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev)
> +{
> + int r;
> +
> + r = virtio_gpu_ttm_global_init(vgdev);
> + if (r)
> + return r;
> + /* No others user of address space so set it to 0 */
No other users?
> + r = ttm_bo_device_init(&vgdev->mman.bdev,
> + vgdev->mman.bo_global_ref.ref.object,
> + &virtio_gpu_bo_driver,
> + vgdev->ddev->anon_inode->i_mapping,
> + DRM_FILE_PAGE_OFFSET, 0);
> + if (r) {
> + DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
> + return r;
> + }
> +
> + r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0);
> + if (r) {
> + DRM_ERROR("Failed initializing GTT heap.\n");
should revert effect of ttm_bo_device_init and
virtio_gpu_ttm_global_init?
> + return r;
> + }
> + return 0;
> +}
> +
> +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev)
> +{
> + ttm_bo_device_release(&vgdev->mman.bdev);
> + virtio_gpu_ttm_global_fini(vgdev);
> + DRM_INFO("virtio_gpu: ttm finalized\n");
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
> new file mode 100644
> index 0000000..a98cda8
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
> @@ -0,0 +1,540 @@
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_ring.h>
> +
> +
> +int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid)
> +{
> + int handle;
> +
> + idr_preload(GFP_KERNEL);
> + spin_lock(&vgdev->resource_idr_lock);
> + handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT);
> + spin_unlock(&vgdev->resource_idr_lock);
> + idr_preload_end();
> + *resid = handle;
> + return 0;
> +}
> +
> +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id)
> +{
> + spin_lock(&vgdev->resource_idr_lock);
> + idr_remove(&vgdev->resource_idr, id);
> + spin_unlock(&vgdev->resource_idr_lock);
> +}
> +
> +void virtio_gpu_ctrl_ack(struct virtqueue *vq)
> +{
> + struct drm_device *dev = vq->vdev->priv;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + schedule_work(&vgdev->ctrlq.dequeue_work);
> +}
> +
> +void virtio_gpu_cursor_ack(struct virtqueue *vq)
> +{
> + struct drm_device *dev = vq->vdev->priv;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + schedule_work(&vgdev->cursorq.dequeue_work);
> +}
> +
> +static struct virtio_gpu_vbuffer*
> +virtio_gpu_allocate_vbuf(struct virtio_gpu_device *vgdev,
> + int size, int resp_size,
> + virtio_gpu_resp_cb resp_cb)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = kzalloc(sizeof(*vbuf) + size + resp_size, GFP_KERNEL);
> + if (!vbuf)
> + goto fail;
> +
> + vbuf->buf = (void *)vbuf + sizeof(*vbuf);
> + vbuf->size = size;
> +
> + vbuf->resp_cb = resp_cb;
> + if (resp_size)
> + vbuf->resp_buf = (void *)vbuf->buf + size;
> + else
> + vbuf->resp_buf = NULL;
> + vbuf->resp_size = resp_size;
> +
> + return vbuf;
> +fail:
> + kfree(vbuf);
> + return ERR_PTR(-ENOMEM);
> +}
> +
> +static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer **vbuffer_p,
> + int size)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = virtio_gpu_allocate_vbuf(vgdev, size,
> + sizeof(struct virtio_gpu_ctrl_hdr), NULL);
> + if (IS_ERR(vbuf)) {
> + *vbuffer_p = NULL;
> + return ERR_CAST(vbuf);
Nice, but then all callers fail to validate the returned values.
What was the point then?
> + }
> + *vbuffer_p = vbuf;
> + return vbuf->buf;
> +}
> +
> +static struct virtio_gpu_update_cursor*
> +virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer **vbuffer_p)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = virtio_gpu_allocate_vbuf
> + (vgdev, sizeof(struct virtio_gpu_update_cursor), 0, NULL);
> + if (IS_ERR(vbuf)) {
> + *vbuffer_p = NULL;
> + return ERR_CAST(vbuf);
> + }
> + *vbuffer_p = vbuf;
> + return (struct virtio_gpu_update_cursor *)vbuf->buf;
> +}
> +
> +static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev,
> + virtio_gpu_resp_cb cb,
> + struct virtio_gpu_vbuffer **vbuffer_p,
> + int cmd_size, int resp_size)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = virtio_gpu_allocate_vbuf(vgdev, cmd_size, resp_size, cb);
> + if (IS_ERR(vbuf)) {
> + *vbuffer_p = NULL;
> + return ERR_CAST(vbuf);
Same issue as virtio_gpu_alloc_cmd
> + }
> + *vbuffer_p = vbuf;
> + return (struct virtio_gpu_command *)vbuf->buf;
> +}
> +
> +static void free_vbuf(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + kfree(vbuf->data_buf);
> + kfree(vbuf);
> +}
> +
> +static int reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> + unsigned int len;
> + int freed = 0;
> + while ((vbuf = virtqueue_get_buf(vq, &len))) {
> + list_add_tail(&vbuf->destroy_list, reclaim_list);
> + freed++;
> + }
> + return freed;
> +}
> +
> +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work)
> +{
> + struct virtio_gpu_device *vgdev =
> + container_of(work, struct virtio_gpu_device,
> + ctrlq.dequeue_work);
> + int ret;
> + struct list_head reclaim_list;
> + struct virtio_gpu_vbuffer *entry, *tmp;
> + struct virtio_gpu_ctrl_hdr *resp;
> + u64 fence_id = 0;
> +
> + INIT_LIST_HEAD(&reclaim_list);
> + spin_lock(&vgdev->ctrlq.qlock);
> + do {
> + virtqueue_disable_cb(vgdev->ctrlq.vq);
> + ret = reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list);
> + if (ret == 0)
> + DRM_DEBUG("cleaned 0 buffers wierd\n");
wierd spelling intentional?
> +
> + } while (!virtqueue_enable_cb(vgdev->ctrlq.vq));
> + spin_unlock(&vgdev->ctrlq.qlock);
> +
> + list_for_each_entry_safe(entry, tmp, &reclaim_list, destroy_list) {
> + resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf;
> + if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA))
> + DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type));
> + if (resp->flags & cpu_to_le32(VIRTIO_GPU_FLAG_FENCE)) {
> + u64 f = le64_to_cpu(resp->fence_id);
> +
> + if (fence_id > f) {
> + DRM_ERROR("%s: Oops: fence %llx -> %llx\n",
> + __func__, fence_id, f);
> + } else {
> + fence_id = f;
> + }
> + }
> + if (entry->resp_cb)
> + entry->resp_cb(vgdev, entry);
> +
> + list_del(&entry->destroy_list);
> + free_vbuf(vgdev, entry);
> + }
> + wake_up(&vgdev->ctrlq.ack_queue);
> +
> + if (fence_id) {
> + virtio_gpu_fence_event_process(vgdev, fence_id);
> + }
don't put {} around single line blocks.
> +}
> +
> +void virtio_gpu_dequeue_cursor_func(struct work_struct *work)
> +{
> + struct virtio_gpu_device *vgdev =
> + container_of(work, struct virtio_gpu_device,
> + cursorq.dequeue_work);
> + struct virtqueue *vq = vgdev->cursorq.vq;
> + struct list_head reclaim_list;
> + struct virtio_gpu_vbuffer *entry, *tmp;
> + unsigned int len;
> + int ret;
> +
> + INIT_LIST_HEAD(&reclaim_list);
> + spin_lock(&vgdev->cursorq.qlock);
> + do {
> + virtqueue_disable_cb(vgdev->cursorq.vq);
> + ret = reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list);
> + if (ret == 0)
> + DRM_DEBUG("cleaned 0 buffers wierd\n");
> + while (virtqueue_get_buf(vq, &len))
> + /* nothing */;
> + } while (!virtqueue_enable_cb(vgdev->cursorq.vq));
> + spin_unlock(&vgdev->cursorq.qlock);
> +
> + list_for_each_entry_safe(entry, tmp, &reclaim_list, destroy_list) {
> + list_del(&entry->destroy_list);
> + free_vbuf(vgdev, entry);
> + }
> + wake_up(&vgdev->cursorq.ack_queue);
> +}
> +
> +static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + struct virtqueue *vq = vgdev->ctrlq.vq;
> + struct scatterlist *sgs[3], vcmd, vout, vresp;
> + int outcnt = 0, incnt = 0;
> + int ret;
> +
> + sg_init_one(&vcmd, vbuf->buf, vbuf->size);
> + sgs[outcnt+incnt] = &vcmd;
> + outcnt++;
> +
> + if (vbuf->data_buf) {
> + sg_init_one(&vout, vbuf->data_buf, vbuf->data_size);
> + sgs[outcnt+incnt] = &vout;
Need space around + here and elsewhere.
> + outcnt++;
> + }
> +
> + if (vbuf->resp_buf) {
> + sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size);
> + sgs[outcnt+incnt] = &vresp;
> + incnt++;
> + }
> +
> + spin_lock(&vgdev->ctrlq.qlock);
> +retry:
> + ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + spin_unlock(&vgdev->ctrlq.qlock);
> + wait_event(vgdev->ctrlq.ack_queue, vq->num_free);
> + spin_lock(&vgdev->ctrlq.qlock);
> + goto retry;
> + } else {
> + virtqueue_kick(vq);
> + }
This can fail on OOM too since you are
using GFP_ATOMIC, you mustn't fail in this case,
you should handle this gracefully by retrying
later.
Maybe change qlock to a mutex, and GFP_ATOMIC to GFP_KERNEL.
> + spin_unlock(&vgdev->ctrlq.qlock);
> +
> + if (!ret)
> + ret = vq->num_free;
> + return ret;
> +}
> +
> +static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + struct virtqueue *vq = vgdev->cursorq.vq;
> + struct scatterlist *sgs[1], ccmd;
> + int ret;
> + int outcnt;
> +
> + sg_init_one(&ccmd, vbuf->buf, vbuf->size);
> + sgs[0] = &ccmd;
> + outcnt = 1;
> +
> + spin_lock(&vgdev->cursorq.qlock);
> +retry:
> + ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + spin_unlock(&vgdev->cursorq.qlock);
> + wait_event(vgdev->cursorq.ack_queue, vq->num_free);
checking num_free outside lock seems risky.
For example, you might see a stale num_free
value and block forever.
> + spin_lock(&vgdev->cursorq.qlock);
> + goto retry;
> + } else {
> + virtqueue_kick(vq);
> + }
> +
> + spin_unlock(&vgdev->cursorq.qlock);
> +
> + if (!ret)
> + ret = vq->num_free;
> + return ret;
> +}
> +
> +/* just create gem objects for userspace and long lived objects,
> + just use dma_alloced pages for the queue objects? */
> +
> +/* create a basic resource */
> +int virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t format,
> + uint32_t width,
> + uint32_t height)
> +{
> + struct virtio_gpu_resource_create_2d *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->format = cpu_to_le32(format);
> + cmd_p->width = cpu_to_le32(width);
> + cmd_p->height = cpu_to_le32(height);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id)
> +{
> + struct virtio_gpu_resource_unref *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id)
> +{
> + struct virtio_gpu_resource_detach_backing *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
> + uint32_t scanout_id, uint32_t resource_id,
> + uint32_t width, uint32_t height,
> + uint32_t x, uint32_t y)
> +{
> + struct virtio_gpu_set_scanout *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SET_SCANOUT);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->scanout_id = cpu_to_le32(scanout_id);
> + cmd_p->r.width = cpu_to_le32(width);
> + cmd_p->r.height = cpu_to_le32(height);
> + cmd_p->r.x = cpu_to_le32(x);
> + cmd_p->r.y = cpu_to_le32(y);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t x, uint32_t y,
> + uint32_t width, uint32_t height)
> +{
> + struct virtio_gpu_resource_flush *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->r.width = cpu_to_le32(width);
> + cmd_p->r.height = cpu_to_le32(height);
> + cmd_p->r.x = cpu_to_le32(x);
> + cmd_p->r.y = cpu_to_le32(y);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id, uint64_t offset,
> + __le32 width, __le32 height,
> + __le32 x, __le32 y,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_transfer_to_host_2d *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->offset = cpu_to_le64(offset);
> + cmd_p->r.width = width;
> + cmd_p->r.height = height;
> + cmd_p->r.x = x;
> + cmd_p->r.y = y;
> +
> + if (fence)
> + virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +static int
> +virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + struct virtio_gpu_mem_entry *ents,
> + uint32_t nents,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_resource_attach_backing *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->nr_entries = cpu_to_le32(nents);
> +
> + vbuf->data_buf = ents;
> + vbuf->data_size = sizeof(*ents) * nents;
> +
> + if (fence)
> + virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + struct virtio_gpu_resp_display_info *resp =
> + (struct virtio_gpu_resp_display_info *)vbuf->resp_buf;
> + int i;
> +
> + spin_lock(&vgdev->display_info_lock);
> + for (i = 0; i < vgdev->num_scanouts; i++) {
> + vgdev->outputs[i].info = resp->pmodes[i];
> + if (resp->pmodes[i].enabled) {
> + DRM_DEBUG("output %d: %dx%d+%d+%d", i,
> + le32_to_cpu(resp->pmodes[i].r.width),
> + le32_to_cpu(resp->pmodes[i].r.height),
> + le32_to_cpu(resp->pmodes[i].r.x),
> + le32_to_cpu(resp->pmodes[i].r.y));
> + } else {
> + DRM_DEBUG("output %d: disabled", i);
> + }
> + }
> +
> + spin_unlock(&vgdev->display_info_lock);
> + wake_up(&vgdev->resp_wq);
> +
> + if (!drm_helper_hpd_irq_event(vgdev->ddev)) {
> + drm_kms_helper_hotplug_event(vgdev->ddev);
> + }
Don't put {} around single-statements.
> +}
> +
> +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
> +{
> + struct virtio_gpu_ctrl_hdr *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd_resp
> + (vgdev, &virtio_gpu_cmd_get_display_info_cb, &vbuf,
> + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_display_info));
Pls split it like this:
+ cmd_p = virtio_gpu_alloc_cmd_resp(vgdev,
+ &virtio_gpu_cmd_get_display_info_cb,
+ &vbuf,
+ sizeof(*cmd_p),
+ sizeof(struct virtio_gpu_resp_display_info));
i.e. ( on same line as function name.
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->type = cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO);
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> + return 0;
> +}
make this function void?
> +
> +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_object *obj,
> + uint32_t resource_id,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_mem_entry *ents;
> + struct scatterlist *sg;
> + int si;
> +
> + if (!obj->pages) {
> + int ret;
> + ret = virtio_gpu_object_get_sg_table(vgdev, obj);
> + if (ret)
> + return ret;
> + }
> +
> + /* gets freed when the ring has consumed it */
> + ents = kmalloc_array(obj->pages->nents,
> + sizeof(struct virtio_gpu_mem_entry),
> + GFP_KERNEL);
> + if (!ents) {
> + DRM_ERROR("failed to allocate ent list\n");
> + return -ENOMEM;
> + }
> +
> + for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) {
> + ents[si].addr = cpu_to_le64(sg_phys(sg));
> + ents[si].length = cpu_to_le32(sg->length);
> + ents[si].padding = 0;
> + }
> +
> + virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id,
> + ents, obj->pages->nents,
> + fence);
> + obj->hw_res_handle = resource_id;
> + return 0;
> +}
> +
> +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_output *output)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> + struct virtio_gpu_update_cursor *cur_p;
> +
> + output->cursor.pos.scanout_id = cpu_to_le32(output->index);
> + cur_p = virtio_gpu_alloc_cursor(vgdev, &vbuf);
> + memcpy(cur_p, &output->cursor, sizeof(output->cursor));
> + virtio_gpu_queue_cursor(vgdev, vbuf);
> +}
> diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
> index e894eb2..a3167fa 100644
> --- a/drivers/virtio/virtio_pci_common.c
> +++ b/drivers/virtio/virtio_pci_common.c
> @@ -510,7 +510,7 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
> goto err_enable_device;
>
> rc = pci_request_regions(pci_dev, "virtio-pci");
> - if (rc)
> + if (rc && ((pci_dev->class >> 8) != PCI_CLASS_DISPLAY_VGA))
> goto err_request_regions;
>
> if (force_legacy) {
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index e928625..a1067c4 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -799,6 +799,7 @@ struct drm_device {
> #endif
>
> struct platform_device *platformdev; /**< Platform device struture */
> + struct virtio_device *virtdev;
>
> struct drm_sg_mem *sg; /**< Scatter gather memory */
> unsigned int num_crtcs; /**< Number of CRTCs on this device */
> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> index 68ceb97..9707e5d 100644
> --- a/include/uapi/linux/Kbuild
> +++ b/include/uapi/linux/Kbuild
> @@ -429,6 +429,7 @@ header-y += virtio_balloon.h
> header-y += virtio_blk.h
> header-y += virtio_config.h
> header-y += virtio_console.h
> +header-y += virtio_gpu.h
> header-y += virtio_ids.h
> header-y += virtio_net.h
> header-y += virtio_pci.h
> diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
> new file mode 100644
> index 0000000..a1bda52
> --- /dev/null
> +++ b/include/uapi/linux/virtio_gpu.h
> @@ -0,0 +1,203 @@
> +/*
> + * Virtio GPU Device
> + *
> + * Copyright Red Hat, Inc. 2013-2014
> + *
> + * Authors:
> + * Dave Airlie <airlied@redhat.com>
> + * Gerd Hoffmann <kraxel@redhat.com>
> + *
> + * This header is BSD licensed so anyone can use the definitions
> + * to implement compatible drivers/servers:
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + * 3. Neither the name of IBM nor the names of its contributors
> + * may be used to endorse or promote products derived from this software
> + * without specific prior written permission.
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
> + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> + * SUCH DAMAGE.
> + */
> +
> +#ifndef VIRTIO_GPU_HW_H
> +#define VIRTIO_GPU_HW_H
> +
> +enum virtio_gpu_ctrl_type {
> + VIRTIO_GPU_UNDEFINED = 0,
> +
> + /* 2d commands */
> + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
> + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
> + VIRTIO_GPU_CMD_RESOURCE_UNREF,
> + VIRTIO_GPU_CMD_SET_SCANOUT,
> + VIRTIO_GPU_CMD_RESOURCE_FLUSH,
> + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
> + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
> + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
> +
> + /* cursor commands */
> + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
> + VIRTIO_GPU_CMD_MOVE_CURSOR,
> +
> + /* success responses */
> + VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
> + VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
> +
> + /* error responses */
> + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
> + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
> + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
> + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
> + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
> + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
> +};
> +
> +#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
> +
> +struct virtio_gpu_ctrl_hdr {
> + __le32 type;
> + __le32 flags;
> + __le64 fence_id;
> + __le32 ctx_id;
> + __le32 padding;
> +};
> +
> +/* data passed in the cursor vq */
> +
> +struct virtio_gpu_cursor_pos {
> + __le32 scanout_id;
> + __le32 x, y;
Prefer
__le32 x;
__le32 y;
so struct size is obvious.
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
> +struct virtio_gpu_update_cursor {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_cursor_pos pos; /* update & move */
> + __le32 resource_id; /* update only */
> + __le32 hot_x; /* update only */
> + __le32 hot_y; /* update only */
> + __le32 padding;
> +};
> +
> +/* data passed in the control vq, 2d related */
> +
refers to struct virtio_gpu_rect only? or all
structs below?
> +struct virtio_gpu_rect {
> + __le32 x, y;
> + __le32 width;
> + __le32 height;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
> +struct virtio_gpu_resource_unref {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
> +struct virtio_gpu_resource_create_2d {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 format;
> + __le32 width;
> + __le32 height;
> +};
> +
> +/* VIRTIO_GPU_CMD_SET_SCANOUT */
> +struct virtio_gpu_set_scanout {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_rect r;
> + __le32 scanout_id;
> + __le32 resource_id;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
> +struct virtio_gpu_resource_flush {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_rect r;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
> +struct virtio_gpu_transfer_to_host_2d {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_rect r;
> + __le64 offset;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +struct virtio_gpu_mem_entry {
> + __le64 addr;
> + __le32 length;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
> +struct virtio_gpu_resource_attach_backing {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 nr_entries;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
> +struct virtio_gpu_resource_detach_backing {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
> +#define VIRTIO_GPU_MAX_SCANOUTS 16
> +struct virtio_gpu_resp_display_info {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_display_one {
> + struct virtio_gpu_rect r;
> + __le32 enabled;
> + __le32 flags;
> + } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
> +};
> +
> +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
> +
> +struct virtio_gpu_config {
> + __u32 events_read;
> + __u32 events_clear;
> + __u32 num_scanouts;
> + __u32 reserved;
> +};
> +
> +/* simple formats for fbcon/X use */
> +enum virtio_gpu_formats {
> + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
> + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2,
> + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3,
> + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4,
> +
> + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
> + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68,
> +
> + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
> + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
> +
drop empty line here
> +};
> +
> +#endif
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 284fc3a..14d77f7 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -39,5 +39,5 @@
> #define VIRTIO_ID_9P 9 /* 9p virtio console */
> #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
> #define VIRTIO_ID_CAIF 12 /* Virtio caif */
> -
> +#define VIRTIO_ID_GPU 16
I think we like the emoty line before endif.
> #endif /* _LINUX_VIRTIO_IDS_H */
> --
> 1.8.3.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH] Add virtio gpu driver.
From: Daniel Vetter @ 2015-03-24 16:50 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b, Michael S. Tsirkin,
open list:ABI/API, Rusty Russell, open list,
open list:DRM DRIVERS, open list:VIRTIO CORE, NET..., Dave Airlie
In-Reply-To: <1427213239-8775-1-git-send-email-kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
On Tue, Mar 24, 2015 at 05:07:18PM +0100, Gerd Hoffmann wrote:
> From: Dave Airlie <airlied-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> This patch adds a kms driver for the virtio gpu. The xorg modesetting
> driver can handle the device just fine, the framebuffer for fbcon is
> there too.
>
> Qemu patches for the host side are under review currently.
>
> The pci version of the device comes in two variants: with and without
> vga compatibility. The former has a extra memory bar for the vga
> framebuffer, the later is a pure virtio device. The only concern for
> this driver is that in the virtio-vga case we have to kick out the
> firmware framebuffer.
>
> Initial revision has only 2d support, 3d (virgl) support requires
> some more work on the qemu side and will be added later.
>
> Signed-off-by: Dave Airlie <airlied-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Standard request from my side for new drm drivers (especially if they're
this simple): Can you please update the drivers to latest drm internal
interfaces, i.e. using universal planes and atomic?
Thanks, Daniel
> ---
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/virtio/Kconfig | 11 +
> drivers/gpu/drm/virtio/Makefile | 9 +
> drivers/gpu/drm/virtio/virtgpu_debugfs.c | 64 ++++
> drivers/gpu/drm/virtio/virtgpu_display.c | 527 ++++++++++++++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 68 ++++
> drivers/gpu/drm/virtio/virtgpu_drv.c | 132 ++++++++
> drivers/gpu/drm/virtio/virtgpu_drv.h | 326 +++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_fb.c | 415 ++++++++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_fence.c | 95 ++++++
> drivers/gpu/drm/virtio/virtgpu_gem.c | 120 +++++++
> drivers/gpu/drm/virtio/virtgpu_kms.c | 125 +++++++
> drivers/gpu/drm/virtio/virtgpu_object.c | 174 ++++++++++
> drivers/gpu/drm/virtio/virtgpu_ttm.c | 451 ++++++++++++++++++++++++++
> drivers/gpu/drm/virtio/virtgpu_vq.c | 540 +++++++++++++++++++++++++++++++
> drivers/virtio/virtio_pci_common.c | 2 +-
> include/drm/drmP.h | 1 +
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/virtio_gpu.h | 203 ++++++++++++
> include/uapi/linux/virtio_ids.h | 2 +-
> 21 files changed, 3267 insertions(+), 2 deletions(-)
> create mode 100644 drivers/gpu/drm/virtio/Kconfig
> create mode 100644 drivers/gpu/drm/virtio/Makefile
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_debugfs.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_display.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.h
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_fb.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_fence.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_gem.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_kms.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_object.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_ttm.c
> create mode 100644 drivers/gpu/drm/virtio/virtgpu_vq.c
> create mode 100644 include/uapi/linux/virtio_gpu.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 151a050..f2388ea 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -197,6 +197,8 @@ source "drivers/gpu/drm/qxl/Kconfig"
>
> source "drivers/gpu/drm/bochs/Kconfig"
>
> +source "drivers/gpu/drm/virtio/Kconfig"
> +
> source "drivers/gpu/drm/msm/Kconfig"
>
> source "drivers/gpu/drm/tegra/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 2c239b9..083d443 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/
> obj-$(CONFIG_DRM_TILCDC) += tilcdc/
> obj-$(CONFIG_DRM_QXL) += qxl/
> obj-$(CONFIG_DRM_BOCHS) += bochs/
> +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
> obj-$(CONFIG_DRM_MSM) += msm/
> obj-$(CONFIG_DRM_TEGRA) += tegra/
> obj-$(CONFIG_DRM_STI) += sti/
> diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig
> new file mode 100644
> index 0000000..55868e2
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/Kconfig
> @@ -0,0 +1,11 @@
> +config DRM_VIRTIO_GPU
> + tristate "QEMU Virtio GPU"
> + depends on DRM && VIRTIO
> + select FB_SYS_FILLRECT
> + select FB_SYS_COPYAREA
> + select FB_SYS_IMAGEBLIT
> + select DRM_KMS_HELPER
> + select DRM_KMS_FB_HELPER
> + select DRM_TTM
> + help
> + QEMU based virtio GPU.
> diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
> new file mode 100644
> index 0000000..57d59ee
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Makefile for the drm device driver. This driver provides support for the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +ccflags-y := -Iinclude/drm
> +
> +virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o
> +
> +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
> diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
> new file mode 100644
> index 0000000..dbc497d
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
> @@ -0,0 +1,64 @@
> +/*
> + * Copyright (C) 2009 Red Hat
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial
> + * portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
> + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +#include <linux/debugfs.h>
> +
> +#include "drmP.h"
> +#include "virtgpu_drv.h"
> +
> +static int
> +virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data)
> +{
> + struct drm_info_node *node = (struct drm_info_node *) m->private;
> + struct virtio_gpu_device *vgdev = node->minor->dev->dev_private;
> +
> + seq_printf(m, "fence %ld %lld\n",
> + atomic64_read(&vgdev->fence_drv.last_seq),
> + vgdev->fence_drv.sync_seq);
> + return 0;
> +}
> +
> +static struct drm_info_list virtio_gpu_debugfs_list[] = {
> + { "irq_fence", virtio_gpu_debugfs_irq_info, 0, NULL },
> +};
> +
> +#define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list)
> +
> +int
> +virtio_gpu_debugfs_init(struct drm_minor *minor)
> +{
> + drm_debugfs_create_files(virtio_gpu_debugfs_list,
> + VIRTIO_GPU_DEBUGFS_ENTRIES,
> + minor->debugfs_root, minor);
> + return 0;
> +}
> +
> +void
> +virtio_gpu_debugfs_takedown(struct drm_minor *minor)
> +{
> + drm_debugfs_remove_files(virtio_gpu_debugfs_list,
> + VIRTIO_GPU_DEBUGFS_ENTRIES,
> + minor);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
> new file mode 100644
> index 0000000..578a02c
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_display.c
> @@ -0,0 +1,527 @@
> +/*
> + * Copyright 2013 Red Hat Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors: Dave Airlie
> + * Alon Levy
> + */
> +
> +#include "virtgpu_drv.h"
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#define XRES_MIN 320
> +#define YRES_MIN 200
> +
> +#define XRES_DEF 1024
> +#define YRES_DEF 768
> +
> +#define XRES_MAX 8192
> +#define YRES_MAX 8192
> +
> +static void virtio_gpu_crtc_gamma_set(struct drm_crtc *crtc,
> + u16 *red, u16 *green, u16 *blue,
> + uint32_t start, uint32_t size)
> +{
> + /* TODO */
> +}
> +
> +static void
> +virtio_gpu_hide_cursor(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_output *output)
> +{
> + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
> + output->cursor.resource_id = 0;
> + virtio_gpu_cursor_ping(vgdev, output);
> +}
> +
> +static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc,
> + struct drm_file *file_priv,
> + uint32_t handle,
> + uint32_t width,
> + uint32_t height,
> + int32_t hot_x, int32_t hot_y)
> +{
> + struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
> + struct virtio_gpu_output *output =
> + container_of(crtc, struct virtio_gpu_output, crtc);
> + struct drm_gem_object *gobj = NULL;
> + struct virtio_gpu_object *qobj = NULL;
> + struct virtio_gpu_fence *fence = NULL;
> + int ret = 0;
> +
> + if (handle == 0) {
> + virtio_gpu_hide_cursor(vgdev, output);
> + return 0;
> + }
> +
> + /* lookup the cursor */
> + gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
> + if (gobj == NULL)
> + return -ENOENT;
> +
> + qobj = gem_to_virtio_gpu_obj(gobj);
> +
> + if (!qobj->hw_res_handle) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ret = virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0,
> + cpu_to_le32(64),
> + cpu_to_le32(64),
> + 0, 0, &fence);
> + if (!ret) {
> + reservation_object_add_excl_fence(qobj->tbo.resv,
> + &fence->f);
> + virtio_gpu_object_wait(qobj, false);
> + }
> +
> + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
> + output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle);
> + output->cursor.hot_x = cpu_to_le32(hot_x);
> + output->cursor.hot_y = cpu_to_le32(hot_y);
> + virtio_gpu_cursor_ping(vgdev, output);
> +out:
> + drm_gem_object_unreference_unlocked(gobj);
> + return ret;
> +}
> +
> +static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc,
> + int x, int y)
> +{
> + struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
> + struct virtio_gpu_output *output =
> + container_of(crtc, struct virtio_gpu_output, crtc);
> +
> + output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR);
> + output->cursor.pos.x = cpu_to_le32(x);
> + output->cursor.pos.y = cpu_to_le32(y);
> + virtio_gpu_cursor_ping(vgdev, output);
> + return 0;
> +}
> +
> +static int virtio_gpu_crtc_page_flip(struct drm_crtc *crtc,
> + struct drm_framebuffer *fb,
> + struct drm_pending_vblank_event *event,
> + uint32_t flags)
> +{
> + return -EINVAL;
> +}
> +
> +
> +static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
> + .cursor_set2 = virtio_gpu_crtc_cursor_set,
> + .cursor_move = virtio_gpu_crtc_cursor_move,
> + .gamma_set = virtio_gpu_crtc_gamma_set,
> + .set_config = drm_crtc_helper_set_config,
> + .page_flip = virtio_gpu_crtc_page_flip,
> + .destroy = drm_crtc_cleanup,
> +};
> +
> +static void virtio_gpu_user_framebuffer_destroy(struct drm_framebuffer *fb)
> +{
> + struct virtio_gpu_framebuffer *virtio_gpu_fb
> + = to_virtio_gpu_framebuffer(fb);
> +
> + if (virtio_gpu_fb->obj)
> + drm_gem_object_unreference_unlocked(virtio_gpu_fb->obj);
> + drm_framebuffer_cleanup(fb);
> + kfree(virtio_gpu_fb);
> +}
> +
> +static int
> +virtio_gpu_framebuffer_surface_dirty(struct drm_framebuffer *fb,
> + struct drm_file *file_priv,
> + unsigned flags, unsigned color,
> + struct drm_clip_rect *clips,
> + unsigned num_clips)
> +{
> + struct virtio_gpu_framebuffer *virtio_gpu_fb
> + = to_virtio_gpu_framebuffer(fb);
> +
> + return virtio_gpu_surface_dirty(virtio_gpu_fb, clips, num_clips);
> +}
> +
> +static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
> + .destroy = virtio_gpu_user_framebuffer_destroy,
> + .dirty = virtio_gpu_framebuffer_surface_dirty,
> +};
> +
> +int
> +virtio_gpu_framebuffer_init(struct drm_device *dev,
> + struct virtio_gpu_framebuffer *vgfb,
> + struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object *obj)
> +{
> + int ret;
> + struct virtio_gpu_object *bo;
> + vgfb->obj = obj;
> +
> + bo = gem_to_virtio_gpu_obj(obj);
> +
> + ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs);
> + if (ret) {
> + vgfb->obj = NULL;
> + return ret;
> + }
> + drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd);
> +
> + spin_lock_init(&vgfb->dirty_lock);
> + vgfb->x1 = vgfb->y1 = INT_MAX;
> + vgfb->x2 = vgfb->y2 = 0;
> + return 0;
> +}
> +
> +static void virtio_gpu_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +}
> +
> +static bool virtio_gpu_crtc_mode_fixup(struct drm_crtc *crtc,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + return true;
> +}
> +
> +static int virtio_gpu_crtc_mode_set(struct drm_crtc *crtc,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode,
> + int x, int y,
> + struct drm_framebuffer *old_fb)
> +{
> + struct drm_device *dev = crtc->dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct virtio_gpu_framebuffer *vgfb;
> + struct virtio_gpu_object *bo, *old_bo = NULL;
> + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
> +
> + if (!crtc->primary->fb) {
> + DRM_DEBUG_KMS("No FB bound\n");
> + return 0;
> + }
> +
> + if (old_fb) {
> + vgfb = to_virtio_gpu_framebuffer(old_fb);
> + old_bo = gem_to_virtio_gpu_obj(vgfb->obj);
> + }
> + vgfb = to_virtio_gpu_framebuffer(crtc->primary->fb);
> + bo = gem_to_virtio_gpu_obj(vgfb->obj);
> + DRM_DEBUG("+%d+%d (%d,%d) => (%d,%d)\n",
> + x, y,
> + mode->hdisplay, mode->vdisplay,
> + adjusted_mode->hdisplay,
> + adjusted_mode->vdisplay);
> +
> + virtio_gpu_cmd_set_scanout(vgdev, output->index, bo->hw_res_handle,
> + mode->hdisplay, mode->vdisplay, x, y);
> +
> + return 0;
> +}
> +
> +static void virtio_gpu_crtc_prepare(struct drm_crtc *crtc)
> +{
> + DRM_DEBUG("current: %dx%d+%d+%d (%d).\n",
> + crtc->mode.hdisplay, crtc->mode.vdisplay,
> + crtc->x, crtc->y, crtc->enabled);
> +}
> +
> +static void virtio_gpu_crtc_commit(struct drm_crtc *crtc)
> +{
> + DRM_DEBUG("\n");
> +}
> +
> +static void virtio_gpu_crtc_load_lut(struct drm_crtc *crtc)
> +{
> +}
> +
> +static void virtio_gpu_crtc_disable(struct drm_crtc *crtc)
> +{
> + struct drm_device *dev = crtc->dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
> +
> + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0);
> +}
> +
> +static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
> + .disable = virtio_gpu_crtc_disable,
> + .dpms = virtio_gpu_crtc_dpms,
> + .mode_fixup = virtio_gpu_crtc_mode_fixup,
> + .mode_set = virtio_gpu_crtc_mode_set,
> + .prepare = virtio_gpu_crtc_prepare,
> + .commit = virtio_gpu_crtc_commit,
> + .load_lut = virtio_gpu_crtc_load_lut,
> +};
> +
> +static void virtio_gpu_enc_dpms(struct drm_encoder *encoder, int mode)
> +{
> +}
> +
> +static bool virtio_gpu_enc_mode_fixup(struct drm_encoder *encoder,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> + return true;
> +}
> +
> +static void virtio_gpu_enc_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void virtio_gpu_enc_commit(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static int virtio_gpu_conn_get_modes(struct drm_connector *connector)
> +{
> + struct virtio_gpu_output *output =
> + drm_connector_to_virtio_gpu_output(connector);
> + struct drm_display_mode *mode = NULL;
> + int count, width, height;
> +
> + width = le32_to_cpu(output->info.r.width);
> + height = le32_to_cpu(output->info.r.height);
> + count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
> +
> + if (width == 0 || height == 0) {
> + width = XRES_DEF;
> + height = YRES_DEF;
> + drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
> + } else {
> + DRM_DEBUG("add mode: %dx%d\n", width, height);
> + mode = drm_cvt_mode(connector->dev, width, height, 60,
> + false, false, false);
> + mode->type |= DRM_MODE_TYPE_PREFERRED;
> + drm_mode_probed_add(connector, mode);
> + count++;
> + }
> +
> + return count;
> +}
> +
> +static int virtio_gpu_conn_mode_valid(struct drm_connector *connector,
> + struct drm_display_mode *mode)
> +{
> + struct virtio_gpu_output *output =
> + drm_connector_to_virtio_gpu_output(connector);
> + int width, height;
> +
> + width = le32_to_cpu(output->info.r.width);
> + height = le32_to_cpu(output->info.r.height);
> +
> + if (!(mode->type & DRM_MODE_TYPE_PREFERRED))
> + return MODE_OK;
> + if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF)
> + return MODE_OK;
> + if (mode->hdisplay <= width && mode->hdisplay >= width - 16 &&
> + mode->vdisplay <= height && mode->vdisplay >= height - 16)
> + return MODE_OK;
> +
> + DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay);
> + return MODE_BAD;
> +}
> +
> +static struct drm_encoder*
> +virtio_gpu_best_encoder(struct drm_connector *connector)
> +{
> + struct virtio_gpu_output *virtio_gpu_output =
> + drm_connector_to_virtio_gpu_output(connector);
> +
> + return &virtio_gpu_output->enc;
> +}
> +
> +
> +static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
> + .dpms = virtio_gpu_enc_dpms,
> + .mode_fixup = virtio_gpu_enc_mode_fixup,
> + .prepare = virtio_gpu_enc_prepare,
> + .mode_set = virtio_gpu_enc_mode_set,
> + .commit = virtio_gpu_enc_commit,
> +};
> +
> +static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
> + .get_modes = virtio_gpu_conn_get_modes,
> + .mode_valid = virtio_gpu_conn_mode_valid,
> + .best_encoder = virtio_gpu_best_encoder,
> +};
> +
> +static void virtio_gpu_conn_save(struct drm_connector *connector)
> +{
> + DRM_DEBUG("\n");
> +}
> +
> +static void virtio_gpu_conn_restore(struct drm_connector *connector)
> +{
> + DRM_DEBUG("\n");
> +}
> +
> +static enum drm_connector_status virtio_gpu_conn_detect(
> + struct drm_connector *connector,
> + bool force)
> +{
> + struct virtio_gpu_output *output =
> + drm_connector_to_virtio_gpu_output(connector);
> +
> + if (output->info.enabled)
> + return connector_status_connected;
> + else
> + return connector_status_disconnected;
> +}
> +
> +static int virtio_gpu_conn_set_property(struct drm_connector *connector,
> + struct drm_property *property,
> + uint64_t value)
> +{
> + DRM_DEBUG("\n");
> + return 0;
> +}
> +
> +static void virtio_gpu_conn_destroy(struct drm_connector *connector)
> +{
> + struct virtio_gpu_output *virtio_gpu_output =
> + drm_connector_to_virtio_gpu_output(connector);
> +
> + drm_connector_unregister(connector);
> + drm_connector_cleanup(connector);
> + kfree(virtio_gpu_output);
> +}
> +
> +static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
> + .dpms = drm_helper_connector_dpms,
> + .save = virtio_gpu_conn_save,
> + .restore = virtio_gpu_conn_restore,
> + .detect = virtio_gpu_conn_detect,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .set_property = virtio_gpu_conn_set_property,
> + .destroy = virtio_gpu_conn_destroy,
> +};
> +
> +static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
> + .destroy = drm_encoder_cleanup,
> +};
> +
> +static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
> +{
> + struct drm_device *dev = vgdev->ddev;
> + struct virtio_gpu_output *output = vgdev->outputs + index;
> + struct drm_connector *connector = &output->conn;
> + struct drm_encoder *encoder = &output->enc;
> + struct drm_crtc *crtc = &output->crtc;
> +
> + output->index = index;
> + if (index == 0) {
> + output->info.enabled = cpu_to_le32(true);
> + output->info.r.width = cpu_to_le32(XRES_DEF);
> + output->info.r.height = cpu_to_le32(YRES_DEF);
> + }
> +
> + drm_crtc_init(dev, crtc, &virtio_gpu_crtc_funcs);
> + drm_mode_crtc_set_gamma_size(crtc, 256);
> + drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
> +
> + drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
> + DRM_MODE_CONNECTOR_VIRTUAL);
> + connector->polled = DRM_CONNECTOR_POLL_HPD;
> + drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
> + DRM_MODE_ENCODER_VIRTUAL);
> +
> + encoder->possible_crtcs = 1 << index;
> + drm_mode_connector_attach_encoder(connector, encoder);
> + drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
> + drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
> + drm_connector_register(connector);
> + return 0;
> +}
> +
> +static struct drm_framebuffer *
> +virtio_gpu_user_framebuffer_create(struct drm_device *dev,
> + struct drm_file *file_priv,
> + struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> + struct drm_gem_object *obj = NULL;
> + struct virtio_gpu_framebuffer *virtio_gpu_fb;
> + int ret;
> +
> + /* lookup object associated with res handle */
> + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> + if (!obj)
> + return ERR_PTR(-EINVAL);
> +
> + virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL);
> + if (virtio_gpu_fb == NULL)
> + return ERR_PTR(-ENOMEM);
> +
> + ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj);
> + if (ret) {
> + kfree(virtio_gpu_fb);
> + if (obj)
> + drm_gem_object_unreference_unlocked(obj);
> + return NULL;
> + }
> +
> + return &virtio_gpu_fb->base;
> +}
> +
> +static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
> + .fb_create = virtio_gpu_user_framebuffer_create,
> +};
> +
> +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
> +{
> + int i;
> + int ret;
> +
> + drm_mode_config_init(vgdev->ddev);
> + vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs;
> +
> + /* modes will be validated against the framebuffer size */
> + vgdev->ddev->mode_config.min_width = XRES_MIN;
> + vgdev->ddev->mode_config.min_height = YRES_MIN;
> + vgdev->ddev->mode_config.max_width = XRES_MAX;
> + vgdev->ddev->mode_config.max_height = YRES_MAX;
> +
> + for (i = 0 ; i < vgdev->num_scanouts; ++i)
> + vgdev_output_init(vgdev, i);
> +
> + /* primary surface must be created by this point, to allow
> + * issuing command queue commands and having them read by
> + * spice server. */
> + ret = virtio_gpu_fbdev_init(vgdev);
> + if (ret)
> + return ret;
> +
> + ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts);
> +
> + drm_kms_helper_poll_init(vgdev->ddev);
> + return ret;
> +}
> +
> +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
> +{
> + virtio_gpu_fbdev_fini(vgdev);
> + drm_mode_config_cleanup(vgdev->ddev);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> new file mode 100644
> index 0000000..e4b50af
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
> @@ -0,0 +1,68 @@
> +#include <linux/pci.h>
> +
> +#include "virtgpu_drv.h"
> +
> +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master)
> +{
> + struct pci_dev *pdev = dev->pdev;
> +
> + if (pdev) {
> + return drm_pci_set_busid(dev, master);
> + }
> + return 0;
> +}
> +
> +static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev)
> +{
> + struct apertures_struct *ap;
> + bool primary;
> + ap = alloc_apertures(1);
> + if (!ap)
> + return;
> +
> + ap->ranges[0].base = pci_resource_start(pci_dev, 2);
> + ap->ranges[0].size = pci_resource_len(pci_dev, 2);
> +
> + primary = pci_dev->resource[PCI_ROM_RESOURCE].flags
> + & IORESOURCE_ROM_SHADOW;
> +
> + remove_conflicting_framebuffers(ap, "virtiodrmfb", primary);
> +
> + kfree(ap);
> +}
> +
> +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
> +{
> + struct drm_device *dev;
> + int ret;
> +
> + dev = drm_dev_alloc(driver, &vdev->dev);
> + if (!dev)
> + return -ENOMEM;
> + dev->virtdev = vdev;
> + vdev->priv = dev;
> +
> + if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
> + struct pci_dev *pdev = to_pci_dev(vdev->dev.parent);
> + bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
> + DRM_INFO("pci: %s detected\n",
> + vga ? "virtio-vga" : "virtio-gpu-pci");
> + dev->pdev = pdev;
> + if (vga)
> + virtio_pci_kick_out_firmware_fb(pdev);
> + }
> +
> + ret = drm_dev_register(dev, 0);
> + if (ret)
> + goto err_free;
> +
> + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
> + driver->major, driver->minor, driver->patchlevel,
> + driver->date, dev->primary->index);
> +
> + return 0;
> +
> +err_free:
> + drm_dev_unref(dev);
> + return ret;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
> new file mode 100644
> index 0000000..3662e86
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
> @@ -0,0 +1,132 @@
> +/*
> + * 2011 Red Hat, Inc.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Dave Airlie <airlied-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/console.h>
> +#include <linux/pci.h>
> +#include "drmP.h"
> +#include "drm/drm.h"
> +
> +#include "virtgpu_drv.h"
> +static struct drm_driver driver;
> +
> +static int virtio_gpu_modeset = -1;
> +
> +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
> +module_param_named(modeset, virtio_gpu_modeset, int, 0400);
> +
> +static int virtio_gpu_probe(struct virtio_device *vdev)
> +{
> +#ifdef CONFIG_VGA_CONSOLE
> + if (vgacon_text_force() && virtio_gpu_modeset == -1)
> + return -EINVAL;
> +#endif
> +
> + if (virtio_gpu_modeset == 0)
> + return -EINVAL;
> +
> + return drm_virtio_init(&driver, vdev);
> +}
> +
> +static void virtio_gpu_remove(struct virtio_device *vdev)
> +{
> + struct drm_device *dev = vdev->priv;
> + drm_put_dev(dev);
> +}
> +
> +static void virtio_gpu_config_changed(struct virtio_device *vdev)
> +{
> + struct drm_device *dev = vdev->priv;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> +
> + schedule_work(&vgdev->config_changed_work);
> +}
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static unsigned int features[] = {
> +};
> +static struct virtio_driver virtio_gpu_driver = {
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .probe = virtio_gpu_probe,
> + .remove = virtio_gpu_remove,
> + .config_changed = virtio_gpu_config_changed
> +};
> +
> +module_virtio_driver(virtio_gpu_driver);
> +
> +MODULE_DEVICE_TABLE(virtio, id_table);
> +MODULE_DESCRIPTION("Virtio GPU driver");
> +MODULE_LICENSE("GPL");
> +
> +static const struct file_operations virtio_gpu_driver_fops = {
> + .owner = THIS_MODULE,
> + .open = drm_open,
> + .mmap = virtio_gpu_mmap,
> + .poll = drm_poll,
> + .read = drm_read,
> + .unlocked_ioctl = drm_ioctl,
> + .release = drm_release,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = drm_compat_ioctl,
> +#endif
> + .llseek = noop_llseek,
> +};
> +
> +
> +static struct drm_driver driver = {
> + .driver_features = DRIVER_MODESET | DRIVER_GEM,
> + .set_busid = drm_virtio_set_busid,
> + .load = virtio_gpu_driver_load,
> + .unload = virtio_gpu_driver_unload,
> +
> + .dumb_create = virtio_gpu_mode_dumb_create,
> + .dumb_map_offset = virtio_gpu_mode_dumb_mmap,
> + .dumb_destroy = virtio_gpu_mode_dumb_destroy,
> +
> +#if defined(CONFIG_DEBUG_FS)
> + .debugfs_init = virtio_gpu_debugfs_init,
> + .debugfs_cleanup = virtio_gpu_debugfs_takedown,
> +#endif
> +
> + .gem_free_object = virtio_gpu_gem_free_object,
> + .fops = &virtio_gpu_driver_fops,
> +
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> + .patchlevel = DRIVER_PATCHLEVEL,
> +};
> diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
> new file mode 100644
> index 0000000..6082ec3
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
> @@ -0,0 +1,326 @@
> +/*
> + * Copyright (C) 2012 Red Hat
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#ifndef VIRTIO_DRV_H
> +#define VIRTIO_DRV_H
> +
> +#include <linux/virtio.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_gpu.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_gem.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <ttm/ttm_bo_api.h>
> +#include <ttm/ttm_bo_driver.h>
> +#include <ttm/ttm_placement.h>
> +#include <ttm/ttm_module.h>
> +
> +#define DRIVER_NAME "virtio_gpu"
> +#define DRIVER_DESC "virtio GPU"
> +#define DRIVER_DATE "0"
> +
> +#define DRIVER_MAJOR 0
> +#define DRIVER_MINOR 0
> +#define DRIVER_PATCHLEVEL 1
> +
> +/* virtgpu_drm_bus.c */
> +int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master);
> +int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev);
> +
> +struct virtio_gpu_object {
> + struct drm_gem_object gem_base;
> + uint32_t hw_res_handle;
> +
> + struct sg_table *pages;
> + void *vmap;
> + bool dumb;
> + struct ttm_place placement_code;
> + struct ttm_placement placement;
> + struct ttm_buffer_object tbo;
> + struct ttm_bo_kmap_obj kmap;
> +};
> +#define gem_to_virtio_gpu_obj(gobj) \
> + container_of((gobj), struct virtio_gpu_object, gem_base)
> +
> +struct virtio_gpu_vbuffer;
> +struct virtio_gpu_device;
> +
> +typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf);
> +
> +struct virtio_gpu_fence_driver {
> + atomic64_t last_seq;
> + uint64_t sync_seq;
> + struct list_head fences;
> + spinlock_t lock;
> +};
> +
> +struct virtio_gpu_fence {
> + struct fence f;
> + struct virtio_gpu_fence_driver *drv;
> + struct list_head node;
> + uint64_t seq;
> +};
> +#define to_virtio_fence(x) \
> + container_of(x, struct virtio_gpu_fence, f)
> +
> +struct virtio_gpu_vbuffer {
> + char *buf;
> + int size;
> + bool debug_dump_sglists;
> +
> + void *data_buf;
> + uint32_t data_size;
> +
> + char *resp_buf;
> + int resp_size;
> +
> + virtio_gpu_resp_cb resp_cb;
> +
> + struct list_head destroy_list;
> +};
> +
> +struct virtio_gpu_output {
> + int index;
> + struct drm_crtc crtc;
> + struct drm_connector conn;
> + struct drm_encoder enc;
> + struct virtio_gpu_display_one info;
> + struct virtio_gpu_update_cursor cursor;
> + int cur_x;
> + int cur_y;
> +};
> +#define drm_crtc_to_virtio_gpu_output(x) \
> + container_of(x, struct virtio_gpu_output, crtc)
> +#define drm_connector_to_virtio_gpu_output(x) \
> + container_of(x, struct virtio_gpu_output, conn)
> +#define drm_encoder_to_virtio_gpu_output(x) \
> + container_of(x, struct virtio_gpu_output, enc)
> +
> +struct virtio_gpu_framebuffer {
> + struct drm_framebuffer base;
> + struct drm_gem_object *obj;
> + int x1, y1, x2, y2; /* dirty rect */
> + spinlock_t dirty_lock;
> + uint32_t hw_res_handle;
> +};
> +#define to_virtio_gpu_framebuffer(x) \
> + container_of(x, struct virtio_gpu_framebuffer, base)
> +
> +struct virtio_gpu_mman {
> + struct ttm_bo_global_ref bo_global_ref;
> + struct drm_global_reference mem_global_ref;
> + bool mem_global_referenced;
> + struct ttm_bo_device bdev;
> +};
> +
> +struct virtio_gpu_fbdev;
> +
> +struct virtio_gpu_queue {
> + struct virtqueue *vq;
> + spinlock_t qlock;
> + wait_queue_head_t ack_queue;
> + struct work_struct dequeue_work;
> +};
> +
> +struct virtio_gpu_device {
> + struct device *dev;
> + struct drm_device *ddev;
> +
> + struct virtio_device *vdev;
> +
> + struct virtio_gpu_mman mman;
> +
> + /* pointer to fbdev info structure */
> + struct virtio_gpu_fbdev *vgfbdev;
> + struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS];
> + uint32_t num_scanouts;
> +
> + struct virtio_gpu_queue ctrlq;
> + struct virtio_gpu_queue cursorq;
> +
> + struct idr resource_idr;
> + spinlock_t resource_idr_lock;
> +
> + wait_queue_head_t resp_wq;
> + /* current display info */
> + spinlock_t display_info_lock;
> +
> + struct virtio_gpu_fence_driver fence_drv;
> +
> + struct idr ctx_id_idr;
> + spinlock_t ctx_id_idr_lock;
> +
> + struct work_struct config_changed_work;
> +};
> +
> +struct virtio_gpu_fpriv {
> + uint32_t ctx_id;
> +};
> +
> +/* virtio_ioctl.c */
> +#define DRM_VIRTIO_NUM_IOCTLS 10
> +extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
> +
> +/* virtio_kms.c */
> +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
> +int virtio_gpu_driver_unload(struct drm_device *dev);
> +
> +/* virtio_gem.c */
> +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj);
> +int virtio_gpu_gem_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_gem_fini(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_gem_create(struct drm_file *file,
> + struct drm_device *dev,
> + uint64_t size,
> + struct drm_gem_object **obj_p,
> + uint32_t *handle_p);
> +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
> + size_t size, bool kernel,
> + bool pinned);
> +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev,
> + struct drm_mode_create_dumb *args);
> +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle);
> +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle, uint64_t *offset_p);
> +
> +/* virtio_fb */
> +#define VIRTIO_GPUFB_CONN_LIMIT 1
> +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb,
> + struct drm_clip_rect *clips,
> + unsigned num_clips);
> +/* virtio vg */
> +int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
> + uint32_t *resid);
> +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id);
> +int virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t format,
> + uint32_t width,
> + uint32_t height);
> +int virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id);
> +int virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id, uint64_t offset,
> + __le32 width, __le32 height,
> + __le32 x, __le32 y,
> + struct virtio_gpu_fence **fence);
> +int virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t x, uint32_t y,
> + uint32_t width, uint32_t height);
> +int virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
> + uint32_t scanout_id, uint32_t resource_id,
> + uint32_t width, uint32_t height,
> + uint32_t x, uint32_t y);
> +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_object *obj,
> + uint32_t resource_id,
> + struct virtio_gpu_fence **fence);
> +int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_output *output);
> +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
> +int virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id);
> +void virtio_gpu_ctrl_ack(struct virtqueue *vq);
> +void virtio_gpu_cursor_ack(struct virtqueue *vq);
> +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
> +void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
> +
> +/* virtio_gpu_display.c */
> +int virtio_gpu_framebuffer_init(struct drm_device *dev,
> + struct virtio_gpu_framebuffer *vgfb,
> + struct drm_mode_fb_cmd2 *mode_cmd,
> + struct drm_gem_object *obj);
> +int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev);
> +
> +/* virtio_gpu_ttm.c */
> +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev);
> +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev);
> +bool virtio_gpu_ttm_bo_is_virtio_gpu_object(struct ttm_buffer_object *bo);
> +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +/* virtio_gpu_fence.c */
> +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_ctrl_hdr *cmd_hdr,
> + struct virtio_gpu_fence **fence);
> +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
> + u64 last_seq);
> +
> +/* virtio_gpu_object */
> +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
> + unsigned long size, bool kernel, bool pinned,
> + struct virtio_gpu_object **bo_ptr);
> +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr);
> +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
> + struct virtio_gpu_object *bo);
> +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
> +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
> +
> +static inline struct virtio_gpu_object*
> +virtio_gpu_object_ref(struct virtio_gpu_object *bo)
> +{
> + ttm_bo_reference(&bo->tbo);
> + return bo;
> +}
> +
> +static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo)
> +{
> + struct ttm_buffer_object *tbo;
> +
> + if ((*bo) == NULL)
> + return;
> + tbo = &((*bo)->tbo);
> + ttm_bo_unref(&tbo);
> + if (tbo == NULL)
> + *bo = NULL;
> +}
> +
> +static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo)
> +{
> + return drm_vma_node_offset_addr(&bo->tbo.vma_node);
> +}
> +
> +static inline int virtio_gpu_object_reserve(struct virtio_gpu_object *bo,
> + bool no_wait)
> +{
> + int r;
> +
> + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
> + if (unlikely(r != 0)) {
> + if (r != -ERESTARTSYS) {
> + struct virtio_gpu_device *qdev =
> + bo->gem_base.dev->dev_private;
> + dev_err(qdev->dev, "%p reserve failed\n", bo);
> + }
> + return r;
> + }
> + return 0;
> +}
> +
> +static inline void virtio_gpu_object_unreserve(struct virtio_gpu_object *bo)
> +{
> + ttm_bo_unreserve(&bo->tbo);
> +}
> +
> +/* virgl debufs */
> +int virtio_gpu_debugfs_init(struct drm_minor *minor);
> +void virtio_gpu_debugfs_takedown(struct drm_minor *minor);
> +
> +#endif
> diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c
> new file mode 100644
> index 0000000..1d79457
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c
> @@ -0,0 +1,415 @@
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include "virtgpu_drv.h"
> +
> +#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60)
> +
> +struct virtio_gpu_fbdev {
> + struct drm_fb_helper helper;
> + struct virtio_gpu_framebuffer vgfb;
> + struct list_head fbdev_list;
> + struct virtio_gpu_device *vgdev;
> + struct delayed_work work;
> +};
> +#define DL_ALIGN_UP(x, a) ALIGN(x, a)
> +#define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a)
> +
> +static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb,
> + bool store, int x, int y,
> + int width, int height)
> +{
> + struct drm_device *dev = fb->base.dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + bool store_for_later = false;
> + int aligned_x;
> + int bpp = (fb->base.bits_per_pixel / 8);
> + int x2, y2;
> + unsigned long flags;
> + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj);
> +
> + aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
> + width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
> + x = aligned_x;
> +
> + if ((width <= 0) ||
> + (x + width > fb->base.width) ||
> + (y + height > fb->base.height)) {
> + DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n",
> + width, height, x, y,
> + fb->base.width, fb->base.height);
> + return -EINVAL;
> + }
> +
> + /* if we are in atomic just store the info
> + can't test inside spin lock */
> + if (in_atomic() || store)
> + store_for_later = true;
> +
> + x2 = x + width - 1;
> + y2 = y + height - 1;
> +
> + spin_lock_irqsave(&fb->dirty_lock, flags);
> +
> + if (fb->y1 < y)
> + y = fb->y1;
> + if (fb->y2 > y2)
> + y2 = fb->y2;
> + if (fb->x1 < x)
> + x = fb->x1;
> + if (fb->x2 > x2)
> + x2 = fb->x2;
> +
> + if (store_for_later) {
> + fb->x1 = x;
> + fb->x2 = x2;
> + fb->y1 = y;
> + fb->y2 = y2;
> + spin_unlock_irqrestore(&fb->dirty_lock, flags);
> + return 0;
> + }
> +
> + fb->x1 = fb->y1 = INT_MAX;
> + fb->x2 = fb->y2 = 0;
> +
> + spin_unlock_irqrestore(&fb->dirty_lock, flags);
> +
> + {
> + uint32_t offset;
> + uint32_t w = x2 - x + 1;
> + uint32_t h = y2 - y + 1;
> +
> + offset = (y * fb->base.pitches[0]) + x * bpp;
> +
> + virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle,
> + offset,
> + cpu_to_le32(w),
> + cpu_to_le32(h),
> + cpu_to_le32(x),
> + cpu_to_le32(y),
> + NULL);
> +
> + }
> + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
> + x, y, x2 - x + 1, y2 - y + 1);
> + return 0;
> +}
> +
> +int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb,
> + struct drm_clip_rect *clips,
> + unsigned num_clips)
> +{
> + struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private;
> + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->obj);
> + struct drm_clip_rect norect;
> + struct drm_clip_rect *clips_ptr;
> + int left, right, top, bottom;
> + int i;
> + int inc = 1;
> + if (!num_clips) {
> + num_clips = 1;
> + clips = &norect;
> + norect.x1 = norect.y1 = 0;
> + norect.x2 = vgfb->base.width;
> + norect.y2 = vgfb->base.height;
> + }
> + left = clips->x1;
> + right = clips->x2;
> + top = clips->y1;
> + bottom = clips->y2;
> +
> + /* skip the first clip rect */
> + for (i = 1, clips_ptr = clips + inc;
> + i < num_clips; i++, clips_ptr += inc) {
> + left = min_t(int, left, (int)clips_ptr->x1);
> + right = max_t(int, right, (int)clips_ptr->x2);
> + top = min_t(int, top, (int)clips_ptr->y1);
> + bottom = max_t(int, bottom, (int)clips_ptr->y2);
> + }
> +
> + if (obj->dumb)
> + return virtio_gpu_dirty_update(vgfb, false, left, top,
> + right - left, bottom - top);
> +
> + virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
> + left, top, right - left, bottom - top);
> + return 0;
> +}
> +
> +static void virtio_gpu_fb_dirty_work(struct work_struct *work)
> +{
> + struct delayed_work *delayed_work = to_delayed_work(work);
> + struct virtio_gpu_fbdev *vfbdev =
> + container_of(delayed_work, struct virtio_gpu_fbdev, work);
> + struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb;
> +
> + virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1,
> + vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1);
> +}
> +
> +static void virtio_gpu_3d_fillrect(struct fb_info *info,
> + const struct fb_fillrect *rect)
> +{
> + struct virtio_gpu_fbdev *vfbdev = info->par;
> + sys_fillrect(info, rect);
> + virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy,
> + rect->width, rect->height);
> + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
> +}
> +
> +static void virtio_gpu_3d_copyarea(struct fb_info *info,
> + const struct fb_copyarea *area)
> +{
> + struct virtio_gpu_fbdev *vfbdev = info->par;
> + sys_copyarea(info, area);
> + virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy,
> + area->width, area->height);
> + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
> +}
> +
> +static void virtio_gpu_3d_imageblit(struct fb_info *info,
> + const struct fb_image *image)
> +{
> + struct virtio_gpu_fbdev *vfbdev = info->par;
> + sys_imageblit(info, image);
> + virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy,
> + image->width, image->height);
> + schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
> +}
> +
> +static struct fb_ops virtio_gpufb_ops = {
> + .owner = THIS_MODULE,
> + .fb_check_var = drm_fb_helper_check_var,
> + .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */
> + .fb_fillrect = virtio_gpu_3d_fillrect,
> + .fb_copyarea = virtio_gpu_3d_copyarea,
> + .fb_imageblit = virtio_gpu_3d_imageblit,
> + .fb_pan_display = drm_fb_helper_pan_display,
> + .fb_blank = drm_fb_helper_blank,
> + .fb_setcmap = drm_fb_helper_setcmap,
> + .fb_debug_enter = drm_fb_helper_debug_enter,
> + .fb_debug_leave = drm_fb_helper_debug_leave,
> +};
> +
> +static int virtio_gpu_vmap_fb(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_object *obj)
> +{
> + return virtio_gpu_object_kmap(obj, NULL);
> +}
> +
> +static int virtio_gpufb_create(struct drm_fb_helper *helper,
> + struct drm_fb_helper_surface_size *sizes)
> +{
> + struct virtio_gpu_fbdev *vfbdev =
> + container_of(helper, struct virtio_gpu_fbdev, helper);
> + struct drm_device *dev = helper->dev;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct fb_info *info;
> + struct drm_framebuffer *fb;
> + struct drm_mode_fb_cmd2 mode_cmd = {};
> + struct virtio_gpu_object *obj;
> + struct device *device = vgdev->dev;
> + uint32_t resid, format, size;
> + int ret;
> +
> + if (sizes->surface_bpp == 24)
> + sizes->surface_bpp = 32;
> + mode_cmd.width = sizes->surface_width;
> + mode_cmd.height = sizes->surface_height;
> + mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
> + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> + sizes->surface_depth);
> +
> + switch (mode_cmd.pixel_format) {
> +#ifdef __BIG_ENDIAN
> + case DRM_FORMAT_XRGB8888:
> + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_ARGB8888:
> + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_BGRX8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
> + break;
> + case DRM_FORMAT_BGRA8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
> + break;
> + case DRM_FORMAT_RGBX8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
> + break;
> + case DRM_FORMAT_RGBA8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM;
> + break;
> + case DRM_FORMAT_XBGR8888:
> + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM;
> + break;
> + case DRM_FORMAT_ABGR8888:
> + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
> + break;
> +#else
> + case DRM_FORMAT_XRGB8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
> + break;
> + case DRM_FORMAT_ARGB8888:
> + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
> + break;
> + case DRM_FORMAT_BGRX8888:
> + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_BGRA8888:
> + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
> + break;
> + case DRM_FORMAT_RGBX8888:
> + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM;
> + break;
> + case DRM_FORMAT_RGBA8888:
> + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
> + break;
> + case DRM_FORMAT_XBGR8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
> + break;
> + case DRM_FORMAT_ABGR8888:
> + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM;
> + break;
> +#endif
> + default:
> + format = 0;
> + break;
> + }
> + if (format == 0) {
> + ret = -EINVAL;
> + DRM_ERROR("failed to find virtio gpu format for %d\n",
> + mode_cmd.pixel_format);
> + goto fail;
> + }
> +
> + size = mode_cmd.pitches[0] * mode_cmd.height;
> + obj = virtio_gpu_alloc_object(dev, size, false, true);
> + if (!obj) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + ret = virtio_gpu_resource_id_get(vgdev, &resid);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_cmd_create_resource(vgdev, resid, format,
> + mode_cmd.width, mode_cmd.height);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_vmap_fb(vgdev, obj);
> + if (ret) {
> + DRM_ERROR("failed to vmap fb %d\n", ret);
> + goto fail;
> + }
> +
> + /* attach the object to the resource */
> + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
> + if (ret)
> + goto fail;
> +
> + info = framebuffer_alloc(0, device);
> + if (!info) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + info->par = helper;
> +
> + ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb,
> + &mode_cmd, &obj->gem_base);
> + if (ret)
> + goto fail;
> +
> + fb = &vfbdev->vgfb.base;
> +
> + vfbdev->helper.fb = fb;
> + vfbdev->helper.fbdev = info;
> +
> + strcpy(info->fix.id, "virtiodrmfb");
> + info->flags = FBINFO_DEFAULT;
> + info->fbops = &virtio_gpufb_ops;
> + info->pixmap.flags = FB_PIXMAP_SYSTEM;
> + ret = fb_alloc_cmap(&info->cmap, 256, 0);
> + if (ret) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + info->screen_base = obj->vmap;
> + info->screen_size = obj->gem_base.size;
> + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
> + drm_fb_helper_fill_var(info, &vfbdev->helper,
> + sizes->fb_width, sizes->fb_height);
> +
> + info->fix.mmio_start = 0;
> + info->fix.mmio_len = 0;
> +
> + return 0;
> +fail:
> +
> + return -EINVAL;
> +}
> +
> +static int virtio_gpu_fbdev_destroy(struct drm_device *dev,
> + struct virtio_gpu_fbdev *vgfbdev)
> +{
> + struct fb_info *info;
> + struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb;
> +
> + if (vgfbdev->helper.fbdev) {
> + info = vgfbdev->helper.fbdev;
> +
> + unregister_framebuffer(info);
> + framebuffer_release(info);
> + }
> + if (vgfb->obj)
> + vgfb->obj = NULL;
> + drm_fb_helper_fini(&vgfbdev->helper);
> + drm_framebuffer_cleanup(&vgfb->base);
> +
> + return 0;
> +}
> +static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = {
> + .fb_probe = virtio_gpufb_create,
> +};
> +
> +int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev)
> +{
> + struct virtio_gpu_fbdev *vgfbdev;
> + int bpp_sel = 32; /* TODO: parameter from somewhere? */
> + int ret;
> +
> + vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL);
> + if (!vgfbdev)
> + return -ENOMEM;
> +
> + vgfbdev->vgdev = vgdev;
> + vgdev->vgfbdev = vgfbdev;
> + INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work);
> +
> + drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper,
> + &virtio_gpu_fb_helper_funcs);
> + ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper,
> + vgdev->num_scanouts,
> + VIRTIO_GPUFB_CONN_LIMIT);
> + if (ret) {
> + kfree(vgfbdev);
> + return ret;
> + }
> +
> + drm_fb_helper_single_add_all_connectors(&vgfbdev->helper);
> + drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel);
> + return 0;
> +}
> +
> +void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev)
> +{
> + if (!vgdev->vgfbdev)
> + return;
> +
> + virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev);
> + kfree(vgdev->vgfbdev);
> + vgdev->vgfbdev = NULL;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c
> new file mode 100644
> index 0000000..552aa49
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c
> @@ -0,0 +1,95 @@
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +
> +static const char *virtio_get_driver_name(struct fence *f)
> +{
> + return "virtio_gpu";
> +}
> +
> +static const char *virtio_get_timeline_name(struct fence *f)
> +{
> + return "controlq";
> +}
> +
> +static bool virtio_enable_signaling(struct fence *f)
> +{
> + return true;
> +}
> +
> +static bool virtio_signaled(struct fence *f)
> +{
> + struct virtio_gpu_fence *fence = to_virtio_fence(f);
> +
> + if (atomic64_read(&fence->drv->last_seq) >= fence->seq) {
> + return true;
> + }
> + return false;
> +}
> +
> +static void virtio_fence_value_str(struct fence *f, char *str, int size)
> +{
> + struct virtio_gpu_fence *fence = to_virtio_fence(f);
> +
> + snprintf(str, size, "%llu", fence->seq);
> +}
> +
> +static void virtio_timeline_value_str(struct fence *f, char *str, int size)
> +{
> + struct virtio_gpu_fence *fence = to_virtio_fence(f);
> +
> + snprintf(str, size, "%lu", atomic64_read(&fence->drv->last_seq));
> +}
> +
> +static const struct fence_ops virtio_fence_ops = {
> + .get_driver_name = virtio_get_driver_name,
> + .get_timeline_name = virtio_get_timeline_name,
> + .enable_signaling = virtio_enable_signaling,
> + .signaled = virtio_signaled,
> + .wait = fence_default_wait,
> + .fence_value_str = virtio_fence_value_str,
> + .timeline_value_str = virtio_timeline_value_str,
> +};
> +
> +int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_ctrl_hdr *cmd_hdr,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
> + unsigned long irq_flags;
> +
> + *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL);
> + if ((*fence) == NULL)
> + return -ENOMEM;
> +
> + spin_lock_irqsave(&drv->lock, irq_flags);
> + (*fence)->drv = drv;
> + (*fence)->seq = ++drv->sync_seq;
> + fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock,
> + 0, (*fence)->seq);
> + fence_get(&(*fence)->f);
> + list_add_tail(&(*fence)->node, &drv->fences);
> + spin_unlock_irqrestore(&drv->lock, irq_flags);
> +
> + cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE);
> + cmd_hdr->fence_id = cpu_to_le64((*fence)->seq);
> + return 0;
> +}
> +
> +void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev,
> + u64 last_seq)
> +{
> + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
> + struct virtio_gpu_fence *fence, *tmp;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&drv->lock, irq_flags);
> + atomic64_set(&vgdev->fence_drv.last_seq, last_seq);
> + list_for_each_entry_safe(fence, tmp, &drv->fences, node) {
> + if (last_seq < fence->seq)
> + continue;
> + fence_signal_locked(&fence->f);
> + list_del(&fence->node);
> + fence_put(&fence->f);
> + }
> + spin_unlock_irqrestore(&drv->lock, irq_flags);
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
> new file mode 100644
> index 0000000..8bc0a24
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
> @@ -0,0 +1,120 @@
> +
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +
> +void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj)
> +{
> + struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj);
> +
> + if (obj)
> + virtio_gpu_object_unref(&obj);
> +}
> +
> +struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
> + size_t size, bool kernel,
> + bool pinned)
> +{
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct virtio_gpu_object *obj;
> + int ret;
> +
> + ret = virtio_gpu_object_create(vgdev, size, kernel, pinned, &obj);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return obj;
> +}
> +
> +int virtio_gpu_gem_create(struct drm_file *file,
> + struct drm_device *dev,
> + uint64_t size,
> + struct drm_gem_object **obj_p,
> + uint32_t *handle_p)
> +{
> + struct virtio_gpu_object *obj;
> + int ret;
> + u32 handle;
> +
> + obj = virtio_gpu_alloc_object(dev, size, false, false);
> + if (IS_ERR(obj))
> + return PTR_ERR(obj);
> +
> + ret = drm_gem_handle_create(file, &obj->gem_base, &handle);
> + if (ret) {
> + drm_gem_object_release(&obj->gem_base);
> + return ret;
> + }
> +
> + *obj_p = &obj->gem_base;
> +
> + /* drop reference from allocate - handle holds it now */
> + drm_gem_object_unreference_unlocked(&obj->gem_base);
> +
> + *handle_p = handle;
> + return 0;
> +}
> +
> +int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev,
> + struct drm_mode_create_dumb *args)
> +{
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + struct drm_gem_object *gobj;
> + struct virtio_gpu_object *obj;
> + int ret;
> + uint32_t pitch;
> + uint32_t resid;
> +
> + pitch = args->width * ((args->bpp + 1) / 8);
> + args->size = pitch * args->height;
> + args->size = ALIGN(args->size, PAGE_SIZE);
> +
> + ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj,
> + &args->handle);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_resource_id_get(vgdev, &resid);
> + if (ret)
> + goto fail;
> +
> + ret = virtio_gpu_cmd_create_resource(vgdev, resid,
> + 2, args->width, args->height);
> + if (ret)
> + goto fail;
> +
> + /* attach the object to the resource */
> + obj = gem_to_virtio_gpu_obj(gobj);
> + ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
> + if (ret)
> + goto fail;
> +
> + obj->dumb = true;
> + args->pitch = pitch;
> + return ret;
> +fail:
> + return ret;
> +}
> +
> +int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle)
> +{
> + return drm_gem_handle_delete(file_priv, handle);
> +}
> +
> +int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
> + struct drm_device *dev,
> + uint32_t handle, uint64_t *offset_p)
> +{
> + struct drm_gem_object *gobj;
> + struct virtio_gpu_object *obj;
> + BUG_ON(!offset_p);
> + gobj = drm_gem_object_lookup(dev, file_priv, handle);
> + if (gobj == NULL)
> + return -ENOENT;
> + obj = gem_to_virtio_gpu_obj(gobj);
> + *offset_p = virtio_gpu_object_mmap_offset(obj);
> + drm_gem_object_unreference_unlocked(gobj);
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
> new file mode 100644
> index 0000000..45c4beb
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
> @@ -0,0 +1,125 @@
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +
> +static void virtio_gpu_config_changed_work_func(struct work_struct *work)
> +{
> + struct virtio_gpu_device *vgdev =
> + container_of(work, struct virtio_gpu_device,
> + config_changed_work);
> + u32 events_read, events_clear = 0;
> +
> + /* read the config space */
> + virtio_cread(vgdev->vdev, struct virtio_gpu_config,
> + events_read, &events_read);
> + if (events_read & VIRTIO_GPU_EVENT_DISPLAY) {
> + virtio_gpu_cmd_get_display_info(vgdev);
> + drm_helper_hpd_irq_event(vgdev->ddev);
> + events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
> + }
> + virtio_cwrite(vgdev->vdev, struct virtio_gpu_config,
> + events_clear, &events_clear);
> +}
> +
> +static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
> + void (*work_func)(struct work_struct *work))
> +{
> + spin_lock_init(&vgvq->qlock);
> + init_waitqueue_head(&vgvq->ack_queue);
> + INIT_WORK(&vgvq->dequeue_work, work_func);
> +}
> +
> +int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
> +{
> + static vq_callback_t *callbacks[] = {
> + virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack
> + };
> + static const char *names[] = { "control", "cursor" };
> +
> + struct virtio_gpu_device *vgdev;
> + /* this will expand later */
> + struct virtqueue *vqs[2];
> + u32 num_scanouts;
> + int ret;
> +
> + if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
> + return -ENODEV;
> +
> + vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
> + if (!vgdev)
> + return -ENOMEM;
> +
> + vgdev->ddev = dev;
> + dev->dev_private = vgdev;
> + vgdev->vdev = dev->virtdev;
> + vgdev->dev = dev->dev;
> +
> + spin_lock_init(&vgdev->display_info_lock);
> + spin_lock_init(&vgdev->ctx_id_idr_lock);
> + idr_init(&vgdev->ctx_id_idr);
> + spin_lock_init(&vgdev->resource_idr_lock);
> + idr_init(&vgdev->resource_idr);
> + init_waitqueue_head(&vgdev->resp_wq);
> + virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func);
> + virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func);
> +
> + spin_lock_init(&vgdev->fence_drv.lock);
> + INIT_LIST_HEAD(&vgdev->fence_drv.fences);
> + INIT_WORK(&vgdev->config_changed_work,
> + virtio_gpu_config_changed_work_func);
> +
> + ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs,
> + callbacks, names);
> + if (ret) {
> + DRM_ERROR("failed to find virt queues\n");
> + goto err_vqs;
> + }
> + vgdev->ctrlq.vq = vqs[0];
> + vgdev->cursorq.vq = vqs[1];
> +
> + ret = virtio_gpu_ttm_init(vgdev);
> + if (ret) {
> + DRM_ERROR("failed to init ttm %d\n", ret);
> + goto err_ttm;
> + }
> +
> + /* get display info */
> + virtio_cread(vgdev->vdev, struct virtio_gpu_config,
> + num_scanouts, &num_scanouts);
> + vgdev->num_scanouts = min_t(uint32_t, num_scanouts,
> + VIRTIO_GPU_MAX_SCANOUTS);
> + if (!vgdev->num_scanouts) {
> + DRM_ERROR("num_scanouts is zero\n");
> + ret = -EINVAL;
> + goto err_scanouts;
> + }
> +
> + ret = virtio_gpu_modeset_init(vgdev);
> + if (ret)
> + goto err_modeset;
> +
> + virtio_device_ready(vgdev->vdev);
> + virtio_gpu_cmd_get_display_info(vgdev);
> + return 0;
> +
> +err_modeset:
> +err_scanouts:
> + virtio_gpu_ttm_fini(vgdev);
> +err_ttm:
> + vgdev->vdev->config->del_vqs(vgdev->vdev);
> +err_vqs:
> + kfree(vgdev);
> + return ret;
> +}
> +
> +int virtio_gpu_driver_unload(struct drm_device *dev)
> +{
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> +
> + virtio_gpu_modeset_fini(vgdev);
> + virtio_gpu_ttm_fini(vgdev);
> + vgdev->vdev->config->del_vqs(vgdev->vdev);
> + kfree(vgdev);
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
> new file mode 100644
> index 0000000..0d98ae4
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_object.c
> @@ -0,0 +1,174 @@
> +#include "virtgpu_drv.h"
> +
> +static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo)
> +{
> + struct virtio_gpu_object *bo;
> + struct virtio_gpu_device *vgdev;
> +
> + bo = container_of(tbo, struct virtio_gpu_object, tbo);
> + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
> +
> + if (bo->hw_res_handle)
> + virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle);
> + if (bo->pages)
> + virtio_gpu_object_free_sg_table(bo);
> + drm_gem_object_release(&bo->gem_base);
> + kfree(bo);
> +}
> +
> +bool virtio_gpu_ttm_bo_is_virtio_gpu_object(struct ttm_buffer_object *bo)
> +{
> + if (bo->destroy == &virtio_gpu_ttm_bo_destroy)
> + return true;
> + return false;
> +}
> +
> +static void virtio_gpu_init_ttm_placement(struct virtio_gpu_object *vgbo,
> + bool pinned)
> +{
> + u32 c = 1;
> + u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
> +
> + vgbo->placement.placement = &vgbo->placement_code;
> + vgbo->placement.busy_placement = &vgbo->placement_code;
> + vgbo->placement_code.fpfn = 0;
> + vgbo->placement_code.lpfn = 0;
> + vgbo->placement_code.flags =
> + TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT | pflag;
> + vgbo->placement.num_placement = c;
> + vgbo->placement.num_busy_placement = c;
> +
> +}
> +
> +int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
> + unsigned long size, bool kernel, bool pinned,
> + struct virtio_gpu_object **bo_ptr)
> +{
> + struct virtio_gpu_object *bo;
> + enum ttm_bo_type type;
> + size_t acc_size;
> + int r;
> +
> + if (kernel)
> + type = ttm_bo_type_kernel;
> + else
> + type = ttm_bo_type_device;
> + *bo_ptr = NULL;
> +
> + acc_size = ttm_bo_dma_acc_size(&vgdev->mman.bdev, size,
> + sizeof(struct virtio_gpu_object));
> +
> + bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL);
> + if (bo == NULL)
> + return -ENOMEM;
> + size = roundup(size, PAGE_SIZE);
> + r = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size);
> + if (unlikely(r)) {
> + kfree(bo);
> + return r;
> + }
> + bo->dumb = false;
> +
> + virtio_gpu_init_ttm_placement(bo, pinned);
> + r = ttm_bo_init(&vgdev->mman.bdev, &bo->tbo, size, type,
> + &bo->placement, 0, !kernel, NULL, acc_size,
> + NULL, NULL, &virtio_gpu_ttm_bo_destroy);
> + if (unlikely(r != 0)) {
> + if (r != -ERESTARTSYS)
> + dev_err(vgdev->dev,
> + "object_init %d failed for (%lu)\n", r,
> + size);
> + return r;
> + }
> + *bo_ptr = bo;
> + return 0;
> +}
> +
> +int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr)
> +{
> + bool is_iomem;
> + int r;
> +
> + if (bo->vmap) {
> + if (ptr)
> + *ptr = bo->vmap;
> + return 0;
> + }
> + r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap);
> + if (r)
> + return r;
> + bo->vmap = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem);
> + if (ptr)
> + *ptr = bo->vmap;
> + return 0;
> +}
> +
> +#if 0
> +void virtio_gpu_object_force_delete(struct virtio_gpu_device *vgdev)
> +{
> + struct virtio_gpu_object *bo, *n;
> +
> +
> + dev_err(vgdev->dev, "Userspace still has active objects !\n");
> + list_for_each_entry_safe(bo, n, &vgdev->gem.objects, list) {
> + mutex_lock(&vgdev->ddev->struct_mutex);
> + dev_err(vgdev->dev, "%p %p %lu %lu force free\n",
> + &bo->gem_base, bo, (unsigned long)bo->gem_base.size,
> + *((unsigned long *)&bo->gem_base.refcount));
> + spin_lock(&vgdev->gem.lock);
> + list_del_init(&bo->list);
> + spin_unlock(&vgdev->gem.lock);
> + /* this should unref the ttm bo */
> + drm_gem_object_unreference(&bo->gem_base);
> + mutex_unlock(&vgdev->ddev->struct_mutex);
> + }
> +}
> +#endif
> +
> +int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
> + struct virtio_gpu_object *bo)
> +{
> + int ret;
> + struct page **pages = bo->tbo.ttm->pages;
> + int nr_pages = bo->tbo.num_pages;
> +
> + /* wtf swapping */
> + if (bo->pages)
> + return 0;
> +
> + if (bo->tbo.ttm->state == tt_unpopulated)
> + bo->tbo.ttm->bdev->driver->ttm_tt_populate(bo->tbo.ttm);
> + bo->pages = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
> + if (!bo->pages)
> + goto out;
> +
> + ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0,
> + nr_pages << PAGE_SHIFT, GFP_KERNEL);
> + if (ret)
> + goto out;
> + return 0;
> +out:
> + kfree(bo->pages);
> + bo->pages = NULL;
> + return -ENOMEM;
> +}
> +
> +void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo)
> +{
> + sg_free_table(bo->pages);
> + kfree(bo->pages);
> + bo->pages = NULL;
> +}
> +
> +int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait)
> +{
> + int r;
> +
> + r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
> + if (unlikely(r != 0))
> + return r;
> + r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
> + ttm_bo_unreserve(&bo->tbo);
> + return r;
> +}
> +
> diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c
> new file mode 100644
> index 0000000..a6f22e0
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c
> @@ -0,0 +1,451 @@
> +/*
> + * Copyright 2013 Red Hat Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors: Dave Airlie
> + * Alon Levy
> + */
> +
> +#include <ttm/ttm_bo_api.h>
> +#include <ttm/ttm_bo_driver.h>
> +#include <ttm/ttm_placement.h>
> +#include <ttm/ttm_page_alloc.h>
> +#include <ttm/ttm_module.h>
> +#include <drm/drmP.h>
> +#include <drm/drm.h>
> +#include "virtgpu_drv.h"
> +
> +#include <linux/delay.h>
> +
> +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
> +
> +static struct
> +virtio_gpu_device *virtio_gpu_get_vgdev(struct ttm_bo_device *bdev)
> +{
> + struct virtio_gpu_mman *mman;
> + struct virtio_gpu_device *vgdev;
> +
> + mman = container_of(bdev, struct virtio_gpu_mman, bdev);
> + vgdev = container_of(mman, struct virtio_gpu_device, mman);
> + return vgdev;
> +}
> +
> +static int virtio_gpu_ttm_mem_global_init(struct drm_global_reference *ref)
> +{
> + return ttm_mem_global_init(ref->object);
> +}
> +
> +static void virtio_gpu_ttm_mem_global_release(struct drm_global_reference *ref)
> +{
> + ttm_mem_global_release(ref->object);
> +}
> +
> +static int virtio_gpu_ttm_global_init(struct virtio_gpu_device *vgdev)
> +{
> + struct drm_global_reference *global_ref;
> + int r;
> +
> + vgdev->mman.mem_global_referenced = false;
> + global_ref = &vgdev->mman.mem_global_ref;
> + global_ref->global_type = DRM_GLOBAL_TTM_MEM;
> + global_ref->size = sizeof(struct ttm_mem_global);
> + global_ref->init = &virtio_gpu_ttm_mem_global_init;
> + global_ref->release = &virtio_gpu_ttm_mem_global_release;
> +
> + r = drm_global_item_ref(global_ref);
> + if (r != 0) {
> + DRM_ERROR("Failed setting up TTM memory accounting "
> + "subsystem.\n");
> + return r;
> + }
> +
> + vgdev->mman.bo_global_ref.mem_glob =
> + vgdev->mman.mem_global_ref.object;
> + global_ref = &vgdev->mman.bo_global_ref.ref;
> + global_ref->global_type = DRM_GLOBAL_TTM_BO;
> + global_ref->size = sizeof(struct ttm_bo_global);
> + global_ref->init = &ttm_bo_global_init;
> + global_ref->release = &ttm_bo_global_release;
> + r = drm_global_item_ref(global_ref);
> + if (r != 0) {
> + DRM_ERROR("Failed setting up TTM BO subsystem.\n");
> + drm_global_item_unref(&vgdev->mman.mem_global_ref);
> + return r;
> + }
> +
> + vgdev->mman.mem_global_referenced = true;
> + return 0;
> +}
> +
> +static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev)
> +{
> + if (vgdev->mman.mem_global_referenced) {
> + drm_global_item_unref(&vgdev->mman.bo_global_ref.ref);
> + drm_global_item_unref(&vgdev->mman.mem_global_ref);
> + vgdev->mman.mem_global_referenced = false;
> + }
> +}
> +
> +static struct vm_operations_struct virtio_gpu_ttm_vm_ops;
> +static const struct vm_operations_struct *ttm_vm_ops;
> +
> +static int virtio_gpu_ttm_fault(struct vm_area_struct *vma,
> + struct vm_fault *vmf)
> +{
> + struct ttm_buffer_object *bo;
> + struct virtio_gpu_device *vgdev;
> + int r;
> +
> + bo = (struct ttm_buffer_object *)vma->vm_private_data;
> + if (bo == NULL)
> + return VM_FAULT_NOPAGE;
> + vgdev = virtio_gpu_get_vgdev(bo->bdev);
> + r = ttm_vm_ops->fault(vma, vmf);
> + return r;
> +}
> +
> +int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + struct drm_file *file_priv;
> + struct virtio_gpu_device *vgdev;
> + int r;
> +
> + file_priv = filp->private_data;
> + vgdev = file_priv->minor->dev->dev_private;
> + if (vgdev == NULL) {
> + DRM_ERROR(
> + "filp->private_data->minor->dev->dev_private == NULL\n");
> + return -EINVAL;
> + }
> + r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev);
> + if (unlikely(r != 0))
> + return r;
> + if (unlikely(ttm_vm_ops == NULL)) {
> + ttm_vm_ops = vma->vm_ops;
> + virtio_gpu_ttm_vm_ops = *ttm_vm_ops;
> + virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault;
> + }
> + vma->vm_ops = &virtio_gpu_ttm_vm_ops;
> + return 0;
> +}
> +
> +static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev,
> + uint32_t flags)
> +{
> + return 0;
> +}
> +
> +static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
> + struct ttm_buffer_object *bo,
> + const struct ttm_place *place,
> + struct ttm_mem_reg *mem)
> +{
> + mem->mm_node = (void *)1;
> + return 0;
> +}
> +
> +static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man,
> + struct ttm_mem_reg *mem)
> +{
> + mem->mm_node = (void *)NULL;
> + return;
> +}
> +
> +static int ttm_bo_man_init(struct ttm_mem_type_manager *man,
> + unsigned long p_size)
> +{
> + return 0;
> +}
> +
> +static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man)
> +{
> + return 0;
> +}
> +
> +static void ttm_bo_man_debug(struct ttm_mem_type_manager *man,
> + const char *prefix)
> +{
> +}
> +
> +static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = {
> + ttm_bo_man_init,
> + ttm_bo_man_takedown,
> + ttm_bo_man_get_node,
> + ttm_bo_man_put_node,
> + ttm_bo_man_debug
> +};
> +
> +static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
> + struct ttm_mem_type_manager *man)
> +{
> + struct virtio_gpu_device *vgdev;
> +
> + vgdev = virtio_gpu_get_vgdev(bdev);
> +
> + switch (type) {
> + case TTM_PL_SYSTEM:
> + /* System memory */
> + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
> + man->available_caching = TTM_PL_MASK_CACHING;
> + man->default_caching = TTM_PL_FLAG_CACHED;
> + break;
> + case TTM_PL_TT:
> + man->func = &virtio_gpu_bo_manager_func;
> + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
> + man->available_caching = TTM_PL_MASK_CACHING;
> + man->default_caching = TTM_PL_FLAG_CACHED;
> + break;
> + default:
> + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static void virtio_gpu_evict_flags(struct ttm_buffer_object *bo,
> + struct ttm_placement *placement)
> +{
> + static struct ttm_place placements = {
> + .fpfn = 0,
> + .lpfn = 0,
> + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM,
> + };
> +
> + placement->placement = &placements;
> + placement->busy_placement = &placements;
> + placement->num_placement = 1;
> + placement->num_busy_placement = 1;
> + return;
> +}
> +
> +static int virtio_gpu_verify_access(struct ttm_buffer_object *bo,
> + struct file *filp)
> +{
> + return 0;
> +}
> +
> +static int virtio_gpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
> + struct ttm_mem_reg *mem)
> +{
> + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
> +
> + mem->bus.addr = NULL;
> + mem->bus.offset = 0;
> + mem->bus.size = mem->num_pages << PAGE_SHIFT;
> + mem->bus.base = 0;
> + mem->bus.is_iomem = false;
> + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
> + return -EINVAL;
> + switch (mem->mem_type) {
> + case TTM_PL_SYSTEM:
> + case TTM_PL_TT:
> + /* system memory */
> + return 0;
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static void virtio_gpu_ttm_io_mem_free(struct ttm_bo_device *bdev,
> + struct ttm_mem_reg *mem)
> +{
> +}
> +
> +/*
> + * TTM backend functions.
> + */
> +struct virtio_gpu_ttm_tt {
> + struct ttm_dma_tt ttm;
> + struct virtio_gpu_device *vgdev;
> + u64 offset;
> +};
> +
> +static int virtio_gpu_ttm_backend_bind(struct ttm_tt *ttm,
> + struct ttm_mem_reg *bo_mem)
> +{
> + struct virtio_gpu_ttm_tt *gtt = (void *)ttm;
> +
> + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
> + if (!ttm->num_pages) {
> + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
> + ttm->num_pages, bo_mem, ttm);
> + }
> + /* Not implemented */
> + return 0;
> +}
> +
> +static int virtio_gpu_ttm_backend_unbind(struct ttm_tt *ttm)
> +{
> + /* Not implemented */
> + return 0;
> +}
> +
> +static void virtio_gpu_ttm_backend_destroy(struct ttm_tt *ttm)
> +{
> + struct virtio_gpu_ttm_tt *gtt = (void *)ttm;
> +
> + ttm_dma_tt_fini(>t->ttm);
> + kfree(gtt);
> +}
> +
> +static struct ttm_backend_func virtio_gpu_backend_func = {
> + .bind = &virtio_gpu_ttm_backend_bind,
> + .unbind = &virtio_gpu_ttm_backend_unbind,
> + .destroy = &virtio_gpu_ttm_backend_destroy,
> +};
> +
> +static int virtio_gpu_ttm_tt_populate(struct ttm_tt *ttm)
> +{
> + if (ttm->state != tt_unpopulated)
> + return 0;
> +
> + return ttm_pool_populate(ttm);
> +}
> +
> +static void virtio_gpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
> +{
> + ttm_pool_unpopulate(ttm);
> +}
> +
> +static struct ttm_tt *virtio_gpu_ttm_tt_create(struct ttm_bo_device *bdev,
> + unsigned long size,
> + uint32_t page_flags,
> + struct page *dummy_read_page)
> +{
> + struct virtio_gpu_device *vgdev;
> + struct virtio_gpu_ttm_tt *gtt;
> +
> + vgdev = virtio_gpu_get_vgdev(bdev);
> + gtt = kzalloc(sizeof(struct virtio_gpu_ttm_tt), GFP_KERNEL);
> + if (gtt == NULL)
> + return NULL;
> + gtt->ttm.ttm.func = &virtio_gpu_backend_func;
> + gtt->vgdev = vgdev;
> + if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags,
> + dummy_read_page)) {
> + kfree(gtt);
> + return NULL;
> + }
> + return >t->ttm.ttm;
> +}
> +
> +static void virtio_gpu_move_null(struct ttm_buffer_object *bo,
> + struct ttm_mem_reg *new_mem)
> +{
> + struct ttm_mem_reg *old_mem = &bo->mem;
> +
> + BUG_ON(old_mem->mm_node != NULL);
> + *old_mem = *new_mem;
> + new_mem->mm_node = NULL;
> +}
> +
> +static int virtio_gpu_bo_move(struct ttm_buffer_object *bo,
> + bool evict, bool interruptible,
> + bool no_wait_gpu,
> + struct ttm_mem_reg *new_mem)
> +{
> + virtio_gpu_move_null(bo, new_mem);
> + return 0;
> +}
> +
> +static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo,
> + struct ttm_mem_reg *new_mem)
> +{
> + struct virtio_gpu_object *bo;
> + struct virtio_gpu_device *vgdev;
> +
> + bo = container_of(tbo, struct virtio_gpu_object, tbo);
> + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
> +
> + if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) {
> + if (bo->hw_res_handle)
> + virtio_gpu_cmd_resource_inval_backing(vgdev,
> + bo->hw_res_handle);
> +
> + } else if (new_mem->placement & TTM_PL_FLAG_TT) {
> + if (bo->hw_res_handle) {
> + virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle,
> + NULL);
> + }
> + }
> +
> + return;
> +}
> +
> +static void virtio_gpu_bo_swap_notify(struct ttm_buffer_object *tbo)
> +{
> + struct virtio_gpu_object *bo;
> + struct virtio_gpu_device *vgdev;
> +
> + bo = container_of(tbo, struct virtio_gpu_object, tbo);
> + vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
> +
> + if (bo->pages)
> + virtio_gpu_object_free_sg_table(bo);
> +}
> +
> +static struct ttm_bo_driver virtio_gpu_bo_driver = {
> + .ttm_tt_create = &virtio_gpu_ttm_tt_create,
> + .ttm_tt_populate = &virtio_gpu_ttm_tt_populate,
> + .ttm_tt_unpopulate = &virtio_gpu_ttm_tt_unpopulate,
> + .invalidate_caches = &virtio_gpu_invalidate_caches,
> + .init_mem_type = &virtio_gpu_init_mem_type,
> + .evict_flags = &virtio_gpu_evict_flags,
> + .move = &virtio_gpu_bo_move,
> + .verify_access = &virtio_gpu_verify_access,
> + .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve,
> + .io_mem_free = &virtio_gpu_ttm_io_mem_free,
> + .move_notify = &virtio_gpu_bo_move_notify,
> + .swap_notify = &virtio_gpu_bo_swap_notify,
> +};
> +
> +int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev)
> +{
> + int r;
> +
> + r = virtio_gpu_ttm_global_init(vgdev);
> + if (r)
> + return r;
> + /* No others user of address space so set it to 0 */
> + r = ttm_bo_device_init(&vgdev->mman.bdev,
> + vgdev->mman.bo_global_ref.ref.object,
> + &virtio_gpu_bo_driver,
> + vgdev->ddev->anon_inode->i_mapping,
> + DRM_FILE_PAGE_OFFSET, 0);
> + if (r) {
> + DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
> + return r;
> + }
> +
> + r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0);
> + if (r) {
> + DRM_ERROR("Failed initializing GTT heap.\n");
> + return r;
> + }
> + return 0;
> +}
> +
> +void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev)
> +{
> + ttm_bo_device_release(&vgdev->mman.bdev);
> + virtio_gpu_ttm_global_fini(vgdev);
> + DRM_INFO("virtio_gpu: ttm finalized\n");
> +}
> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
> new file mode 100644
> index 0000000..a98cda8
> --- /dev/null
> +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
> @@ -0,0 +1,540 @@
> +#include <drm/drmP.h>
> +#include "virtgpu_drv.h"
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_ring.h>
> +
> +
> +int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid)
> +{
> + int handle;
> +
> + idr_preload(GFP_KERNEL);
> + spin_lock(&vgdev->resource_idr_lock);
> + handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT);
> + spin_unlock(&vgdev->resource_idr_lock);
> + idr_preload_end();
> + *resid = handle;
> + return 0;
> +}
> +
> +void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id)
> +{
> + spin_lock(&vgdev->resource_idr_lock);
> + idr_remove(&vgdev->resource_idr, id);
> + spin_unlock(&vgdev->resource_idr_lock);
> +}
> +
> +void virtio_gpu_ctrl_ack(struct virtqueue *vq)
> +{
> + struct drm_device *dev = vq->vdev->priv;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + schedule_work(&vgdev->ctrlq.dequeue_work);
> +}
> +
> +void virtio_gpu_cursor_ack(struct virtqueue *vq)
> +{
> + struct drm_device *dev = vq->vdev->priv;
> + struct virtio_gpu_device *vgdev = dev->dev_private;
> + schedule_work(&vgdev->cursorq.dequeue_work);
> +}
> +
> +static struct virtio_gpu_vbuffer*
> +virtio_gpu_allocate_vbuf(struct virtio_gpu_device *vgdev,
> + int size, int resp_size,
> + virtio_gpu_resp_cb resp_cb)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = kzalloc(sizeof(*vbuf) + size + resp_size, GFP_KERNEL);
> + if (!vbuf)
> + goto fail;
> +
> + vbuf->buf = (void *)vbuf + sizeof(*vbuf);
> + vbuf->size = size;
> +
> + vbuf->resp_cb = resp_cb;
> + if (resp_size)
> + vbuf->resp_buf = (void *)vbuf->buf + size;
> + else
> + vbuf->resp_buf = NULL;
> + vbuf->resp_size = resp_size;
> +
> + return vbuf;
> +fail:
> + kfree(vbuf);
> + return ERR_PTR(-ENOMEM);
> +}
> +
> +static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer **vbuffer_p,
> + int size)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = virtio_gpu_allocate_vbuf(vgdev, size,
> + sizeof(struct virtio_gpu_ctrl_hdr), NULL);
> + if (IS_ERR(vbuf)) {
> + *vbuffer_p = NULL;
> + return ERR_CAST(vbuf);
> + }
> + *vbuffer_p = vbuf;
> + return vbuf->buf;
> +}
> +
> +static struct virtio_gpu_update_cursor*
> +virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer **vbuffer_p)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = virtio_gpu_allocate_vbuf
> + (vgdev, sizeof(struct virtio_gpu_update_cursor), 0, NULL);
> + if (IS_ERR(vbuf)) {
> + *vbuffer_p = NULL;
> + return ERR_CAST(vbuf);
> + }
> + *vbuffer_p = vbuf;
> + return (struct virtio_gpu_update_cursor *)vbuf->buf;
> +}
> +
> +static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev,
> + virtio_gpu_resp_cb cb,
> + struct virtio_gpu_vbuffer **vbuffer_p,
> + int cmd_size, int resp_size)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + vbuf = virtio_gpu_allocate_vbuf(vgdev, cmd_size, resp_size, cb);
> + if (IS_ERR(vbuf)) {
> + *vbuffer_p = NULL;
> + return ERR_CAST(vbuf);
> + }
> + *vbuffer_p = vbuf;
> + return (struct virtio_gpu_command *)vbuf->buf;
> +}
> +
> +static void free_vbuf(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + kfree(vbuf->data_buf);
> + kfree(vbuf);
> +}
> +
> +static int reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> + unsigned int len;
> + int freed = 0;
> + while ((vbuf = virtqueue_get_buf(vq, &len))) {
> + list_add_tail(&vbuf->destroy_list, reclaim_list);
> + freed++;
> + }
> + return freed;
> +}
> +
> +void virtio_gpu_dequeue_ctrl_func(struct work_struct *work)
> +{
> + struct virtio_gpu_device *vgdev =
> + container_of(work, struct virtio_gpu_device,
> + ctrlq.dequeue_work);
> + int ret;
> + struct list_head reclaim_list;
> + struct virtio_gpu_vbuffer *entry, *tmp;
> + struct virtio_gpu_ctrl_hdr *resp;
> + u64 fence_id = 0;
> +
> + INIT_LIST_HEAD(&reclaim_list);
> + spin_lock(&vgdev->ctrlq.qlock);
> + do {
> + virtqueue_disable_cb(vgdev->ctrlq.vq);
> + ret = reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list);
> + if (ret == 0)
> + DRM_DEBUG("cleaned 0 buffers wierd\n");
> +
> + } while (!virtqueue_enable_cb(vgdev->ctrlq.vq));
> + spin_unlock(&vgdev->ctrlq.qlock);
> +
> + list_for_each_entry_safe(entry, tmp, &reclaim_list, destroy_list) {
> + resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf;
> + if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA))
> + DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type));
> + if (resp->flags & cpu_to_le32(VIRTIO_GPU_FLAG_FENCE)) {
> + u64 f = le64_to_cpu(resp->fence_id);
> +
> + if (fence_id > f) {
> + DRM_ERROR("%s: Oops: fence %llx -> %llx\n",
> + __func__, fence_id, f);
> + } else {
> + fence_id = f;
> + }
> + }
> + if (entry->resp_cb)
> + entry->resp_cb(vgdev, entry);
> +
> + list_del(&entry->destroy_list);
> + free_vbuf(vgdev, entry);
> + }
> + wake_up(&vgdev->ctrlq.ack_queue);
> +
> + if (fence_id) {
> + virtio_gpu_fence_event_process(vgdev, fence_id);
> + }
> +}
> +
> +void virtio_gpu_dequeue_cursor_func(struct work_struct *work)
> +{
> + struct virtio_gpu_device *vgdev =
> + container_of(work, struct virtio_gpu_device,
> + cursorq.dequeue_work);
> + struct virtqueue *vq = vgdev->cursorq.vq;
> + struct list_head reclaim_list;
> + struct virtio_gpu_vbuffer *entry, *tmp;
> + unsigned int len;
> + int ret;
> +
> + INIT_LIST_HEAD(&reclaim_list);
> + spin_lock(&vgdev->cursorq.qlock);
> + do {
> + virtqueue_disable_cb(vgdev->cursorq.vq);
> + ret = reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list);
> + if (ret == 0)
> + DRM_DEBUG("cleaned 0 buffers wierd\n");
> + while (virtqueue_get_buf(vq, &len))
> + /* nothing */;
> + } while (!virtqueue_enable_cb(vgdev->cursorq.vq));
> + spin_unlock(&vgdev->cursorq.qlock);
> +
> + list_for_each_entry_safe(entry, tmp, &reclaim_list, destroy_list) {
> + list_del(&entry->destroy_list);
> + free_vbuf(vgdev, entry);
> + }
> + wake_up(&vgdev->cursorq.ack_queue);
> +}
> +
> +static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + struct virtqueue *vq = vgdev->ctrlq.vq;
> + struct scatterlist *sgs[3], vcmd, vout, vresp;
> + int outcnt = 0, incnt = 0;
> + int ret;
> +
> + sg_init_one(&vcmd, vbuf->buf, vbuf->size);
> + sgs[outcnt+incnt] = &vcmd;
> + outcnt++;
> +
> + if (vbuf->data_buf) {
> + sg_init_one(&vout, vbuf->data_buf, vbuf->data_size);
> + sgs[outcnt+incnt] = &vout;
> + outcnt++;
> + }
> +
> + if (vbuf->resp_buf) {
> + sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size);
> + sgs[outcnt+incnt] = &vresp;
> + incnt++;
> + }
> +
> + spin_lock(&vgdev->ctrlq.qlock);
> +retry:
> + ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + spin_unlock(&vgdev->ctrlq.qlock);
> + wait_event(vgdev->ctrlq.ack_queue, vq->num_free);
> + spin_lock(&vgdev->ctrlq.qlock);
> + goto retry;
> + } else {
> + virtqueue_kick(vq);
> + }
> + spin_unlock(&vgdev->ctrlq.qlock);
> +
> + if (!ret)
> + ret = vq->num_free;
> + return ret;
> +}
> +
> +static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + struct virtqueue *vq = vgdev->cursorq.vq;
> + struct scatterlist *sgs[1], ccmd;
> + int ret;
> + int outcnt;
> +
> + sg_init_one(&ccmd, vbuf->buf, vbuf->size);
> + sgs[0] = &ccmd;
> + outcnt = 1;
> +
> + spin_lock(&vgdev->cursorq.qlock);
> +retry:
> + ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + spin_unlock(&vgdev->cursorq.qlock);
> + wait_event(vgdev->cursorq.ack_queue, vq->num_free);
> + spin_lock(&vgdev->cursorq.qlock);
> + goto retry;
> + } else {
> + virtqueue_kick(vq);
> + }
> +
> + spin_unlock(&vgdev->cursorq.qlock);
> +
> + if (!ret)
> + ret = vq->num_free;
> + return ret;
> +}
> +
> +/* just create gem objects for userspace and long lived objects,
> + just use dma_alloced pages for the queue objects? */
> +
> +/* create a basic resource */
> +int virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t format,
> + uint32_t width,
> + uint32_t height)
> +{
> + struct virtio_gpu_resource_create_2d *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->format = cpu_to_le32(format);
> + cmd_p->width = cpu_to_le32(width);
> + cmd_p->height = cpu_to_le32(height);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id)
> +{
> + struct virtio_gpu_resource_unref *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id)
> +{
> + struct virtio_gpu_resource_detach_backing *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
> + uint32_t scanout_id, uint32_t resource_id,
> + uint32_t width, uint32_t height,
> + uint32_t x, uint32_t y)
> +{
> + struct virtio_gpu_set_scanout *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SET_SCANOUT);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->scanout_id = cpu_to_le32(scanout_id);
> + cmd_p->r.width = cpu_to_le32(width);
> + cmd_p->r.height = cpu_to_le32(height);
> + cmd_p->r.x = cpu_to_le32(x);
> + cmd_p->r.y = cpu_to_le32(y);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + uint32_t x, uint32_t y,
> + uint32_t width, uint32_t height)
> +{
> + struct virtio_gpu_resource_flush *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->r.width = cpu_to_le32(width);
> + cmd_p->r.height = cpu_to_le32(height);
> + cmd_p->r.x = cpu_to_le32(x);
> + cmd_p->r.y = cpu_to_le32(y);
> +
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +int virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id, uint64_t offset,
> + __le32 width, __le32 height,
> + __le32 x, __le32 y,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_transfer_to_host_2d *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->offset = cpu_to_le64(offset);
> + cmd_p->r.width = width;
> + cmd_p->r.height = height;
> + cmd_p->r.x = x;
> + cmd_p->r.y = y;
> +
> + if (fence)
> + virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +static int
> +virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev,
> + uint32_t resource_id,
> + struct virtio_gpu_mem_entry *ents,
> + uint32_t nents,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_resource_attach_backing *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING);
> + cmd_p->resource_id = cpu_to_le32(resource_id);
> + cmd_p->nr_entries = cpu_to_le32(nents);
> +
> + vbuf->data_buf = ents;
> + vbuf->data_size = sizeof(*ents) * nents;
> +
> + if (fence)
> + virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> +
> + return 0;
> +}
> +
> +static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_vbuffer *vbuf)
> +{
> + struct virtio_gpu_resp_display_info *resp =
> + (struct virtio_gpu_resp_display_info *)vbuf->resp_buf;
> + int i;
> +
> + spin_lock(&vgdev->display_info_lock);
> + for (i = 0; i < vgdev->num_scanouts; i++) {
> + vgdev->outputs[i].info = resp->pmodes[i];
> + if (resp->pmodes[i].enabled) {
> + DRM_DEBUG("output %d: %dx%d+%d+%d", i,
> + le32_to_cpu(resp->pmodes[i].r.width),
> + le32_to_cpu(resp->pmodes[i].r.height),
> + le32_to_cpu(resp->pmodes[i].r.x),
> + le32_to_cpu(resp->pmodes[i].r.y));
> + } else {
> + DRM_DEBUG("output %d: disabled", i);
> + }
> + }
> +
> + spin_unlock(&vgdev->display_info_lock);
> + wake_up(&vgdev->resp_wq);
> +
> + if (!drm_helper_hpd_irq_event(vgdev->ddev)) {
> + drm_kms_helper_hotplug_event(vgdev->ddev);
> + }
> +}
> +
> +int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
> +{
> + struct virtio_gpu_ctrl_hdr *cmd_p;
> + struct virtio_gpu_vbuffer *vbuf;
> +
> + cmd_p = virtio_gpu_alloc_cmd_resp
> + (vgdev, &virtio_gpu_cmd_get_display_info_cb, &vbuf,
> + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_display_info));
> + memset(cmd_p, 0, sizeof(*cmd_p));
> +
> + cmd_p->type = cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO);
> + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
> + return 0;
> +}
> +
> +int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_object *obj,
> + uint32_t resource_id,
> + struct virtio_gpu_fence **fence)
> +{
> + struct virtio_gpu_mem_entry *ents;
> + struct scatterlist *sg;
> + int si;
> +
> + if (!obj->pages) {
> + int ret;
> + ret = virtio_gpu_object_get_sg_table(vgdev, obj);
> + if (ret)
> + return ret;
> + }
> +
> + /* gets freed when the ring has consumed it */
> + ents = kmalloc_array(obj->pages->nents,
> + sizeof(struct virtio_gpu_mem_entry),
> + GFP_KERNEL);
> + if (!ents) {
> + DRM_ERROR("failed to allocate ent list\n");
> + return -ENOMEM;
> + }
> +
> + for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) {
> + ents[si].addr = cpu_to_le64(sg_phys(sg));
> + ents[si].length = cpu_to_le32(sg->length);
> + ents[si].padding = 0;
> + }
> +
> + virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id,
> + ents, obj->pages->nents,
> + fence);
> + obj->hw_res_handle = resource_id;
> + return 0;
> +}
> +
> +void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
> + struct virtio_gpu_output *output)
> +{
> + struct virtio_gpu_vbuffer *vbuf;
> + struct virtio_gpu_update_cursor *cur_p;
> +
> + output->cursor.pos.scanout_id = cpu_to_le32(output->index);
> + cur_p = virtio_gpu_alloc_cursor(vgdev, &vbuf);
> + memcpy(cur_p, &output->cursor, sizeof(output->cursor));
> + virtio_gpu_queue_cursor(vgdev, vbuf);
> +}
> diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
> index e894eb2..a3167fa 100644
> --- a/drivers/virtio/virtio_pci_common.c
> +++ b/drivers/virtio/virtio_pci_common.c
> @@ -510,7 +510,7 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
> goto err_enable_device;
>
> rc = pci_request_regions(pci_dev, "virtio-pci");
> - if (rc)
> + if (rc && ((pci_dev->class >> 8) != PCI_CLASS_DISPLAY_VGA))
> goto err_request_regions;
>
> if (force_legacy) {
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index e928625..a1067c4 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -799,6 +799,7 @@ struct drm_device {
> #endif
>
> struct platform_device *platformdev; /**< Platform device struture */
> + struct virtio_device *virtdev;
>
> struct drm_sg_mem *sg; /**< Scatter gather memory */
> unsigned int num_crtcs; /**< Number of CRTCs on this device */
> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> index 68ceb97..9707e5d 100644
> --- a/include/uapi/linux/Kbuild
> +++ b/include/uapi/linux/Kbuild
> @@ -429,6 +429,7 @@ header-y += virtio_balloon.h
> header-y += virtio_blk.h
> header-y += virtio_config.h
> header-y += virtio_console.h
> +header-y += virtio_gpu.h
> header-y += virtio_ids.h
> header-y += virtio_net.h
> header-y += virtio_pci.h
> diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
> new file mode 100644
> index 0000000..a1bda52
> --- /dev/null
> +++ b/include/uapi/linux/virtio_gpu.h
> @@ -0,0 +1,203 @@
> +/*
> + * Virtio GPU Device
> + *
> + * Copyright Red Hat, Inc. 2013-2014
> + *
> + * Authors:
> + * Dave Airlie <airlied-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> + * Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> + *
> + * This header is BSD licensed so anyone can use the definitions
> + * to implement compatible drivers/servers:
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + * 3. Neither the name of IBM nor the names of its contributors
> + * may be used to endorse or promote products derived from this software
> + * without specific prior written permission.
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
> + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> + * SUCH DAMAGE.
> + */
> +
> +#ifndef VIRTIO_GPU_HW_H
> +#define VIRTIO_GPU_HW_H
> +
> +enum virtio_gpu_ctrl_type {
> + VIRTIO_GPU_UNDEFINED = 0,
> +
> + /* 2d commands */
> + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
> + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
> + VIRTIO_GPU_CMD_RESOURCE_UNREF,
> + VIRTIO_GPU_CMD_SET_SCANOUT,
> + VIRTIO_GPU_CMD_RESOURCE_FLUSH,
> + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
> + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
> + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
> +
> + /* cursor commands */
> + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
> + VIRTIO_GPU_CMD_MOVE_CURSOR,
> +
> + /* success responses */
> + VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
> + VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
> +
> + /* error responses */
> + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
> + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
> + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
> + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
> + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
> + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
> +};
> +
> +#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
> +
> +struct virtio_gpu_ctrl_hdr {
> + __le32 type;
> + __le32 flags;
> + __le64 fence_id;
> + __le32 ctx_id;
> + __le32 padding;
> +};
> +
> +/* data passed in the cursor vq */
> +
> +struct virtio_gpu_cursor_pos {
> + __le32 scanout_id;
> + __le32 x, y;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
> +struct virtio_gpu_update_cursor {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_cursor_pos pos; /* update & move */
> + __le32 resource_id; /* update only */
> + __le32 hot_x; /* update only */
> + __le32 hot_y; /* update only */
> + __le32 padding;
> +};
> +
> +/* data passed in the control vq, 2d related */
> +
> +struct virtio_gpu_rect {
> + __le32 x, y;
> + __le32 width;
> + __le32 height;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
> +struct virtio_gpu_resource_unref {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
> +struct virtio_gpu_resource_create_2d {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 format;
> + __le32 width;
> + __le32 height;
> +};
> +
> +/* VIRTIO_GPU_CMD_SET_SCANOUT */
> +struct virtio_gpu_set_scanout {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_rect r;
> + __le32 scanout_id;
> + __le32 resource_id;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
> +struct virtio_gpu_resource_flush {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_rect r;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
> +struct virtio_gpu_transfer_to_host_2d {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_rect r;
> + __le64 offset;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +struct virtio_gpu_mem_entry {
> + __le64 addr;
> + __le32 length;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
> +struct virtio_gpu_resource_attach_backing {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 nr_entries;
> +};
> +
> +/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
> +struct virtio_gpu_resource_detach_backing {
> + struct virtio_gpu_ctrl_hdr hdr;
> + __le32 resource_id;
> + __le32 padding;
> +};
> +
> +/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
> +#define VIRTIO_GPU_MAX_SCANOUTS 16
> +struct virtio_gpu_resp_display_info {
> + struct virtio_gpu_ctrl_hdr hdr;
> + struct virtio_gpu_display_one {
> + struct virtio_gpu_rect r;
> + __le32 enabled;
> + __le32 flags;
> + } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
> +};
> +
> +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
> +
> +struct virtio_gpu_config {
> + __u32 events_read;
> + __u32 events_clear;
> + __u32 num_scanouts;
> + __u32 reserved;
> +};
> +
> +/* simple formats for fbcon/X use */
> +enum virtio_gpu_formats {
> + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
> + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2,
> + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3,
> + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4,
> +
> + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
> + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68,
> +
> + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
> + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
> +
> +};
> +
> +#endif
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 284fc3a..14d77f7 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -39,5 +39,5 @@
> #define VIRTIO_ID_9P 9 /* 9p virtio console */
> #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
> #define VIRTIO_ID_CAIF 12 /* Virtio caif */
> -
> +#define VIRTIO_ID_GPU 16
> #endif /* _LINUX_VIRTIO_IDS_H */
> --
> 1.8.3.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply
* Re: [PATCH 13/14] kdbus: add walk-through user space example
From: Jiri Slaby @ 2015-03-24 16:46 UTC (permalink / raw)
To: Greg Kroah-Hartman, arnd-r2nGTMty4D4,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io, teg-B22kvLQNl6c,
jkosina-AlSwsSmVLrQ, luto-kltTT9wpgjJwATOyAt5JVQ,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: daniel-cYrQPVfZoowdnm+yROfE0A, dh.herrmann-Re5JQEeQqe8AvxtiuMwx3w,
tixxdz-Umm1ozX2/EEdnm+yROfE0A
In-Reply-To: <1425906560-13798-14-git-send-email-gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
On 03/09/2015, 02:09 PM, Greg Kroah-Hartman wrote:
> --- /dev/null
> +++ b/samples/kdbus/Makefile
> @@ -0,0 +1,10 @@
> +# kbuild trick to avoid linker error. Can be omitted if a module is built.
> +obj- := dummy.o
> +
> +hostprogs-y += kdbus-workers
> +
> +always := $(hostprogs-y)
Errr, no. Not only it causes build failures (even with KDBUS=n), it
definitely should not be built for everyone.
And why is it a host prog? It's a sample prog for the kernel I am
building, i.e. for the destination arch, like all the other samples.
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [PATCH v3] Add virtio-input driver.
From: Dmitry Torokhov @ 2015-03-24 16:23 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: virtio-dev, open list:ABI/API, open list, virtualization,
David Herrmann
In-Reply-To: <20150324105829-mutt-send-email-mst@redhat.com>
On Tue, Mar 24, 2015 at 11:36:31AM +0100, Michael S. Tsirkin wrote:
> On Tue, Mar 24, 2015 at 08:32:01AM +0100, Gerd Hoffmann wrote:
> > virtio-input is basically evdev-events-over-virtio, so this driver isn't
> > much more than reading configuration from config space and forwarding
> > incoming events to the linux input layer.
> >
> > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>
> Looks pretty neat overall. I think I still see some
> small issues, but it's getting there.
>
> > ---
> > MAINTAINERS | 6 +
> > drivers/virtio/Kconfig | 10 ++
> > drivers/virtio/Makefile | 1 +
> > drivers/virtio/virtio_input.c | 313 ++++++++++++++++++++++++++++++++++++++
> > include/uapi/linux/Kbuild | 1 +
> > include/uapi/linux/virtio_ids.h | 1 +
> > include/uapi/linux/virtio_input.h | 76 +++++++++
> > 7 files changed, 408 insertions(+)
> > create mode 100644 drivers/virtio/virtio_input.c
> > create mode 100644 include/uapi/linux/virtio_input.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 358eb01..6f233dd 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -10442,6 +10442,12 @@ S: Maintained
> > F: drivers/vhost/
> > F: include/uapi/linux/vhost.h
> >
> > +VIRTIO INPUT DRIVER
> > +M: Gerd Hoffmann <kraxel@redhat.com>
> > +S: Maintained
> > +F: drivers/virtio/virtio_input.c
> > +F: include/uapi/linux/virtio_input.h
> > +
> > VIA RHINE NETWORK DRIVER
> > M: Roger Luethi <rl@hellgate.ch>
> > S: Maintained
> > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> > index b546da5..cab9f3f 100644
> > --- a/drivers/virtio/Kconfig
> > +++ b/drivers/virtio/Kconfig
> > @@ -48,6 +48,16 @@ config VIRTIO_BALLOON
> >
> > If unsure, say M.
> >
> > +config VIRTIO_INPUT
> > + tristate "Virtio input driver"
> > + depends on VIRTIO
> > + depends on INPUT
> > + ---help---
> > + This driver supports virtio input devices such as
> > + keyboards, mice and tablets.
> > +
> > + If unsure, say M.
> > +
> > config VIRTIO_MMIO
> > tristate "Platform bus driver for memory mapped virtio devices"
> > depends on HAS_IOMEM
> > diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> > index d85565b..41e30e3 100644
> > --- a/drivers/virtio/Makefile
> > +++ b/drivers/virtio/Makefile
> > @@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
> > virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
> > virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
> > obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
> > +obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
> > diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
> > new file mode 100644
> > index 0000000..cf112b2
> > --- /dev/null
> > +++ b/drivers/virtio/virtio_input.c
> > @@ -0,0 +1,313 @@
> > +#include <linux/module.h>
> > +#include <linux/virtio.h>
> > +#include <linux/input.h>
> > +
> > +#include <uapi/linux/virtio_ids.h>
> > +#include <uapi/linux/virtio_input.h>
> > +
> > +struct virtio_input {
> > + struct virtio_device *vdev;
> > + struct input_dev *idev;
> > + char name[64];
> > + char serial[64];
> > + char phys[64];
> > + struct virtqueue *evt, *sts;
> > + struct virtio_input_event evts[64];
> > + spinlock_t lock;
> > +};
> > +
> > +static void virtinput_queue_evtbuf(struct virtio_input *vi,
> > + struct virtio_input_event *evtbuf)
> > +{
> > + struct scatterlist sg[1];
> > +
> > + sg_init_one(sg, evtbuf, sizeof(*evtbuf));
> > + virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
> > +}
> > +
> > +static void virtinput_recv_events(struct virtqueue *vq)
> > +{
> > + struct virtio_input *vi = vq->vdev->priv;
> > + struct virtio_input_event *event;
> > + unsigned long flags;
> > + unsigned int len;
> > +
> > + spin_lock_irqsave(&vi->lock, flags);
> > + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
> > + input_event(vi->idev,
> > + le16_to_cpu(event->type),
> > + le16_to_cpu(event->code),
> > + le32_to_cpu(event->value));
>
> What happens if input layer gets an
> unexpected event code or value?
> Or does something prevent it?
>
>
>
> > + virtinput_queue_evtbuf(vi, event);
> > + }
> > + virtqueue_kick(vq);
> > + spin_unlock_irqrestore(&vi->lock, flags);
> > +}
> > +
> > +static int virtinput_send_status(struct virtio_input *vi,
> > + u16 type, u16 code, s32 value)
> > +{
> > + struct virtio_input_event *stsbuf;
> > + struct scatterlist sg[1];
> > + unsigned long flags;
> > + int rc;
> > +
> > + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
> > + if (!stsbuf)
> > + return -ENOMEM;
> > +
> > + stsbuf->type = cpu_to_le16(type);
> > + stsbuf->code = cpu_to_le16(code);
> > + stsbuf->value = cpu_to_le32(value);
> > + sg_init_one(sg, stsbuf, sizeof(*stsbuf));
> > +
> > + spin_lock_irqsave(&vi->lock, flags);
> > + rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
> > + virtqueue_kick(vi->sts);
> > + spin_unlock_irqrestore(&vi->lock, flags);
I think locking is wrong here. This is basically input_dev->event()
which is called with input_dev->event_lock spinlock held, and it is
taking vi->lock. OTOH virtinput_recv_events() takes vi->lock and then
calls input_event(), which will try taking input_dev->event_lock. It is
bound to deadlock at some point.
I guess the easiest way would be to drop vi->lock() after fetching
virtio event and before calling input_event().
> > +
> > + if (rc != 0)
> > + kfree(stsbuf);
> > + return rc;
>
> This means that caller will get errors if it happens to call
> send_status at a rate that's faster than host's consumption of them.
> To me this looks wrong.
> Poking at input layer, it seems to simply discard errors.
> Is it always safe to discard status updates?
> If yes, some kind of comment to clarify the logic would
> make sense IMHO.
>
>
>
> > +}
> > +
> > +static void virtinput_recv_status(struct virtqueue *vq)
> > +{
> > + struct virtio_input *vi = vq->vdev->priv;
> > + struct virtio_input_event *stsbuf;
> > + unsigned long flags;
> > + unsigned int len;
> > +
> > + spin_lock_irqsave(&vi->lock, flags);
> > + while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
> > + kfree(stsbuf);
> > + spin_unlock_irqrestore(&vi->lock, flags);
> > +}
> > +
> > +static int virtinput_status(struct input_dev *idev, unsigned int type,
> > + unsigned int code, int value)
> > +{
> > + struct virtio_input *vi = input_get_drvdata(idev);
> > +
> > + return virtinput_send_status(vi, type, code, value);
> > +}
> > +
> > +static size_t virtinput_cfg_select(struct virtio_input *vi,
> > + u8 select, u8 subsel)
> > +{
> > + u8 size;
> > +
> > + virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select);
> > + virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel);
> > + virtio_cread(vi->vdev, struct virtio_input_config, size, &size);
> > + return size;
> > +}
> > +
> > +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
> > + unsigned long *bits, unsigned int bitcount)
> > +{
> > + unsigned int bit;
> > + size_t bytes;
> > + u8 *virtio_bits;
> > +
> > + bytes = virtinput_cfg_select(vi, select, subsel);
> > + if (!bytes)
> > + return;
>
> How about limiting bytes to sizeof struct virtio_input_config->u?
>
> > + if (bitcount > bytes*8)
> > + bitcount = bytes*8;
>
> Space around * pls.
>
> > +
> > + /*
> > + * Bitmap in virtio config space is a simple stream of bytes,
> > + * with the first byte carrying bits 0-7, second bits 8-15 and
> > + * so on.
> > + */
> > + virtio_bits = kzalloc(bytes, GFP_KERNEL);
> > + if (!virtio_bits)
> > + return;
> > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > + virtio_bits, bytes);
> > + for (bit = 0; bit < bitcount; bit++) {
> > + if (virtio_bits[bit / 8] & (1 << (bit % 8)))
> > + __set_bit(bit, bits);
> > + }
> > + kfree(virtio_bits);
> > +
> > + if (select == VIRTIO_INPUT_CFG_EV_BITS)
> > + __set_bit(subsel, vi->idev->evbit);
> > +}
> > +
> > +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> > +{
> > + u32 mi, ma, re, fu, fl;
> > +
> > + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> > + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> > + input_abs_set_res(vi->idev, abs, re);
> > +}
> > +
> > +static int virtinput_init_vqs(struct virtio_input *vi)
> > +{
> > + struct virtqueue *vqs[2];
> > + vq_callback_t *cbs[] = { virtinput_recv_events,
> > + virtinput_recv_status };
> > + static const char * names[] = { "events", "status" };
>
> No space between * and names expected
>
> > + int i, err, size;
> > +
> > + err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names);
> > + if (err)
> > + return err;
> > + vi->evt = vqs[0];
> > + vi->sts = vqs[1];
> > +
> > + size = virtqueue_get_vring_size(vi->evt);
> > + if (size > ARRAY_SIZE(vi->evts))
> > + size = ARRAY_SIZE(vi->evts);
> > + for (i = 0; i < size; i++)
> > + virtinput_queue_evtbuf(vi, &vi->evts[i]);
> > + virtqueue_kick(vi->evt);
> > +
> > + return 0;
> > +}
> > +
> > +static int virtinput_probe(struct virtio_device *vdev)
> > +{
> > + struct virtio_input *vi;
> > + size_t size;
> > + int abs, err;
> > +
> > + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
> > + return -ENODEV;
> > +
> > + vi = kzalloc(sizeof(*vi), GFP_KERNEL);
> > + if (!vi)
> > + return -ENOMEM;
> > +
> > + vdev->priv = vi;
> > + vi->vdev = vdev;
> > + spin_lock_init(&vi->lock);
> > +
> > + err = virtinput_init_vqs(vi);
> > + if (err)
> > + goto err_init_vq;
> > +
> > + vi->idev = input_allocate_device();
> > + if (!vi->idev) {
> > + err = -ENOMEM;
> > + goto err_input_alloc;
> > + }
> > + input_set_drvdata(vi->idev, vi);
> > +
> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
> > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > + vi->name, min(size, sizeof(vi->name)));
> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);
> > + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> > + vi->serial, min(size, sizeof(vi->serial)));
> > + snprintf(vi->phys, sizeof(vi->phys),
> > + "virtio%d/input0", vdev->index);
> > + vi->idev->name = vi->name;
> > + vi->idev->phys = vi->phys;
> > + vi->idev->uniq = vi->serial;
> > +
> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
> > + if (size >= 8) {
>
> What does 8 mean here? Should be sizeof virtio_input_devids?
>
> > + virtio_cread(vi->vdev, struct virtio_input_config,
> > + u.ids.bustype, &vi->idev->id.bustype);
> > + virtio_cread(vi->vdev, struct virtio_input_config,
> > + u.ids.vendor, &vi->idev->id.vendor);
> > + virtio_cread(vi->vdev, struct virtio_input_config,
> > + u.ids.product, &vi->idev->id.product);
> > + virtio_cread(vi->vdev, struct virtio_input_config,
> > + u.ids.version, &vi->idev->id.version);
> > + } else {
> > + vi->idev->id.bustype = BUS_VIRTUAL;
> > + }
> > +
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,
> > + vi->idev->propbit, INPUT_PROP_CNT);
> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);
> > + if (size)
> > + __set_bit(EV_REP, vi->idev->evbit);
> > +
> > + vi->idev->dev.parent = &vdev->dev;
> > + vi->idev->event = virtinput_status;
> > +
> > + /* device -> kernel */
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,
> > + vi->idev->keybit, KEY_CNT);
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,
> > + vi->idev->relbit, REL_CNT);
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,
> > + vi->idev->absbit, ABS_CNT);
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,
> > + vi->idev->mscbit, MSC_CNT);
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,
> > + vi->idev->swbit, SW_CNT);
> > +
> > + /* kernel -> device */
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,
> > + vi->idev->ledbit, LED_CNT);
> > + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
> > + vi->idev->sndbit, SND_CNT);
> > +
> > + if (test_bit(EV_ABS, vi->idev->evbit)) {
> > + for (abs = 0; abs < ABS_CNT; abs++) {
> > + if (!test_bit(abs, vi->idev->absbit))
> > + continue;
> > + virtinput_cfg_abs(vi, abs);
> > + }
> > + }
> > + virtio_device_ready(vdev);
> > +
> At this point you can already get interrupts.
> This will cause events to be forwarded.
> I'm guessing this is ok since you called
> input_allocate_device, but worth checking,
> and maybe adding a comment.
Yes, it is OK to send events though yet unregistered input device, as
long as it was allocated with input_allocate_device().
>
> > + err = input_register_device(vi->idev);
> > + if (err)
> > + goto err_input_register;
> > +
> > + return 0;
> > +
> > +err_input_register:
>
> > + input_free_device(vi->idev);
>
> At this point you can already get interrupts
> since you called virtio_device_ready, and
> getting events from a freed device likely won't
> DTRT.
Right. I guess you want to mark the virtio device ready only after
registering input device.
>
> > +err_input_alloc:
> > + vdev->config->del_vqs(vdev);
> > +err_init_vq:
> > + kfree(vi);
> > + return err;
> > +}
> > +
> > +static void virtinput_remove(struct virtio_device *vdev)
> > +{
> > + struct virtio_input *vi = vdev->priv;
> > +
> > + input_unregister_device(vi->idev);
>
> same thing here, you might get an event at this point.
> You need to somehow block new events
> being sent to device while keeping
> device around.
>
> Since you already do everything under a spinlock,
> it's probably easiest to add a flag discarding
> recv events. You can then check it in virtinput_recv_events
> before calling input_event.
Instead of checking the flag is it possible to pause virio device? Maybe
virtio_break_device()?
>
>
>
> > + vdev->config->del_vqs(vdev);
> > + kfree(vi);
> > +}
> > +
> > +static unsigned int features[] = {
> > +};
> > +static struct virtio_device_id id_table[] = {
> > + { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },
> > + { 0 },
> > +};
> > +
> > +static struct virtio_driver virtio_input_driver = {
> > + .driver.name = KBUILD_MODNAME,
> > + .driver.owner = THIS_MODULE,
> > + .feature_table = features,
> > + .feature_table_size = ARRAY_SIZE(features),
> > + .id_table = id_table,
> > + .probe = virtinput_probe,
> > + .remove = virtinput_remove,
>
> I note this driver doesn't seem to handle hybernation,
> that's probably a bug?
>
>
> > +};
> > +
> > +module_virtio_driver(virtio_input_driver);
> > +MODULE_DEVICE_TABLE(virtio, id_table);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("Virtio input device driver");
> > +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
> > diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> > index 68ceb97..04b829e 100644
> > --- a/include/uapi/linux/Kbuild
> > +++ b/include/uapi/linux/Kbuild
> > @@ -430,6 +430,7 @@ header-y += virtio_blk.h
> > header-y += virtio_config.h
> > header-y += virtio_console.h
> > header-y += virtio_ids.h
> > +header-y += virtio_input.h
> > header-y += virtio_net.h
> > header-y += virtio_pci.h
> > header-y += virtio_ring.h
> > diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> > index 284fc3a..5f60aa4 100644
> > --- a/include/uapi/linux/virtio_ids.h
> > +++ b/include/uapi/linux/virtio_ids.h
> > @@ -39,5 +39,6 @@
> > #define VIRTIO_ID_9P 9 /* 9p virtio console */
> > #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
> > #define VIRTIO_ID_CAIF 12 /* Virtio caif */
> > +#define VIRTIO_ID_INPUT 18 /* virtio input */
> >
> > #endif /* _LINUX_VIRTIO_IDS_H */
> > diff --git a/include/uapi/linux/virtio_input.h b/include/uapi/linux/virtio_input.h
> > new file mode 100644
> > index 0000000..7fceabd
> > --- /dev/null
> > +++ b/include/uapi/linux/virtio_input.h
> > @@ -0,0 +1,76 @@
> > +#ifndef _LINUX_VIRTIO_INPUT_H
> > +#define _LINUX_VIRTIO_INPUT_H
> > +/* This header is BSD licensed so anyone can use the definitions to implement
> > + * compatible drivers/servers.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + * notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + * notice, this list of conditions and the following disclaimer in the
> > + * documentation and/or other materials provided with the distribution.
> > + * 3. Neither the name of IBM nor the names of its contributors
> > + * may be used to endorse or promote products derived from this software
> > + * without specific prior written permission.
> > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> > + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> > + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
> > + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> > + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> > + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> > + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> > + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > + * SUCH DAMAGE. */
> > +#include <linux/virtio_ids.h>
> > +#include <linux/virtio_config.h>
> > +
> > +enum virtio_input_config_select {
> > + VIRTIO_INPUT_CFG_UNSET = 0x00,
> > + VIRTIO_INPUT_CFG_ID_NAME = 0x01,
> > + VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
> > + VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
> > + VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
> > + VIRTIO_INPUT_CFG_EV_BITS = 0x11,
> > + VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
> > +};
> > +
> > +struct virtio_input_absinfo {
> > + __virtio32 min;
> > + __virtio32 max;
> > + __virtio32 fuzz;
> > + __virtio32 flat;
> > + __virtio32 res;
> > +};
> > +
> > +struct virtio_input_devids {
> > + __virtio16 bustype;
> > + __virtio16 vendor;
> > + __virtio16 product;
> > + __virtio16 version;
> > +};
> > +
>
> this padding bt two spaces looks weird.
>
> > +struct virtio_input_config {
> > + __u8 select;
> > + __u8 subsel;
> > + __u8 size;
> > + __u8 reserved;
> > + union {
> > + char string[128];
> > + __u8 bitmap[128];
>
> I note that neither string nor bitmap are used by
> driver. What are they in aid of?
Also, what happens if we need more than 1024 bits to pass bitmap data?
We might get there with keyboards.
>
> > + struct virtio_input_absinfo abs;
> > + struct virtio_input_devids ids;
> > + } u;
> > +};
> > +
> > +struct virtio_input_event {
> > + __le16 type;
> > + __le16 code;
> > + __le32 value;
> > +};
> > +
> > +#endif /* _LINUX_VIRTIO_INPUT_H */
> > --
> > 1.8.3.1
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] Add virtio gpu driver.
From: Michael S. Tsirkin @ 2015-03-24 16:15 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, Dave Airlie, Dave Airlie, David Airlie, Rusty Russell,
open list, open list:DRM DRIVERS, open list:VIRTIO CORE, NET...,
open list:ABI/API
In-Reply-To: <1427213239-8775-1-git-send-email-kraxel@redhat.com>
On Tue, Mar 24, 2015 at 05:07:18PM +0100, Gerd Hoffmann wrote:
> From: Dave Airlie <airlied@gmail.com>
>
> This patch adds a kms driver for the virtio gpu. The xorg modesetting
> driver can handle the device just fine, the framebuffer for fbcon is
> there too.
>
> Qemu patches for the host side are under review currently.
>
> The pci version of the device comes in two variants: with and without
> vga compatibility. The former has a extra memory bar for the vga
> framebuffer, the later is a pure virtio device. The only concern for
> this driver is that in the virtio-vga case we have to kick out the
> firmware framebuffer.
>
> Initial revision has only 2d support, 3d (virgl) support requires
> some more work on the qemu side and will be added later.
>
> Signed-off-by: Dave Airlie <airlied@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
...
> diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
> index e894eb2..a3167fa 100644
> --- a/drivers/virtio/virtio_pci_common.c
> +++ b/drivers/virtio/virtio_pci_common.c
> @@ -510,7 +510,7 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
> goto err_enable_device;
>
> rc = pci_request_regions(pci_dev, "virtio-pci");
> - if (rc)
> + if (rc && ((pci_dev->class >> 8) != PCI_CLASS_DISPLAY_VGA))
> goto err_request_regions;
>
> if (force_legacy) {
This is probably what you described as "the only concern? Can you
explain why you are doing this? If we only need to request specific
regions, I think we should do exactly that, requesting only parts of
regions that are covered by the virtio capabilities.
Seems cleaner than looking for a specific class.
Didn't look at device code in depth yet.
^ permalink raw reply
* [PATCH] Add virtio gpu driver.
From: Gerd Hoffmann @ 2015-03-24 16:07 UTC (permalink / raw)
To: virtio-dev
Cc: Michael S. Tsirkin, David Airlie, open list:ABI/API, open list,
open list:DRM DRIVERS, open list:VIRTIO CORE, NET..., Dave Airlie,
Dave Airlie
From: Dave Airlie <airlied@gmail.com>
This patch adds a kms driver for the virtio gpu. The xorg modesetting
driver can handle the device just fine, the framebuffer for fbcon is
there too.
Qemu patches for the host side are under review currently.
The pci version of the device comes in two variants: with and without
vga compatibility. The former has a extra memory bar for the vga
framebuffer, the later is a pure virtio device. The only concern for
this driver is that in the virtio-vga case we have to kick out the
firmware framebuffer.
Initial revision has only 2d support, 3d (virgl) support requires
some more work on the qemu side and will be added later.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/virtio/Kconfig | 11 +
drivers/gpu/drm/virtio/Makefile | 9 +
drivers/gpu/drm/virtio/virtgpu_debugfs.c | 64 ++++
drivers/gpu/drm/virtio/virtgpu_display.c | 527 ++++++++++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 68 ++++
drivers/gpu/drm/virtio/virtgpu_drv.c | 132 ++++++++
drivers/gpu/drm/virtio/virtgpu_drv.h | 326 +++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_fb.c | 415 ++++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_fence.c | 95 ++++++
drivers/gpu/drm/virtio/virtgpu_gem.c | 120 +++++++
drivers/gpu/drm/virtio/virtgpu_kms.c | 125 +++++++
drivers/gpu/drm/virtio/virtgpu_object.c | 174 ++++++++++
drivers/gpu/drm/virtio/virtgpu_ttm.c | 451 ++++++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_vq.c | 540 +++++++++++++++++++++++++++++++
drivers/virtio/virtio_pci_common.c | 2 +-
include/drm/drmP.h | 1 +
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/virtio_gpu.h | 203 ++++++++++++
include/uapi/linux/virtio_ids.h | 2 +-
21 files changed, 3267 insertions(+), 2 deletions(-)
create mode 100644 drivers/gpu/drm/virtio/Kconfig
create mode 100644 drivers/gpu/drm/virtio/Makefile
create mode 100644 drivers/gpu/drm/virtio/virtgpu_debugfs.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_display.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_drm_bus.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_drv.h
create mode 100644 drivers/gpu/drm/virtio/virtgpu_fb.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_fence.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_gem.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_kms.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_object.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_ttm.c
create mode 100644 drivers/gpu/drm/virtio/virtgpu_vq.c
create mode 100644 include/uapi/linux/virtio_gpu.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 151a050..f2388ea 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -197,6 +197,8 @@ source "drivers/gpu/drm/qxl/Kconfig"
source "drivers/gpu/drm/bochs/Kconfig"
+source "drivers/gpu/drm/virtio/Kconfig"
+
source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2c239b9..083d443 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/
obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
+obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-$(CONFIG_DRM_STI) += sti/
diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig
new file mode 100644
index 0000000..55868e2
--- /dev/null
+++ b/drivers/gpu/drm/virtio/Kconfig
@@ -0,0 +1,11 @@
+config DRM_VIRTIO_GPU
+ tristate "QEMU Virtio GPU"
+ depends on DRM && VIRTIO
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select DRM_KMS_HELPER
+ select DRM_KMS_FB_HELPER
+ select DRM_TTM
+ help
+ QEMU based virtio GPU.
diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
new file mode 100644
index 0000000..57d59ee
--- /dev/null
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+
+virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_drm_bus.o virtgpu_gem.o virtgpu_fb.o virtgpu_display.o virtgpu_vq.o virtgpu_ttm.o virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o
+
+obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
new file mode 100644
index 0000000..dbc497d
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 Red Hat
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <linux/debugfs.h>
+
+#include "drmP.h"
+#include "virtgpu_drv.h"
+
+static int
+virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct virtio_gpu_device *vgdev = node->minor->dev->dev_private;
+
+ seq_printf(m, "fence %ld %lld\n",
+ atomic64_read(&vgdev->fence_drv.last_seq),
+ vgdev->fence_drv.sync_seq);
+ return 0;
+}
+
+static struct drm_info_list virtio_gpu_debugfs_list[] = {
+ { "irq_fence", virtio_gpu_debugfs_irq_info, 0, NULL },
+};
+
+#define VIRTIO_GPU_DEBUGFS_ENTRIES ARRAY_SIZE(virtio_gpu_debugfs_list)
+
+int
+virtio_gpu_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(virtio_gpu_debugfs_list,
+ VIRTIO_GPU_DEBUGFS_ENTRIES,
+ minor->debugfs_root, minor);
+ return 0;
+}
+
+void
+virtio_gpu_debugfs_takedown(struct drm_minor *minor)
+{
+ drm_debugfs_remove_files(virtio_gpu_debugfs_list,
+ VIRTIO_GPU_DEBUGFS_ENTRIES,
+ minor);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
new file mode 100644
index 0000000..578a02c
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alon Levy
+ */
+
+#include "virtgpu_drv.h"
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#define XRES_MIN 320
+#define YRES_MIN 200
+
+#define XRES_DEF 1024
+#define YRES_DEF 768
+
+#define XRES_MAX 8192
+#define YRES_MAX 8192
+
+static void virtio_gpu_crtc_gamma_set(struct drm_crtc *crtc,
+ u16 *red, u16 *green, u16 *blue,
+ uint32_t start, uint32_t size)
+{
+ /* TODO */
+}
+
+static void
+virtio_gpu_hide_cursor(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_output *output)
+{
+ output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
+ output->cursor.resource_id = 0;
+ virtio_gpu_cursor_ping(vgdev, output);
+}
+
+static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ uint32_t width,
+ uint32_t height,
+ int32_t hot_x, int32_t hot_y)
+{
+ struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
+ struct virtio_gpu_output *output =
+ container_of(crtc, struct virtio_gpu_output, crtc);
+ struct drm_gem_object *gobj = NULL;
+ struct virtio_gpu_object *qobj = NULL;
+ struct virtio_gpu_fence *fence = NULL;
+ int ret = 0;
+
+ if (handle == 0) {
+ virtio_gpu_hide_cursor(vgdev, output);
+ return 0;
+ }
+
+ /* lookup the cursor */
+ gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
+ if (gobj == NULL)
+ return -ENOENT;
+
+ qobj = gem_to_virtio_gpu_obj(gobj);
+
+ if (!qobj->hw_res_handle) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0,
+ cpu_to_le32(64),
+ cpu_to_le32(64),
+ 0, 0, &fence);
+ if (!ret) {
+ reservation_object_add_excl_fence(qobj->tbo.resv,
+ &fence->f);
+ virtio_gpu_object_wait(qobj, false);
+ }
+
+ output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR);
+ output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle);
+ output->cursor.hot_x = cpu_to_le32(hot_x);
+ output->cursor.hot_y = cpu_to_le32(hot_y);
+ virtio_gpu_cursor_ping(vgdev, output);
+out:
+ drm_gem_object_unreference_unlocked(gobj);
+ return ret;
+}
+
+static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc,
+ int x, int y)
+{
+ struct virtio_gpu_device *vgdev = crtc->dev->dev_private;
+ struct virtio_gpu_output *output =
+ container_of(crtc, struct virtio_gpu_output, crtc);
+
+ output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_MOVE_CURSOR);
+ output->cursor.pos.x = cpu_to_le32(x);
+ output->cursor.pos.y = cpu_to_le32(y);
+ virtio_gpu_cursor_ping(vgdev, output);
+ return 0;
+}
+
+static int virtio_gpu_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t flags)
+{
+ return -EINVAL;
+}
+
+
+static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
+ .cursor_set2 = virtio_gpu_crtc_cursor_set,
+ .cursor_move = virtio_gpu_crtc_cursor_move,
+ .gamma_set = virtio_gpu_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = virtio_gpu_crtc_page_flip,
+ .destroy = drm_crtc_cleanup,
+};
+
+static void virtio_gpu_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct virtio_gpu_framebuffer *virtio_gpu_fb
+ = to_virtio_gpu_framebuffer(fb);
+
+ if (virtio_gpu_fb->obj)
+ drm_gem_object_unreference_unlocked(virtio_gpu_fb->obj);
+ drm_framebuffer_cleanup(fb);
+ kfree(virtio_gpu_fb);
+}
+
+static int
+virtio_gpu_framebuffer_surface_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct virtio_gpu_framebuffer *virtio_gpu_fb
+ = to_virtio_gpu_framebuffer(fb);
+
+ return virtio_gpu_surface_dirty(virtio_gpu_fb, clips, num_clips);
+}
+
+static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
+ .destroy = virtio_gpu_user_framebuffer_destroy,
+ .dirty = virtio_gpu_framebuffer_surface_dirty,
+};
+
+int
+virtio_gpu_framebuffer_init(struct drm_device *dev,
+ struct virtio_gpu_framebuffer *vgfb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ int ret;
+ struct virtio_gpu_object *bo;
+ vgfb->obj = obj;
+
+ bo = gem_to_virtio_gpu_obj(obj);
+
+ ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs);
+ if (ret) {
+ vgfb->obj = NULL;
+ return ret;
+ }
+ drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd);
+
+ spin_lock_init(&vgfb->dirty_lock);
+ vgfb->x1 = vgfb->y1 = INT_MAX;
+ vgfb->x2 = vgfb->y2 = 0;
+ return 0;
+}
+
+static void virtio_gpu_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+}
+
+static bool virtio_gpu_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static int virtio_gpu_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct virtio_gpu_framebuffer *vgfb;
+ struct virtio_gpu_object *bo, *old_bo = NULL;
+ struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
+
+ if (!crtc->primary->fb) {
+ DRM_DEBUG_KMS("No FB bound\n");
+ return 0;
+ }
+
+ if (old_fb) {
+ vgfb = to_virtio_gpu_framebuffer(old_fb);
+ old_bo = gem_to_virtio_gpu_obj(vgfb->obj);
+ }
+ vgfb = to_virtio_gpu_framebuffer(crtc->primary->fb);
+ bo = gem_to_virtio_gpu_obj(vgfb->obj);
+ DRM_DEBUG("+%d+%d (%d,%d) => (%d,%d)\n",
+ x, y,
+ mode->hdisplay, mode->vdisplay,
+ adjusted_mode->hdisplay,
+ adjusted_mode->vdisplay);
+
+ virtio_gpu_cmd_set_scanout(vgdev, output->index, bo->hw_res_handle,
+ mode->hdisplay, mode->vdisplay, x, y);
+
+ return 0;
+}
+
+static void virtio_gpu_crtc_prepare(struct drm_crtc *crtc)
+{
+ DRM_DEBUG("current: %dx%d+%d+%d (%d).\n",
+ crtc->mode.hdisplay, crtc->mode.vdisplay,
+ crtc->x, crtc->y, crtc->enabled);
+}
+
+static void virtio_gpu_crtc_commit(struct drm_crtc *crtc)
+{
+ DRM_DEBUG("\n");
+}
+
+static void virtio_gpu_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static void virtio_gpu_crtc_disable(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
+
+ virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0);
+}
+
+static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
+ .disable = virtio_gpu_crtc_disable,
+ .dpms = virtio_gpu_crtc_dpms,
+ .mode_fixup = virtio_gpu_crtc_mode_fixup,
+ .mode_set = virtio_gpu_crtc_mode_set,
+ .prepare = virtio_gpu_crtc_prepare,
+ .commit = virtio_gpu_crtc_commit,
+ .load_lut = virtio_gpu_crtc_load_lut,
+};
+
+static void virtio_gpu_enc_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool virtio_gpu_enc_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void virtio_gpu_enc_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void virtio_gpu_enc_commit(struct drm_encoder *encoder)
+{
+}
+
+static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static int virtio_gpu_conn_get_modes(struct drm_connector *connector)
+{
+ struct virtio_gpu_output *output =
+ drm_connector_to_virtio_gpu_output(connector);
+ struct drm_display_mode *mode = NULL;
+ int count, width, height;
+
+ width = le32_to_cpu(output->info.r.width);
+ height = le32_to_cpu(output->info.r.height);
+ count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
+
+ if (width == 0 || height == 0) {
+ width = XRES_DEF;
+ height = YRES_DEF;
+ drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
+ } else {
+ DRM_DEBUG("add mode: %dx%d\n", width, height);
+ mode = drm_cvt_mode(connector->dev, width, height, 60,
+ false, false, false);
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+ count++;
+ }
+
+ return count;
+}
+
+static int virtio_gpu_conn_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct virtio_gpu_output *output =
+ drm_connector_to_virtio_gpu_output(connector);
+ int width, height;
+
+ width = le32_to_cpu(output->info.r.width);
+ height = le32_to_cpu(output->info.r.height);
+
+ if (!(mode->type & DRM_MODE_TYPE_PREFERRED))
+ return MODE_OK;
+ if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF)
+ return MODE_OK;
+ if (mode->hdisplay <= width && mode->hdisplay >= width - 16 &&
+ mode->vdisplay <= height && mode->vdisplay >= height - 16)
+ return MODE_OK;
+
+ DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay);
+ return MODE_BAD;
+}
+
+static struct drm_encoder*
+virtio_gpu_best_encoder(struct drm_connector *connector)
+{
+ struct virtio_gpu_output *virtio_gpu_output =
+ drm_connector_to_virtio_gpu_output(connector);
+
+ return &virtio_gpu_output->enc;
+}
+
+
+static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
+ .dpms = virtio_gpu_enc_dpms,
+ .mode_fixup = virtio_gpu_enc_mode_fixup,
+ .prepare = virtio_gpu_enc_prepare,
+ .mode_set = virtio_gpu_enc_mode_set,
+ .commit = virtio_gpu_enc_commit,
+};
+
+static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
+ .get_modes = virtio_gpu_conn_get_modes,
+ .mode_valid = virtio_gpu_conn_mode_valid,
+ .best_encoder = virtio_gpu_best_encoder,
+};
+
+static void virtio_gpu_conn_save(struct drm_connector *connector)
+{
+ DRM_DEBUG("\n");
+}
+
+static void virtio_gpu_conn_restore(struct drm_connector *connector)
+{
+ DRM_DEBUG("\n");
+}
+
+static enum drm_connector_status virtio_gpu_conn_detect(
+ struct drm_connector *connector,
+ bool force)
+{
+ struct virtio_gpu_output *output =
+ drm_connector_to_virtio_gpu_output(connector);
+
+ if (output->info.enabled)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static int virtio_gpu_conn_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ DRM_DEBUG("\n");
+ return 0;
+}
+
+static void virtio_gpu_conn_destroy(struct drm_connector *connector)
+{
+ struct virtio_gpu_output *virtio_gpu_output =
+ drm_connector_to_virtio_gpu_output(connector);
+
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+ kfree(virtio_gpu_output);
+}
+
+static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .save = virtio_gpu_conn_save,
+ .restore = virtio_gpu_conn_restore,
+ .detect = virtio_gpu_conn_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = virtio_gpu_conn_set_property,
+ .destroy = virtio_gpu_conn_destroy,
+};
+
+static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
+{
+ struct drm_device *dev = vgdev->ddev;
+ struct virtio_gpu_output *output = vgdev->outputs + index;
+ struct drm_connector *connector = &output->conn;
+ struct drm_encoder *encoder = &output->enc;
+ struct drm_crtc *crtc = &output->crtc;
+
+ output->index = index;
+ if (index == 0) {
+ output->info.enabled = cpu_to_le32(true);
+ output->info.r.width = cpu_to_le32(XRES_DEF);
+ output->info.r.height = cpu_to_le32(YRES_DEF);
+ }
+
+ drm_crtc_init(dev, crtc, &virtio_gpu_crtc_funcs);
+ drm_mode_crtc_set_gamma_size(crtc, 256);
+ drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
+
+ drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
+ DRM_MODE_ENCODER_VIRTUAL);
+
+ encoder->possible_crtcs = 1 << index;
+ drm_mode_connector_attach_encoder(connector, encoder);
+ drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
+ drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
+ drm_connector_register(connector);
+ return 0;
+}
+
+static struct drm_framebuffer *
+virtio_gpu_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_gem_object *obj = NULL;
+ struct virtio_gpu_framebuffer *virtio_gpu_fb;
+ int ret;
+
+ /* lookup object associated with res handle */
+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+ if (!obj)
+ return ERR_PTR(-EINVAL);
+
+ virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL);
+ if (virtio_gpu_fb == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj);
+ if (ret) {
+ kfree(virtio_gpu_fb);
+ if (obj)
+ drm_gem_object_unreference_unlocked(obj);
+ return NULL;
+ }
+
+ return &virtio_gpu_fb->base;
+}
+
+static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
+ .fb_create = virtio_gpu_user_framebuffer_create,
+};
+
+int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
+{
+ int i;
+ int ret;
+
+ drm_mode_config_init(vgdev->ddev);
+ vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs;
+
+ /* modes will be validated against the framebuffer size */
+ vgdev->ddev->mode_config.min_width = XRES_MIN;
+ vgdev->ddev->mode_config.min_height = YRES_MIN;
+ vgdev->ddev->mode_config.max_width = XRES_MAX;
+ vgdev->ddev->mode_config.max_height = YRES_MAX;
+
+ for (i = 0 ; i < vgdev->num_scanouts; ++i)
+ vgdev_output_init(vgdev, i);
+
+ /* primary surface must be created by this point, to allow
+ * issuing command queue commands and having them read by
+ * spice server. */
+ ret = virtio_gpu_fbdev_init(vgdev);
+ if (ret)
+ return ret;
+
+ ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts);
+
+ drm_kms_helper_poll_init(vgdev->ddev);
+ return ret;
+}
+
+void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
+{
+ virtio_gpu_fbdev_fini(vgdev);
+ drm_mode_config_cleanup(vgdev->ddev);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
new file mode 100644
index 0000000..e4b50af
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
@@ -0,0 +1,68 @@
+#include <linux/pci.h>
+
+#include "virtgpu_drv.h"
+
+int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master)
+{
+ struct pci_dev *pdev = dev->pdev;
+
+ if (pdev) {
+ return drm_pci_set_busid(dev, master);
+ }
+ return 0;
+}
+
+static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev)
+{
+ struct apertures_struct *ap;
+ bool primary;
+ ap = alloc_apertures(1);
+ if (!ap)
+ return;
+
+ ap->ranges[0].base = pci_resource_start(pci_dev, 2);
+ ap->ranges[0].size = pci_resource_len(pci_dev, 2);
+
+ primary = pci_dev->resource[PCI_ROM_RESOURCE].flags
+ & IORESOURCE_ROM_SHADOW;
+
+ remove_conflicting_framebuffers(ap, "virtiodrmfb", primary);
+
+ kfree(ap);
+}
+
+int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
+{
+ struct drm_device *dev;
+ int ret;
+
+ dev = drm_dev_alloc(driver, &vdev->dev);
+ if (!dev)
+ return -ENOMEM;
+ dev->virtdev = vdev;
+ vdev->priv = dev;
+
+ if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) {
+ struct pci_dev *pdev = to_pci_dev(vdev->dev.parent);
+ bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
+ DRM_INFO("pci: %s detected\n",
+ vga ? "virtio-vga" : "virtio-gpu-pci");
+ dev->pdev = pdev;
+ if (vga)
+ virtio_pci_kick_out_firmware_fb(pdev);
+ }
+
+ ret = drm_dev_register(dev, 0);
+ if (ret)
+ goto err_free;
+
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
+ driver->major, driver->minor, driver->patchlevel,
+ driver->date, dev->primary->index);
+
+ return 0;
+
+err_free:
+ drm_dev_unref(dev);
+ return ret;
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
new file mode 100644
index 0000000..3662e86
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -0,0 +1,132 @@
+/*
+ * 2011 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/pci.h>
+#include "drmP.h"
+#include "drm/drm.h"
+
+#include "virtgpu_drv.h"
+static struct drm_driver driver;
+
+static int virtio_gpu_modeset = -1;
+
+MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
+module_param_named(modeset, virtio_gpu_modeset, int, 0400);
+
+static int virtio_gpu_probe(struct virtio_device *vdev)
+{
+#ifdef CONFIG_VGA_CONSOLE
+ if (vgacon_text_force() && virtio_gpu_modeset == -1)
+ return -EINVAL;
+#endif
+
+ if (virtio_gpu_modeset == 0)
+ return -EINVAL;
+
+ return drm_virtio_init(&driver, vdev);
+}
+
+static void virtio_gpu_remove(struct virtio_device *vdev)
+{
+ struct drm_device *dev = vdev->priv;
+ drm_put_dev(dev);
+}
+
+static void virtio_gpu_config_changed(struct virtio_device *vdev)
+{
+ struct drm_device *dev = vdev->priv;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+
+ schedule_work(&vgdev->config_changed_work);
+}
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static unsigned int features[] = {
+};
+static struct virtio_driver virtio_gpu_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = virtio_gpu_probe,
+ .remove = virtio_gpu_remove,
+ .config_changed = virtio_gpu_config_changed
+};
+
+module_virtio_driver(virtio_gpu_driver);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio GPU driver");
+MODULE_LICENSE("GPL");
+
+static const struct file_operations virtio_gpu_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = virtio_gpu_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+
+static struct drm_driver driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .set_busid = drm_virtio_set_busid,
+ .load = virtio_gpu_driver_load,
+ .unload = virtio_gpu_driver_unload,
+
+ .dumb_create = virtio_gpu_mode_dumb_create,
+ .dumb_map_offset = virtio_gpu_mode_dumb_mmap,
+ .dumb_destroy = virtio_gpu_mode_dumb_destroy,
+
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = virtio_gpu_debugfs_init,
+ .debugfs_cleanup = virtio_gpu_debugfs_takedown,
+#endif
+
+ .gem_free_object = virtio_gpu_gem_free_object,
+ .fops = &virtio_gpu_driver_fops,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
new file mode 100644
index 0000000..6082ec3
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef VIRTIO_DRV_H
+#define VIRTIO_DRV_H
+
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_gpu.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_crtc_helper.h>
+#include <ttm/ttm_bo_api.h>
+#include <ttm/ttm_bo_driver.h>
+#include <ttm/ttm_placement.h>
+#include <ttm/ttm_module.h>
+
+#define DRIVER_NAME "virtio_gpu"
+#define DRIVER_DESC "virtio GPU"
+#define DRIVER_DATE "0"
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 1
+
+/* virtgpu_drm_bus.c */
+int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master);
+int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev);
+
+struct virtio_gpu_object {
+ struct drm_gem_object gem_base;
+ uint32_t hw_res_handle;
+
+ struct sg_table *pages;
+ void *vmap;
+ bool dumb;
+ struct ttm_place placement_code;
+ struct ttm_placement placement;
+ struct ttm_buffer_object tbo;
+ struct ttm_bo_kmap_obj kmap;
+};
+#define gem_to_virtio_gpu_obj(gobj) \
+ container_of((gobj), struct virtio_gpu_object, gem_base)
+
+struct virtio_gpu_vbuffer;
+struct virtio_gpu_device;
+
+typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf);
+
+struct virtio_gpu_fence_driver {
+ atomic64_t last_seq;
+ uint64_t sync_seq;
+ struct list_head fences;
+ spinlock_t lock;
+};
+
+struct virtio_gpu_fence {
+ struct fence f;
+ struct virtio_gpu_fence_driver *drv;
+ struct list_head node;
+ uint64_t seq;
+};
+#define to_virtio_fence(x) \
+ container_of(x, struct virtio_gpu_fence, f)
+
+struct virtio_gpu_vbuffer {
+ char *buf;
+ int size;
+ bool debug_dump_sglists;
+
+ void *data_buf;
+ uint32_t data_size;
+
+ char *resp_buf;
+ int resp_size;
+
+ virtio_gpu_resp_cb resp_cb;
+
+ struct list_head destroy_list;
+};
+
+struct virtio_gpu_output {
+ int index;
+ struct drm_crtc crtc;
+ struct drm_connector conn;
+ struct drm_encoder enc;
+ struct virtio_gpu_display_one info;
+ struct virtio_gpu_update_cursor cursor;
+ int cur_x;
+ int cur_y;
+};
+#define drm_crtc_to_virtio_gpu_output(x) \
+ container_of(x, struct virtio_gpu_output, crtc)
+#define drm_connector_to_virtio_gpu_output(x) \
+ container_of(x, struct virtio_gpu_output, conn)
+#define drm_encoder_to_virtio_gpu_output(x) \
+ container_of(x, struct virtio_gpu_output, enc)
+
+struct virtio_gpu_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_gem_object *obj;
+ int x1, y1, x2, y2; /* dirty rect */
+ spinlock_t dirty_lock;
+ uint32_t hw_res_handle;
+};
+#define to_virtio_gpu_framebuffer(x) \
+ container_of(x, struct virtio_gpu_framebuffer, base)
+
+struct virtio_gpu_mman {
+ struct ttm_bo_global_ref bo_global_ref;
+ struct drm_global_reference mem_global_ref;
+ bool mem_global_referenced;
+ struct ttm_bo_device bdev;
+};
+
+struct virtio_gpu_fbdev;
+
+struct virtio_gpu_queue {
+ struct virtqueue *vq;
+ spinlock_t qlock;
+ wait_queue_head_t ack_queue;
+ struct work_struct dequeue_work;
+};
+
+struct virtio_gpu_device {
+ struct device *dev;
+ struct drm_device *ddev;
+
+ struct virtio_device *vdev;
+
+ struct virtio_gpu_mman mman;
+
+ /* pointer to fbdev info structure */
+ struct virtio_gpu_fbdev *vgfbdev;
+ struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS];
+ uint32_t num_scanouts;
+
+ struct virtio_gpu_queue ctrlq;
+ struct virtio_gpu_queue cursorq;
+
+ struct idr resource_idr;
+ spinlock_t resource_idr_lock;
+
+ wait_queue_head_t resp_wq;
+ /* current display info */
+ spinlock_t display_info_lock;
+
+ struct virtio_gpu_fence_driver fence_drv;
+
+ struct idr ctx_id_idr;
+ spinlock_t ctx_id_idr_lock;
+
+ struct work_struct config_changed_work;
+};
+
+struct virtio_gpu_fpriv {
+ uint32_t ctx_id;
+};
+
+/* virtio_ioctl.c */
+#define DRM_VIRTIO_NUM_IOCTLS 10
+extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
+
+/* virtio_kms.c */
+int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags);
+int virtio_gpu_driver_unload(struct drm_device *dev);
+
+/* virtio_gem.c */
+void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj);
+int virtio_gpu_gem_init(struct virtio_gpu_device *vgdev);
+void virtio_gpu_gem_fini(struct virtio_gpu_device *vgdev);
+int virtio_gpu_gem_create(struct drm_file *file,
+ struct drm_device *dev,
+ uint64_t size,
+ struct drm_gem_object **obj_p,
+ uint32_t *handle_p);
+struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
+ size_t size, bool kernel,
+ bool pinned);
+int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev,
+ uint32_t handle);
+int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
+ struct drm_device *dev,
+ uint32_t handle, uint64_t *offset_p);
+
+/* virtio_fb */
+#define VIRTIO_GPUFB_CONN_LIMIT 1
+int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev);
+void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev);
+int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb,
+ struct drm_clip_rect *clips,
+ unsigned num_clips);
+/* virtio vg */
+int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
+ uint32_t *resid);
+void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id);
+int virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id,
+ uint32_t format,
+ uint32_t width,
+ uint32_t height);
+int virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id);
+int virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id, uint64_t offset,
+ __le32 width, __le32 height,
+ __le32 x, __le32 y,
+ struct virtio_gpu_fence **fence);
+int virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height);
+int virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
+ uint32_t scanout_id, uint32_t resource_id,
+ uint32_t width, uint32_t height,
+ uint32_t x, uint32_t y);
+int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *obj,
+ uint32_t resource_id,
+ struct virtio_gpu_fence **fence);
+int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev);
+int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev);
+void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_output *output);
+int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
+int virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id);
+void virtio_gpu_ctrl_ack(struct virtqueue *vq);
+void virtio_gpu_cursor_ack(struct virtqueue *vq);
+void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
+void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
+
+/* virtio_gpu_display.c */
+int virtio_gpu_framebuffer_init(struct drm_device *dev,
+ struct virtio_gpu_framebuffer *vgfb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj);
+int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev);
+void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev);
+
+/* virtio_gpu_ttm.c */
+int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev);
+void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev);
+bool virtio_gpu_ttm_bo_is_virtio_gpu_object(struct ttm_buffer_object *bo);
+int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/* virtio_gpu_fence.c */
+int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_ctrl_hdr *cmd_hdr,
+ struct virtio_gpu_fence **fence);
+void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
+ u64 last_seq);
+
+/* virtio_gpu_object */
+int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
+ unsigned long size, bool kernel, bool pinned,
+ struct virtio_gpu_object **bo_ptr);
+int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr);
+int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
+ struct virtio_gpu_object *bo);
+void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
+int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
+
+static inline struct virtio_gpu_object*
+virtio_gpu_object_ref(struct virtio_gpu_object *bo)
+{
+ ttm_bo_reference(&bo->tbo);
+ return bo;
+}
+
+static inline void virtio_gpu_object_unref(struct virtio_gpu_object **bo)
+{
+ struct ttm_buffer_object *tbo;
+
+ if ((*bo) == NULL)
+ return;
+ tbo = &((*bo)->tbo);
+ ttm_bo_unref(&tbo);
+ if (tbo == NULL)
+ *bo = NULL;
+}
+
+static inline u64 virtio_gpu_object_mmap_offset(struct virtio_gpu_object *bo)
+{
+ return drm_vma_node_offset_addr(&bo->tbo.vma_node);
+}
+
+static inline int virtio_gpu_object_reserve(struct virtio_gpu_object *bo,
+ bool no_wait)
+{
+ int r;
+
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
+ if (unlikely(r != 0)) {
+ if (r != -ERESTARTSYS) {
+ struct virtio_gpu_device *qdev =
+ bo->gem_base.dev->dev_private;
+ dev_err(qdev->dev, "%p reserve failed\n", bo);
+ }
+ return r;
+ }
+ return 0;
+}
+
+static inline void virtio_gpu_object_unreserve(struct virtio_gpu_object *bo)
+{
+ ttm_bo_unreserve(&bo->tbo);
+}
+
+/* virgl debufs */
+int virtio_gpu_debugfs_init(struct drm_minor *minor);
+void virtio_gpu_debugfs_takedown(struct drm_minor *minor);
+
+#endif
diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c
new file mode 100644
index 0000000..1d79457
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_fb.c
@@ -0,0 +1,415 @@
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include "virtgpu_drv.h"
+
+#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60)
+
+struct virtio_gpu_fbdev {
+ struct drm_fb_helper helper;
+ struct virtio_gpu_framebuffer vgfb;
+ struct list_head fbdev_list;
+ struct virtio_gpu_device *vgdev;
+ struct delayed_work work;
+};
+#define DL_ALIGN_UP(x, a) ALIGN(x, a)
+#define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a)
+
+static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb,
+ bool store, int x, int y,
+ int width, int height)
+{
+ struct drm_device *dev = fb->base.dev;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ bool store_for_later = false;
+ int aligned_x;
+ int bpp = (fb->base.bits_per_pixel / 8);
+ int x2, y2;
+ unsigned long flags;
+ struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj);
+
+ aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
+ width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
+ x = aligned_x;
+
+ if ((width <= 0) ||
+ (x + width > fb->base.width) ||
+ (y + height > fb->base.height)) {
+ DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n",
+ width, height, x, y,
+ fb->base.width, fb->base.height);
+ return -EINVAL;
+ }
+
+ /* if we are in atomic just store the info
+ can't test inside spin lock */
+ if (in_atomic() || store)
+ store_for_later = true;
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+
+ spin_lock_irqsave(&fb->dirty_lock, flags);
+
+ if (fb->y1 < y)
+ y = fb->y1;
+ if (fb->y2 > y2)
+ y2 = fb->y2;
+ if (fb->x1 < x)
+ x = fb->x1;
+ if (fb->x2 > x2)
+ x2 = fb->x2;
+
+ if (store_for_later) {
+ fb->x1 = x;
+ fb->x2 = x2;
+ fb->y1 = y;
+ fb->y2 = y2;
+ spin_unlock_irqrestore(&fb->dirty_lock, flags);
+ return 0;
+ }
+
+ fb->x1 = fb->y1 = INT_MAX;
+ fb->x2 = fb->y2 = 0;
+
+ spin_unlock_irqrestore(&fb->dirty_lock, flags);
+
+ {
+ uint32_t offset;
+ uint32_t w = x2 - x + 1;
+ uint32_t h = y2 - y + 1;
+
+ offset = (y * fb->base.pitches[0]) + x * bpp;
+
+ virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle,
+ offset,
+ cpu_to_le32(w),
+ cpu_to_le32(h),
+ cpu_to_le32(x),
+ cpu_to_le32(y),
+ NULL);
+
+ }
+ virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
+ x, y, x2 - x + 1, y2 - y + 1);
+ return 0;
+}
+
+int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private;
+ struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->obj);
+ struct drm_clip_rect norect;
+ struct drm_clip_rect *clips_ptr;
+ int left, right, top, bottom;
+ int i;
+ int inc = 1;
+ if (!num_clips) {
+ num_clips = 1;
+ clips = &norect;
+ norect.x1 = norect.y1 = 0;
+ norect.x2 = vgfb->base.width;
+ norect.y2 = vgfb->base.height;
+ }
+ left = clips->x1;
+ right = clips->x2;
+ top = clips->y1;
+ bottom = clips->y2;
+
+ /* skip the first clip rect */
+ for (i = 1, clips_ptr = clips + inc;
+ i < num_clips; i++, clips_ptr += inc) {
+ left = min_t(int, left, (int)clips_ptr->x1);
+ right = max_t(int, right, (int)clips_ptr->x2);
+ top = min_t(int, top, (int)clips_ptr->y1);
+ bottom = max_t(int, bottom, (int)clips_ptr->y2);
+ }
+
+ if (obj->dumb)
+ return virtio_gpu_dirty_update(vgfb, false, left, top,
+ right - left, bottom - top);
+
+ virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle,
+ left, top, right - left, bottom - top);
+ return 0;
+}
+
+static void virtio_gpu_fb_dirty_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct virtio_gpu_fbdev *vfbdev =
+ container_of(delayed_work, struct virtio_gpu_fbdev, work);
+ struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb;
+
+ virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1,
+ vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1);
+}
+
+static void virtio_gpu_3d_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ struct virtio_gpu_fbdev *vfbdev = info->par;
+ sys_fillrect(info, rect);
+ virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy,
+ rect->width, rect->height);
+ schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
+}
+
+static void virtio_gpu_3d_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ struct virtio_gpu_fbdev *vfbdev = info->par;
+ sys_copyarea(info, area);
+ virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy,
+ area->width, area->height);
+ schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
+}
+
+static void virtio_gpu_3d_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ struct virtio_gpu_fbdev *vfbdev = info->par;
+ sys_imageblit(info, image);
+ virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy,
+ image->width, image->height);
+ schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD);
+}
+
+static struct fb_ops virtio_gpufb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par, /* TODO: copy vmwgfx */
+ .fb_fillrect = virtio_gpu_3d_fillrect,
+ .fb_copyarea = virtio_gpu_3d_copyarea,
+ .fb_imageblit = virtio_gpu_3d_imageblit,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_setcmap = drm_fb_helper_setcmap,
+ .fb_debug_enter = drm_fb_helper_debug_enter,
+ .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int virtio_gpu_vmap_fb(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *obj)
+{
+ return virtio_gpu_object_kmap(obj, NULL);
+}
+
+static int virtio_gpufb_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct virtio_gpu_fbdev *vfbdev =
+ container_of(helper, struct virtio_gpu_fbdev, helper);
+ struct drm_device *dev = helper->dev;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct fb_info *info;
+ struct drm_framebuffer *fb;
+ struct drm_mode_fb_cmd2 mode_cmd = {};
+ struct virtio_gpu_object *obj;
+ struct device *device = vgdev->dev;
+ uint32_t resid, format, size;
+ int ret;
+
+ if (sizes->surface_bpp == 24)
+ sizes->surface_bpp = 32;
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ switch (mode_cmd.pixel_format) {
+#ifdef __BIG_ENDIAN
+ case DRM_FORMAT_XRGB8888:
+ format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
+ break;
+ case DRM_FORMAT_BGRX8888:
+ format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
+ break;
+ case DRM_FORMAT_BGRA8888:
+ format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
+ break;
+ case DRM_FORMAT_RGBX8888:
+ format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
+ break;
+ case DRM_FORMAT_RGBA8888:
+ format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM;
+ break;
+ case DRM_FORMAT_XBGR8888:
+ format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM;
+ break;
+ case DRM_FORMAT_ABGR8888:
+ format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
+ break;
+#else
+ case DRM_FORMAT_XRGB8888:
+ format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM;
+ break;
+ case DRM_FORMAT_BGRX8888:
+ format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM;
+ break;
+ case DRM_FORMAT_BGRA8888:
+ format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
+ break;
+ case DRM_FORMAT_RGBX8888:
+ format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM;
+ break;
+ case DRM_FORMAT_RGBA8888:
+ format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
+ break;
+ case DRM_FORMAT_XBGR8888:
+ format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
+ break;
+ case DRM_FORMAT_ABGR8888:
+ format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM;
+ break;
+#endif
+ default:
+ format = 0;
+ break;
+ }
+ if (format == 0) {
+ ret = -EINVAL;
+ DRM_ERROR("failed to find virtio gpu format for %d\n",
+ mode_cmd.pixel_format);
+ goto fail;
+ }
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+ obj = virtio_gpu_alloc_object(dev, size, false, true);
+ if (!obj) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = virtio_gpu_resource_id_get(vgdev, &resid);
+ if (ret)
+ goto fail;
+
+ ret = virtio_gpu_cmd_create_resource(vgdev, resid, format,
+ mode_cmd.width, mode_cmd.height);
+ if (ret)
+ goto fail;
+
+ ret = virtio_gpu_vmap_fb(vgdev, obj);
+ if (ret) {
+ DRM_ERROR("failed to vmap fb %d\n", ret);
+ goto fail;
+ }
+
+ /* attach the object to the resource */
+ ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
+ if (ret)
+ goto fail;
+
+ info = framebuffer_alloc(0, device);
+ if (!info) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ info->par = helper;
+
+ ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb,
+ &mode_cmd, &obj->gem_base);
+ if (ret)
+ goto fail;
+
+ fb = &vfbdev->vgfb.base;
+
+ vfbdev->helper.fb = fb;
+ vfbdev->helper.fbdev = info;
+
+ strcpy(info->fix.id, "virtiodrmfb");
+ info->flags = FBINFO_DEFAULT;
+ info->fbops = &virtio_gpufb_ops;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ info->screen_base = obj->vmap;
+ info->screen_size = obj->gem_base.size;
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(info, &vfbdev->helper,
+ sizes->fb_width, sizes->fb_height);
+
+ info->fix.mmio_start = 0;
+ info->fix.mmio_len = 0;
+
+ return 0;
+fail:
+
+ return -EINVAL;
+}
+
+static int virtio_gpu_fbdev_destroy(struct drm_device *dev,
+ struct virtio_gpu_fbdev *vgfbdev)
+{
+ struct fb_info *info;
+ struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb;
+
+ if (vgfbdev->helper.fbdev) {
+ info = vgfbdev->helper.fbdev;
+
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+ }
+ if (vgfb->obj)
+ vgfb->obj = NULL;
+ drm_fb_helper_fini(&vgfbdev->helper);
+ drm_framebuffer_cleanup(&vgfb->base);
+
+ return 0;
+}
+static struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = {
+ .fb_probe = virtio_gpufb_create,
+};
+
+int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_fbdev *vgfbdev;
+ int bpp_sel = 32; /* TODO: parameter from somewhere? */
+ int ret;
+
+ vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL);
+ if (!vgfbdev)
+ return -ENOMEM;
+
+ vgfbdev->vgdev = vgdev;
+ vgdev->vgfbdev = vgfbdev;
+ INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work);
+
+ drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper,
+ &virtio_gpu_fb_helper_funcs);
+ ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper,
+ vgdev->num_scanouts,
+ VIRTIO_GPUFB_CONN_LIMIT);
+ if (ret) {
+ kfree(vgfbdev);
+ return ret;
+ }
+
+ drm_fb_helper_single_add_all_connectors(&vgfbdev->helper);
+ drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel);
+ return 0;
+}
+
+void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev)
+{
+ if (!vgdev->vgfbdev)
+ return;
+
+ virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev);
+ kfree(vgdev->vgfbdev);
+ vgdev->vgfbdev = NULL;
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c
new file mode 100644
index 0000000..552aa49
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_fence.c
@@ -0,0 +1,95 @@
+#include <drm/drmP.h>
+#include "virtgpu_drv.h"
+
+static const char *virtio_get_driver_name(struct fence *f)
+{
+ return "virtio_gpu";
+}
+
+static const char *virtio_get_timeline_name(struct fence *f)
+{
+ return "controlq";
+}
+
+static bool virtio_enable_signaling(struct fence *f)
+{
+ return true;
+}
+
+static bool virtio_signaled(struct fence *f)
+{
+ struct virtio_gpu_fence *fence = to_virtio_fence(f);
+
+ if (atomic64_read(&fence->drv->last_seq) >= fence->seq) {
+ return true;
+ }
+ return false;
+}
+
+static void virtio_fence_value_str(struct fence *f, char *str, int size)
+{
+ struct virtio_gpu_fence *fence = to_virtio_fence(f);
+
+ snprintf(str, size, "%llu", fence->seq);
+}
+
+static void virtio_timeline_value_str(struct fence *f, char *str, int size)
+{
+ struct virtio_gpu_fence *fence = to_virtio_fence(f);
+
+ snprintf(str, size, "%lu", atomic64_read(&fence->drv->last_seq));
+}
+
+static const struct fence_ops virtio_fence_ops = {
+ .get_driver_name = virtio_get_driver_name,
+ .get_timeline_name = virtio_get_timeline_name,
+ .enable_signaling = virtio_enable_signaling,
+ .signaled = virtio_signaled,
+ .wait = fence_default_wait,
+ .fence_value_str = virtio_fence_value_str,
+ .timeline_value_str = virtio_timeline_value_str,
+};
+
+int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_ctrl_hdr *cmd_hdr,
+ struct virtio_gpu_fence **fence)
+{
+ struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
+ unsigned long irq_flags;
+
+ *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_KERNEL);
+ if ((*fence) == NULL)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&drv->lock, irq_flags);
+ (*fence)->drv = drv;
+ (*fence)->seq = ++drv->sync_seq;
+ fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock,
+ 0, (*fence)->seq);
+ fence_get(&(*fence)->f);
+ list_add_tail(&(*fence)->node, &drv->fences);
+ spin_unlock_irqrestore(&drv->lock, irq_flags);
+
+ cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE);
+ cmd_hdr->fence_id = cpu_to_le64((*fence)->seq);
+ return 0;
+}
+
+void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev,
+ u64 last_seq)
+{
+ struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
+ struct virtio_gpu_fence *fence, *tmp;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&drv->lock, irq_flags);
+ atomic64_set(&vgdev->fence_drv.last_seq, last_seq);
+ list_for_each_entry_safe(fence, tmp, &drv->fences, node) {
+ if (last_seq < fence->seq)
+ continue;
+ fence_signal_locked(&fence->f);
+ list_del(&fence->node);
+ fence_put(&fence->f);
+ }
+ spin_unlock_irqrestore(&drv->lock, irq_flags);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
new file mode 100644
index 0000000..8bc0a24
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -0,0 +1,120 @@
+
+#include <drm/drmP.h>
+#include "virtgpu_drv.h"
+
+void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj)
+{
+ struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj);
+
+ if (obj)
+ virtio_gpu_object_unref(&obj);
+}
+
+struct virtio_gpu_object *virtio_gpu_alloc_object(struct drm_device *dev,
+ size_t size, bool kernel,
+ bool pinned)
+{
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct virtio_gpu_object *obj;
+ int ret;
+
+ ret = virtio_gpu_object_create(vgdev, size, kernel, pinned, &obj);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return obj;
+}
+
+int virtio_gpu_gem_create(struct drm_file *file,
+ struct drm_device *dev,
+ uint64_t size,
+ struct drm_gem_object **obj_p,
+ uint32_t *handle_p)
+{
+ struct virtio_gpu_object *obj;
+ int ret;
+ u32 handle;
+
+ obj = virtio_gpu_alloc_object(dev, size, false, false);
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
+ ret = drm_gem_handle_create(file, &obj->gem_base, &handle);
+ if (ret) {
+ drm_gem_object_release(&obj->gem_base);
+ return ret;
+ }
+
+ *obj_p = &obj->gem_base;
+
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference_unlocked(&obj->gem_base);
+
+ *handle_p = handle;
+ return 0;
+}
+
+int virtio_gpu_mode_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct drm_gem_object *gobj;
+ struct virtio_gpu_object *obj;
+ int ret;
+ uint32_t pitch;
+ uint32_t resid;
+
+ pitch = args->width * ((args->bpp + 1) / 8);
+ args->size = pitch * args->height;
+ args->size = ALIGN(args->size, PAGE_SIZE);
+
+ ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj,
+ &args->handle);
+ if (ret)
+ goto fail;
+
+ ret = virtio_gpu_resource_id_get(vgdev, &resid);
+ if (ret)
+ goto fail;
+
+ ret = virtio_gpu_cmd_create_resource(vgdev, resid,
+ 2, args->width, args->height);
+ if (ret)
+ goto fail;
+
+ /* attach the object to the resource */
+ obj = gem_to_virtio_gpu_obj(gobj);
+ ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
+ if (ret)
+ goto fail;
+
+ obj->dumb = true;
+ args->pitch = pitch;
+ return ret;
+fail:
+ return ret;
+}
+
+int virtio_gpu_mode_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev,
+ uint32_t handle)
+{
+ return drm_gem_handle_delete(file_priv, handle);
+}
+
+int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv,
+ struct drm_device *dev,
+ uint32_t handle, uint64_t *offset_p)
+{
+ struct drm_gem_object *gobj;
+ struct virtio_gpu_object *obj;
+ BUG_ON(!offset_p);
+ gobj = drm_gem_object_lookup(dev, file_priv, handle);
+ if (gobj == NULL)
+ return -ENOENT;
+ obj = gem_to_virtio_gpu_obj(gobj);
+ *offset_p = virtio_gpu_object_mmap_offset(obj);
+ drm_gem_object_unreference_unlocked(gobj);
+ return 0;
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
new file mode 100644
index 0000000..45c4beb
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -0,0 +1,125 @@
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <drm/drmP.h>
+#include "virtgpu_drv.h"
+
+static void virtio_gpu_config_changed_work_func(struct work_struct *work)
+{
+ struct virtio_gpu_device *vgdev =
+ container_of(work, struct virtio_gpu_device,
+ config_changed_work);
+ u32 events_read, events_clear = 0;
+
+ /* read the config space */
+ virtio_cread(vgdev->vdev, struct virtio_gpu_config,
+ events_read, &events_read);
+ if (events_read & VIRTIO_GPU_EVENT_DISPLAY) {
+ virtio_gpu_cmd_get_display_info(vgdev);
+ drm_helper_hpd_irq_event(vgdev->ddev);
+ events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
+ }
+ virtio_cwrite(vgdev->vdev, struct virtio_gpu_config,
+ events_clear, &events_clear);
+}
+
+static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
+ void (*work_func)(struct work_struct *work))
+{
+ spin_lock_init(&vgvq->qlock);
+ init_waitqueue_head(&vgvq->ack_queue);
+ INIT_WORK(&vgvq->dequeue_work, work_func);
+}
+
+int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
+{
+ static vq_callback_t *callbacks[] = {
+ virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack
+ };
+ static const char *names[] = { "control", "cursor" };
+
+ struct virtio_gpu_device *vgdev;
+ /* this will expand later */
+ struct virtqueue *vqs[2];
+ u32 num_scanouts;
+ int ret;
+
+ if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1))
+ return -ENODEV;
+
+ vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL);
+ if (!vgdev)
+ return -ENOMEM;
+
+ vgdev->ddev = dev;
+ dev->dev_private = vgdev;
+ vgdev->vdev = dev->virtdev;
+ vgdev->dev = dev->dev;
+
+ spin_lock_init(&vgdev->display_info_lock);
+ spin_lock_init(&vgdev->ctx_id_idr_lock);
+ idr_init(&vgdev->ctx_id_idr);
+ spin_lock_init(&vgdev->resource_idr_lock);
+ idr_init(&vgdev->resource_idr);
+ init_waitqueue_head(&vgdev->resp_wq);
+ virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func);
+ virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func);
+
+ spin_lock_init(&vgdev->fence_drv.lock);
+ INIT_LIST_HEAD(&vgdev->fence_drv.fences);
+ INIT_WORK(&vgdev->config_changed_work,
+ virtio_gpu_config_changed_work_func);
+
+ ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs,
+ callbacks, names);
+ if (ret) {
+ DRM_ERROR("failed to find virt queues\n");
+ goto err_vqs;
+ }
+ vgdev->ctrlq.vq = vqs[0];
+ vgdev->cursorq.vq = vqs[1];
+
+ ret = virtio_gpu_ttm_init(vgdev);
+ if (ret) {
+ DRM_ERROR("failed to init ttm %d\n", ret);
+ goto err_ttm;
+ }
+
+ /* get display info */
+ virtio_cread(vgdev->vdev, struct virtio_gpu_config,
+ num_scanouts, &num_scanouts);
+ vgdev->num_scanouts = min_t(uint32_t, num_scanouts,
+ VIRTIO_GPU_MAX_SCANOUTS);
+ if (!vgdev->num_scanouts) {
+ DRM_ERROR("num_scanouts is zero\n");
+ ret = -EINVAL;
+ goto err_scanouts;
+ }
+
+ ret = virtio_gpu_modeset_init(vgdev);
+ if (ret)
+ goto err_modeset;
+
+ virtio_device_ready(vgdev->vdev);
+ virtio_gpu_cmd_get_display_info(vgdev);
+ return 0;
+
+err_modeset:
+err_scanouts:
+ virtio_gpu_ttm_fini(vgdev);
+err_ttm:
+ vgdev->vdev->config->del_vqs(vgdev->vdev);
+err_vqs:
+ kfree(vgdev);
+ return ret;
+}
+
+int virtio_gpu_driver_unload(struct drm_device *dev)
+{
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+
+ virtio_gpu_modeset_fini(vgdev);
+ virtio_gpu_ttm_fini(vgdev);
+ vgdev->vdev->config->del_vqs(vgdev->vdev);
+ kfree(vgdev);
+ return 0;
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
new file mode 100644
index 0000000..0d98ae4
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -0,0 +1,174 @@
+#include "virtgpu_drv.h"
+
+static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo)
+{
+ struct virtio_gpu_object *bo;
+ struct virtio_gpu_device *vgdev;
+
+ bo = container_of(tbo, struct virtio_gpu_object, tbo);
+ vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
+
+ if (bo->hw_res_handle)
+ virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle);
+ if (bo->pages)
+ virtio_gpu_object_free_sg_table(bo);
+ drm_gem_object_release(&bo->gem_base);
+ kfree(bo);
+}
+
+bool virtio_gpu_ttm_bo_is_virtio_gpu_object(struct ttm_buffer_object *bo)
+{
+ if (bo->destroy == &virtio_gpu_ttm_bo_destroy)
+ return true;
+ return false;
+}
+
+static void virtio_gpu_init_ttm_placement(struct virtio_gpu_object *vgbo,
+ bool pinned)
+{
+ u32 c = 1;
+ u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
+
+ vgbo->placement.placement = &vgbo->placement_code;
+ vgbo->placement.busy_placement = &vgbo->placement_code;
+ vgbo->placement_code.fpfn = 0;
+ vgbo->placement_code.lpfn = 0;
+ vgbo->placement_code.flags =
+ TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT | pflag;
+ vgbo->placement.num_placement = c;
+ vgbo->placement.num_busy_placement = c;
+
+}
+
+int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
+ unsigned long size, bool kernel, bool pinned,
+ struct virtio_gpu_object **bo_ptr)
+{
+ struct virtio_gpu_object *bo;
+ enum ttm_bo_type type;
+ size_t acc_size;
+ int r;
+
+ if (kernel)
+ type = ttm_bo_type_kernel;
+ else
+ type = ttm_bo_type_device;
+ *bo_ptr = NULL;
+
+ acc_size = ttm_bo_dma_acc_size(&vgdev->mman.bdev, size,
+ sizeof(struct virtio_gpu_object));
+
+ bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL);
+ if (bo == NULL)
+ return -ENOMEM;
+ size = roundup(size, PAGE_SIZE);
+ r = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size);
+ if (unlikely(r)) {
+ kfree(bo);
+ return r;
+ }
+ bo->dumb = false;
+
+ virtio_gpu_init_ttm_placement(bo, pinned);
+ r = ttm_bo_init(&vgdev->mman.bdev, &bo->tbo, size, type,
+ &bo->placement, 0, !kernel, NULL, acc_size,
+ NULL, NULL, &virtio_gpu_ttm_bo_destroy);
+ if (unlikely(r != 0)) {
+ if (r != -ERESTARTSYS)
+ dev_err(vgdev->dev,
+ "object_init %d failed for (%lu)\n", r,
+ size);
+ return r;
+ }
+ *bo_ptr = bo;
+ return 0;
+}
+
+int virtio_gpu_object_kmap(struct virtio_gpu_object *bo, void **ptr)
+{
+ bool is_iomem;
+ int r;
+
+ if (bo->vmap) {
+ if (ptr)
+ *ptr = bo->vmap;
+ return 0;
+ }
+ r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap);
+ if (r)
+ return r;
+ bo->vmap = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem);
+ if (ptr)
+ *ptr = bo->vmap;
+ return 0;
+}
+
+#if 0
+void virtio_gpu_object_force_delete(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_object *bo, *n;
+
+
+ dev_err(vgdev->dev, "Userspace still has active objects !\n");
+ list_for_each_entry_safe(bo, n, &vgdev->gem.objects, list) {
+ mutex_lock(&vgdev->ddev->struct_mutex);
+ dev_err(vgdev->dev, "%p %p %lu %lu force free\n",
+ &bo->gem_base, bo, (unsigned long)bo->gem_base.size,
+ *((unsigned long *)&bo->gem_base.refcount));
+ spin_lock(&vgdev->gem.lock);
+ list_del_init(&bo->list);
+ spin_unlock(&vgdev->gem.lock);
+ /* this should unref the ttm bo */
+ drm_gem_object_unreference(&bo->gem_base);
+ mutex_unlock(&vgdev->ddev->struct_mutex);
+ }
+}
+#endif
+
+int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev,
+ struct virtio_gpu_object *bo)
+{
+ int ret;
+ struct page **pages = bo->tbo.ttm->pages;
+ int nr_pages = bo->tbo.num_pages;
+
+ /* wtf swapping */
+ if (bo->pages)
+ return 0;
+
+ if (bo->tbo.ttm->state == tt_unpopulated)
+ bo->tbo.ttm->bdev->driver->ttm_tt_populate(bo->tbo.ttm);
+ bo->pages = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!bo->pages)
+ goto out;
+
+ ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0,
+ nr_pages << PAGE_SHIFT, GFP_KERNEL);
+ if (ret)
+ goto out;
+ return 0;
+out:
+ kfree(bo->pages);
+ bo->pages = NULL;
+ return -ENOMEM;
+}
+
+void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo)
+{
+ sg_free_table(bo->pages);
+ kfree(bo->pages);
+ bo->pages = NULL;
+}
+
+int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait)
+{
+ int r;
+
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
+ if (unlikely(r != 0))
+ return r;
+ r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
+ ttm_bo_unreserve(&bo->tbo);
+ return r;
+}
+
diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c
new file mode 100644
index 0000000..a6f22e0
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alon Levy
+ */
+
+#include <ttm/ttm_bo_api.h>
+#include <ttm/ttm_bo_driver.h>
+#include <ttm/ttm_placement.h>
+#include <ttm/ttm_page_alloc.h>
+#include <ttm/ttm_module.h>
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include "virtgpu_drv.h"
+
+#include <linux/delay.h>
+
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
+
+static struct
+virtio_gpu_device *virtio_gpu_get_vgdev(struct ttm_bo_device *bdev)
+{
+ struct virtio_gpu_mman *mman;
+ struct virtio_gpu_device *vgdev;
+
+ mman = container_of(bdev, struct virtio_gpu_mman, bdev);
+ vgdev = container_of(mman, struct virtio_gpu_device, mman);
+ return vgdev;
+}
+
+static int virtio_gpu_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+ return ttm_mem_global_init(ref->object);
+}
+
+static void virtio_gpu_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+ ttm_mem_global_release(ref->object);
+}
+
+static int virtio_gpu_ttm_global_init(struct virtio_gpu_device *vgdev)
+{
+ struct drm_global_reference *global_ref;
+ int r;
+
+ vgdev->mman.mem_global_referenced = false;
+ global_ref = &vgdev->mman.mem_global_ref;
+ global_ref->global_type = DRM_GLOBAL_TTM_MEM;
+ global_ref->size = sizeof(struct ttm_mem_global);
+ global_ref->init = &virtio_gpu_ttm_mem_global_init;
+ global_ref->release = &virtio_gpu_ttm_mem_global_release;
+
+ r = drm_global_item_ref(global_ref);
+ if (r != 0) {
+ DRM_ERROR("Failed setting up TTM memory accounting "
+ "subsystem.\n");
+ return r;
+ }
+
+ vgdev->mman.bo_global_ref.mem_glob =
+ vgdev->mman.mem_global_ref.object;
+ global_ref = &vgdev->mman.bo_global_ref.ref;
+ global_ref->global_type = DRM_GLOBAL_TTM_BO;
+ global_ref->size = sizeof(struct ttm_bo_global);
+ global_ref->init = &ttm_bo_global_init;
+ global_ref->release = &ttm_bo_global_release;
+ r = drm_global_item_ref(global_ref);
+ if (r != 0) {
+ DRM_ERROR("Failed setting up TTM BO subsystem.\n");
+ drm_global_item_unref(&vgdev->mman.mem_global_ref);
+ return r;
+ }
+
+ vgdev->mman.mem_global_referenced = true;
+ return 0;
+}
+
+static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev)
+{
+ if (vgdev->mman.mem_global_referenced) {
+ drm_global_item_unref(&vgdev->mman.bo_global_ref.ref);
+ drm_global_item_unref(&vgdev->mman.mem_global_ref);
+ vgdev->mman.mem_global_referenced = false;
+ }
+}
+
+static struct vm_operations_struct virtio_gpu_ttm_vm_ops;
+static const struct vm_operations_struct *ttm_vm_ops;
+
+static int virtio_gpu_ttm_fault(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ struct ttm_buffer_object *bo;
+ struct virtio_gpu_device *vgdev;
+ int r;
+
+ bo = (struct ttm_buffer_object *)vma->vm_private_data;
+ if (bo == NULL)
+ return VM_FAULT_NOPAGE;
+ vgdev = virtio_gpu_get_vgdev(bo->bdev);
+ r = ttm_vm_ops->fault(vma, vmf);
+ return r;
+}
+
+int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *file_priv;
+ struct virtio_gpu_device *vgdev;
+ int r;
+
+ file_priv = filp->private_data;
+ vgdev = file_priv->minor->dev->dev_private;
+ if (vgdev == NULL) {
+ DRM_ERROR(
+ "filp->private_data->minor->dev->dev_private == NULL\n");
+ return -EINVAL;
+ }
+ r = ttm_bo_mmap(filp, vma, &vgdev->mman.bdev);
+ if (unlikely(r != 0))
+ return r;
+ if (unlikely(ttm_vm_ops == NULL)) {
+ ttm_vm_ops = vma->vm_ops;
+ virtio_gpu_ttm_vm_ops = *ttm_vm_ops;
+ virtio_gpu_ttm_vm_ops.fault = &virtio_gpu_ttm_fault;
+ }
+ vma->vm_ops = &virtio_gpu_ttm_vm_ops;
+ return 0;
+}
+
+static int virtio_gpu_invalidate_caches(struct ttm_bo_device *bdev,
+ uint32_t flags)
+{
+ return 0;
+}
+
+static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
+ struct ttm_buffer_object *bo,
+ const struct ttm_place *place,
+ struct ttm_mem_reg *mem)
+{
+ mem->mm_node = (void *)1;
+ return 0;
+}
+
+static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man,
+ struct ttm_mem_reg *mem)
+{
+ mem->mm_node = (void *)NULL;
+ return;
+}
+
+static int ttm_bo_man_init(struct ttm_mem_type_manager *man,
+ unsigned long p_size)
+{
+ return 0;
+}
+
+static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man)
+{
+ return 0;
+}
+
+static void ttm_bo_man_debug(struct ttm_mem_type_manager *man,
+ const char *prefix)
+{
+}
+
+static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = {
+ ttm_bo_man_init,
+ ttm_bo_man_takedown,
+ ttm_bo_man_get_node,
+ ttm_bo_man_put_node,
+ ttm_bo_man_debug
+};
+
+static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+ struct ttm_mem_type_manager *man)
+{
+ struct virtio_gpu_device *vgdev;
+
+ vgdev = virtio_gpu_get_vgdev(bdev);
+
+ switch (type) {
+ case TTM_PL_SYSTEM:
+ /* System memory */
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_MASK_CACHING;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ case TTM_PL_TT:
+ man->func = &virtio_gpu_bo_manager_func;
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_MASK_CACHING;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ default:
+ DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void virtio_gpu_evict_flags(struct ttm_buffer_object *bo,
+ struct ttm_placement *placement)
+{
+ static struct ttm_place placements = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM,
+ };
+
+ placement->placement = &placements;
+ placement->busy_placement = &placements;
+ placement->num_placement = 1;
+ placement->num_busy_placement = 1;
+ return;
+}
+
+static int virtio_gpu_verify_access(struct ttm_buffer_object *bo,
+ struct file *filp)
+{
+ return 0;
+}
+
+static int virtio_gpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+ struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
+
+ mem->bus.addr = NULL;
+ mem->bus.offset = 0;
+ mem->bus.size = mem->num_pages << PAGE_SHIFT;
+ mem->bus.base = 0;
+ mem->bus.is_iomem = false;
+ if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
+ return -EINVAL;
+ switch (mem->mem_type) {
+ case TTM_PL_SYSTEM:
+ case TTM_PL_TT:
+ /* system memory */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void virtio_gpu_ttm_io_mem_free(struct ttm_bo_device *bdev,
+ struct ttm_mem_reg *mem)
+{
+}
+
+/*
+ * TTM backend functions.
+ */
+struct virtio_gpu_ttm_tt {
+ struct ttm_dma_tt ttm;
+ struct virtio_gpu_device *vgdev;
+ u64 offset;
+};
+
+static int virtio_gpu_ttm_backend_bind(struct ttm_tt *ttm,
+ struct ttm_mem_reg *bo_mem)
+{
+ struct virtio_gpu_ttm_tt *gtt = (void *)ttm;
+
+ gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
+ if (!ttm->num_pages) {
+ WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
+ ttm->num_pages, bo_mem, ttm);
+ }
+ /* Not implemented */
+ return 0;
+}
+
+static int virtio_gpu_ttm_backend_unbind(struct ttm_tt *ttm)
+{
+ /* Not implemented */
+ return 0;
+}
+
+static void virtio_gpu_ttm_backend_destroy(struct ttm_tt *ttm)
+{
+ struct virtio_gpu_ttm_tt *gtt = (void *)ttm;
+
+ ttm_dma_tt_fini(>t->ttm);
+ kfree(gtt);
+}
+
+static struct ttm_backend_func virtio_gpu_backend_func = {
+ .bind = &virtio_gpu_ttm_backend_bind,
+ .unbind = &virtio_gpu_ttm_backend_unbind,
+ .destroy = &virtio_gpu_ttm_backend_destroy,
+};
+
+static int virtio_gpu_ttm_tt_populate(struct ttm_tt *ttm)
+{
+ if (ttm->state != tt_unpopulated)
+ return 0;
+
+ return ttm_pool_populate(ttm);
+}
+
+static void virtio_gpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
+{
+ ttm_pool_unpopulate(ttm);
+}
+
+static struct ttm_tt *virtio_gpu_ttm_tt_create(struct ttm_bo_device *bdev,
+ unsigned long size,
+ uint32_t page_flags,
+ struct page *dummy_read_page)
+{
+ struct virtio_gpu_device *vgdev;
+ struct virtio_gpu_ttm_tt *gtt;
+
+ vgdev = virtio_gpu_get_vgdev(bdev);
+ gtt = kzalloc(sizeof(struct virtio_gpu_ttm_tt), GFP_KERNEL);
+ if (gtt == NULL)
+ return NULL;
+ gtt->ttm.ttm.func = &virtio_gpu_backend_func;
+ gtt->vgdev = vgdev;
+ if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags,
+ dummy_read_page)) {
+ kfree(gtt);
+ return NULL;
+ }
+ return >t->ttm.ttm;
+}
+
+static void virtio_gpu_move_null(struct ttm_buffer_object *bo,
+ struct ttm_mem_reg *new_mem)
+{
+ struct ttm_mem_reg *old_mem = &bo->mem;
+
+ BUG_ON(old_mem->mm_node != NULL);
+ *old_mem = *new_mem;
+ new_mem->mm_node = NULL;
+}
+
+static int virtio_gpu_bo_move(struct ttm_buffer_object *bo,
+ bool evict, bool interruptible,
+ bool no_wait_gpu,
+ struct ttm_mem_reg *new_mem)
+{
+ virtio_gpu_move_null(bo, new_mem);
+ return 0;
+}
+
+static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo,
+ struct ttm_mem_reg *new_mem)
+{
+ struct virtio_gpu_object *bo;
+ struct virtio_gpu_device *vgdev;
+
+ bo = container_of(tbo, struct virtio_gpu_object, tbo);
+ vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
+
+ if (!new_mem || (new_mem->placement & TTM_PL_FLAG_SYSTEM)) {
+ if (bo->hw_res_handle)
+ virtio_gpu_cmd_resource_inval_backing(vgdev,
+ bo->hw_res_handle);
+
+ } else if (new_mem->placement & TTM_PL_FLAG_TT) {
+ if (bo->hw_res_handle) {
+ virtio_gpu_object_attach(vgdev, bo, bo->hw_res_handle,
+ NULL);
+ }
+ }
+
+ return;
+}
+
+static void virtio_gpu_bo_swap_notify(struct ttm_buffer_object *tbo)
+{
+ struct virtio_gpu_object *bo;
+ struct virtio_gpu_device *vgdev;
+
+ bo = container_of(tbo, struct virtio_gpu_object, tbo);
+ vgdev = (struct virtio_gpu_device *)bo->gem_base.dev->dev_private;
+
+ if (bo->pages)
+ virtio_gpu_object_free_sg_table(bo);
+}
+
+static struct ttm_bo_driver virtio_gpu_bo_driver = {
+ .ttm_tt_create = &virtio_gpu_ttm_tt_create,
+ .ttm_tt_populate = &virtio_gpu_ttm_tt_populate,
+ .ttm_tt_unpopulate = &virtio_gpu_ttm_tt_unpopulate,
+ .invalidate_caches = &virtio_gpu_invalidate_caches,
+ .init_mem_type = &virtio_gpu_init_mem_type,
+ .evict_flags = &virtio_gpu_evict_flags,
+ .move = &virtio_gpu_bo_move,
+ .verify_access = &virtio_gpu_verify_access,
+ .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve,
+ .io_mem_free = &virtio_gpu_ttm_io_mem_free,
+ .move_notify = &virtio_gpu_bo_move_notify,
+ .swap_notify = &virtio_gpu_bo_swap_notify,
+};
+
+int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev)
+{
+ int r;
+
+ r = virtio_gpu_ttm_global_init(vgdev);
+ if (r)
+ return r;
+ /* No others user of address space so set it to 0 */
+ r = ttm_bo_device_init(&vgdev->mman.bdev,
+ vgdev->mman.bo_global_ref.ref.object,
+ &virtio_gpu_bo_driver,
+ vgdev->ddev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET, 0);
+ if (r) {
+ DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
+ return r;
+ }
+
+ r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0);
+ if (r) {
+ DRM_ERROR("Failed initializing GTT heap.\n");
+ return r;
+ }
+ return 0;
+}
+
+void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev)
+{
+ ttm_bo_device_release(&vgdev->mman.bdev);
+ virtio_gpu_ttm_global_fini(vgdev);
+ DRM_INFO("virtio_gpu: ttm finalized\n");
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
new file mode 100644
index 0000000..a98cda8
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -0,0 +1,540 @@
+#include <drm/drmP.h>
+#include "virtgpu_drv.h"
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+
+
+int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid)
+{
+ int handle;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&vgdev->resource_idr_lock);
+ handle = idr_alloc(&vgdev->resource_idr, NULL, 1, 0, GFP_NOWAIT);
+ spin_unlock(&vgdev->resource_idr_lock);
+ idr_preload_end();
+ *resid = handle;
+ return 0;
+}
+
+void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id)
+{
+ spin_lock(&vgdev->resource_idr_lock);
+ idr_remove(&vgdev->resource_idr, id);
+ spin_unlock(&vgdev->resource_idr_lock);
+}
+
+void virtio_gpu_ctrl_ack(struct virtqueue *vq)
+{
+ struct drm_device *dev = vq->vdev->priv;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ schedule_work(&vgdev->ctrlq.dequeue_work);
+}
+
+void virtio_gpu_cursor_ack(struct virtqueue *vq)
+{
+ struct drm_device *dev = vq->vdev->priv;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ schedule_work(&vgdev->cursorq.dequeue_work);
+}
+
+static struct virtio_gpu_vbuffer*
+virtio_gpu_allocate_vbuf(struct virtio_gpu_device *vgdev,
+ int size, int resp_size,
+ virtio_gpu_resp_cb resp_cb)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ vbuf = kzalloc(sizeof(*vbuf) + size + resp_size, GFP_KERNEL);
+ if (!vbuf)
+ goto fail;
+
+ vbuf->buf = (void *)vbuf + sizeof(*vbuf);
+ vbuf->size = size;
+
+ vbuf->resp_cb = resp_cb;
+ if (resp_size)
+ vbuf->resp_buf = (void *)vbuf->buf + size;
+ else
+ vbuf->resp_buf = NULL;
+ vbuf->resp_size = resp_size;
+
+ return vbuf;
+fail:
+ kfree(vbuf);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void *virtio_gpu_alloc_cmd(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer **vbuffer_p,
+ int size)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ vbuf = virtio_gpu_allocate_vbuf(vgdev, size,
+ sizeof(struct virtio_gpu_ctrl_hdr), NULL);
+ if (IS_ERR(vbuf)) {
+ *vbuffer_p = NULL;
+ return ERR_CAST(vbuf);
+ }
+ *vbuffer_p = vbuf;
+ return vbuf->buf;
+}
+
+static struct virtio_gpu_update_cursor*
+virtio_gpu_alloc_cursor(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer **vbuffer_p)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ vbuf = virtio_gpu_allocate_vbuf
+ (vgdev, sizeof(struct virtio_gpu_update_cursor), 0, NULL);
+ if (IS_ERR(vbuf)) {
+ *vbuffer_p = NULL;
+ return ERR_CAST(vbuf);
+ }
+ *vbuffer_p = vbuf;
+ return (struct virtio_gpu_update_cursor *)vbuf->buf;
+}
+
+static void *virtio_gpu_alloc_cmd_resp(struct virtio_gpu_device *vgdev,
+ virtio_gpu_resp_cb cb,
+ struct virtio_gpu_vbuffer **vbuffer_p,
+ int cmd_size, int resp_size)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ vbuf = virtio_gpu_allocate_vbuf(vgdev, cmd_size, resp_size, cb);
+ if (IS_ERR(vbuf)) {
+ *vbuffer_p = NULL;
+ return ERR_CAST(vbuf);
+ }
+ *vbuffer_p = vbuf;
+ return (struct virtio_gpu_command *)vbuf->buf;
+}
+
+static void free_vbuf(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf)
+{
+ kfree(vbuf->data_buf);
+ kfree(vbuf);
+}
+
+static int reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+ unsigned int len;
+ int freed = 0;
+ while ((vbuf = virtqueue_get_buf(vq, &len))) {
+ list_add_tail(&vbuf->destroy_list, reclaim_list);
+ freed++;
+ }
+ return freed;
+}
+
+void virtio_gpu_dequeue_ctrl_func(struct work_struct *work)
+{
+ struct virtio_gpu_device *vgdev =
+ container_of(work, struct virtio_gpu_device,
+ ctrlq.dequeue_work);
+ int ret;
+ struct list_head reclaim_list;
+ struct virtio_gpu_vbuffer *entry, *tmp;
+ struct virtio_gpu_ctrl_hdr *resp;
+ u64 fence_id = 0;
+
+ INIT_LIST_HEAD(&reclaim_list);
+ spin_lock(&vgdev->ctrlq.qlock);
+ do {
+ virtqueue_disable_cb(vgdev->ctrlq.vq);
+ ret = reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list);
+ if (ret == 0)
+ DRM_DEBUG("cleaned 0 buffers wierd\n");
+
+ } while (!virtqueue_enable_cb(vgdev->ctrlq.vq));
+ spin_unlock(&vgdev->ctrlq.qlock);
+
+ list_for_each_entry_safe(entry, tmp, &reclaim_list, destroy_list) {
+ resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf;
+ if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA))
+ DRM_DEBUG("response 0x%x\n", le32_to_cpu(resp->type));
+ if (resp->flags & cpu_to_le32(VIRTIO_GPU_FLAG_FENCE)) {
+ u64 f = le64_to_cpu(resp->fence_id);
+
+ if (fence_id > f) {
+ DRM_ERROR("%s: Oops: fence %llx -> %llx\n",
+ __func__, fence_id, f);
+ } else {
+ fence_id = f;
+ }
+ }
+ if (entry->resp_cb)
+ entry->resp_cb(vgdev, entry);
+
+ list_del(&entry->destroy_list);
+ free_vbuf(vgdev, entry);
+ }
+ wake_up(&vgdev->ctrlq.ack_queue);
+
+ if (fence_id) {
+ virtio_gpu_fence_event_process(vgdev, fence_id);
+ }
+}
+
+void virtio_gpu_dequeue_cursor_func(struct work_struct *work)
+{
+ struct virtio_gpu_device *vgdev =
+ container_of(work, struct virtio_gpu_device,
+ cursorq.dequeue_work);
+ struct virtqueue *vq = vgdev->cursorq.vq;
+ struct list_head reclaim_list;
+ struct virtio_gpu_vbuffer *entry, *tmp;
+ unsigned int len;
+ int ret;
+
+ INIT_LIST_HEAD(&reclaim_list);
+ spin_lock(&vgdev->cursorq.qlock);
+ do {
+ virtqueue_disable_cb(vgdev->cursorq.vq);
+ ret = reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list);
+ if (ret == 0)
+ DRM_DEBUG("cleaned 0 buffers wierd\n");
+ while (virtqueue_get_buf(vq, &len))
+ /* nothing */;
+ } while (!virtqueue_enable_cb(vgdev->cursorq.vq));
+ spin_unlock(&vgdev->cursorq.qlock);
+
+ list_for_each_entry_safe(entry, tmp, &reclaim_list, destroy_list) {
+ list_del(&entry->destroy_list);
+ free_vbuf(vgdev, entry);
+ }
+ wake_up(&vgdev->cursorq.ack_queue);
+}
+
+static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf)
+{
+ struct virtqueue *vq = vgdev->ctrlq.vq;
+ struct scatterlist *sgs[3], vcmd, vout, vresp;
+ int outcnt = 0, incnt = 0;
+ int ret;
+
+ sg_init_one(&vcmd, vbuf->buf, vbuf->size);
+ sgs[outcnt+incnt] = &vcmd;
+ outcnt++;
+
+ if (vbuf->data_buf) {
+ sg_init_one(&vout, vbuf->data_buf, vbuf->data_size);
+ sgs[outcnt+incnt] = &vout;
+ outcnt++;
+ }
+
+ if (vbuf->resp_buf) {
+ sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size);
+ sgs[outcnt+incnt] = &vresp;
+ incnt++;
+ }
+
+ spin_lock(&vgdev->ctrlq.qlock);
+retry:
+ ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
+ if (ret == -ENOSPC) {
+ spin_unlock(&vgdev->ctrlq.qlock);
+ wait_event(vgdev->ctrlq.ack_queue, vq->num_free);
+ spin_lock(&vgdev->ctrlq.qlock);
+ goto retry;
+ } else {
+ virtqueue_kick(vq);
+ }
+ spin_unlock(&vgdev->ctrlq.qlock);
+
+ if (!ret)
+ ret = vq->num_free;
+ return ret;
+}
+
+static int virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf)
+{
+ struct virtqueue *vq = vgdev->cursorq.vq;
+ struct scatterlist *sgs[1], ccmd;
+ int ret;
+ int outcnt;
+
+ sg_init_one(&ccmd, vbuf->buf, vbuf->size);
+ sgs[0] = &ccmd;
+ outcnt = 1;
+
+ spin_lock(&vgdev->cursorq.qlock);
+retry:
+ ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC);
+ if (ret == -ENOSPC) {
+ spin_unlock(&vgdev->cursorq.qlock);
+ wait_event(vgdev->cursorq.ack_queue, vq->num_free);
+ spin_lock(&vgdev->cursorq.qlock);
+ goto retry;
+ } else {
+ virtqueue_kick(vq);
+ }
+
+ spin_unlock(&vgdev->cursorq.qlock);
+
+ if (!ret)
+ ret = vq->num_free;
+ return ret;
+}
+
+/* just create gem objects for userspace and long lived objects,
+ just use dma_alloced pages for the queue objects? */
+
+/* create a basic resource */
+int virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id,
+ uint32_t format,
+ uint32_t width,
+ uint32_t height)
+{
+ struct virtio_gpu_resource_create_2d *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+ cmd_p->format = cpu_to_le32(format);
+ cmd_p->width = cpu_to_le32(width);
+ cmd_p->height = cpu_to_le32(height);
+
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+ return 0;
+}
+
+int virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id)
+{
+ struct virtio_gpu_resource_unref *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+ return 0;
+}
+
+int virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id)
+{
+ struct virtio_gpu_resource_detach_backing *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+ return 0;
+}
+
+int virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
+ uint32_t scanout_id, uint32_t resource_id,
+ uint32_t width, uint32_t height,
+ uint32_t x, uint32_t y)
+{
+ struct virtio_gpu_set_scanout *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_SET_SCANOUT);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+ cmd_p->scanout_id = cpu_to_le32(scanout_id);
+ cmd_p->r.width = cpu_to_le32(width);
+ cmd_p->r.height = cpu_to_le32(height);
+ cmd_p->r.x = cpu_to_le32(x);
+ cmd_p->r.y = cpu_to_le32(y);
+
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+ return 0;
+}
+
+int virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ struct virtio_gpu_resource_flush *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+ cmd_p->r.width = cpu_to_le32(width);
+ cmd_p->r.height = cpu_to_le32(height);
+ cmd_p->r.x = cpu_to_le32(x);
+ cmd_p->r.y = cpu_to_le32(y);
+
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+ return 0;
+}
+
+int virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id, uint64_t offset,
+ __le32 width, __le32 height,
+ __le32 x, __le32 y,
+ struct virtio_gpu_fence **fence)
+{
+ struct virtio_gpu_transfer_to_host_2d *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+ cmd_p->offset = cpu_to_le64(offset);
+ cmd_p->r.width = width;
+ cmd_p->r.height = height;
+ cmd_p->r.x = x;
+ cmd_p->r.y = y;
+
+ if (fence)
+ virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+ return 0;
+}
+
+static int
+virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id,
+ struct virtio_gpu_mem_entry *ents,
+ uint32_t nents,
+ struct virtio_gpu_fence **fence)
+{
+ struct virtio_gpu_resource_attach_backing *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+ cmd_p->nr_entries = cpu_to_le32(nents);
+
+ vbuf->data_buf = ents;
+ vbuf->data_size = sizeof(*ents) * nents;
+
+ if (fence)
+ virtio_gpu_fence_emit(vgdev, &cmd_p->hdr, fence);
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+
+ return 0;
+}
+
+static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf)
+{
+ struct virtio_gpu_resp_display_info *resp =
+ (struct virtio_gpu_resp_display_info *)vbuf->resp_buf;
+ int i;
+
+ spin_lock(&vgdev->display_info_lock);
+ for (i = 0; i < vgdev->num_scanouts; i++) {
+ vgdev->outputs[i].info = resp->pmodes[i];
+ if (resp->pmodes[i].enabled) {
+ DRM_DEBUG("output %d: %dx%d+%d+%d", i,
+ le32_to_cpu(resp->pmodes[i].r.width),
+ le32_to_cpu(resp->pmodes[i].r.height),
+ le32_to_cpu(resp->pmodes[i].r.x),
+ le32_to_cpu(resp->pmodes[i].r.y));
+ } else {
+ DRM_DEBUG("output %d: disabled", i);
+ }
+ }
+
+ spin_unlock(&vgdev->display_info_lock);
+ wake_up(&vgdev->resp_wq);
+
+ if (!drm_helper_hpd_irq_event(vgdev->ddev)) {
+ drm_kms_helper_hotplug_event(vgdev->ddev);
+ }
+}
+
+int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_ctrl_hdr *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd_resp
+ (vgdev, &virtio_gpu_cmd_get_display_info_cb, &vbuf,
+ sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_display_info));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->type = cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO);
+ virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+ return 0;
+}
+
+int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *obj,
+ uint32_t resource_id,
+ struct virtio_gpu_fence **fence)
+{
+ struct virtio_gpu_mem_entry *ents;
+ struct scatterlist *sg;
+ int si;
+
+ if (!obj->pages) {
+ int ret;
+ ret = virtio_gpu_object_get_sg_table(vgdev, obj);
+ if (ret)
+ return ret;
+ }
+
+ /* gets freed when the ring has consumed it */
+ ents = kmalloc_array(obj->pages->nents,
+ sizeof(struct virtio_gpu_mem_entry),
+ GFP_KERNEL);
+ if (!ents) {
+ DRM_ERROR("failed to allocate ent list\n");
+ return -ENOMEM;
+ }
+
+ for_each_sg(obj->pages->sgl, sg, obj->pages->nents, si) {
+ ents[si].addr = cpu_to_le64(sg_phys(sg));
+ ents[si].length = cpu_to_le32(sg->length);
+ ents[si].padding = 0;
+ }
+
+ virtio_gpu_cmd_resource_attach_backing(vgdev, resource_id,
+ ents, obj->pages->nents,
+ fence);
+ obj->hw_res_handle = resource_id;
+ return 0;
+}
+
+void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_output *output)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+ struct virtio_gpu_update_cursor *cur_p;
+
+ output->cursor.pos.scanout_id = cpu_to_le32(output->index);
+ cur_p = virtio_gpu_alloc_cursor(vgdev, &vbuf);
+ memcpy(cur_p, &output->cursor, sizeof(output->cursor));
+ virtio_gpu_queue_cursor(vgdev, vbuf);
+}
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index e894eb2..a3167fa 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -510,7 +510,7 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
goto err_enable_device;
rc = pci_request_regions(pci_dev, "virtio-pci");
- if (rc)
+ if (rc && ((pci_dev->class >> 8) != PCI_CLASS_DISPLAY_VGA))
goto err_request_regions;
if (force_legacy) {
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index e928625..a1067c4 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -799,6 +799,7 @@ struct drm_device {
#endif
struct platform_device *platformdev; /**< Platform device struture */
+ struct virtio_device *virtdev;
struct drm_sg_mem *sg; /**< Scatter gather memory */
unsigned int num_crtcs; /**< Number of CRTCs on this device */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 68ceb97..9707e5d 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -429,6 +429,7 @@ header-y += virtio_balloon.h
header-y += virtio_blk.h
header-y += virtio_config.h
header-y += virtio_console.h
+header-y += virtio_gpu.h
header-y += virtio_ids.h
header-y += virtio_net.h
header-y += virtio_pci.h
diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
new file mode 100644
index 0000000..a1bda52
--- /dev/null
+++ b/include/uapi/linux/virtio_gpu.h
@@ -0,0 +1,203 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef VIRTIO_GPU_HW_H
+#define VIRTIO_GPU_HW_H
+
+enum virtio_gpu_ctrl_type {
+ VIRTIO_GPU_UNDEFINED = 0,
+
+ /* 2d commands */
+ VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+ VIRTIO_GPU_CMD_RESOURCE_UNREF,
+ VIRTIO_GPU_CMD_SET_SCANOUT,
+ VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+ VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+ VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+
+ /* cursor commands */
+ VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+ VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+ /* success responses */
+ VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+ VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+
+ /* error responses */
+ VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+ VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+ VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
+
+struct virtio_gpu_ctrl_hdr {
+ __le32 type;
+ __le32 flags;
+ __le64 fence_id;
+ __le32 ctx_id;
+ __le32 padding;
+};
+
+/* data passed in the cursor vq */
+
+struct virtio_gpu_cursor_pos {
+ __le32 scanout_id;
+ __le32 x, y;
+ __le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+struct virtio_gpu_update_cursor {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_cursor_pos pos; /* update & move */
+ __le32 resource_id; /* update only */
+ __le32 hot_x; /* update only */
+ __le32 hot_y; /* update only */
+ __le32 padding;
+};
+
+/* data passed in the control vq, 2d related */
+
+struct virtio_gpu_rect {
+ __le32 x, y;
+ __le32 width;
+ __le32 height;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+struct virtio_gpu_resource_unref {
+ struct virtio_gpu_ctrl_hdr hdr;
+ __le32 resource_id;
+ __le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+struct virtio_gpu_resource_create_2d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ __le32 resource_id;
+ __le32 format;
+ __le32 width;
+ __le32 height;
+};
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+struct virtio_gpu_set_scanout {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ __le32 scanout_id;
+ __le32 resource_id;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+struct virtio_gpu_resource_flush {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ __le32 resource_id;
+ __le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+struct virtio_gpu_transfer_to_host_2d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ __le64 offset;
+ __le32 resource_id;
+ __le32 padding;
+};
+
+struct virtio_gpu_mem_entry {
+ __le64 addr;
+ __le32 length;
+ __le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+struct virtio_gpu_resource_attach_backing {
+ struct virtio_gpu_ctrl_hdr hdr;
+ __le32 resource_id;
+ __le32 nr_entries;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+struct virtio_gpu_resource_detach_backing {
+ struct virtio_gpu_ctrl_hdr hdr;
+ __le32 resource_id;
+ __le32 padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+#define VIRTIO_GPU_MAX_SCANOUTS 16
+struct virtio_gpu_resp_display_info {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_display_one {
+ struct virtio_gpu_rect r;
+ __le32 enabled;
+ __le32 flags;
+ } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
+};
+
+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
+
+struct virtio_gpu_config {
+ __u32 events_read;
+ __u32 events_clear;
+ __u32 num_scanouts;
+ __u32 reserved;
+};
+
+/* simple formats for fbcon/X use */
+enum virtio_gpu_formats {
+ VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
+ VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2,
+ VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3,
+ VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4,
+
+ VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
+ VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68,
+
+ VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
+ VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
+
+};
+
+#endif
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index 284fc3a..14d77f7 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -39,5 +39,5 @@
#define VIRTIO_ID_9P 9 /* 9p virtio console */
#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
#define VIRTIO_ID_CAIF 12 /* Virtio caif */
-
+#define VIRTIO_ID_GPU 16
#endif /* _LINUX_VIRTIO_IDS_H */
--
1.8.3.1
^ permalink raw reply related
* Re: [virtio-dev] Re: [PATCH v3] Add virtio-input driver.
From: Dmitry Torokhov @ 2015-03-24 16:05 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, Michael S. Tsirkin, open list:ABI/API, open list,
virtualization, David Herrmann
In-Reply-To: <1427210714.18768.23.camel@nilsson.home.kraxel.org>
On Tue, Mar 24, 2015 at 04:25:14PM +0100, Gerd Hoffmann wrote:
> On Di, 2015-03-24 at 15:14 +0100, Michael S. Tsirkin wrote:
> > On Tue, Mar 24, 2015 at 02:37:24PM +0100, Gerd Hoffmann wrote:
> > > Hi,
> > >
> > > > > input layer checks it and ignores events not supported (according to the
> > > > > support bitmaps).
> > > >
> > > > Right but support bitmaps come from host too, no?
> > >
> > > Yes, but the driver will not set invalid bits (bitcount argument for the
> > > virtinput_cfg_bits() function is the number of valid bits of the
> > > specific bitmap).
> > >
> > > cheers,
> > > Gerd
> > >
> > >
> > >
> >
> > Question: does linux ever get such events from userspace
> > as opposed to sending them to userspace?
>
> Yes, it's possible using the userspace input driver
> (CONFIG_INPUT_UINPUT)
No, not through uinput (as from kernel POV uinput is also a driver), but
users can write into evdev.
Sending unknown codes is OK: events of unknown type will be dropped by
the input core, unknown event codes will be passed on; users not
recognizing event code should simply ignore it.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [virtio-dev] Re: [PATCH v3] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-24 15:25 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: virtio-dev, Dmitry Torokhov, open list:ABI/API, open list,
virtualization, David Herrmann
In-Reply-To: <20150324143839-mutt-send-email-mst@redhat.com>
On Di, 2015-03-24 at 15:14 +0100, Michael S. Tsirkin wrote:
> On Tue, Mar 24, 2015 at 02:37:24PM +0100, Gerd Hoffmann wrote:
> > Hi,
> >
> > > > input layer checks it and ignores events not supported (according to the
> > > > support bitmaps).
> > >
> > > Right but support bitmaps come from host too, no?
> >
> > Yes, but the driver will not set invalid bits (bitcount argument for the
> > virtinput_cfg_bits() function is the number of valid bits of the
> > specific bitmap).
> >
> > cheers,
> > Gerd
> >
> >
> >
>
> Question: does linux ever get such events from userspace
> as opposed to sending them to userspace?
Yes, it's possible using the userspace input driver
(CONFIG_INPUT_UINPUT)
cheers,
Gerd
^ permalink raw reply
* Re: [PATCH 12/14] kdbus: add Makefile, Kconfig and MAINTAINERS entry
From: Jiri Slaby @ 2015-03-24 15:15 UTC (permalink / raw)
To: Greg Kroah-Hartman, arnd, ebiederm, gnomes, teg, jkosina, luto,
linux-api, linux-kernel
Cc: daniel, dh.herrmann, tixxdz
In-Reply-To: <1425906560-13798-13-git-send-email-gregkh@linuxfoundation.org>
On 03/09/2015, 02:09 PM, Greg Kroah-Hartman wrote:
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -261,6 +261,18 @@ config POSIX_MQUEUE_SYSCTL
> depends on SYSCTL
> default y
>
> +config KDBUS
> + tristate "kdbus interprocess communication"
> + depends on TMPFS
> + help
> + D-Bus is a system for low-latency, low-overhead, easy to use
> + interprocess communication (IPC).
> +
> + See Documentation/kdbus.txt
This one is missing from the series.
> + To compile this driver as a module, choose M here: the
> + module will be called kdbus.
It would be nice to also know who actually needs this... The old good
'if you have an ordinary machine, select m here'.
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [PATCH] mremap: add MREMAP_NOHOLE flag --resend
From: Daniel Micay @ 2015-03-24 14:54 UTC (permalink / raw)
To: Aliaksey Kandratsenka
Cc: Andrew Morton, Shaohua Li, linux-mm, linux-api, Rik van Riel,
Hugh Dickins, Mel Gorman, Johannes Weiner, Michal Hocko,
Andy Lutomirski, google-perftools@googlegroups.com
In-Reply-To: <CADpJO7wP+dvXyxP7SW7F12jra_cWrEba7orRXMJGytvgOJfHkA@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1840 bytes --]
> Given that mremap is holding mmap_sem exclusively, how about userspace
> malloc implementation taking some exclusive malloc lock and doing
> normal mremap followed by mmap with MAP_FIXED to fill the hole ? It
> might end up having largely same overhead. Well, modulo some extra TLB
> flushing. But arguably, reducing TLB flushes for sequence of page
> table updates could be usefully addressed separately (e.g. maybe by
> matching those syscalls, maybe via syslets).
You can't use MAP_FIXED because it has a race with other users of mmap.
The address hint will *usually* work, but you need to deal with the case
where it fails and then cope with the fallout of the fragmentation.
PaX ASLR ignores address hints so that's something else to consider if
you care about running on PaX/Grsecurity patched kernels.
I'm doing this in my own allocator that's heavily based on the jemalloc
design. It just unmaps the memory given by the hinted mmap call if it
fails to get back the hole:
https://github.com/thestinger/allocator/blob/e80d2d0c2863c490b650ecffeb33beaccfcfdc46/huge.c#L167-L180
On 64-bit, it relies on 1TiB of reserved address space (works even with
overcommit disabled) to do per-CPU allocation for chunks and huge (>=
chunk size) allocations via address range checks so it also needs this
ugly workaround too:
https://github.com/thestinger/allocator/blob/e80d2d0c2863c490b650ecffeb33beaccfcfdc46/huge.c#L67-L75
I'm convinced that the mmap_sem writer lock can be avoided for the case
with MREMAP_FIXED via a good heuristic though. It just needs to check
that dst is a single VMA that matches the src properties and fall back
to the writer lock if that's not the case. This will have the same
performance as a separate syscall to move pages in all the cases where
that syscall would work.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH] mremap: add MREMAP_NOHOLE flag --resend
From: Daniel Micay @ 2015-03-24 14:39 UTC (permalink / raw)
To: Aliaksey Kandratsenka, Shaohua Li
Cc: Andrew Morton, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
linux-api-u79uwXL29TY76Z2rM5mHXA, Rik van Riel, Hugh Dickins,
Mel Gorman, Johannes Weiner, Michal Hocko, Andy Lutomirski,
google-perftools-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
In-Reply-To: <CADpJO7zk8J3q7Bw9NibV9CzLarO+YkfeshyFTTq=XeS5qziBiA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3787 bytes --]
On 24/03/15 01:25 AM, Aliaksey Kandratsenka wrote:
>
> Well, I don't have any workloads. I'm just maintaining a library that
> others run various workloads on. Part of the problem is lack of good
> and varied malloc benchmarks which could allow us that prevent
> regression. So this makes me a bit more cautious on performance
> matters.
>
> But I see your point. Indeed I have no evidence at all that exclusive
> locking might cause observable performance difference.
I'm sure it matters but I expect you'd need *many* cores running many
threads before it started to outweigh the benefit of copying pages
instead of data.
Thinking about it a bit more, it would probably make sense for mremap to
start with the optimistic assumption that the reader lock is enough here
when using MREMAP_NOHOLE|MREMAP_FIXED. It only needs the writer lock if
the destination mapping is incomplete or doesn't match, which is an edge
case as holes would mean thread unsafety.
An ideal allocator will toggle on PROT_NONE when overcommit is disabled
so this assumption would be wrong. The heuristic could just be adjusted
to assume the dest VMA will match with MREMAP_NOHOLE|MREMAP_FIXED when
full memory accounting isn't enabled. The fallback would never ended up
being needed in existing use cases that I'm aware of, and would just add
the overhead of a quick lock, O(log n) check and unlock with the reader
lock held anyway. Another flag isn't really necessary.
>>> Another notable thing is how mlock effectively disables MADV_DONTNEED for
>>> jemalloc{1,2} and tcmalloc, lowers page faults count and thus improves
>>> runtime. It can be seen that tcmalloc+mlock on thp-less configuration is
>>> slightly better on runtime to glibc. The later spends a ton of time in
>>> kernel,
>>> probably handling minor page faults, and the former burns cpu in user space
>>> doing memcpy-s. So "tons of memcpys" seems to be competitive to what glibc
>>> is
>>> doing in this benchmark.
>>
>> mlock disables MADV_DONTNEED, so this is an unfair comparsion. With it,
>> allocator will use more memory than expected.
>
> Do not agree with unfair. I'm actually hoping MADV_FREE to provide
> most if not all of benefits of mlock in this benchmark. I believe it's
> not too unreasonable expectation.
MADV_FREE will still result in as many page faults, just no zeroing.
I get ~20k requests/s with jemalloc on the ebizzy benchmark with this
dual core ivy bridge laptop. It jumps to ~60k requests/s with MADV_FREE
IIRC, but disabling purging via MALLOC_CONF=lg_dirty_mult:-1 leads to
3.5 *million* requests/s. It has a similar impact with TCMalloc.
>> I'm kind of confused why we talk about THP, mlock here. When application
>> uses allocator, it doesn't need to be forced to use THP or mlock. Can we
>> forcus on normal case?
>
> See my note on mlock above.
>
> THP it is actually "normal". I know for certain, that many production
> workloads are run on boxes with THP enabled. Red Hat famously ships
> it's distros with THP set to "always". And I also know that some other
> many production workloads are run on boxes with THP disabled. Also, as
> seen above, "teleporting" pages is more efficient with THP due to much
> smaller overhead of moving those pages. So I felt it was important not
> to omit THP in my runs.
Yeah, it's quite normal for it to be enabled. Allocators might as well
give up on fine-grained purging when it is though :P. I think it only
really makes sense to purge at 2M boundaries in multiples of 2M if it's
going to end up breaking any other purging over the long-term.
I was originally only testing with THP since Arch uses "always" but I
realized it had an enormous impact and started testing without it too.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [virtio-dev] Re: [PATCH v3] Add virtio-input driver.
From: Michael S. Tsirkin @ 2015-03-24 14:14 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, virtualization, David Herrmann, Dmitry Torokhov,
Rusty Russell, open list, open list:ABI/API
In-Reply-To: <1427204244.18768.21.camel@nilsson.home.kraxel.org>
On Tue, Mar 24, 2015 at 02:37:24PM +0100, Gerd Hoffmann wrote:
> Hi,
>
> > > input layer checks it and ignores events not supported (according to the
> > > support bitmaps).
> >
> > Right but support bitmaps come from host too, no?
>
> Yes, but the driver will not set invalid bits (bitcount argument for the
> virtinput_cfg_bits() function is the number of valid bits of the
> specific bitmap).
>
> cheers,
> Gerd
>
>
>
Question: does linux ever get such events from userspace
as opposed to sending them to userspace?
--
MST
^ permalink raw reply
* Re: [virtio-dev] Re: [PATCH v3] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-24 13:37 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b,
virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
David Herrmann, Dmitry Torokhov, Rusty Russell, open list,
open list:ABI/API
In-Reply-To: <20150324135908-mutt-send-email-mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Hi,
> > input layer checks it and ignores events not supported (according to the
> > support bitmaps).
>
> Right but support bitmaps come from host too, no?
Yes, but the driver will not set invalid bits (bitcount argument for the
virtinput_cfg_bits() function is the number of valid bits of the
specific bitmap).
cheers,
Gerd
^ permalink raw reply
* Re: [virtio-dev] Re: [PATCH v3] Add virtio-input driver.
From: Michael S. Tsirkin @ 2015-03-24 13:02 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, Dmitry Torokhov, open list:ABI/API, open list,
virtualization, David Herrmann
In-Reply-To: <1427197581.18768.16.camel@nilsson.home.kraxel.org>
On Tue, Mar 24, 2015 at 12:46:21PM +0100, Gerd Hoffmann wrote:
> Hi,
>
> > > + spin_lock_irqsave(&vi->lock, flags);
> > > + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
> > > + input_event(vi->idev,
> > > + le16_to_cpu(event->type),
> > > + le16_to_cpu(event->code),
> > > + le32_to_cpu(event->value));
> >
> > What happens if input layer gets an
> > unexpected event code or value?
>
> input layer checks it and ignores events not supported (according to the
> support bitmaps).
Right but support bitmaps come from host too, no?
> > > +static int virtinput_send_status(struct virtio_input *vi,
> > > + u16 type, u16 code, s32 value)
> > > +{
>
> > This means that caller will get errors if it happens to call
> > send_status at a rate that's faster than host's consumption of them.
> > To me this looks wrong.
> > Poking at input layer, it seems to simply discard errors.
> > Is it always safe to discard status updates?
>
> Typical use case is updating the leds of your keyboard. Loosing one
> update isn't the end of the world. Also that are _very_ low rate
> events, and in case that really actually happens you likely have bigger
> problems anyway ;)
Until QE opens a bug report I guess :)
OK but pls add a comment.
> > > +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
> > > + unsigned long *bits, unsigned int bitcount)
> > > +{
> > > + unsigned int bit;
> > > + size_t bytes;
> > > + u8 *virtio_bits;
> > > +
> > > + bytes = virtinput_cfg_select(vi, select, subsel);
> > > + if (!bytes)
> > > + return;
> >
> > How about limiting bytes to sizeof struct virtio_input_config->u?
>
> It's limited to 256 anyway because size is u8 in config space.
That struct is 128 bytes though.
Also, change bytes and virtinput_cfg_select to be u8 to make this
clearer?
> > > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
> > > + if (size >= 8) {
> >
> > What does 8 mean here? Should be sizeof virtio_input_devids?
>
> Yes, fixed.
>
> > > +struct virtio_input_config {
> > > + __u8 select;
> > > + __u8 subsel;
> > > + __u8 size;
> > > + __u8 reserved;
> > > + union {
> > > + char string[128];
> > > + __u8 bitmap[128];
> >
> > I note that neither string nor bitmap are used by
> > driver. What are they in aid of?
>
> Fixed. Just sloppy coding, the name should use u.string, the bitmap
> u.bitmap, instead of just "u" (although for offsetof it doesn't make a
> difference).
>
> cheers,
> Gerd
>
^ permalink raw reply
* [PATCH v4] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-24 12:08 UTC (permalink / raw)
To: virtio-dev, virtualization
Cc: Dmitry Torokhov, mst, open list:ABI/API, open list,
David Herrmann
virtio-input is basically evdev-events-over-virtio, so this driver isn't
much more than reading configuration from config space and forwarding
incoming events to the linux input layer.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
MAINTAINERS | 6 +
drivers/virtio/Kconfig | 10 ++
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio_input.c | 363 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/virtio_ids.h | 1 +
include/uapi/linux/virtio_input.h | 76 ++++++++
7 files changed, 458 insertions(+)
create mode 100644 drivers/virtio/virtio_input.c
create mode 100644 include/uapi/linux/virtio_input.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 358eb01..6f233dd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10442,6 +10442,12 @@ S: Maintained
F: drivers/vhost/
F: include/uapi/linux/vhost.h
+VIRTIO INPUT DRIVER
+M: Gerd Hoffmann <kraxel@redhat.com>
+S: Maintained
+F: drivers/virtio/virtio_input.c
+F: include/uapi/linux/virtio_input.h
+
VIA RHINE NETWORK DRIVER
M: Roger Luethi <rl@hellgate.ch>
S: Maintained
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index b546da5..cab9f3f 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -48,6 +48,16 @@ config VIRTIO_BALLOON
If unsure, say M.
+config VIRTIO_INPUT
+ tristate "Virtio input driver"
+ depends on VIRTIO
+ depends on INPUT
+ ---help---
+ This driver supports virtio input devices such as
+ keyboards, mice and tablets.
+
+ If unsure, say M.
+
config VIRTIO_MMIO
tristate "Platform bus driver for memory mapped virtio devices"
depends on HAS_IOMEM
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index d85565b..41e30e3 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
+obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
new file mode 100644
index 0000000..fc99a15
--- /dev/null
+++ b/drivers/virtio/virtio_input.c
@@ -0,0 +1,363 @@
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/input.h>
+
+#include <uapi/linux/virtio_ids.h>
+#include <uapi/linux/virtio_input.h>
+
+struct virtio_input {
+ struct virtio_device *vdev;
+ struct input_dev *idev;
+ char name[64];
+ char serial[64];
+ char phys[64];
+ struct virtqueue *evt, *sts;
+ struct virtio_input_event evts[64];
+ spinlock_t lock;
+ bool ready;
+};
+
+static void virtinput_queue_evtbuf(struct virtio_input *vi,
+ struct virtio_input_event *evtbuf)
+{
+ struct scatterlist sg[1];
+
+ sg_init_one(sg, evtbuf, sizeof(*evtbuf));
+ virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
+}
+
+static void virtinput_recv_events(struct virtqueue *vq)
+{
+ struct virtio_input *vi = vq->vdev->priv;
+ struct virtio_input_event *event;
+ unsigned long flags;
+ unsigned int len;
+
+ spin_lock_irqsave(&vi->lock, flags);
+ if (vi->ready) {
+ while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
+ input_event(vi->idev,
+ le16_to_cpu(event->type),
+ le16_to_cpu(event->code),
+ le32_to_cpu(event->value));
+ virtinput_queue_evtbuf(vi, event);
+ }
+ virtqueue_kick(vq);
+ }
+ spin_unlock_irqrestore(&vi->lock, flags);
+}
+
+static int virtinput_send_status(struct virtio_input *vi,
+ u16 type, u16 code, s32 value)
+{
+ struct virtio_input_event *stsbuf;
+ struct scatterlist sg[1];
+ unsigned long flags;
+ int rc;
+
+ stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
+ if (!stsbuf)
+ return -ENOMEM;
+
+ stsbuf->type = cpu_to_le16(type);
+ stsbuf->code = cpu_to_le16(code);
+ stsbuf->value = cpu_to_le32(value);
+ sg_init_one(sg, stsbuf, sizeof(*stsbuf));
+
+ spin_lock_irqsave(&vi->lock, flags);
+ if (vi->ready) {
+ rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
+ virtqueue_kick(vi->sts);
+ } else {
+ rc = -ENODEV;
+ }
+ spin_unlock_irqrestore(&vi->lock, flags);
+
+ if (rc != 0)
+ kfree(stsbuf);
+ return rc;
+}
+
+static void virtinput_recv_status(struct virtqueue *vq)
+{
+ struct virtio_input *vi = vq->vdev->priv;
+ struct virtio_input_event *stsbuf;
+ unsigned long flags;
+ unsigned int len;
+
+ spin_lock_irqsave(&vi->lock, flags);
+ while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
+ kfree(stsbuf);
+ spin_unlock_irqrestore(&vi->lock, flags);
+}
+
+static int virtinput_status(struct input_dev *idev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct virtio_input *vi = input_get_drvdata(idev);
+
+ return virtinput_send_status(vi, type, code, value);
+}
+
+static size_t virtinput_cfg_select(struct virtio_input *vi,
+ u8 select, u8 subsel)
+{
+ u8 size;
+
+ virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select);
+ virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel);
+ virtio_cread(vi->vdev, struct virtio_input_config, size, &size);
+ return size;
+}
+
+static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
+ unsigned long *bits, unsigned int bitcount)
+{
+ unsigned int bit;
+ size_t bytes;
+ u8 *virtio_bits;
+
+ bytes = virtinput_cfg_select(vi, select, subsel);
+ if (!bytes)
+ return;
+ if (bitcount > bytes * 8)
+ bitcount = bytes * 8;
+
+ /*
+ * Bitmap in virtio config space is a simple stream of bytes,
+ * with the first byte carrying bits 0-7, second bits 8-15 and
+ * so on.
+ */
+ virtio_bits = kzalloc(bytes, GFP_KERNEL);
+ if (!virtio_bits)
+ return;
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config,
+ u.bitmap),
+ virtio_bits, bytes);
+ for (bit = 0; bit < bitcount; bit++) {
+ if (virtio_bits[bit / 8] & (1 << (bit % 8)))
+ __set_bit(bit, bits);
+ }
+ kfree(virtio_bits);
+
+ if (select == VIRTIO_INPUT_CFG_EV_BITS)
+ __set_bit(subsel, vi->idev->evbit);
+}
+
+static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
+{
+ u32 mi, ma, re, fu, fl;
+
+ virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
+ input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
+ input_abs_set_res(vi->idev, abs, re);
+}
+
+static int virtinput_init_vqs(struct virtio_input *vi)
+{
+ struct virtqueue *vqs[2];
+ vq_callback_t *cbs[] = { virtinput_recv_events,
+ virtinput_recv_status };
+ static const char *names[] = { "events", "status" };
+ int err;
+
+ err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names);
+ if (err)
+ return err;
+ vi->evt = vqs[0];
+ vi->sts = vqs[1];
+
+ return 0;
+}
+
+static void virtinput_fill_evt(struct virtio_input *vi)
+{
+ unsigned long flags;
+ int i, size;
+
+ spin_lock_irqsave(&vi->lock, flags);
+ size = virtqueue_get_vring_size(vi->evt);
+ if (size > ARRAY_SIZE(vi->evts))
+ size = ARRAY_SIZE(vi->evts);
+ for (i = 0; i < size; i++)
+ virtinput_queue_evtbuf(vi, &vi->evts[i]);
+ virtqueue_kick(vi->evt);
+ spin_unlock_irqrestore(&vi->lock, flags);
+}
+
+static int virtinput_probe(struct virtio_device *vdev)
+{
+ struct virtio_input *vi;
+ size_t size;
+ int abs, err;
+
+ if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
+ return -ENODEV;
+
+ vi = kzalloc(sizeof(*vi), GFP_KERNEL);
+ if (!vi)
+ return -ENOMEM;
+
+ vdev->priv = vi;
+ vi->vdev = vdev;
+ spin_lock_init(&vi->lock);
+
+ err = virtinput_init_vqs(vi);
+ if (err)
+ goto err_init_vq;
+
+ vi->idev = input_allocate_device();
+ if (!vi->idev) {
+ err = -ENOMEM;
+ goto err_input_alloc;
+ }
+ input_set_drvdata(vi->idev, vi);
+
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config,
+ u.string),
+ vi->name, min(size, sizeof(vi->name)));
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config,
+ u.string),
+ vi->serial, min(size, sizeof(vi->serial)));
+ snprintf(vi->phys, sizeof(vi->phys),
+ "virtio%d/input0", vdev->index);
+ vi->idev->name = vi->name;
+ vi->idev->phys = vi->phys;
+ vi->idev->uniq = vi->serial;
+
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
+ if (size >= sizeof(struct virtio_input_devids)) {
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.bustype, &vi->idev->id.bustype);
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.vendor, &vi->idev->id.vendor);
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.product, &vi->idev->id.product);
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.version, &vi->idev->id.version);
+ } else {
+ vi->idev->id.bustype = BUS_VIRTUAL;
+ }
+
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,
+ vi->idev->propbit, INPUT_PROP_CNT);
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);
+ if (size)
+ __set_bit(EV_REP, vi->idev->evbit);
+
+ vi->idev->dev.parent = &vdev->dev;
+ vi->idev->event = virtinput_status;
+
+ /* device -> kernel */
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,
+ vi->idev->keybit, KEY_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,
+ vi->idev->relbit, REL_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,
+ vi->idev->absbit, ABS_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,
+ vi->idev->mscbit, MSC_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,
+ vi->idev->swbit, SW_CNT);
+
+ /* kernel -> device */
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,
+ vi->idev->ledbit, LED_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
+ vi->idev->sndbit, SND_CNT);
+
+ if (test_bit(EV_ABS, vi->idev->evbit)) {
+ for (abs = 0; abs < ABS_CNT; abs++) {
+ if (!test_bit(abs, vi->idev->absbit))
+ continue;
+ virtinput_cfg_abs(vi, abs);
+ }
+ }
+ err = input_register_device(vi->idev);
+ if (err)
+ goto err_input_register;
+
+ virtio_device_ready(vdev);
+ vi->ready = true;
+ virtinput_fill_evt(vi);
+ return 0;
+
+err_input_register:
+ input_free_device(vi->idev);
+err_input_alloc:
+ vdev->config->del_vqs(vdev);
+err_init_vq:
+ kfree(vi);
+ return err;
+}
+
+static void virtinput_remove(struct virtio_device *vdev)
+{
+ struct virtio_input *vi = vdev->priv;
+
+ vi->ready = false;
+ input_unregister_device(vi->idev);
+ vdev->config->del_vqs(vdev);
+ kfree(vi);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int virtinput_freeze(struct virtio_device *vdev)
+{
+ struct virtio_input *vi = vdev->priv;
+
+ vi->ready = false;
+ vdev->config->del_vqs(vdev);
+ return 0;
+}
+
+static int virtinput_restore(struct virtio_device *vdev)
+{
+ struct virtio_input *vi = vdev->priv;
+ int err;
+
+ err = virtinput_init_vqs(vi);
+ if (err)
+ return err;
+
+ virtio_device_ready(vdev);
+ vi->ready = true;
+ virtinput_fill_evt(vi);
+ return 0;
+}
+#endif
+
+static unsigned int features[] = {
+};
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static struct virtio_driver virtio_input_driver = {
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .id_table = id_table,
+ .probe = virtinput_probe,
+ .remove = virtinput_remove,
+#ifdef CONFIG_PM_SLEEP
+ .freeze = virtinput_freeze,
+ .restore = virtinput_restore,
+#endif
+};
+
+module_virtio_driver(virtio_input_driver);
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Virtio input device driver");
+MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 68ceb97..04b829e 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -430,6 +430,7 @@ header-y += virtio_blk.h
header-y += virtio_config.h
header-y += virtio_console.h
header-y += virtio_ids.h
+header-y += virtio_input.h
header-y += virtio_net.h
header-y += virtio_pci.h
header-y += virtio_ring.h
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index 284fc3a..5f60aa4 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -39,5 +39,6 @@
#define VIRTIO_ID_9P 9 /* 9p virtio console */
#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
#define VIRTIO_ID_CAIF 12 /* Virtio caif */
+#define VIRTIO_ID_INPUT 18 /* virtio input */
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/uapi/linux/virtio_input.h b/include/uapi/linux/virtio_input.h
new file mode 100644
index 0000000..dc61b28
--- /dev/null
+++ b/include/uapi/linux/virtio_input.h
@@ -0,0 +1,76 @@
+#ifndef _LINUX_VIRTIO_INPUT_H
+#define _LINUX_VIRTIO_INPUT_H
+/* This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+
+enum virtio_input_config_select {
+ VIRTIO_INPUT_CFG_UNSET = 0x00,
+ VIRTIO_INPUT_CFG_ID_NAME = 0x01,
+ VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
+ VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
+ VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
+ VIRTIO_INPUT_CFG_EV_BITS = 0x11,
+ VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
+};
+
+struct virtio_input_absinfo {
+ __u32 min;
+ __u32 max;
+ __u32 fuzz;
+ __u32 flat;
+ __u32 res;
+};
+
+struct virtio_input_devids {
+ __u16 bustype;
+ __u16 vendor;
+ __u16 product;
+ __u16 version;
+};
+
+struct virtio_input_config {
+ __u8 select;
+ __u8 subsel;
+ __u8 size;
+ __u8 reserved;
+ union {
+ char string[128];
+ __u8 bitmap[128];
+ struct virtio_input_absinfo abs;
+ struct virtio_input_devids ids;
+ } u;
+};
+
+struct virtio_input_event {
+ __le16 type;
+ __le16 code;
+ __le32 value;
+};
+
+#endif /* _LINUX_VIRTIO_INPUT_H */
--
1.8.3.1
^ permalink raw reply related
* Re: [virtio-dev] Re: [PATCH v3] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-24 11:46 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: virtio-dev, virtualization, David Herrmann, Dmitry Torokhov,
Rusty Russell, open list, open list:ABI/API
In-Reply-To: <20150324105829-mutt-send-email-mst@redhat.com>
Hi,
> > + spin_lock_irqsave(&vi->lock, flags);
> > + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
> > + input_event(vi->idev,
> > + le16_to_cpu(event->type),
> > + le16_to_cpu(event->code),
> > + le32_to_cpu(event->value));
>
> What happens if input layer gets an
> unexpected event code or value?
input layer checks it and ignores events not supported (according to the
support bitmaps).
> > +static int virtinput_send_status(struct virtio_input *vi,
> > + u16 type, u16 code, s32 value)
> > +{
> This means that caller will get errors if it happens to call
> send_status at a rate that's faster than host's consumption of them.
> To me this looks wrong.
> Poking at input layer, it seems to simply discard errors.
> Is it always safe to discard status updates?
Typical use case is updating the leds of your keyboard. Loosing one
update isn't the end of the world. Also that are _very_ low rate
events, and in case that really actually happens you likely have bigger
problems anyway ;)
> > +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
> > + unsigned long *bits, unsigned int bitcount)
> > +{
> > + unsigned int bit;
> > + size_t bytes;
> > + u8 *virtio_bits;
> > +
> > + bytes = virtinput_cfg_select(vi, select, subsel);
> > + if (!bytes)
> > + return;
>
> How about limiting bytes to sizeof struct virtio_input_config->u?
It's limited to 256 anyway because size is u8 in config space.
> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
> > + if (size >= 8) {
>
> What does 8 mean here? Should be sizeof virtio_input_devids?
Yes, fixed.
> > +struct virtio_input_config {
> > + __u8 select;
> > + __u8 subsel;
> > + __u8 size;
> > + __u8 reserved;
> > + union {
> > + char string[128];
> > + __u8 bitmap[128];
>
> I note that neither string nor bitmap are used by
> driver. What are they in aid of?
Fixed. Just sloppy coding, the name should use u.string, the bitmap
u.bitmap, instead of just "u" (although for offsetof it doesn't make a
difference).
cheers,
Gerd
^ permalink raw reply
* Re: [PATCH v3] Add virtio-input driver.
From: Michael S. Tsirkin @ 2015-03-24 10:48 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, Dmitry Torokhov, open list:ABI/API, open list,
virtualization, David Herrmann
In-Reply-To: <1427192810.18768.4.camel@nilsson.home.kraxel.org>
On Tue, Mar 24, 2015 at 11:26:50AM +0100, Gerd Hoffmann wrote:
> Hi,
>
> > +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> > +{
> > + u32 mi, ma, re, fu, fl;
> > +
> > + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> > + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> > + input_abs_set_res(vi->idev, abs, re);
> > +}
>
> > +struct virtio_input_absinfo {
> > + __virtio32 min;
> > + __virtio32 max;
> > + __virtio32 fuzz;
> > + __virtio32 flat;
> > + __virtio32 res;
> > +};
>
> Damn, had sparse disabled for the test builds. [ Too bad there are way
> too many warnings on a full kernel build so having sparse enabled all
> the time doesn't fly. ]
>
> So this doesn't work either.
>
> Hmm, back to using "u32" in the virtio config structs?
>
> cheers,
> Gerd
>
You are right, I was confused. Currently everyone uses simple
__u32 and friends in config structs, under the understanding
that they are always accessed using virtio_cread and friends.
Maybe I'll look into adding more type safety, but the
issue is not virtio-input specific so, let's not
defer virtio input because of this.
Sorry about wasting your time on this.
--
MST
^ permalink raw reply
* Re: [PATCH v3] Add virtio-input driver.
From: Michael S. Tsirkin @ 2015-03-24 10:40 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, virtualization, David Herrmann, Dmitry Torokhov,
Rusty Russell, open list, open list:ABI/API
In-Reply-To: <1427192810.18768.4.camel@nilsson.home.kraxel.org>
On Tue, Mar 24, 2015 at 11:26:50AM +0100, Gerd Hoffmann wrote:
> Hi,
>
> > +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> > +{
> > + u32 mi, ma, re, fu, fl;
> > +
> > + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> > + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> > + input_abs_set_res(vi->idev, abs, re);
> > +}
>
> > +struct virtio_input_absinfo {
> > + __virtio32 min;
> > + __virtio32 max;
> > + __virtio32 fuzz;
> > + __virtio32 flat;
> > + __virtio32 res;
> > +};
>
> Damn, had sparse disabled for the test builds. [ Too bad there are way
> too many warnings on a full kernel build so having sparse enabled all
> the time doesn't fly. ]
>
> So this doesn't work either.
>
> Hmm, back to using "u32" in the virtio config structs?
>
> cheers,
> Gerd
>
Weird.
Let me try this and figure out what the issue is.
^ permalink raw reply
* Re: [PATCH v3] Add virtio-input driver.
From: Michael S. Tsirkin @ 2015-03-24 10:36 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev, virtualization, David Herrmann, Dmitry Torokhov,
Rusty Russell, open list, open list:ABI/API
In-Reply-To: <1427182321-19451-1-git-send-email-kraxel@redhat.com>
On Tue, Mar 24, 2015 at 08:32:01AM +0100, Gerd Hoffmann wrote:
> virtio-input is basically evdev-events-over-virtio, so this driver isn't
> much more than reading configuration from config space and forwarding
> incoming events to the linux input layer.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Looks pretty neat overall. I think I still see some
small issues, but it's getting there.
> ---
> MAINTAINERS | 6 +
> drivers/virtio/Kconfig | 10 ++
> drivers/virtio/Makefile | 1 +
> drivers/virtio/virtio_input.c | 313 ++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/Kbuild | 1 +
> include/uapi/linux/virtio_ids.h | 1 +
> include/uapi/linux/virtio_input.h | 76 +++++++++
> 7 files changed, 408 insertions(+)
> create mode 100644 drivers/virtio/virtio_input.c
> create mode 100644 include/uapi/linux/virtio_input.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 358eb01..6f233dd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10442,6 +10442,12 @@ S: Maintained
> F: drivers/vhost/
> F: include/uapi/linux/vhost.h
>
> +VIRTIO INPUT DRIVER
> +M: Gerd Hoffmann <kraxel@redhat.com>
> +S: Maintained
> +F: drivers/virtio/virtio_input.c
> +F: include/uapi/linux/virtio_input.h
> +
> VIA RHINE NETWORK DRIVER
> M: Roger Luethi <rl@hellgate.ch>
> S: Maintained
> diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> index b546da5..cab9f3f 100644
> --- a/drivers/virtio/Kconfig
> +++ b/drivers/virtio/Kconfig
> @@ -48,6 +48,16 @@ config VIRTIO_BALLOON
>
> If unsure, say M.
>
> +config VIRTIO_INPUT
> + tristate "Virtio input driver"
> + depends on VIRTIO
> + depends on INPUT
> + ---help---
> + This driver supports virtio input devices such as
> + keyboards, mice and tablets.
> +
> + If unsure, say M.
> +
> config VIRTIO_MMIO
> tristate "Platform bus driver for memory mapped virtio devices"
> depends on HAS_IOMEM
> diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> index d85565b..41e30e3 100644
> --- a/drivers/virtio/Makefile
> +++ b/drivers/virtio/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
> virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
> virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
> obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
> +obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
> diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
> new file mode 100644
> index 0000000..cf112b2
> --- /dev/null
> +++ b/drivers/virtio/virtio_input.c
> @@ -0,0 +1,313 @@
> +#include <linux/module.h>
> +#include <linux/virtio.h>
> +#include <linux/input.h>
> +
> +#include <uapi/linux/virtio_ids.h>
> +#include <uapi/linux/virtio_input.h>
> +
> +struct virtio_input {
> + struct virtio_device *vdev;
> + struct input_dev *idev;
> + char name[64];
> + char serial[64];
> + char phys[64];
> + struct virtqueue *evt, *sts;
> + struct virtio_input_event evts[64];
> + spinlock_t lock;
> +};
> +
> +static void virtinput_queue_evtbuf(struct virtio_input *vi,
> + struct virtio_input_event *evtbuf)
> +{
> + struct scatterlist sg[1];
> +
> + sg_init_one(sg, evtbuf, sizeof(*evtbuf));
> + virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
> +}
> +
> +static void virtinput_recv_events(struct virtqueue *vq)
> +{
> + struct virtio_input *vi = vq->vdev->priv;
> + struct virtio_input_event *event;
> + unsigned long flags;
> + unsigned int len;
> +
> + spin_lock_irqsave(&vi->lock, flags);
> + while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
> + input_event(vi->idev,
> + le16_to_cpu(event->type),
> + le16_to_cpu(event->code),
> + le32_to_cpu(event->value));
What happens if input layer gets an
unexpected event code or value?
Or does something prevent it?
> + virtinput_queue_evtbuf(vi, event);
> + }
> + virtqueue_kick(vq);
> + spin_unlock_irqrestore(&vi->lock, flags);
> +}
> +
> +static int virtinput_send_status(struct virtio_input *vi,
> + u16 type, u16 code, s32 value)
> +{
> + struct virtio_input_event *stsbuf;
> + struct scatterlist sg[1];
> + unsigned long flags;
> + int rc;
> +
> + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
> + if (!stsbuf)
> + return -ENOMEM;
> +
> + stsbuf->type = cpu_to_le16(type);
> + stsbuf->code = cpu_to_le16(code);
> + stsbuf->value = cpu_to_le32(value);
> + sg_init_one(sg, stsbuf, sizeof(*stsbuf));
> +
> + spin_lock_irqsave(&vi->lock, flags);
> + rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
> + virtqueue_kick(vi->sts);
> + spin_unlock_irqrestore(&vi->lock, flags);
> +
> + if (rc != 0)
> + kfree(stsbuf);
> + return rc;
This means that caller will get errors if it happens to call
send_status at a rate that's faster than host's consumption of them.
To me this looks wrong.
Poking at input layer, it seems to simply discard errors.
Is it always safe to discard status updates?
If yes, some kind of comment to clarify the logic would
make sense IMHO.
> +}
> +
> +static void virtinput_recv_status(struct virtqueue *vq)
> +{
> + struct virtio_input *vi = vq->vdev->priv;
> + struct virtio_input_event *stsbuf;
> + unsigned long flags;
> + unsigned int len;
> +
> + spin_lock_irqsave(&vi->lock, flags);
> + while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
> + kfree(stsbuf);
> + spin_unlock_irqrestore(&vi->lock, flags);
> +}
> +
> +static int virtinput_status(struct input_dev *idev, unsigned int type,
> + unsigned int code, int value)
> +{
> + struct virtio_input *vi = input_get_drvdata(idev);
> +
> + return virtinput_send_status(vi, type, code, value);
> +}
> +
> +static size_t virtinput_cfg_select(struct virtio_input *vi,
> + u8 select, u8 subsel)
> +{
> + u8 size;
> +
> + virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select);
> + virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel);
> + virtio_cread(vi->vdev, struct virtio_input_config, size, &size);
> + return size;
> +}
> +
> +static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
> + unsigned long *bits, unsigned int bitcount)
> +{
> + unsigned int bit;
> + size_t bytes;
> + u8 *virtio_bits;
> +
> + bytes = virtinput_cfg_select(vi, select, subsel);
> + if (!bytes)
> + return;
How about limiting bytes to sizeof struct virtio_input_config->u?
> + if (bitcount > bytes*8)
> + bitcount = bytes*8;
Space around * pls.
> +
> + /*
> + * Bitmap in virtio config space is a simple stream of bytes,
> + * with the first byte carrying bits 0-7, second bits 8-15 and
> + * so on.
> + */
> + virtio_bits = kzalloc(bytes, GFP_KERNEL);
> + if (!virtio_bits)
> + return;
> + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> + virtio_bits, bytes);
> + for (bit = 0; bit < bitcount; bit++) {
> + if (virtio_bits[bit / 8] & (1 << (bit % 8)))
> + __set_bit(bit, bits);
> + }
> + kfree(virtio_bits);
> +
> + if (select == VIRTIO_INPUT_CFG_EV_BITS)
> + __set_bit(subsel, vi->idev->evbit);
> +}
> +
> +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> +{
> + u32 mi, ma, re, fu, fl;
> +
> + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> + input_abs_set_res(vi->idev, abs, re);
> +}
> +
> +static int virtinput_init_vqs(struct virtio_input *vi)
> +{
> + struct virtqueue *vqs[2];
> + vq_callback_t *cbs[] = { virtinput_recv_events,
> + virtinput_recv_status };
> + static const char * names[] = { "events", "status" };
No space between * and names expected
> + int i, err, size;
> +
> + err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names);
> + if (err)
> + return err;
> + vi->evt = vqs[0];
> + vi->sts = vqs[1];
> +
> + size = virtqueue_get_vring_size(vi->evt);
> + if (size > ARRAY_SIZE(vi->evts))
> + size = ARRAY_SIZE(vi->evts);
> + for (i = 0; i < size; i++)
> + virtinput_queue_evtbuf(vi, &vi->evts[i]);
> + virtqueue_kick(vi->evt);
> +
> + return 0;
> +}
> +
> +static int virtinput_probe(struct virtio_device *vdev)
> +{
> + struct virtio_input *vi;
> + size_t size;
> + int abs, err;
> +
> + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
> + return -ENODEV;
> +
> + vi = kzalloc(sizeof(*vi), GFP_KERNEL);
> + if (!vi)
> + return -ENOMEM;
> +
> + vdev->priv = vi;
> + vi->vdev = vdev;
> + spin_lock_init(&vi->lock);
> +
> + err = virtinput_init_vqs(vi);
> + if (err)
> + goto err_init_vq;
> +
> + vi->idev = input_allocate_device();
> + if (!vi->idev) {
> + err = -ENOMEM;
> + goto err_input_alloc;
> + }
> + input_set_drvdata(vi->idev, vi);
> +
> + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
> + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> + vi->name, min(size, sizeof(vi->name)));
> + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);
> + virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
> + vi->serial, min(size, sizeof(vi->serial)));
> + snprintf(vi->phys, sizeof(vi->phys),
> + "virtio%d/input0", vdev->index);
> + vi->idev->name = vi->name;
> + vi->idev->phys = vi->phys;
> + vi->idev->uniq = vi->serial;
> +
> + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
> + if (size >= 8) {
What does 8 mean here? Should be sizeof virtio_input_devids?
> + virtio_cread(vi->vdev, struct virtio_input_config,
> + u.ids.bustype, &vi->idev->id.bustype);
> + virtio_cread(vi->vdev, struct virtio_input_config,
> + u.ids.vendor, &vi->idev->id.vendor);
> + virtio_cread(vi->vdev, struct virtio_input_config,
> + u.ids.product, &vi->idev->id.product);
> + virtio_cread(vi->vdev, struct virtio_input_config,
> + u.ids.version, &vi->idev->id.version);
> + } else {
> + vi->idev->id.bustype = BUS_VIRTUAL;
> + }
> +
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,
> + vi->idev->propbit, INPUT_PROP_CNT);
> + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);
> + if (size)
> + __set_bit(EV_REP, vi->idev->evbit);
> +
> + vi->idev->dev.parent = &vdev->dev;
> + vi->idev->event = virtinput_status;
> +
> + /* device -> kernel */
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,
> + vi->idev->keybit, KEY_CNT);
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,
> + vi->idev->relbit, REL_CNT);
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,
> + vi->idev->absbit, ABS_CNT);
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,
> + vi->idev->mscbit, MSC_CNT);
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,
> + vi->idev->swbit, SW_CNT);
> +
> + /* kernel -> device */
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,
> + vi->idev->ledbit, LED_CNT);
> + virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
> + vi->idev->sndbit, SND_CNT);
> +
> + if (test_bit(EV_ABS, vi->idev->evbit)) {
> + for (abs = 0; abs < ABS_CNT; abs++) {
> + if (!test_bit(abs, vi->idev->absbit))
> + continue;
> + virtinput_cfg_abs(vi, abs);
> + }
> + }
> + virtio_device_ready(vdev);
> +
At this point you can already get interrupts.
This will cause events to be forwarded.
I'm guessing this is ok since you called
input_allocate_device, but worth checking,
and maybe adding a comment.
> + err = input_register_device(vi->idev);
> + if (err)
> + goto err_input_register;
> +
> + return 0;
> +
> +err_input_register:
> + input_free_device(vi->idev);
At this point you can already get interrupts
since you called virtio_device_ready, and
getting events from a freed device likely won't
DTRT.
> +err_input_alloc:
> + vdev->config->del_vqs(vdev);
> +err_init_vq:
> + kfree(vi);
> + return err;
> +}
> +
> +static void virtinput_remove(struct virtio_device *vdev)
> +{
> + struct virtio_input *vi = vdev->priv;
> +
> + input_unregister_device(vi->idev);
same thing here, you might get an event at this point.
You need to somehow block new events
being sent to device while keeping
device around.
Since you already do everything under a spinlock,
it's probably easiest to add a flag discarding
recv events. You can then check it in virtinput_recv_events
before calling input_event.
> + vdev->config->del_vqs(vdev);
> + kfree(vi);
> +}
> +
> +static unsigned int features[] = {
> +};
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static struct virtio_driver virtio_input_driver = {
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .id_table = id_table,
> + .probe = virtinput_probe,
> + .remove = virtinput_remove,
I note this driver doesn't seem to handle hybernation,
that's probably a bug?
> +};
> +
> +module_virtio_driver(virtio_input_driver);
> +MODULE_DEVICE_TABLE(virtio, id_table);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Virtio input device driver");
> +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
> diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
> index 68ceb97..04b829e 100644
> --- a/include/uapi/linux/Kbuild
> +++ b/include/uapi/linux/Kbuild
> @@ -430,6 +430,7 @@ header-y += virtio_blk.h
> header-y += virtio_config.h
> header-y += virtio_console.h
> header-y += virtio_ids.h
> +header-y += virtio_input.h
> header-y += virtio_net.h
> header-y += virtio_pci.h
> header-y += virtio_ring.h
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 284fc3a..5f60aa4 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -39,5 +39,6 @@
> #define VIRTIO_ID_9P 9 /* 9p virtio console */
> #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
> #define VIRTIO_ID_CAIF 12 /* Virtio caif */
> +#define VIRTIO_ID_INPUT 18 /* virtio input */
>
> #endif /* _LINUX_VIRTIO_IDS_H */
> diff --git a/include/uapi/linux/virtio_input.h b/include/uapi/linux/virtio_input.h
> new file mode 100644
> index 0000000..7fceabd
> --- /dev/null
> +++ b/include/uapi/linux/virtio_input.h
> @@ -0,0 +1,76 @@
> +#ifndef _LINUX_VIRTIO_INPUT_H
> +#define _LINUX_VIRTIO_INPUT_H
> +/* This header is BSD licensed so anyone can use the definitions to implement
> + * compatible drivers/servers.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + * 3. Neither the name of IBM nor the names of its contributors
> + * may be used to endorse or promote products derived from this software
> + * without specific prior written permission.
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
> + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> + * SUCH DAMAGE. */
> +#include <linux/virtio_ids.h>
> +#include <linux/virtio_config.h>
> +
> +enum virtio_input_config_select {
> + VIRTIO_INPUT_CFG_UNSET = 0x00,
> + VIRTIO_INPUT_CFG_ID_NAME = 0x01,
> + VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
> + VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
> + VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
> + VIRTIO_INPUT_CFG_EV_BITS = 0x11,
> + VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
> +};
> +
> +struct virtio_input_absinfo {
> + __virtio32 min;
> + __virtio32 max;
> + __virtio32 fuzz;
> + __virtio32 flat;
> + __virtio32 res;
> +};
> +
> +struct virtio_input_devids {
> + __virtio16 bustype;
> + __virtio16 vendor;
> + __virtio16 product;
> + __virtio16 version;
> +};
> +
this padding bt two spaces looks weird.
> +struct virtio_input_config {
> + __u8 select;
> + __u8 subsel;
> + __u8 size;
> + __u8 reserved;
> + union {
> + char string[128];
> + __u8 bitmap[128];
I note that neither string nor bitmap are used by
driver. What are they in aid of?
> + struct virtio_input_absinfo abs;
> + struct virtio_input_devids ids;
> + } u;
> +};
> +
> +struct virtio_input_event {
> + __le16 type;
> + __le16 code;
> + __le32 value;
> +};
> +
> +#endif /* _LINUX_VIRTIO_INPUT_H */
> --
> 1.8.3.1
^ permalink raw reply
* Re: [PATCH v3] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-24 10:26 UTC (permalink / raw)
To: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b
Cc: virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
mst-H+wXaHxf7aLQT0dZR+AlfA, David Herrmann, Dmitry Torokhov,
Rusty Russell, open list, open list:ABI/API
In-Reply-To: <1427182321-19451-1-git-send-email-kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Hi,
> +static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
> +{
> + u32 mi, ma, re, fu, fl;
> +
> + virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.res, &re);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
> + input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
> + input_abs_set_res(vi->idev, abs, re);
> +}
> +struct virtio_input_absinfo {
> + __virtio32 min;
> + __virtio32 max;
> + __virtio32 fuzz;
> + __virtio32 flat;
> + __virtio32 res;
> +};
Damn, had sparse disabled for the test builds. [ Too bad there are way
too many warnings on a full kernel build so having sparse enabled all
the time doesn't fly. ]
So this doesn't work either.
Hmm, back to using "u32" in the virtio config structs?
cheers,
Gerd
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox