* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Thierry Reding @ 2015-05-22 10:00 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Kevin Hilman, Scot Doyle, Tomi Valkeinen, Michael Kerrisk,
Jiri Slaby, Jean-Christophe Plagniol-Villard, Pavel Machek,
Geert Uytterhoeven, lkml, linux-fbdev, linux-man, linux-api,
Tyler Baker, Olof Johansson, Daniel Stone, Arnd Bergmann
In-Reply-To: <20150522020031.GA3516@kroah.com>
[-- Attachment #1: Type: text/plain, Size: 4661 bytes --]
On Thu, May 21, 2015 at 07:00:31PM -0700, Greg Kroah-Hartman wrote:
> On Thu, May 21, 2015 at 10:00:50AM +0200, Thierry Reding wrote:
> > On Wed, May 20, 2015 at 09:26:38PM -0700, Greg Kroah-Hartman wrote:
> > > On Wed, May 20, 2015 at 02:36:17PM +0200, Thierry Reding wrote:
> > > > On Tue, May 19, 2015 at 04:41:12PM -0700, Greg Kroah-Hartman wrote:
> > > > > On Tue, May 19, 2015 at 11:52:29PM +0200, Thierry Reding wrote:
> > > > > > On Tue, May 19, 2015 at 02:45:19PM -0700, Kevin Hilman wrote:
> > > > > > > On Tue, May 19, 2015 at 2:40 PM, Thierry Reding
> > > > > > > <thierry.reding@gmail.com> wrote:
> > > > > > > > On Tue, May 19, 2015 at 02:15:41PM -0700, Kevin Hilman wrote:
> > > > > > > >> On Thu, Mar 26, 2015 at 6:56 AM, Scot Doyle <lkml14@scotdoyle.com> wrote:
> > > > > > > >> > vt now provides a cursor blink interval via vc_data. Use this
> > > > > > > >> > interval instead of the currently hardcoded 200 msecs. Store it in
> > > > > > > >> > fbcon_ops to avoid locking the console in cursor_timer_handler().
> > > > > > > >> >
> > > > > > > >> > Signed-off-by: Scot Doyle <lkml14@scotdoyle.com>
> > > > > > > >> > Acked-by: Pavel Machek <pavel@ucw.cz>
> > > > > > > >>
> > > > > > > >> This patch hit next-20150519 in the form of commit 27a4c827c34a
> > > > > > > >> (fbcon: use the cursor blink interval provided by vt) and has caused
> > > > > > > >> boot failure on a handful of ARM platforms when booting a MMC root
> > > > > > > >> filesystem. This error was spotted by the kernelci.org bot on
> > > > > > > >> exynos5800-peach-pi[1] and Thierry and Daniel (Cc'd) have seen it on
> > > > > > > >> some tegra platforms too.
> > > > > > > >>
> > > > > > > >> Thierry spotted this commit as a potential cause, and both Daniel and
> > > > > > > >> I have reverted and boot tested on exynos5 and tegra respectively and
> > > > > > > >> the boot panics disappear.
> > > > > > > >
> > > > > > > > FWIW, if I apply the below on top of next-20150519 things seem to be
> > > > > > > > back to normal as well:
> > > > > > > >
> > > > > > > > diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
> > > > > > > > index 05b1d1a71ef9..658c34bb9076 100644
> > > > > > > > --- a/drivers/video/console/fbcon.c
> > > > > > > > +++ b/drivers/video/console/fbcon.c
> > > > > > > > @@ -1310,8 +1310,9 @@ static void fbcon_cursor(struct vc_data *vc, int mode)
> > > > > > > > return;
> > > > > > > >
> > > > > > > > ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
> > > > > > > > - fbcon_del_cursor_timer(info);
> > > > > > > > - if (!(vc->vc_cursor_type & 0x10))
> > > > > > > > + if (vc->vc_cursor_type & 0x10)
> > > > > > > > + fbcon_del_cursor_timer(info);
> > > > > > > > + else
> > > > > > > > fbcon_add_cursor_timer(info);
> > > > > > > >
> > > > > > > > ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
> > > > > > >
> > > > > > > Applying this on next-20150519 makes my exynos board happily boot again as well.
> > > > > > >
> > > > > > > Tested-by: Kevin Hilman <khilman@linaro.org>
> > > > > >
> > > > > > Excellent. Greg, Scot, any opinions on whether or not this is the right
> > > > > > thing to do? It restores a bit that looks suspiciously like it snuck in
> > > > > > in the original (at least it isn't documented in the commit message).
> > > > > >
> > > > > > Greg, feel free to squash this in if everybody agrees this is good to
> > > > > > go. If you prefer a patch on top let me know and I'll come up with a
> > > > > > proper commit message.
> > > > >
> > > > > Please send a real patch and I'll apply it on top, as I can't rebase my
> > > > > public tree.
> > > >
> > > > Attached.
> > >
> > > Ugh, no, please resend it as a stand-alone patch, I can't easily apply
> > > attachments.
> >
> > Really? Your MUA can't dissect multipart messages? Anyway, sent
> > separately for your convenience.
>
> "git am" doesn't do that. I apply patches in huge chunks of mbox files.
What I frequently end up doing is apply patches straight from mutt by
piping the mail or an attached patch to git am. I guess I had expected
that you'd have something similar to simplify applying patches.
> Remember, if I have to hand-edit, or do something special with your
> patch, I will not do it, you need to do it correctly to make
> maintainer's lives easier, not harder, given that maintainers are the
> limited resouce, not developers.
I understand. I'll make a mental note to never send you patches as
attachment again.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Philipp Zabel @ 2015-05-22 10:07 UTC (permalink / raw)
To: Maxime Ripard
Cc: Mark Rutland, linux-doc@vger.kernel.org, Linus Walleij,
Will Deacon, Stefan Agner, Nikolay Borisov, Peter Meerwald,
linux-api@vger.kernel.org, Lee Jones, Mauro Carvalho Chehab,
Linux-Arch, Daniel Thompson, Russell King, Pawel Moll,
Jonathan Corbet, Jiri Slaby, Daniel Lezcano, Chanwoo Choi,
Andy Shevchenko, Antti Palosaari, Geert Uytterhoeven
In-Reply-To: <20150522091822.GF8557@lukather>
Am Freitag, den 22.05.2015, 11:18 +0200 schrieb Maxime Ripard:
> On Fri, May 22, 2015 at 11:06:28AM +0200, Philipp Zabel wrote:
> > > In the probe function, it would check the number of reg resources.
> > > If a single resource is passed, it would take it, else it would look
> > > the one named "reset".
> > > The driver and bindings would be the same for the two families, and
> > > the bindings would be backward compatible with sunxi ones.
> > >
> > > Philip, Arnd, what do you think?
> >
> > I'm not a fan of describing the register layout in the device tree as
> > detailed as the sunxi bindings do. I'd prefer the reg property to
> > describe the device's register address space with one entry per
> > contiguous block of registers.
>
> That's exactly what we do.
Sorry, what I mean is 'as detailed as reusing the sunxi bindings for
stm32xx here would do'. I don't know enough about the Allwinner register
layouts to form an opinion.
The STM32F427xx/STM32F429xx manual, Table 13. "STM32F427xx and
STM32F429xx register boundary addresses" contains this entry:
Bus Boundary address Peripheral
AHB1 0x40023800-0x400238bf RCC
And that's how I'd expect it to be described by the device tree:
rcc: rcc@40023800 {
compatible = "st,stm32-rcc";
reg = <0x40023800 0xc0>;
};
Instead of "reg = <0x40023810 0x20>" for the resets. Where in the
address range the reset, clock gate and clock configuration registers
reside could be derived from the compatible value.
> > Unifying the mostly identical drivers is a good idea though, and reusing
> > preexisting bindings is better than inventing new ones. I favor the
> > socfpga binding, but I still like the sunxi bindings and this proposal
> > better than encoding the register offset in the reset index.
>
> I don't really get the difference between the socfpga and our bindings
> actually. Would you mind to explain a bit further what you don't like
> about it ?
The socfpga driver currently hardcodes the reset register offset (0x10)
and number of banks (4), the sunxi driver has no register offset (0x0)
and derives the number of banks from the resource size.
I'd store the internal register offsets and number of banks in the
driver, together with the compatible string.
regards
Philipp
^ permalink raw reply
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Arnd Bergmann @ 2015-05-22 10:33 UTC (permalink / raw)
To: Thierry Reding
Cc: Greg Kroah-Hartman, Kevin Hilman, Scot Doyle, Tomi Valkeinen,
Michael Kerrisk, Jiri Slaby, Jean-Christophe Plagniol-Villard,
Pavel Machek, Geert Uytterhoeven, lkml,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker, Olof Johansson,
Daniel Stone
In-Reply-To: <20150522100002.GB16507@ulmo>
On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
>
> > Remember, if I have to hand-edit, or do something special with your
> > patch, I will not do it, you need to do it correctly to make
> > maintainer's lives easier, not harder, given that maintainers are the
> > limited resouce, not developers.
>
> I understand. I'll make a mental note to never send you patches as
> attachment again.
>
Better make that a general rule. My workflow is different from Greg's
but also doesn't cope well with attachments. A lot of people in turn
have problems quoting from an attachment when replying to the patch,
which happens to work for me.
Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 01/11] regmap: Introduce regmap_get_max_register.
From: Mark Brown @ 2015-05-22 11:18 UTC (permalink / raw)
To: Srinivas Kandagatla
Cc: linux-arm-kernel, Maxime Ripard, Rob Herring, Kumar Gala, s.hauer,
Greg Kroah-Hartman, linux-api, linux-kernel, devicetree,
linux-arm-msm, arnd, sboyd, pantelis.antoniou, mporter
In-Reply-To: <1432226563-8689-1-git-send-email-srinivas.kandagatla@linaro.org>
[-- Attachment #1: Type: text/plain, Size: 370 bytes --]
On Thu, May 21, 2015 at 05:42:43PM +0100, Srinivas Kandagatla wrote:
> This patch introduces regmap_get_max_register() function which would be
> used by the infrastructures like nvmem framework built on top of
> regmap.
Applied, thanks. If the rest of the framework is ready to get merged
somewhere before it reaches Linus' tree let me know and we can handle it
then.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* Re: [PATCH v5 02/11] regmap: Introduce regmap_get_reg_stride.
From: Mark Brown @ 2015-05-22 11:19 UTC (permalink / raw)
To: Srinivas Kandagatla
Cc: linux-arm-kernel, Maxime Ripard, Rob Herring, Kumar Gala, s.hauer,
Greg Kroah-Hartman, linux-api, linux-kernel, devicetree,
linux-arm-msm, arnd, sboyd, pantelis.antoniou, mporter
In-Reply-To: <1432226574-8733-1-git-send-email-srinivas.kandagatla@linaro.org>
[-- Attachment #1: Type: text/plain, Size: 329 bytes --]
On Thu, May 21, 2015 at 05:42:54PM +0100, Srinivas Kandagatla wrote:
> This patch introduces regmap_get_reg_stride() function which would
> be used by the infrastructures like nvmem framework built on top of
> regmap. Mostly this function would be used for sanity checks on inputs
> within such infrastructure.
Applied, thanks.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Thierry Reding @ 2015-05-22 11:49 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Greg Kroah-Hartman, Kevin Hilman, Scot Doyle, Tomi Valkeinen,
Michael Kerrisk, Jiri Slaby, Jean-Christophe Plagniol-Villard,
Pavel Machek, Geert Uytterhoeven, lkml,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker, Olof Johansson,
Daniel Stone
In-Reply-To: <3661627.JqpS87pBks@wuerfel>
[-- Attachment #1: Type: text/plain, Size: 1075 bytes --]
On Fri, May 22, 2015 at 12:33:42PM +0200, Arnd Bergmann wrote:
> On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
> >
> > > Remember, if I have to hand-edit, or do something special with your
> > > patch, I will not do it, you need to do it correctly to make
> > > maintainer's lives easier, not harder, given that maintainers are the
> > > limited resouce, not developers.
> >
> > I understand. I'll make a mental note to never send you patches as
> > attachment again.
> >
>
> Better make that a general rule. My workflow is different from Greg's
> but also doesn't cope well with attachments. A lot of people in turn
> have problems quoting from an attachment when replying to the patch,
> which happens to work for me.
Okay. Any hints on how to simplify sending out such patches with the
same list of recipients? I find it very annoying to have to manually
copy each recipient into the git send-email command-line, but I don't
know of a better way to do it. Replying to an email from the MUA will
at least do that automatically.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Maxime Coquelin @ 2015-05-22 12:32 UTC (permalink / raw)
To: Philipp Zabel
Cc: Mark Rutland, linux-doc@vger.kernel.org, Linus Walleij,
Will Deacon, Stefan Agner, Nikolay Borisov, Peter Meerwald,
linux-api@vger.kernel.org, Lee Jones, Mauro Carvalho Chehab,
Linux-Arch, Daniel Thompson, Russell King, Pawel Moll,
Jonathan Corbet, Jiri Slaby, Daniel Lezcano, Nicolae Rosia,
Chanwoo Choi, Andy Shevchenko, Antti Palosaari,
Geert Uytterhoeven
In-Reply-To: <1432289231.3929.60.camel@pengutronix.de>
2015-05-22 12:07 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
> The STM32F427xx/STM32F429xx manual, Table 13. "STM32F427xx and
> STM32F429xx register boundary addresses" contains this entry:
> Bus Boundary address Peripheral
> AHB1 0x40023800-0x400238bf RCC
>
> And that's how I'd expect it to be described by the device tree:
>
> rcc: rcc@40023800 {
> compatible = "st,stm32-rcc";
> reg = <0x40023800 0xc0>;
> };
>
Doing that, since this register bank contains both reset and clock
registers, the reset device cannot get the IO resource at probe time
because the clock driver has already reserved it.
Daniel, who has started to work on the clock driver is facing this issue.
This is why I proposed this binding for "reg" property.
We could think of creating a MFD driver, but the problem is that clock
need to be intialized before a MFD device can be probed.
Maybe there is a way to have you binding working properly, but I
haven't found one for now.
Regards,
Maxime
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Daniel Thompson @ 2015-05-22 12:43 UTC (permalink / raw)
To: Maxime Coquelin, Philipp Zabel
Cc: Maxime Ripard, Arnd Bergmann, Uwe Kleine-König,
Andreas Färber, Geert Uytterhoeven, Rob Herring,
Linus Walleij, Stefan Agner, Peter Meerwald, Paul Bolle,
Peter Hurley, Andy Shevchenko, Chanwoo Choi, Russell King,
Daniel Lezcano, Joe Perches, Vladimir Zapolskiy, Lee Jones,
Jonathan Corbet, Pawel Moll, Mark Rutland
In-Reply-To: <CALszF6CQ1_HQXJ8=vgytvVo2t4_ue5_nsE06HPyHMzSLrntqKg@mail.gmail.com>
On 22/05/15 13:32, Maxime Coquelin wrote:
> 2015-05-22 12:07 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>> And that's how I'd expect it to be described by the device tree:
>>
>> rcc: rcc@40023800 {
>> compatible = "st,stm32-rcc";
>> reg = <0x40023800 0xc0>;
>> };
>>
>
> Doing that, since this register bank contains both reset and clock
> registers, the reset device cannot get the IO resource at probe time
> because the clock driver has already reserved it.
> Daniel, who has started to work on the clock driver is facing this issue.
> This is why I proposed this binding for "reg" property.
Temporarily I've kept things working for me by avoiding
of_io_request_and_map() in the clock driver (I am using raw of_iomap()
instead).
I view this as a hack rather than a solution!
Note that, with or without the hack, I do hope to be able to post the
clock driver this weekend. I am acutely aware that at the moment we are
discussing code I haven't posted yet.
> We could think of creating a MFD driver, but the problem is that clock
> need to be intialized before a MFD device can be probed.
>
> Maybe there is a way to have you binding working properly, but I
> haven't found one for now.
I guess a driver doesn't *have* to reserve all the register space
described in DT. The driver could replace of_io_request_and_map() with
lower level calls.
Thus if we wanted to keep this detail out of DT then I guess the two
drivers could simply make an agreement not to request registers they do
not use.
^ permalink raw reply
* [PATCH] fs, proc: introduce CONFIG_PROC_CHILDREN
From: Iago López Galeiras @ 2015-05-22 12:47 UTC (permalink / raw)
To: Cyrill Gorcunov
Cc: Iago López Galeiras, Oleg Nesterov, Kees Cook,
Pavel Emelyanov, Serge Hallyn, KAMEZAWA Hiroyuki, linux-api,
Alexander Viro, linux-fsdevel, Andrew Morton, Andy Lutomirski,
Alban Crequy
In-Reply-To: <20150521135703.22af370fdd0799101a6a1b91@linux-foundation.org>
commit 818411616baf ("fs, proc: introduce
/proc/<pid>/task/<tid>/children entry") introduced the children entry
for checkpoint restore and the file is only available on kernels
configured with CONFIG_EXPERT and CONFIG_CHECKPOINT_RESTORE.
This is available in most distributions (Fedora, Debian, Ubuntu, CoreOS)
because they usually enable CONFIG_EXPERT and CONFIG_CHECKPOINT_RESTORE.
But Arch does not enable CONFIG_EXPERT or CONFIG_CHECKPOINT_RESTORE.
However, the children proc file is useful outside of checkpoint restore.
I would like to use it in rkt. The rkt process exec() another program it
does not control, and that other program will fork()+exec() a child
process. I would like to find the pid of the child process from an
external tool without iterating in /proc over all processes to find
which one has a parent pid equal to rkt.
This commit introduces CONFIG_PROC_CHILDREN and makes
CONFIG_CHECKPOINT_RESTORE depend on it. This allows enabling
/proc/<pid>/task/<tid>/children without needing to enable
CONFIG_CHECKPOINT_RESTORE and CONFIG_EXPERT.
Alban tested that /proc/<pid>/task/<tid>/children is present when the
kernel is configured with CONFIG_PROC_CHILDREN=y but without
CONFIG_CHECKPOINT_RESTORE
Signed-off-by: Iago López Galeiras <iago@endocode.com>
Cc: Alban Crequy <alban@endocode.com>
Tested-by: Alban Crequy <alban@endocode.com>
---
fs/proc/Kconfig | 4 ++++
fs/proc/array.c | 4 ++--
fs/proc/base.c | 2 +-
init/Kconfig | 1 +
4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig
index 2183fcf..d751fcb 100644
--- a/fs/proc/Kconfig
+++ b/fs/proc/Kconfig
@@ -71,3 +71,7 @@ config PROC_PAGE_MONITOR
/proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap,
/proc/kpagecount, and /proc/kpageflags. Disabling these
interfaces will reduce the size of the kernel by approximately 4kb.
+
+config PROC_CHILDREN
+ bool "Include /proc/<pid>/task/<tid>/children file"
+ default n
diff --git a/fs/proc/array.c b/fs/proc/array.c
index fd02a9e..ddc4b2c 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -569,7 +569,7 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
return 0;
}
-#ifdef CONFIG_CHECKPOINT_RESTORE
+#ifdef CONFIG_PROC_CHILDREN
static struct pid *
get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)
{
@@ -692,4 +692,4 @@ const struct file_operations proc_tid_children_operations = {
.llseek = seq_lseek,
.release = children_seq_release,
};
-#endif /* CONFIG_CHECKPOINT_RESTORE */
+#endif /* CONFIG_PROC_CHILDREN */
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 093ca14..5477a4a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2922,7 +2922,7 @@ static const struct pid_entry tid_base_stuff[] = {
ONE("stat", S_IRUGO, proc_tid_stat),
ONE("statm", S_IRUGO, proc_pid_statm),
REG("maps", S_IRUGO, proc_tid_maps_operations),
-#ifdef CONFIG_CHECKPOINT_RESTORE
+#ifdef CONFIG_PROC_CHILDREN
REG("children", S_IRUGO, proc_tid_children_operations),
#endif
#ifdef CONFIG_NUMA
diff --git a/init/Kconfig b/init/Kconfig
index dc24dec..daa7941 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1145,6 +1145,7 @@ endif # CGROUPS
config CHECKPOINT_RESTORE
bool "Checkpoint/restore support" if EXPERT
+ depends on PROC_CHILDREN
default n
help
Enables additional kernel features in a sake of checkpoint/restore.
--
2.4.1
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH] [RFC] fs, proc: don't guard /proc/<pid>/task/<tid>/children on CONFIG_CHECKPOINT_RESTORE
From: Alban Crequy @ 2015-05-22 12:49 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Alban Crequy, Cyrill Gorcunov, Oleg Nesterov, Kees Cook,
Pavel Emelyanov, Serge Hallyn, KAMEZAWA Hiroyuki, Linux API,
Alexander Viro, Linux FS Devel, Andrew Morton,
Iago Lopez Galeiras
In-Reply-To: <CALCETrVbaKnqj7qLBr+ufirPHfdjAYzo6_e362N0muVGnH7HTg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Thu, May 21, 2015 at 11:47 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
> On Thu, May 21, 2015 at 3:30 AM, Alban Crequy <alban.crequy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> From: Alban Crequy <alban-973cpzSjLbNWk0Htik3J/w@public.gmane.org>
>>
>> commit 818411616baf ("fs, proc: introduce
>> /proc/<pid>/task/<tid>/children entry") introduced the children entry
>> for checkpoint restore and the file is only available on kernels
>> configured with CONFIG_EXPERT and CONFIG_CHECKPOINT_RESTORE.
>>
>> This is available in most distributions (Fedora, Debian, Ubuntu, CoreOS)
>> because they usually enable CONFIG_EXPERT and CONFIG_CHECKPOINT_RESTORE.
>> But Arch does not enable CONFIG_EXPERT or CONFIG_CHECKPOINT_RESTORE.
>>
>> However, the children proc file is useful outside of checkpoint restore.
>> I would like to use it in rkt. The rkt process exec() another program it
>> does not control, and that other program will fork()+exec() a child
>> process. I would like to find the pid of the child process from an
>> external tool without iterating in /proc over all processes to find
>> which one has a parent pid equal to rkt.
>>
>> Since the children proc file is useful outside of checkpoint-restore,
>> I am removing the guard on CONFIG_CHECKPOINT_RESTORE.
>
> I sent an essentially identical patch a couple years ago, and it got
> some interesting comments:
>
> http://lkml.kernel.org/g/0e00e9073855c02a382d49ba1ede9c4fda3451b7.1372189875.git.luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org
Thanks for the pointer, I didn't see it.
> See also:
>
> http://lkml.kernel.org/g/5f9a6b3ab75b12f2c5ba61ea1f6f3b08e9952b55.1372280661.git.luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org
Your paragraph seems good to have in the documentation but I am also
not qualified to comment on the behavior.
^ permalink raw reply
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Silvan Jegen @ 2015-05-22 13:07 UTC (permalink / raw)
To: Thierry Reding
Cc: Arnd Bergmann, Greg Kroah-Hartman, Kevin Hilman, Scot Doyle,
Tomi Valkeinen, Michael Kerrisk, Jiri Slaby,
Jean-Christophe Plagniol-Villard, Pavel Machek,
Geert Uytterhoeven, lkml, linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man, linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker,
Olof Johansson, Daniel Stone
In-Reply-To: <20150522114957.GF16507@ulmo>
On Fri, May 22, 2015 at 1:49 PM, Thierry Reding
<thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Fri, May 22, 2015 at 12:33:42PM +0200, Arnd Bergmann wrote:
>> On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
>> >
>> > > Remember, if I have to hand-edit, or do something special with your
>> > > patch, I will not do it, you need to do it correctly to make
>> > > maintainer's lives easier, not harder, given that maintainers are the
>> > > limited resouce, not developers.
>> >
>> > I understand. I'll make a mental note to never send you patches as
>> > attachment again.
>> >
>>
>> Better make that a general rule. My workflow is different from Greg's
>> but also doesn't cope well with attachments. A lot of people in turn
>> have problems quoting from an attachment when replying to the patch,
>> which happens to work for me.
>
> Okay. Any hints on how to simplify sending out such patches with the
> same list of recipients? I find it very annoying to have to manually
> copy each recipient into the git send-email command-line, but I don't
> know of a better way to do it. Replying to an email from the MUA will
> at least do that automatically.
You can write your recipients to the "To:" header of the patch you
generated using git format-patch. git send-email will pick all
recipients thus specified up automatically. In later iterations you
can just copy the "To:" line.
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Andreas Färber @ 2015-05-22 13:09 UTC (permalink / raw)
To: Maxime Coquelin
Cc: Mark Rutland, linux-doc@vger.kernel.org, Linus Walleij,
Will Deacon, Stefan Agner, Nikolay Borisov, Peter Meerwald,
linux-api@vger.kernel.org, Lee Jones, Mauro Carvalho Chehab,
Linux-Arch, Daniel Thompson, Russell King, Pawel Moll,
Jonathan Corbet, Jiri Slaby, Daniel Lezcano, Nicolae Rosia,
Chanwoo Choi, Andy Shevchenko, Antti Palosaari,
Geert Uytterhoeven
In-Reply-To: <CALszF6CQ1_HQXJ8=vgytvVo2t4_ue5_nsE06HPyHMzSLrntqKg@mail.gmail.com>
Am 22.05.2015 um 14:32 schrieb Maxime Coquelin:
> 2015-05-22 12:07 GMT+02:00 Philipp Zabel <p.zabel@pengutronix.de>:
>> The STM32F427xx/STM32F429xx manual, Table 13. "STM32F427xx and
>> STM32F429xx register boundary addresses" contains this entry:
>> Bus Boundary address Peripheral
>> AHB1 0x40023800-0x400238bf RCC
>>
>> And that's how I'd expect it to be described by the device tree:
>>
>> rcc: rcc@40023800 {
>> compatible = "st,stm32-rcc";
>> reg = <0x40023800 0xc0>;
>> };
>>
>
> Doing that, since this register bank contains both reset and clock
> registers, the reset device cannot get the IO resource at probe time
> because the clock driver has already reserved it.
> Daniel, who has started to work on the clock driver is facing this issue.
> This is why I proposed this binding for "reg" property.
As you should know, I did have an RCC clk driver, and there is no such
issue. The two drivers use different mechanisms for initialization. And
I'm pretty sure that I've already remarked that on the list, too.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Dilip Upmanyu, Graham Norton; HRB
21284 (AG Nürnberg)
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v3 4/4] Add virtio gpu driver.
From: Gerd Hoffmann @ 2015-05-22 13:11 UTC (permalink / raw)
To: dri-devel
Cc: mst, David Airlie, open list:ABI/API, open list,
open list:VIRTIO CORE, NET..., airlied, Dave Airlie
In-Reply-To: <1432300312-24792-1-git-send-email-kraxel@redhat.com>
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 | 14 +
drivers/gpu/drm/virtio/Makefile | 11 +
drivers/gpu/drm/virtio/virtgpu_debugfs.c | 64 ++++
drivers/gpu/drm/virtio/virtgpu_display.c | 485 ++++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_drm_bus.c | 91 +++++
drivers/gpu/drm/virtio/virtgpu_drv.c | 136 +++++++
drivers/gpu/drm/virtio/virtgpu_drv.h | 350 ++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_fb.c | 431 ++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_fence.c | 119 ++++++
drivers/gpu/drm/virtio/virtgpu_gem.c | 140 +++++++
drivers/gpu/drm/virtio/virtgpu_kms.c | 164 +++++++++
drivers/gpu/drm/virtio/virtgpu_object.c | 170 +++++++++
drivers/gpu/drm/virtio/virtgpu_plane.c | 120 ++++++
drivers/gpu/drm/virtio/virtgpu_ttm.c | 469 +++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_vq.c | 614 +++++++++++++++++++++++++++++++
include/drm/drmP.h | 1 +
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/virtio_gpu.h | 204 ++++++++++
include/uapi/linux/virtio_ids.h | 1 +
21 files changed, 3588 insertions(+)
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_plane.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 47f2ce8..d4b6545 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -206,6 +206,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 7d4944e..dbbc101 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -63,6 +63,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..9983ead
--- /dev/null
+++ b/drivers/gpu/drm/virtio/Kconfig
@@ -0,0 +1,14 @@
+config DRM_VIRTIO_GPU
+ tristate "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
+ This is the virtual GPU driver for virtio. It can be used with
+ QEMU based VMMs (like KVM or Xen).
+
+ If unsure say M.
diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
new file mode 100644
index 0000000..2ee1602
--- /dev/null
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -0,0 +1,11 @@
+#
+# 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 virtgpu_plane.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..db8b491
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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 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..a2eca5f
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Authors:
+ * Dave Airlie
+ * Alon Levy
+ *
+ * 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.
+ */
+
+#include "virtgpu_drv.h"
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_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 int virtio_gpu_fbdev = 1;
+
+MODULE_PARM_DESC(fbdev, "Disable/Enable framebuffer device & console");
+module_param_named(fbdev, virtio_gpu_fbdev, int, 0400);
+
+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;
+ }
+
+ virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0,
+ cpu_to_le32(64),
+ cpu_to_le32(64),
+ 0, 0, &fence);
+
+ 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);
+ ret = 0;
+
+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 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_atomic_helper_set_config,
+ .destroy = drm_crtc_cleanup,
+
+#if 0 /* not (yet) working without vblank support according to docs */
+ .page_flip = drm_atomic_helper_page_flip,
+#endif
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+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 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 void virtio_gpu_crtc_mode_set_nofb(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,
+ crtc->mode.hdisplay,
+ crtc->mode.vdisplay, 0, 0);
+}
+
+static void virtio_gpu_crtc_enable(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 int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ return 0;
+}
+
+static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
+ .enable = virtio_gpu_crtc_enable,
+ .disable = virtio_gpu_crtc_disable,
+ .mode_fixup = virtio_gpu_crtc_mode_fixup,
+ .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb,
+ .atomic_check = virtio_gpu_crtc_atomic_check,
+};
+
+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_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void virtio_gpu_enc_enable(struct drm_encoder *encoder)
+{
+}
+
+static void virtio_gpu_enc_disable(struct drm_encoder *encoder)
+{
+}
+
+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 = {
+ .mode_fixup = virtio_gpu_enc_mode_fixup,
+ .mode_set = virtio_gpu_enc_mode_set,
+ .enable = virtio_gpu_enc_enable,
+ .disable = virtio_gpu_enc_disable,
+};
+
+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 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_atomic_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,
+ .destroy = virtio_gpu_conn_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+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;
+ struct drm_plane *plane;
+
+ 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);
+ }
+
+ plane = virtio_gpu_plane_init(vgdev, index);
+ if (IS_ERR(plane))
+ return PTR_ERR(plane);
+ drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+ &virtio_gpu_crtc_funcs);
+ drm_mode_crtc_set_gamma_size(crtc, 256);
+ drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
+ plane->crtc = crtc;
+
+ drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
+
+ drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
+ DRM_MODE_ENCODER_VIRTUAL);
+ drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
+ encoder->possible_crtcs = 1 << index;
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+ 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,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
+{
+ int i, 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);
+
+ drm_mode_config_reset(vgdev->ddev);
+
+ if (virtio_gpu_fbdev) {
+ ret = virtio_gpu_fbdev_init(vgdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+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..f4ec816
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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 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/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;
+}
+
+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;
+
+ if (vga) {
+ /*
+ * Need to make sure we don't have two drivers
+ * for the same hardware here. Some day we
+ * will simply kick out the firmware
+ * (vesa/efi) framebuffer.
+ *
+ * Virtual hardware specs for virtio-vga are
+ * not finalized yet, therefore we can't add
+ * code for that yet.
+ *
+ * So ignore the device for the time being,
+ * and suggest to the user use the device
+ * variant without vga compatibility mode.
+ */
+ DRM_ERROR("virtio-vga not (yet) supported\n");
+ DRM_ERROR("please use virtio-gpu-pci instead\n");
+ ret = -ENODEV;
+ goto err_free;
+ }
+ dev->pdev = 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..7d9610a
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * 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.
+ */
+
+#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 and additional rights");
+MODULE_AUTHOR("Dave Airlie <airlied@redhat.com>");
+MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
+MODULE_AUTHOR("Alon Levy");
+
+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..e5a2c09
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2015 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 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.
+ */
+
+#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;
+
+ void *data_buf;
+ uint32_t data_size;
+
+ char *resp_buf;
+ int resp_size;
+
+ virtio_gpu_resp_cb resp_cb;
+
+ struct list_head 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 list_head free_vbufs;
+ void *vbufs;
+ bool vqs_ready;
+
+ 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_alloc_vbufs(struct virtio_gpu_device *vgdev);
+void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev);
+void 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);
+void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id,
+ uint32_t format,
+ uint32_t width,
+ uint32_t height);
+void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
+ uint32_t resource_id);
+void 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);
+void 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);
+void 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);
+void 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_plane.c */
+struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
+ int index);
+
+/* virtio_gpu_ttm.c */
+int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev);
+void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev);
+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..25bf333
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_fb.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2015 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 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 <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;
+};
+
+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 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);
+
+ 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;
+ }
+
+ /*
+ * Can be called with pretty much any context (console output
+ * path). If we are in atomic just store the dirty rect info
+ * to send out the update later.
+ *
+ * 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;
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = mode_cmd.width * 4;
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(32, 24);
+
+ 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:
+ DRM_ERROR("failed to find virtio gpu format for %d\n",
+ mode_cmd.pixel_format);
+ return -EINVAL;
+ }
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+ obj = virtio_gpu_alloc_object(dev, size, false, true);
+ if (!obj)
+ return -ENOMEM;
+
+ virtio_gpu_resource_id_get(vgdev, &resid);
+ virtio_gpu_cmd_create_resource(vgdev, resid, format,
+ mode_cmd.width, mode_cmd.height);
+
+ ret = virtio_gpu_vmap_fb(vgdev, obj);
+ if (ret) {
+ DRM_ERROR("failed to vmap fb %d\n", ret);
+ goto err_obj_vmap;
+ }
+
+ /* attach the object to the resource */
+ ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL);
+ if (ret)
+ goto err_obj_attach;
+
+ info = framebuffer_alloc(0, device);
+ if (!info) {
+ ret = -ENOMEM;
+ goto err_fb_alloc;
+ }
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ ret = -ENOMEM;
+ goto err_fb_alloc_cmap;
+ }
+
+ info->par = helper;
+
+ ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb,
+ &mode_cmd, &obj->gem_base);
+ if (ret)
+ goto err_fb_init;
+
+ 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;
+
+ 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;
+
+err_fb_init:
+ fb_dealloc_cmap(&info->cmap);
+err_fb_alloc_cmap:
+ framebuffer_release(info);
+err_fb_alloc:
+ virtio_gpu_cmd_resource_inval_backing(vgdev, resid);
+err_obj_attach:
+err_obj_vmap:
+ virtio_gpu_gem_free_object(&obj->gem_base);
+ return ret;
+}
+
+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..1da6326
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_fence.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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 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 <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..cfa0d27
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 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 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 <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;
+
+ virtio_gpu_resource_id_get(vgdev, &resid);
+ virtio_gpu_cmd_create_resource(vgdev, resid,
+ 2, args->width, args->height);
+
+ /* 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..e503ffb
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 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 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/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_alloc_vbufs(vgdev);
+ if (ret) {
+ DRM_ERROR("failed to alloc vbufs\n");
+ goto err_vbufs;
+ }
+
+ 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);
+ vgdev->vqs_ready = true;
+ virtio_gpu_cmd_get_display_info(vgdev);
+ return 0;
+
+err_modeset:
+err_scanouts:
+ virtio_gpu_ttm_fini(vgdev);
+err_ttm:
+ virtio_gpu_free_vbufs(vgdev);
+err_vbufs:
+ 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;
+
+ vgdev->vqs_ready = false;
+ flush_work(&vgdev->ctrlq.dequeue_work);
+ flush_work(&vgdev->cursorq.dequeue_work);
+ flush_work(&vgdev->config_changed_work);
+ vgdev->vdev->config->del_vqs(vgdev->vdev);
+
+ virtio_gpu_modeset_fini(vgdev);
+ virtio_gpu_ttm_fini(vgdev);
+ virtio_gpu_free_vbufs(vgdev);
+ 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..2c624c7
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 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 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 "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);
+}
+
+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 ret;
+
+ 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);
+ ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size);
+ if (ret != 0)
+ goto err_gem_init;
+ bo->dumb = false;
+ virtio_gpu_init_ttm_placement(bo, pinned);
+
+ ret = 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 (ret != 0)
+ goto err_ttm_init;
+
+ *bo_ptr = bo;
+ return 0;
+
+err_ttm_init:
+ drm_gem_object_release(&bo->gem_base);
+err_gem_init:
+ kfree(bo);
+ return ret;
+}
+
+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;
+}
+
+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_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
new file mode 100644
index 0000000..4a74129
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 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 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 "virtgpu_drv.h"
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+static const uint32_t virtio_gpu_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+};
+
+static void virtio_gpu_plane_destroy(struct drm_plane *plane)
+{
+ kfree(plane);
+}
+
+static const struct drm_plane_funcs virtio_gpu_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = virtio_gpu_plane_destroy,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int virtio_gpu_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ return 0;
+}
+
+static void virtio_gpu_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_device *dev = plane->dev;
+ struct virtio_gpu_device *vgdev = dev->dev_private;
+ struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(plane->crtc);
+ struct virtio_gpu_framebuffer *vgfb;
+ struct virtio_gpu_object *bo;
+ uint32_t handle;
+
+ if (plane->fb) {
+ vgfb = to_virtio_gpu_framebuffer(plane->fb);
+ bo = gem_to_virtio_gpu_obj(vgfb->obj);
+ handle = bo->hw_res_handle;
+ } else {
+ handle = 0;
+ }
+
+ DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d\n", handle,
+ plane->state->crtc_w, plane->state->crtc_h,
+ plane->state->crtc_x, plane->state->crtc_y);
+ virtio_gpu_cmd_set_scanout(vgdev, output->index, handle,
+ plane->state->crtc_w,
+ plane->state->crtc_h,
+ plane->state->crtc_x,
+ plane->state->crtc_y);
+}
+
+
+static const struct drm_plane_helper_funcs virtio_gpu_plane_helper_funcs = {
+ .atomic_check = virtio_gpu_plane_atomic_check,
+ .atomic_update = virtio_gpu_plane_atomic_update,
+};
+
+struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
+ int index)
+{
+ struct drm_device *dev = vgdev->ddev;
+ struct drm_plane *plane;
+ int ret;
+
+ plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ ret = drm_universal_plane_init(dev, plane, 1 << index,
+ &virtio_gpu_plane_funcs,
+ virtio_gpu_formats,
+ ARRAY_SIZE(virtio_gpu_formats),
+ DRM_PLANE_TYPE_PRIMARY);
+ if (ret)
+ goto err_plane_init;
+
+ drm_plane_helper_add(plane, &virtio_gpu_plane_helper_funcs);
+ return plane;
+
+err_plane_init:
+ kfree(plane);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c
new file mode 100644
index 0000000..e0e74c6
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Authors:
+ * Dave Airlie
+ * Alon Levy
+ *
+ * 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.
+ */
+
+#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;
+ }
+}
+
+#if 0
+/*
+ * Hmm, seems to not do anything useful. Leftover debug hack?
+ * Something like printing pagefaults to kernel log?
+ */
+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;
+}
+#endif
+
+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 0
+ 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;
+#else
+ return r;
+#endif
+}
+
+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);
+ }
+ }
+}
+
+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);
+ goto err_dev_init;
+ return r;
+ }
+
+ r = ttm_bo_init_mm(&vgdev->mman.bdev, TTM_PL_TT, 0);
+ if (r) {
+ DRM_ERROR("Failed initializing GTT heap.\n");
+ goto err_mm_init;
+ return r;
+ }
+ return 0;
+
+err_mm_init:
+ ttm_bo_device_release(&vgdev->mman.bdev);
+err_dev_init:
+ virtio_gpu_ttm_global_fini(vgdev);
+ return r;
+}
+
+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..8fa6513e
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * 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.
+ */
+
+#include <drm/drmP.h>
+#include "virtgpu_drv.h"
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+
+#define MAX_INLINE_CMD_SIZE 96
+#define MAX_INLINE_RESP_SIZE 24
+#define VBUFFER_SIZE (sizeof(struct virtio_gpu_vbuffer) \
+ + MAX_INLINE_CMD_SIZE \
+ + MAX_INLINE_RESP_SIZE)
+
+void 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;
+}
+
+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);
+}
+
+int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+ int i, size, count = 0;
+ void *ptr;
+
+ INIT_LIST_HEAD(&vgdev->free_vbufs);
+ count += virtqueue_get_vring_size(vgdev->ctrlq.vq);
+ count += virtqueue_get_vring_size(vgdev->cursorq.vq);
+ size = count * VBUFFER_SIZE;
+ DRM_INFO("virtio vbuffers: %d bufs, %zdB each, %dkB total.\n",
+ count, VBUFFER_SIZE, size / 1024);
+
+ vgdev->vbufs = kzalloc(size, GFP_KERNEL);
+ if (!vgdev->vbufs)
+ return -ENOMEM;
+
+ for (i = 0, ptr = vgdev->vbufs;
+ i < count;
+ i++, ptr += VBUFFER_SIZE) {
+ vbuf = ptr;
+ list_add(&vbuf->list, &vgdev->free_vbufs);
+ }
+ return 0;
+}
+
+void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+ int i, count = 0;
+
+ count += virtqueue_get_vring_size(vgdev->ctrlq.vq);
+ count += virtqueue_get_vring_size(vgdev->cursorq.vq);
+
+ for (i = 0; i < count; i++) {
+ if (WARN_ON(list_empty(&vgdev->free_vbufs)))
+ return;
+ vbuf = list_first_entry(&vgdev->free_vbufs,
+ struct virtio_gpu_vbuffer, list);
+ list_del(&vbuf->list);
+ }
+ kfree(vgdev->vbufs);
+}
+
+static struct virtio_gpu_vbuffer*
+virtio_gpu_get_vbuf(struct virtio_gpu_device *vgdev,
+ int size, int resp_size, void *resp_buf,
+ virtio_gpu_resp_cb resp_cb)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ BUG_ON(list_empty(&vgdev->free_vbufs));
+ vbuf = list_first_entry(&vgdev->free_vbufs,
+ struct virtio_gpu_vbuffer, list);
+ list_del(&vbuf->list);
+ memset(vbuf, 0, VBUFFER_SIZE);
+
+ BUG_ON(size > MAX_INLINE_CMD_SIZE);
+ vbuf->buf = (void *)vbuf + sizeof(*vbuf);
+ vbuf->size = size;
+
+ vbuf->resp_cb = resp_cb;
+ vbuf->resp_size = resp_size;
+ if (resp_size <= MAX_INLINE_RESP_SIZE)
+ vbuf->resp_buf = (void *)vbuf->buf + size;
+ else
+ vbuf->resp_buf = resp_buf;
+ BUG_ON(!vbuf->resp_buf);
+ return vbuf;
+}
+
+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_get_vbuf(vgdev, size,
+ sizeof(struct virtio_gpu_ctrl_hdr),
+ NULL, 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_get_vbuf
+ (vgdev, sizeof(struct virtio_gpu_update_cursor),
+ 0, NULL, 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,
+ void *resp_buf)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ vbuf = virtio_gpu_get_vbuf(vgdev, cmd_size,
+ resp_size, resp_buf, 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)
+{
+ if (vbuf->resp_size > MAX_INLINE_RESP_SIZE)
+ kfree(vbuf->resp_buf);
+ kfree(vbuf->data_buf);
+ list_add(&vbuf->list, &vgdev->free_vbufs);
+}
+
+static void 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->list, reclaim_list);
+ freed++;
+ }
+ if (freed == 0)
+ DRM_DEBUG("Huh? zero vbufs reclaimed");
+}
+
+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);
+ 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);
+ reclaim_vbufs(vgdev->ctrlq.vq, &reclaim_list);
+
+ } while (!virtqueue_enable_cb(vgdev->ctrlq.vq));
+ spin_unlock(&vgdev->ctrlq.qlock);
+
+ list_for_each_entry_safe(entry, tmp, &reclaim_list, 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->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 list_head reclaim_list;
+ struct virtio_gpu_vbuffer *entry, *tmp;
+
+ INIT_LIST_HEAD(&reclaim_list);
+ spin_lock(&vgdev->cursorq.qlock);
+ do {
+ virtqueue_disable_cb(vgdev->cursorq.vq);
+ reclaim_vbufs(vgdev->cursorq.vq, &reclaim_list);
+ } while (!virtqueue_enable_cb(vgdev->cursorq.vq));
+ spin_unlock(&vgdev->cursorq.qlock);
+
+ list_for_each_entry_safe(entry, tmp, &reclaim_list, list) {
+ list_del(&entry->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;
+
+ if (!vgdev->vqs_ready)
+ return -ENODEV;
+
+ sg_init_one(&vcmd, vbuf->buf, vbuf->size);
+ sgs[outcnt+incnt] = &vcmd;
+ outcnt++;
+
+ if (vbuf->data_size) {
+ sg_init_one(&vout, vbuf->data_buf, vbuf->data_size);
+ sgs[outcnt + incnt] = &vout;
+ outcnt++;
+ }
+
+ if (vbuf->resp_size) {
+ 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;
+
+ if (!vgdev->vqs_ready)
+ return -ENODEV;
+
+ 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 */
+void 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);
+}
+
+void 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);
+}
+
+void 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);
+}
+
+void 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);
+}
+
+void 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);
+}
+
+void 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);
+}
+
+static void
+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);
+}
+
+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;
+ void *resp_buf;
+
+ resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_display_info),
+ GFP_KERNEL);
+ if (!resp_buf)
+ return -ENOMEM;
+
+ 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),
+ resp_buf);
+ 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/include/drm/drmP.h b/include/drm/drmP.h
index 62c40777..1544bb1 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -812,6 +812,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 1a0006a..4460e58 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -430,6 +430,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_input.h
header-y += virtio_net.h
diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
new file mode 100644
index 0000000..571c4cb
--- /dev/null
+++ b/include/uapi/linux/virtio_gpu.h
@@ -0,0 +1,204 @@
+/*
+ * 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;
+ __le32 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;
+ __le32 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 5f60aa4..77925f5 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -39,6 +39,7 @@
#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 /* virtio GPU */
#define VIRTIO_ID_INPUT 18 /* virtio input */
#endif /* _LINUX_VIRTIO_IDS_H */
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Arnd Bergmann @ 2015-05-22 13:24 UTC (permalink / raw)
To: Thierry Reding
Cc: Greg Kroah-Hartman, Kevin Hilman, Scot Doyle, Tomi Valkeinen,
Michael Kerrisk, Jiri Slaby, Jean-Christophe Plagniol-Villard,
Pavel Machek, Geert Uytterhoeven, lkml,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker, Olof Johansson,
Daniel Stone
In-Reply-To: <20150522114957.GF16507@ulmo>
On Friday 22 May 2015 13:49:58 Thierry Reding wrote:
> On Fri, May 22, 2015 at 12:33:42PM +0200, Arnd Bergmann wrote:
> > On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
> > >
> > > > Remember, if I have to hand-edit, or do something special with your
> > > > patch, I will not do it, you need to do it correctly to make
> > > > maintainer's lives easier, not harder, given that maintainers are the
> > > > limited resouce, not developers.
> > >
> > > I understand. I'll make a mental note to never send you patches as
> > > attachment again.
> > >
> >
> > Better make that a general rule. My workflow is different from Greg's
> > but also doesn't cope well with attachments. A lot of people in turn
> > have problems quoting from an attachment when replying to the patch,
> > which happens to work for me.
>
> Okay. Any hints on how to simplify sending out such patches with the
> same list of recipients? I find it very annoying to have to manually
> copy each recipient into the git send-email command-line, but I don't
> know of a better way to do it. Replying to an email from the MUA will
> at least do that automatically.
You can have a line starting with 8<------ (the scissors symbol) after
your reply, and then paste the patch below.
I usually use 'git show --format=email | xclip' to copy the patch into
the X clipboard and paste it into the email window from there.
Arnd
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Maxime Coquelin @ 2015-05-22 13:57 UTC (permalink / raw)
To: Andreas Färber, Daniel Thompson
Cc: Philipp Zabel, Maxime Ripard, Arnd Bergmann,
Uwe Kleine-König, Geert Uytterhoeven, Rob Herring,
Linus Walleij, Stefan Agner, Peter Meerwald, Paul Bolle,
Peter Hurley, Andy Shevchenko, Chanwoo Choi, Russell King,
Daniel Lezcano, Joe Perches, Vladimir Zapolskiy, Lee Jones,
Jonathan Corbet, Pawel Moll, Mark Rutland, Ian Campbell
In-Reply-To: <555F2A8A.5020205@suse.de>
2015-05-22 15:09 GMT+02:00 Andreas Färber <afaerber@suse.de>:
> As you should know, I did have an RCC clk driver, and there is no such
> issue. The two drivers use different mechanisms for initialization. And
> I'm pretty sure that I've already remarked that on the list, too.
Yes, you use of_iomap in your clock driver [0].
Daniel, would you accept to do the same?
That would remove one difference between stm32/sunxi/socfpga reset drivers.
Regards,
Maxime
[0]: https://github.com/afaerber/linux/blob/stm32/drivers/clk/clk-stm32f42xxx-rcc.c
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v8 07/16] drivers: reset: Add STM32 reset driver
From: Maxime Coquelin @ 2015-05-22 14:04 UTC (permalink / raw)
To: Andreas Färber
Cc: Kamil Lulko, Rob Herring, Arnd Bergmann, Mark Rutland,
linux-doc@vger.kernel.org, Linus Walleij, Will Deacon,
Stefan Agner, Nikolay Borisov, Peter Meerwald, Lee Jones,
Linux-Arch, Daniel Thompson, Russell King, Pawel Moll,
Jonathan Corbet, Jiri Slaby, Daniel Lezcano, Chanwoo Choi,
Andy Shevchenko, Antti Palosaari, Geert Uytterhoeven,
"linux-serial@vger.kernel.org" <linux-serial>
In-Reply-To: <555E559F.8040403@suse.de>
2015-05-22 0:01 GMT+02:00 Andreas Färber <afaerber@suse.de>:
> Am 21.05.2015 um 21:57 schrieb Maxime Coquelin:
>> Note that for now, I still use your bootloader.
>> I have done the changes to reset the timers in the afboot-stm32.
>> That's the reason why I asked you under which licence it is delivered
>> few months ago.
>
> Sorry, too many mails... The stm32 one is GPL-2.0, as parts of it were
> derived from a U-Boot fork. (Personally I prefer GPL-2.0+; fm4 and
> xmc4000 are MIT/X11.)
Not a problem, thanks for providing the licence.
>> I can share you the patch if you want, even if I understand it is more
>> about the concept that you are reluctant.
>>
>> On my side, I plan to move to U-Boot soon, as Kamil Lulko added STM32
>> support in mainline [1].
>
> You're free to use any bootloader you like, but you will find it
> difficult to build in USB etc. drivers given the sheer size of U-Boot.
> That was my motivation for writing the tiny one. ;)
I think the two bootloaders make sense. Indeed, using U-Boot restricts
the size of the Kernel.
I also have the stm32429i-eval board, with 32MB NOR and 32MB SD-Ram.
At least on this one I will use U-Boot, as tftp could be used to load
Kernel since it has Ethernet port.
>> In case of U-Boot, the timer reset should be de-asserted when jumping
>> into the Kernel, as Rob mentionned [0].
>
> Thanks, I've updated the xmc4000 one accordingly and can do the same for
> stm32. But you are right that I consider that an ugly workaround,
> although on the other hand my earlyprintk patches also depend on the
> bootloader setting up GPIOs and UART.
Yes, the Kernel always need to rely on the bootloader to provide a
minimal setup (clock/ddr/muxing...).
Regards,
Maxime
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Andreas Färber @ 2015-05-22 14:06 UTC (permalink / raw)
To: Maxime Coquelin, Daniel Thompson
Cc: Philipp Zabel, Maxime Ripard, Arnd Bergmann,
Uwe Kleine-König, Geert Uytterhoeven, Rob Herring,
Linus Walleij, Stefan Agner, Peter Meerwald, Paul Bolle,
Peter Hurley, Andy Shevchenko, Chanwoo Choi, Russell King,
Daniel Lezcano, Joe Perches, Vladimir Zapolskiy, Lee Jones,
Jonathan Corbet, Pawel Moll, Mark Rutland, Ian Campbell
In-Reply-To: <CALszF6A=TZ_EROrfuBOAGiY3mbydC5fepWLSWrZLLt4nV9sr4w@mail.gmail.com>
Am 22.05.2015 um 15:57 schrieb Maxime Coquelin:
> 2015-05-22 15:09 GMT+02:00 Andreas Färber <afaerber@suse.de>:
>> As you should know, I did have an RCC clk driver, and there is no such
>> issue. The two drivers use different mechanisms for initialization. And
>> I'm pretty sure that I've already remarked that on the list, too.
>
> Yes, you use of_iomap in your clock driver [0].
Which was inspired by the efm32 driver iirc.
> Daniel, would you accept to do the same?
> That would remove one difference between stm32/sunxi/socfpga reset drivers.
>
> Regards,
> Maxime
>
> [0]: https://github.com/afaerber/linux/blob/stm32/drivers/clk/clk-stm32f42xxx-rcc.c
For the record, that still has some internal clocks that shouldn't be
exposed.
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Dilip Upmanyu, Graham Norton; HRB
21284 (AG Nürnberg)
^ permalink raw reply
* Re: [PATCH v8 14/16] ARM: dts: Introduce STM32F429 MCU
From: Daniel Thompson @ 2015-05-22 14:14 UTC (permalink / raw)
To: Maxime Coquelin, Andreas Färber
Cc: Philipp Zabel, Maxime Ripard, Arnd Bergmann,
Uwe Kleine-König, Geert Uytterhoeven, Rob Herring,
Linus Walleij, Stefan Agner, Peter Meerwald, Paul Bolle,
Peter Hurley, Andy Shevchenko, Chanwoo Choi, Russell King,
Daniel Lezcano, Joe Perches, Vladimir Zapolskiy, Lee Jones,
Jonathan Corbet, Pawel Moll, Mark Rutland, Ian Campbell
In-Reply-To: <CALszF6A=TZ_EROrfuBOAGiY3mbydC5fepWLSWrZLLt4nV9sr4w@mail.gmail.com>
On 22/05/15 14:57, Maxime Coquelin wrote:
> 2015-05-22 15:09 GMT+02:00 Andreas Färber <afaerber@suse.de>:
>> As you should know, I did have an RCC clk driver, and there is no such
>> issue. The two drivers use different mechanisms for initialization. And
>> I'm pretty sure that I've already remarked that on the list, too.
>
> Yes, you use of_iomap in your clock driver [0].
> Daniel, would you accept to do the same?
> That would remove one difference between stm32/sunxi/socfpga reset drivers.
In fact, that is exactly what I am currently doing, though I was
planning for it to be temporary.
It seems a bit weird to me that one driver (which requests too much
register space) only works because another driver chooses not to request
any.
BTW in drivers/clk there are ~110 of_iomaps and only 10
of_io_request_and_maps... so I don't think anyone will yell at me for
using of_iomap().
Daniel.
^ permalink raw reply
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Greg Kroah-Hartman @ 2015-05-22 14:32 UTC (permalink / raw)
To: Thierry Reding
Cc: Arnd Bergmann, Kevin Hilman, Scot Doyle, Tomi Valkeinen,
Michael Kerrisk, Jiri Slaby, Jean-Christophe Plagniol-Villard,
Pavel Machek, Geert Uytterhoeven, lkml,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker, Olof Johansson,
Daniel Stone
In-Reply-To: <20150522114957.GF16507@ulmo>
On Fri, May 22, 2015 at 01:49:58PM +0200, Thierry Reding wrote:
> On Fri, May 22, 2015 at 12:33:42PM +0200, Arnd Bergmann wrote:
> > On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
> > >
> > > > Remember, if I have to hand-edit, or do something special with your
> > > > patch, I will not do it, you need to do it correctly to make
> > > > maintainer's lives easier, not harder, given that maintainers are the
> > > > limited resouce, not developers.
> > >
> > > I understand. I'll make a mental note to never send you patches as
> > > attachment again.
> > >
> >
> > Better make that a general rule. My workflow is different from Greg's
> > but also doesn't cope well with attachments. A lot of people in turn
> > have problems quoting from an attachment when replying to the patch,
> > which happens to work for me.
>
> Okay. Any hints on how to simplify sending out such patches with the
> same list of recipients? I find it very annoying to have to manually
> copy each recipient into the git send-email command-line, but I don't
> know of a better way to do it. Replying to an email from the MUA will
> at least do that automatically.
Reply from the MUA and then just put the patch in the email body. If
you have a good MUA it should be trivial to do[1]
thanks,
greg k-h
1) mutt drops you to your editor, and then you can just read in the
patch file directly to that buffer.
^ permalink raw reply
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Thierry Reding @ 2015-05-22 14:41 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Greg Kroah-Hartman, Kevin Hilman, Scot Doyle, Tomi Valkeinen,
Michael Kerrisk, Jiri Slaby, Jean-Christophe Plagniol-Villard,
Pavel Machek, Geert Uytterhoeven, lkml,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker, Olof Johansson,
Daniel Stone
In-Reply-To: <2820529.TShzski8m6@wuerfel>
On Fri, May 22, 2015 at 03:24:30PM +0200, Arnd Bergmann wrote:
> On Friday 22 May 2015 13:49:58 Thierry Reding wrote:
> > On Fri, May 22, 2015 at 12:33:42PM +0200, Arnd Bergmann wrote:
> > > On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
> > > >
> > > > > Remember, if I have to hand-edit, or do something special with your
> > > > > patch, I will not do it, you need to do it correctly to make
> > > > > maintainer's lives easier, not harder, given that maintainers are the
> > > > > limited resouce, not developers.
> > > >
> > > > I understand. I'll make a mental note to never send you patches as
> > > > attachment again.
> > > >
> > >
> > > Better make that a general rule. My workflow is different from Greg's
> > > but also doesn't cope well with attachments. A lot of people in turn
> > > have problems quoting from an attachment when replying to the patch,
> > > which happens to work for me.
> >
> > Okay. Any hints on how to simplify sending out such patches with the
> > same list of recipients? I find it very annoying to have to manually
> > copy each recipient into the git send-email command-line, but I don't
> > know of a better way to do it. Replying to an email from the MUA will
> > at least do that automatically.
>
> You can have a line starting with 8<------ (the scissors symbol) after
> your reply, and then paste the patch below.
>
> I usually use 'git show --format=email | xclip' to copy the patch into
> the X clipboard and paste it into the email window from there.
Cool, that's pretty useful. I should be able to do that without going
through the X clipboard with mutt/vim even.
Thanks,
Thierry
^ permalink raw reply
* Re: [PATCH v2 2/3] fbcon: use the cursor blink interval provided by vt
From: Thierry Reding @ 2015-05-22 14:44 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Arnd Bergmann, Kevin Hilman, Scot Doyle, Tomi Valkeinen,
Michael Kerrisk, Jiri Slaby, Jean-Christophe Plagniol-Villard,
Pavel Machek, Geert Uytterhoeven, lkml,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-man-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Tyler Baker, Olof Johansson,
Daniel Stone
In-Reply-To: <20150522143205.GA6508-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 2000 bytes --]
On Fri, May 22, 2015 at 07:32:05AM -0700, Greg Kroah-Hartman wrote:
> On Fri, May 22, 2015 at 01:49:58PM +0200, Thierry Reding wrote:
> > On Fri, May 22, 2015 at 12:33:42PM +0200, Arnd Bergmann wrote:
> > > On Friday 22 May 2015 12:00:03 Thierry Reding wrote:
> > > >
> > > > > Remember, if I have to hand-edit, or do something special with your
> > > > > patch, I will not do it, you need to do it correctly to make
> > > > > maintainer's lives easier, not harder, given that maintainers are the
> > > > > limited resouce, not developers.
> > > >
> > > > I understand. I'll make a mental note to never send you patches as
> > > > attachment again.
> > > >
> > >
> > > Better make that a general rule. My workflow is different from Greg's
> > > but also doesn't cope well with attachments. A lot of people in turn
> > > have problems quoting from an attachment when replying to the patch,
> > > which happens to work for me.
> >
> > Okay. Any hints on how to simplify sending out such patches with the
> > same list of recipients? I find it very annoying to have to manually
> > copy each recipient into the git send-email command-line, but I don't
> > know of a better way to do it. Replying to an email from the MUA will
> > at least do that automatically.
>
> Reply from the MUA and then just put the patch in the email body. If
> you have a good MUA it should be trivial to do[1]
>
> thanks,
>
> greg k-h
>
> 1) mutt drops you to your editor, and then you can just read in the
> patch file directly to that buffer.
Indeed, that should work. As I understand it, I wouldn't even have to
further edit the email (except strip the reply) because git am prefers
headers in the patch to headers in the message (it certainly does that
for From:, so I suspect it would do it for Date: and Subject: as well).
Or if that doesn't work, Arnd's suggestion to use a scissors line is a
good alternative as well.
Thanks guys for the suggestions,
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH] fs, proc: introduce CONFIG_PROC_CHILDREN
From: Cyrill Gorcunov @ 2015-05-22 15:37 UTC (permalink / raw)
To: Iago López Galeiras
Cc: Oleg Nesterov, Kees Cook, Pavel Emelyanov, Serge Hallyn,
KAMEZAWA Hiroyuki, linux-api-u79uwXL29TY76Z2rM5mHXA,
Alexander Viro, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
Andrew Morton, Andy Lutomirski, Alban Crequy
In-Reply-To: <1432298859-2059-1-git-send-email-iago-973cpzSjLbNWk0Htik3J/w@public.gmane.org>
On Fri, May 22, 2015 at 02:47:39PM +0200, Iago López Galeiras wrote:
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1145,6 +1145,7 @@ endif # CGROUPS
>
> config CHECKPOINT_RESTORE
> bool "Checkpoint/restore support" if EXPERT
> + depends on PROC_CHILDREN
> default n
> help
> Enables additional kernel features in a sake of checkpoint/restore.
Maybe better select PROC_CHILDREN here? Also I must admit I don't understand
why similar patch from Andy has not been merged earlier? Andy, there was
some problem I forgot about?
(to be clear, I don't mind enabling it since we use it in criu alot ;)
^ permalink raw reply
* Re: [PATCH v2] Documentation/arch: Add kernel feature descriptions and arch support status under Documentation/features/
From: Jonathan Corbet @ 2015-05-22 15:49 UTC (permalink / raw)
To: Ingo Molnar
Cc: Andrew Morton, Josh Triplett, Borislav Petkov, Peter Zijlstra,
Andy Lutomirski, Ingo Molnar, H. Peter Anvin, Thomas Gleixner,
Linus Torvalds, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, x86-DgEjT+Ai2ygdnm+yROfE0A,
linux-arch-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20150514103544.GA7027-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Thu, 14 May 2015 12:35:44 +0200
Ingo Molnar <mingo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> Updated patch attached.
This seems to have slowed down a bit; I've taken the liberty of pulling it
into the docs tree. It can be replaced, though, should you decide to
completely rework the format again...:)
Thanks,
jon
^ permalink raw reply
* Re: [PATCH 00/23] userfaultfd v4
From: Kirill Smelkov @ 2015-05-22 16:35 UTC (permalink / raw)
To: Andrea Arcangeli
Cc: Andrew Morton, linux-kernel, linux-mm, qemu-devel, kvm, linux-api,
Pavel Emelyanov, Sanidhya Kashyap, zhang.zhanghailiang,
Linus Torvalds, Kirill A. Shutemov, Andres Lagar-Cavilla,
Dave Hansen, Paolo Bonzini, Rik van Riel, Mel Gorman,
Andy Lutomirski, Hugh Dickins, Peter Feiner,
Dr. David Alan Gilbert, Johannes Weiner, Huangpeng (Peter)
In-Reply-To: <20150521155251.GB4643@redhat.com>
Hi Andrea,
On Thu, May 21, 2015 at 05:52:51PM +0200, Andrea Arcangeli wrote:
> Hi Kirill,
>
> On Thu, May 21, 2015 at 04:11:11PM +0300, Kirill Smelkov wrote:
> > Sorry for maybe speaking up too late, but here is additional real
>
> Not too late, in fact I don't think there's any change required for
> this at this stage, but it'd be great if you could help me to review.
Thanks
> > Since arrays can be large, it would be slow and thus not practical to
> [..]
> > So I've implemented a scheme where array data is initially PROT_READ
> > protected, then we catch SIGSEGV, if it is write and area belongs to array
>
> In the case of postcopy live migration (for qemu and/or containers) and
> postcopy live snapshotting, splitting the vmas is not an option
> because we may run out of them.
>
> If your PROT_READ areas are limited perhaps this isn't an issue but
> with hundreds GB guests (currently plenty in production) that needs to
> live migrate fully reliably and fast, the vmas could exceed the limit
> if we were to use mprotect. If your arrays are very large and the
> PROT_READ aren't limited, using userfaultfd this isn't only an
> optimization for you too, it's actually a must to avoid a potential
> -ENOMEM.
I understand. To somehow mitigate this issue for every array/file I try
to allocate ram pages from separate file on tmpfs with the same offset.
This way if we allocate a lot of pages and mmap them in with PROT_READ,
if they are adjacent to each other, the kernel will merge adjacent vmas
into one vma:
https://lab.nexedi.cn/kirr/wendelin.core/blob/ca064f75/include/wendelin/bigfile/ram.h#L100
https://lab.nexedi.cn/kirr/wendelin.core/blob/ca064f75/bigfile/ram_shmfs.c#L102
https://lab.nexedi.cn/kirr/wendelin.core/blob/ca064f75/bigfile/virtmem.c#L435
I agree this is only a half-measure - file parts accessed could be
sparse, and also there is a in-shmfs overhead maintaining tables what
real pages are allocated to which part of file on shmfs.
So yes, if userfaultfd allows to overcomes vma layer and work directly
on page tables, this helps.
> > Also, since arrays could be large - bigger than RAM, and only sparse
> > parts of it could be needed to get needed information, for reading it
> > also makes sense to lazily load data in SIGSEGV handler with initial
> > PROT_NONE protection.
>
> Similarly I heard somebody wrote a fastresume to load the suspended
> (on disk) guest ram using userfaultfd. That is a slightly less
> fundamental case than postcopy because you could do it also with
> MAP_SHARED, but it's still interesting in allowing to compress or
> decompress the suspended ram on the fly with lz4 for example,
> something MAP_PRIVATE/MAP_SHARED wouldn't do (plus there's the
> additional benefit of not having an orphaned inode left open even if
> the file is deleted, that prevents to unmount the filesystem for the
> whole lifetime of the guest).
I see. Just a note - transparent compression/decompression could be
achieved with MAP_SHARED if the compression is being performed by
underlying filesystem - e.g. implemented with FUSE.
( I have not measured performance though )
> > This is very similar to how memory mapped files work, but adds
> > transactionality which, as far as I know, is not provided by any
> > currently in-kernel filesystem on Linux.
>
> That's another benefit yes.
>
> > The gist of virtual memory-manager is this:
> >
> > https://lab.nexedi.cn/kirr/wendelin.core/blob/master/include/wendelin/bigfile/virtmem.h
> > https://lab.nexedi.cn/kirr/wendelin.core/blob/master/bigfile/virtmem.c (vma_on_pagefault)
>
> I'll check it more in detail ASAP, thanks for the pointers!
>
> > For operations it currently needs
> >
> > - establishing virtual memory areas and connecting to tracking it
>
> That's the UFFDIO_REGISTER/UNREGISTER.
Yes
> > - changing pages protection
> >
> > PROT_NONE or absent - initially
>
> absent is what works with -mm already. The lazy loading already works.
Yes
> > PROT_NONE -> PROT_READ - after read
>
> Current UFFDIO_COPY will map it using vma->vm_page_prot.
>
> We'll need a new flag for UFFDIO_COPY to map it readonly. This is
> already contemplated:
>
> /*
> * There will be a wrprotection flag later that allows to map
> * pages wrprotected on the fly. And such a flag will be
> * available if the wrprotection ioctl are implemented for the
> * range according to the uffdio_register.ioctls.
> */
> #define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0)
> __u64 mode;
>
> If the memory protection framework exists (either through the
> uffdio_register.ioctl out value, or through uffdio_api.features
> out-only value) you can pass a new flag (MODE_WP) above to transition
> from "absent" to "PROT_READ".
Yes. The same probably applies to UFFDIO_ZEROPAGE (to mmap-in zeropage
as RO on read, if that part of file is currently hole)
So we settle on adding
UFFDIO_COPY_MODE_WP and
UFFDIO_ZEROPAGE_MODE_WP
?
Or maybe why we have both COPY_DONTWAKE and ZEROPAGE_DONTWAKE (and
previously REMAP_MODE_DONTWAKE) and now duplicate _MODE_WP to all them?
Maybe it makes sense to move those common flags related to waking up or
not, mmaping in as R or RW (and maybe other in the future) to common
place.
> > PROT_READ -> PROT_READWRITE - after write
>
> This will need to add UFFDIO_MPROTECT.
Yes
> > PROT_READWRITE -> PROT_READ - after commit
>
> UFFDIO_MPROTECT again (but harder if going from rw to ro, because of a
> slight mess to solve with regard to FAULT_FLAG_TRIED, in case you want
> to run this UFFDIO_MPROTECT without stopping the threads that are
> accessing the memory concurrently).
Yes. I understand the race of a manager making pages RW -> R, and an
other thread in user process simultaneously making a write to the same
page.
On user-level this race can be solved this way: before commit, changed
pages are first marked as R and only then written to storage.
- If a write is racing with RW->R protection being made - it's a client
problem (of having one thread still modifying data, and other thread
triggering commit) - we are ok with committing what has already been
written at the moment RW->R has happened.
- If a write happened after RW->R protection has been made (even if it
is racy), we are ok if we get a proper notification to userfaultfd
handler of write to WP area.
So if on kernel side the "slight mess" can be solved, userspace is ok to
live with the potential race and solve it itself.
> And this should only work if the uffdio_register.mode had MODE_WP set,
> so we don't run into the races created by COWs (gup vs fork race).
It is ok to start with registering with
(UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP)
for whole area at the beginning. In other words we are asking
userfaultfd "we want to handle all kind of faults - both reads and writes"
> > PROT_READWRITE -> PROT_NONE or absent (again) - after abort
>
> UFFDIO_MPROTECT again,
My idea here is that on transaction abort, we just don't need that
changed memory - we can both forget the changes and free the appropriate
pages - if in next transaction someone will need the file data of that
same part again, it just reloads the usual way from file.
So it is maybe
UFFDIO_MFREE ( make pte absent, and free(*) referenced page
(*) free maybe = decrement its refcount )
what is needed here.
But for generality, I agree it make sense to have a way to just MPROTECT
with PROT_NONE without freeing the page.
> but you won't be able to read the page contents
> inside the memory manager thread (the one working with
> userfaultfd).
With PROT_NONE I see.
> The manager at all times if forbidden to touch the memory it is
> tracking with userfaultfd (if it does it'll deadlock, but kill -9 will
> get rid of it). gdb ironically because it is using an underoptimized
> access_process_vm wouldn't hang, because FAULT_FLAG_RETRY won't be set
> in handle_userfault in the gdb context, and it'll just receive a
> sigbus if by mistake the user tries to touch the memory. Even if it
> will hung later as get_user_pages_locked|unlocked gets used there too,
> kill -9 would solve gdb too.
Wait. I partly understand, because I have no much experience in mm. But
are you saying the manager cannot access the memory it tracks, if pages
even have PROT_READ or PROT_READWRITE protection, and we know they were
already loaded, e.g. they are not missing?
If yes, then for sure, there need to be a way to get the data back from
memory to manager to implement storing changes back.
And maybe for some cases it would make sense to first set just
protection to PROT_NONE so manager know the client cannot mess with the
data while it accesses it.
> Back to the problem of accessing the UFFDIO_MPROTECT(PROT_NONE)
> memory: to do that a new ioctl should be required. I'd rather not go
> back to the route of UFFDIO_REMAP, but it could copy the data using
> the kernel address.
>
> It could be simply a reverse UFFDIO_COPY. We could add a
> UFFDIO_COPY_MODE_REVERSE flag to the "mode" of UFFDIO_COPY to mean
> "read source from kernel address and write destination in user
> address". By default it reads the source from user address and write
> the destination in kernel address (to be atomic).
Yes, with small clarification that "write to kernel address" is write to
"kernel address associated to address in destination mm"
> If you want to put data back before lifting the PROT_NONE, UFFDIO_COPY
> could be used in the standard way but with a
> UFFDIO_COPY_MODE_OVERWRITE flag that just overwrites the contents of
> the old page if it's not mapped (protnone), or just get rid of the old
> page (currently it'd return -EEXIST if the pte is not none).
>
> So the process would be:
>
> UFFDIO_COPY(dst_tmpaddr, src_addr, mode=REVERSE)
> UFFDIO_COPY(src_addr, dst_tmpaddr, mode=OVERWRITE)
>
> Then if you also set mode=READONLY in the last UFFDIO_COPY, it'll
> create a wrprotected mapping atomically before giving visibility to
> the new page contents:
>
> UFFDIO_COPY(src_addr, dst_tmpaddr, mode=OVERWRITE|WP)
I agree in general.
The only thing which confuses me a bit is the REVERSE flag and dst/src
being skipped. I would rather keep the src / dst ordering and in mode
have COPY_TO and COPY_FROM or have both UFFDIO_COPY_TO and
UFFDIO_COPY_FROM to clarify API.
But this is only a style and does not change semantics.
But I wonder though again - is it maybe possible to get managed memory
content (with PROT_READ or PROT_READWRITE) without copying?
> > PROT_READ -> PROT_NONE or absent (again) - on reclaim
>
> Same as above.
The same as above about abort applies to reclaim - we just need
to forget and free memory here - so UFFDIO_MFREE.
> > - working with aliasable memory (thus taken from tmpfs)
> >
> > there could be two overlapping-in-file mapping for file (array)
> > requested at different time, and changes from one mapping should
> > propagate to another one -> for common parts only 1 page should
> > be memory-mapped into 2 places in address-space.
>
> Why isn't the manager thread taking care of calling UFFDIO_MPROTECT in
> two places?
>
> And UFFDIO_COPY would fill the page and replace the old page and the
> effect would be visible as far as the "data" is concerned, but the
> protection bits would be more naturally different for each
> mapping, like a double mmap call is also required to map such an area
> in two places.
Because if we have a write on one place, the manager will
write-unprotect it, and would not get notified on further writes to the
same area.
And I need further writes to be visible in other mappings instantly.
Here is simplified example:
In [1]: from numpy import *
In [2]: A = zeros(10) # it simulates big array backed by manager
In [3]: a = A[0:5] # one part is mapped with start/stop = (0,5)
# something unrelated is done in between
In [4]: b = A[3:10] # another part is mapped with start/stop = (3,10)
# NOTE a and b overlaps in array/backing file.
# but since we already allocated address-space for a and did other
# things, address space beyond a could be already allocated, so we
# cannot just extend it and make b referencing addresses starting at
# a tail.
In [5]: a
Out[5]: array([ 0., 0., 0., 0., 0.])
In [6]: b
Out[6]: array([ 0., 0., 0., 0., 0., 0., 0.])
In [7]: a[4] = 1 # first change to a; the manager removes WP
In [8]: a
Out[8]: array([ 0., 0., 0., 0., 1.])
In [9]: b
Out[9]: array([ 0., 1., 0., 0., 0., 0., 0.]) # propagated to b
# write again - the manager does not get a WP fault, but the change have to
# propagate again
In [10]: a[4] = 2
In [11]: a
Out[11]: array([ 0., 0., 0., 0., 2.])
In [12]: b
Out[12]: array([ 0., 2., 0., 0., 0., 0., 0.])
so if we resolve fault for a[4], the exact page mapped in, should be
also eventually mapped into b[1].
> You could have a MAP_PRIVATE vma with PROT_READ, you can't create a
> writable pte into it, just because you called
> UFFDIO_MPROTECT(PROT_READ|PROT_WRITE) in a different mapping of the
> same tmpfs page.
I don't fully understand here, but I use MAP_SHARED so changes propagate
back to tmpfs file and are visible in other mappings.
https://lab.nexedi.cn/kirr/wendelin.core/blob/ca064f75/bigfile/ram_shmfs.c#L89
> NOTE: the availability of the UFFDIO_MPROTECT|COPY on tmpfs ares would
> still depend on UFFDIO_REGISTER to return the respecteve ioctl id in
> the uffdio_register.ioctl (out value of the register ioctl).
It is ok to check. But I'd like to note: here we mmap two overlapping
parts of a tmpfs file in two regions, and register both regions to
userfaultfd.
> > so what is currently lacking on userfaultfd side is:
> >
> > - ability to remove / make PROT_NONE already mapped pages
> > (UFFDIO_REMAP was recently dropped)
> >
> > - ability to arbitrarily change pages protection (e.g. RW -> R)
> >
> > - inject aliasable memory from tmpfs (or better hugetlbfs) and into
> > several places (UFFDIO_REMAP + some mapping copy semantic).
>
> I think UFFDIO_COPY if added with OVERWRITE|REVERSE|WP flags is an ok
> substitute for UFFDIO_REMAP.
>
> If UFFDIO_COPY sees the page is protnone during the REVERSE copy (to
> extract the memory atomically), it can also skip the tlb flush (and
> obviously there's no tlb flush in the reverse direction). If the page
> was not protnone, it can turn it in protnone, do a tlb flush, and then
> copy it to the destination address using the userland mapping.
We are also ok if the page is PROT_READ - in this case no need to do a
tlbflush - nothing can change to page while we are copying it - only we
have to care not to allow changing protection to PROT_READWRITE in the
process.
> UFFDIO_MPROTECT is definitely necessary for postcopy live snapshotting
> too (the reverse UFFDIO_COPY is not, it never deals with
> PROT_NONE and it never cares about missing faults).
>
> MPROTECT(PROT_NONE) so far seems needed only by this and perhaps UML
> (and perhaps qemu linux-user).
>
> I posted in another email why these features aren't implemented yet
>
> ==
> There will be some complications in adding the wrprotection/protnone
> feature: if faults could already happen when the wrprotect/protnone is
> armed, the handle_userfault() could be invoked in a retry-fault, that
> is not ok without allowing the userfault to return VM_FAULT_RETRY even
> during a refault (i.e. FAULT_FLAG_TRIED set but FAULT_FLAG_ALLOW_RETRY
> not set). The invariants of vma->vm_page_prot and pte/trans_huge_pmd
> permissions must also not break anywhere. These are the two main
> reasons why these features that requires to flip protection bits are
> left implemented later and made visible later with uffdio_api.feature
> flags and/or through uffdio_register.ioctl during UFFDIO_REGISTER.
> ==
I understand, maybe not in full details though. The changes would anyway
be needed to make userfaultfd capable of generic memory managing,
instead of populating it only in one way.
And as I noted above, besides UFFDIO_COPY(both direction, overwrite) and
UFFDIO_MPROTECT, UFFDIO_MFREE is also needed.
> > The performance currently is not great, partly because of page clearing
> > when getting ram from tmpfs, and partly because of mprotect/SIGSEGV/vmas
> > overhead and other dumb things on my side.
>
> Also the page faults get slowed down when the rbtree grows a lot,
> userfaultfd won't let the rbtree grow.
Yes. This is the same as splitting or not vmas.
> > I still wanted to show the case, as userfaultd here has potential to
> > remove overhead related to kernel.
>
> That's very useful and interesting feedback!
>
> Could you review the API to be sure we don't have to modify it when we
> extend it like described above?
>
> 1) tmpfs returning uffdio_register.ioctl |=
> UFFDIO_MPROTECT|UFFDIO_COPY when enabled
and UFFDIO_MFREE with maybe ability to automatically punch hole of freed
memory (we need to return the memory to the system on reclaim, and
preferable on abort).
> 2) UFFDIO_MPROTECT(PROT_NONE|READ|WRITE|EXEC) and in turn
> UFFDIO_COPY_MODE_REVERSE|UFFDIO_COPY_MODE_OVERWRITE|UFFDIO_COPY_MODE_WP
> being available if uffdio_register.ioctl includes UFFDIO_MPROTECT
UFFDIO_MPROTECT -> UFFDIO_MPROTECT(PROT_NONE|READ|WRITE|EXEC) looks ok.
but imho various copy modes could be allowed besides mprotect - e.g. for
a manager to get back managed memory. Thus, maybe
UFFDIO_COPY_MODE_REVERSE (or analogues) and UFFDIO_COPY_MODE_OVERWRITE
should be available for when just UFFDIO_COPY is set.
>From userspace point of view, it would be simpler to operate when all
those operations are always allowed though.
> (and uffdio_api.features will then include
> UFFD_FEATURE_PAGEFAULT_WP to signal the uffd_msg.pagefault.flag WP
> is available [bit 1], and UFFDIO_REGISTER_MODE_WP can be used in
> uffdio_register.mode)
Yes, though it is not fully represents the capability - e.g.
MPROTECT(PROT_NONE) should work too, so it should be maybe
UFFD_FEATURE_PAGEFAULT_PROTECT.
Again, from clients point of view it would be simpler if those features
are in base - e.g. they are always available.
> All of it could just check for uffdio_api.features &
> UFFD_FEATURE_PAGEFAULT_WP being set, but you'd still have to check for
> UFFDIO_MPROTECT being set in uffdio_register.ioctl for tmpfs areas (or
> to know it's not available yet on hugetlbfs), so I think it's more
> robust to check UFFDIO_MPROTECT ioctl being set in
> uffdio_register.ioctl to assume all mprotection and writeprotect
> tracking features are available for that specific range. The feature
> flag will just tell that UFFDIO_REGISTER_MODE_WP can be used in the
> register ioctl, that is something you need to know before in order to
> "arm" the VMA for wrprotect faults.
ok
> For your usage I think you probably want to set
> UFFDIO_REGISTER_MODE_WP|UFFDIO_REGISTER_MODE_MISSING and you'll be
> told through uffdio_msg.flags if it's a WP or MISSING fault.
ok
> You won't be told if it's missing because of PROT_NONE or absent.
looks like not good - to know whether a page is mapped there already or
not. Is it possible to distinguish this cases too?
>
> On a side note: all of the above is completely orthognal from the
> non-cooperative usage: as far as memory protection features it doesn't
> need any, it just needs to track more events like fork/mremap to
> adjust its offsets as the memory manager is not part of the app and it
> has no way to orchestrate by other means.
>
> Doing it all at once (non-cooperative + full memory protection) looked
> too much. We should just try to get the API right in a way that won't
> require an UFFD_API bump passed to uffdio_api.api. Even then, if an
> api bump is required, that's not a big deal, until recently the
> non-cooperative usage already did the API bumb but we accomodated the
> read(2) API to avoid it.
>
> Thinking at the worst case scenario, if the API gets bumped the only
> thing that has to remain fixed is the ioctl number of the UFFDIO_API
> and the uffdio_api structure. Everything else can be upgraded without
> risk of ABI breakage, even the ioctl numbers can be reused (except the
> very UFFDIO_API). When the non-cooperative usage bumped the API it
> actually kept all ioctl the same except the read(2) format.
I agree we can change API / ABI with versioning, but it is better to try
to get it right from the beginning and not fixup with several api
versions on top.
Thanks for your comments and feedback,
Kirill
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [CFT][PATCH 00/10] Making new mounts of proc and sysfs as safe as bind mounts (take 2)
From: Eric W. Biederman @ 2015-05-22 17:39 UTC (permalink / raw)
To: Linux Containers
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA, Linux API, Serge E. Hallyn,
Andy Lutomirski, Richard Weinberger, Kenton Varda,
Michael Kerrisk-manpages, Stéphane Graber, Eric Windisch,
Greg Kroah-Hartman, Tejun Heo, Seth Forshee
In-Reply-To: <87siaxuvik.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
I had hoped to get some Tested-By's on that patch series.
Oh well. The fundamentals seem sound, and my biggest concern the
implicit nodev does not apply so I will put this patchset in linux-next
and aim at merging it in the next merge window. Hopefully that will
leave enough time catch problems.
Eric
^ 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