* Re: [PATCH 7/7] [v4] drivers/virt: introduce Freescale hypervisor management driver
From: Chris Metcalf @ 2011-06-10 14:17 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Randy Dunlap, konrad.wilk, greg, kumar.gala, linux-kernel,
Linux Virtualization, dsaxena, linux-console, linuxppc-dev,
Timur Tabi, alan
In-Reply-To: <201106090938.23027.arnd@arndb.de>
On 6/9/2011 3:38 AM, Arnd Bergmann wrote:
> On Thursday 09 June 2011 01:10:09 Randy Dunlap wrote:
>> On Wed, 8 Jun 2011 17:45:54 -0500 Timur Tabi wrote:
>>
>>> Add the drivers/virt directory, which houses drivers that support
>>> virtualization environments, and add the Freescale hypervisor management
>>> driver.
>> It can't go in linux/virt or linux/virt/fsl instead? why drivers/ ?
>>
>> or maybe linux/virt should be drivers/virt ?
> See discussion for v2 of this patch. I suggested that drivers/firmware and virt/
> as options, the counterarguments were that drivers/firmware is for passive
> firmware as opposed to firmware that acts as a hypervisor, and that virt/ is
> for the host side of hypervisors like kvm, not for guests.
>
> The driver in here most closely resembles the xen dom0 model, where a
> priviledged guest controls other guests, but unlike xen there is a single
> driver file, so there is no need to have drivers/fsl-hv directory just
> for this one file. We do have a number of other hypervisors that fit in the
> same category, so they can be added here later.
This still leaves open the question of what really should go in this new
directory. Is it just for drivers that manage/control the hypervisor? Or
is it also for drivers that just use the hypervisor to do I/O of some kind,
but aren't related to any other "family" of drivers, i.e., a driver that
would have been dumped in drivers/char or drivers/misc in the old days?
My specific interest at the moment is the proposed tile-srom.c driver
(https://patchwork.kernel.org/patch/843892/), which uses a simple
hypervisor read/write API to access the portion of the SPI ROM used to hold
the boot stream for a TILE processor.
--
Chris Metcalf, Tilera Corp.
http://www.tilera.com
^ permalink raw reply
* Re: virtio scsi host draft specification, v3
From: Hannes Reinecke @ 2011-06-10 12:55 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Christoph Hellwig, Stefan Hajnoczi, kvm@vger.kernel.org,
Michael S. Tsirkin, qemu-devel, Linux Kernel Mailing List,
Linux Virtualization
In-Reply-To: <4DEE2B15.4090809@redhat.com>
On 06/07/2011 03:43 PM, Paolo Bonzini wrote:
> Hi all,
>
> after some preliminary discussion on the QEMU mailing list, I present a
> draft specification for a virtio-based SCSI host (controller, HBA, you
> name it).
>
> The virtio SCSI host is the basis of an alternative storage stack for
> KVM. This stack would overcome several limitations of the current
> solution, virtio-blk:
>
> 1) scalability limitations: virtio-blk-over-PCI puts a strong upper
> limit on the number of devices that can be added to a guest. Common
> configurations have a limit of ~30 devices. While this can be worked
> around by implementing a PCI-to-PCI bridge, or by using multifunction
> virtio-blk devices, these solutions either have not been implemented
> yet, or introduce management restrictions. On the other hand, the SCSI
> architecture is well known for its scalability and virtio-scsi supports
> advanced feature such as multiqueueing.
>
> 2) limited flexibility: virtio-blk does not support all possible storage
> scenarios. For example, it does not allow SCSI passthrough or persistent
> reservations. In principle, virtio-scsi provides anything that the
> underlying SCSI target (be it physical storage, iSCSI or the in-kernel
> target) supports.
>
> 3) limited extensibility: over the time, many features have been added
> to virtio-blk. Each such change requires modifications to the virtio
> specification, to the guest drivers, and to the device model in the
> host. The virtio-scsi spec has been written to follow SAM conventions,
> and exposing new features to the guest will only require changes to the
> host's SCSI target implementation.
>
>
> Comments are welcome.
>
> Paolo
>
> ------------------------------->8 -----------------------------------
>
>
> Virtio SCSI Host Device Spec
> ============================
>
> The virtio SCSI host device groups together one or more simple virtual
> devices (ie. disk), and allows communicating to these devices using the
> SCSI protocol. An instance of the device represents a SCSI host with
> possibly many buses, targets and LUN attached.
>
> The virtio SCSI device services two kinds of requests:
>
> - command requests for a logical unit;
>
> - task management functions related to a logical unit, target or
> command.
>
> The device is also able to send out notifications about added
> and removed logical units.
>
> v1:
> First public version
>
> v2:
> Merged all virtqueues into one, removed separate TARGET fields
>
> v3:
> Added configuration information and reworked descriptor structure.
> Added back multiqueue on Avi's request, while still leaving TARGET
> fields out. Added dummy event and clarified some aspects of the
> event protocol. First version sent to a wider audience (linux-kernel
> and virtio lists).
>
> Configuration
> -------------
>
> Subsystem Device ID
> TBD
>
> Virtqueues
> 0:controlq
> 1:eventq
> 2..n:request queues
>
> Feature bits
> VIRTIO_SCSI_F_INOUT (0) - Whether a single request can include both
> read-only and write-only data buffers.
>
> Device configuration layout
> struct virtio_scsi_config {
> u32 num_queues;
> u32 event_info_size;
> u32 sense_size;
> u32 cdb_size;
> }
>
> num_queues is the total number of virtqueues exposed by the
> device. The driver is free to use only one request queue, or
> it can use more to achieve better performance.
>
> event_info_size is the maximum size that the device will fill
> for buffers that the driver places in the eventq. The
> driver should always put buffers at least of this size.
>
> sense_size is the maximum size of the sense data that the device
> will write. The default value is written by the device and
> will always be 96, but the driver can modify it.
>
> cdb_size is the maximum size of the CBD that the driver
> will write. The default value is written by the device and
> will always be 32, but the driver can likewise modify it.
>
> Device initialization
> ---------------------
>
> The initialization routine should first of all discover the device's
> virtqueues.
>
> The driver should then place at least a buffer in the eventq.
> Buffers returned by the device on the eventq may be referred
> to as "events" in the rest of the document.
>
> The driver can immediately issue requests (for example, INQUIRY or
> REPORT LUNS) or task management functions (for example, I_T RESET).
>
> Device operation: request queues
> --------------------------------
>
> The driver queues requests to an arbitrary request queue, and they are
> used by the device on that same queue.
>
What about request ordering?
If requests are placed on arbitrary queues you'll inevitably run on
locking issues to ensure strict request ordering.
I would add here:
If a device uses more than one queue it is the responsibility of the
device to ensure strict request ordering.
> Requests have the following format:
>
> struct virtio_scsi_req_cmd {
> u8 lun[8];
> u64 id;
> u8 task_attr;
> u8 prio;
> u8 crn;
> char cdb[cdb_size];
> char dataout[];
>
> u8 sense[sense_size];
> u32 sense_len;
> u32 residual;
> u16 status_qualifier;
> u8 status;
> u8 response;
> char datain[];
> };
>
> /* command-specific response values */
> #define VIRTIO_SCSI_S_OK 0
> #define VIRTIO_SCSI_S_UNDERRUN 1
> #define VIRTIO_SCSI_S_ABORTED 2
> #define VIRTIO_SCSI_S_FAILURE 3
>
> /* task_attr */
> #define VIRTIO_SCSI_S_SIMPLE 0
> #define VIRTIO_SCSI_S_ORDERED 1
> #define VIRTIO_SCSI_S_HEAD 2
> #define VIRTIO_SCSI_S_ACA 3
>
> The lun field addresses a bus, target and logical unit in the SCSI
> host. The id field is the command identifier as defined in SAM.
>
Please do not rely in bus/target/lun here. These are leftovers from
parallel SCSI and do not have any meaning on modern SCSI
implementation (eg FC or SAS). Rephrase that to
The lun field is the Logical Unit Number as defined in SAM.
> Task_attr, prio and CRN are defined in SAM. The prio field should
> always be zero, as command priority is explicitly not supported by
> this version of the device. task_attr defines the task attribute as
> in the table above, Note that all task attributes may be mapped to
> SIMPLE by the device. CRN is generally expected to be 0, but clients
> can provide it. The maximum CRN value defined by the protocol is 255,
> since CRN is stored in an 8-bit integer.
>
> All of these fields are always read-only, as are the cdb and dataout
> field. sense and subsequent fields are always write-only.
>
> The sense_len field indicates the number of bytes actually written
> to the sense buffer. The residual field indicates the residual
> size, calculated as data_length - number_of_transferred_bytes, for
> read or write operations.
>
> The status byte is written by the device to be the SCSI status code.
>
?? I doubt that exists. Make that:
The status byte is written by the device to be the status code as
defined in SAM.
> The response byte is written by the device to be one of the following:
>
> - VIRTIO_SCSI_S_OK when the request was completed and the status byte
> is filled with a SCSI status code (not necessarily "GOOD").
>
> - VIRTIO_SCSI_S_UNDERRUN if the content of the CDB requires transferring
> more data than is available in the data buffers.
>
> - VIRTIO_SCSI_S_ABORTED if the request was cancelled due to a reset
> or another task management function.
>
> - VIRTIO_SCSI_S_FAILURE for other host or guest error. In particular,
> if neither dataout nor datain is empty, and the VIRTIO_SCSI_F_INOUT
> feature has not been negotiated, the request will be immediately
> returned with a response equal to VIRTIO_SCSI_S_FAILURE.
>
And, of course:
VIRTIO_SCSI_S_DISCONNECT if the request could not be processed due
to a communication failure (eg device was removed or could not be
reached).
The remaining bits seem to be okay.
One general question:
This specification implies a strict one-to-one mapping between host
and target. IE there is no way of specifying more than one target
per host.
This will make things like ALUA (Asymmetric Logical Unit Access)
a bit tricky to implement, as the port states there are bound to
target port groups. So with the virtio host spec we would need to
specify two hosts to represent that.
If that's the intention here I'm fine, but maybe we should be
specifying this expressis verbis somewhere.
Cheers,
Hannes
--
Dr. Hannes Reinecke zSeries & Storage
hare@suse.de +49 911 74053 688
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 16746 (AG Nürnberg)
^ permalink raw reply
* Re: virtio scsi host draft specification, v3
From: Paolo Bonzini @ 2011-06-10 12:22 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Christoph Hellwig, Stefan Hajnoczi, kvm@vger.kernel.org,
Michael S. Tsirkin, qemu-devel, Linux Kernel Mailing List,
Linux Virtualization
In-Reply-To: <BANLkTimqrZYnnVq5KTh3s68_o3Lfr9oWeA@mail.gmail.com>
On 06/10/2011 02:14 PM, Stefan Hajnoczi wrote:
> Paolo, I'll switch the Linux guest LLD and QEMU virtio-scsi skeleton
> that I have to comply with the spec. Does this sound good or did you
> want to write these from scratch?
Why should I want to write things from scratch? :) Just send me again a
pointer to your git tree, I'll make sure to add it as a remote this time
(private mail will do).
Thanks,
Paolo
^ permalink raw reply
* Re: virtio scsi host draft specification, v3
From: Stefan Hajnoczi @ 2011-06-10 12:14 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Christoph Hellwig, Stefan Hajnoczi, kvm@vger.kernel.org,
Michael S. Tsirkin, qemu-devel, Linux Kernel Mailing List,
Linux Virtualization
In-Reply-To: <87lix9dioo.fsf@rustcorp.com.au>
On Fri, Jun 10, 2011 at 12:33 PM, Rusty Russell <rusty@rustcorp.com.au> wrote:
> On Thu, 09 Jun 2011 08:59:27 +0200, Paolo Bonzini <pbonzini@redhat.com> wrote:
>> On 06/09/2011 01:28 AM, Rusty Russell wrote:
>> >> > after some preliminary discussion on the QEMU mailing list, I present a
>> >> > draft specification for a virtio-based SCSI host (controller, HBA, you
>> >> > name it).
>> >
>> > OK, I'm impressed. This is very well written and it doesn't make any of
>> > the obvious mistakes wrt. virtio.
>>
>> Thanks very much, and thanks to those who corrected my early mistakes.
>>
>> > I assume you have an implementation, as well?
>>
>> Unfortunately not; "we're working on it", which means I should start in
>> July when I come back from vacation.
>>
>> Do you prefer to wait for one before I make a patch to the LyX source?
>> In the meanwhile, can you reserve a subsystem ID for me?
>>
>> Paolo
>
> Sure, you can have the next subsystem ID.
>
> It's a pain to patch once it's in LyX, so let's get the implementation
> base on what you posted here an see how much it changes first...
Paolo, I'll switch the Linux guest LLD and QEMU virtio-scsi skeleton
that I have to comply with the spec. Does this sound good or did you
want to write these from scratch?
Stefan
^ permalink raw reply
* Re: virtio scsi host draft specification, v3
From: Rusty Russell @ 2011-06-10 11:33 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Christoph Hellwig, Stefan Hajnoczi, kvm@vger.kernel.org,
Michael S. Tsirkin, qemu-devel, Linux Kernel Mailing List,
Linux Virtualization
In-Reply-To: <4DF06F4F.8050308@redhat.com>
On Thu, 09 Jun 2011 08:59:27 +0200, Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 06/09/2011 01:28 AM, Rusty Russell wrote:
> >> > after some preliminary discussion on the QEMU mailing list, I present a
> >> > draft specification for a virtio-based SCSI host (controller, HBA, you
> >> > name it).
> >
> > OK, I'm impressed. This is very well written and it doesn't make any of
> > the obvious mistakes wrt. virtio.
>
> Thanks very much, and thanks to those who corrected my early mistakes.
>
> > I assume you have an implementation, as well?
>
> Unfortunately not; "we're working on it", which means I should start in
> July when I come back from vacation.
>
> Do you prefer to wait for one before I make a patch to the LyX source?
> In the meanwhile, can you reserve a subsystem ID for me?
>
> Paolo
Sure, you can have the next subsystem ID.
It's a pain to patch once it's in LyX, so let's get the implementation
base on what you posted here an see how much it changes first...
Cheers,
Rusty.
^ permalink raw reply
* Re: [PATCH] virtio_net: introduce VIRTIO_NET_HDR_F_DATA_VALID
From: Michael S. Tsirkin @ 2011-06-10 11:28 UTC (permalink / raw)
To: Jason Wang
Cc: yvugenfi, kvm, netdev, linux-kernel, virtualization, herbert,
davem
In-Reply-To: <1307703377-3798-1-git-send-email-jasowang@redhat.com>
On Fri, Jun 10, 2011 at 06:56:17PM +0800, Jason Wang wrote:
> There's no need for the guest to validate the checksum if it have been
> validated by host nics. So this patch introduces a new flag -
> VIRTIO_NET_HDR_F_DATA_VALID which is used to bypass the checksum
> examing in guest. The backend (tap/macvtap) may set this flag when
> met skbs with CHECKSUM_UNNECESSARY to save cpu utilization.
>
> No feature negotiation is needed as old driver just ignore this flag.
This wasn't required by the spec, but maybe it should be.
> Iperf shows 12%-30% performance improvement for UDP traffic. For TCP,
> when gro is on no difference as it produces skb with partial
> checksum. But when gro is disabled, 20% or even higher improvement
> could be measured by netperf.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
> ---
> drivers/net/macvtap.c | 2 ++
> drivers/net/tun.c | 2 ++
> drivers/net/virtio_net.c | 2 ++
> include/linux/virtio_net.h | 1 +
> net/packet/af_packet.c | 2 ++
> 5 files changed, 9 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
> index 6696e56..ecee0fe 100644
> --- a/drivers/net/macvtap.c
> +++ b/drivers/net/macvtap.c
> @@ -508,6 +508,8 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
> vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
> vnet_hdr->csum_start = skb_checksum_start_offset(skb);
> vnet_hdr->csum_offset = skb->csum_offset;
> + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
> + vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
> } /* else everything is zero */
>
> return 0;
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index 74e9405..f43fa45 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -788,6 +788,8 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
> gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
> gso.csum_start = skb_checksum_start_offset(skb);
> gso.csum_offset = skb->csum_offset;
> + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
> + gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
> } /* else everything is zero */
>
> if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index f685324..be3686a 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -274,6 +274,8 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len)
> hdr->hdr.csum_start,
> hdr->hdr.csum_offset))
> goto frame_err;
> + } else if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID) {
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> }
>
> skb->protocol = eth_type_trans(skb, dev);
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index 136040b..970d5a2 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
> @@ -63,6 +63,7 @@ struct virtio_net_config {
> * specify GSO or CSUM features, you can simply ignore the header. */
> struct virtio_net_hdr {
> #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset
> +#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid
> __u8 flags;
> #define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame
> #define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index c0c3cda..99ca471 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -1681,6 +1681,8 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
> vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
> vnet_hdr.csum_start = skb_checksum_start_offset(skb);
> vnet_hdr.csum_offset = skb->csum_offset;
> + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
> + vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID;
> } /* else everything is zero */
>
> err = memcpy_toiovec(msg->msg_iov, (void *)&vnet_hdr,
> --
> 1.7.1
^ permalink raw reply
* [PATCH] virtio_net: introduce VIRTIO_NET_HDR_F_DATA_VALID
From: Jason Wang @ 2011-06-10 10:56 UTC (permalink / raw)
To: rusty, mst, davem, netdev, linux-kernel, virtualization
Cc: herbert, yvugenfi, kvm
There's no need for the guest to validate the checksum if it have been
validated by host nics. So this patch introduces a new flag -
VIRTIO_NET_HDR_F_DATA_VALID which is used to bypass the checksum
examing in guest. The backend (tap/macvtap) may set this flag when
met skbs with CHECKSUM_UNNECESSARY to save cpu utilization.
No feature negotiation is needed as old driver just ignore this flag.
Iperf shows 12%-30% performance improvement for UDP traffic. For TCP,
when gro is on no difference as it produces skb with partial
checksum. But when gro is disabled, 20% or even higher improvement
could be measured by netperf.
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
drivers/net/macvtap.c | 2 ++
drivers/net/tun.c | 2 ++
drivers/net/virtio_net.c | 2 ++
include/linux/virtio_net.h | 1 +
net/packet/af_packet.c | 2 ++
5 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 6696e56..ecee0fe 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -508,6 +508,8 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
vnet_hdr->csum_start = skb_checksum_start_offset(skb);
vnet_hdr->csum_offset = skb->csum_offset;
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
return 0;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 74e9405..f43fa45 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -788,6 +788,8 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
gso.csum_start = skb_checksum_start_offset(skb);
gso.csum_offset = skb->csum_offset;
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index f685324..be3686a 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -274,6 +274,8 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len)
hdr->hdr.csum_start,
hdr->hdr.csum_offset))
goto frame_err;
+ } else if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
}
skb->protocol = eth_type_trans(skb, dev);
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 136040b..970d5a2 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -63,6 +63,7 @@ struct virtio_net_config {
* specify GSO or CSUM features, you can simply ignore the header. */
struct virtio_net_hdr {
#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset
+#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid
__u8 flags;
#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame
#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index c0c3cda..99ca471 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1681,6 +1681,8 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
vnet_hdr.csum_start = skb_checksum_start_offset(skb);
vnet_hdr.csum_offset = skb->csum_offset;
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
err = memcpy_toiovec(msg->msg_iov, (void *)&vnet_hdr,
--
1.7.1
^ permalink raw reply related
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Mark Brown @ 2011-06-10 10:45 UTC (permalink / raw)
To: Arnd Bergmann
Cc: konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
linux-console, greg, virtualization, linuxppc-dev, Timur Tabi,
alan
In-Reply-To: <201106092233.08030.arnd@arndb.de>
On Thu, Jun 09, 2011 at 10:33:07PM +0200, Arnd Bergmann wrote:
> On Thursday 09 June 2011 22:18:28 Timur Tabi wrote:
> > Ok, I was really hoping to avoid doing this. Like I said, binary compatibility
> > is important, and changing the type will break my existing apps. Are you
> > insisting that I pick a new number?
> I definitely insist that you have a proper interface in the driver at the
> time that it gets merged, and that probably includes a collision-free
> ioctl code.
This sort of stuff is one of the issues that should be being factored in
to any decision not to publish and submit the kernel code - ABIs that
haven't been reviewed upstream may well have this sort of issue.
^ permalink raw reply
* RE: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisormanagement driver
From: David Laight @ 2011-06-10 8:32 UTC (permalink / raw)
To: Arnd Bergmann, Timur Tabi, linuxppc-dev
Cc: konrad.wilk, greg, kumar.gala, linux-kernel, cmetcalf,
linux-console, virtualization, alan
In-Reply-To: <201106092213.13755.arnd@arndb.de>
> > +enum fsl_hv_ioctl_cmd {
> > + FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct
fsl_hv_ioctl_restart),
...
> > +};
>
> Using a #define here is usually preferred because then you
> can use #ifdef in a user application to check if a given
> value has been assigned.
It is also possible to add:
#define FSL_HV_IOCTL_PARTITION_RESTART FSL_HV_IOCTL_PARTITION_RESTART
to have much the same effect.
But there are many cases where #defines are better.
I only tend to use enums when the constanst are beting generated
by the expansion of a #define.
> More importantly, the code you have chose (0) conflicts with
> existing drivers (frame buffer, scsi and wavefront among others).
> Please chose a free one and
> add it to Documentation/ioctl/ioctl-number.txt in the same patch.
It is rather a PITA that, when 'int' went from 16 to 32 bits, the
BSD people used the high 16 bits for size/flags rather than
using the extra bits to help make ioctl's unique.
Linux seems to have copied BSD here - rather than SYSV.
One problem with clashing ioctl commands is when systems like
NetBSD are running linux binaries and need to translate ioctl
buffers to/from native format. If the ioctl commands are
unique this can be done much more easily.
David
^ permalink raw reply
* Re: [PATCH 7/7] [v6] drivers/virt: introduce Freescale hypervisor management driver
From: Arnd Bergmann @ 2011-06-09 21:04 UTC (permalink / raw)
To: virtualization
Cc: konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf, dsaxena,
linux-console, greg, linuxppc-dev, Timur Tabi, alan
In-Reply-To: <1307652726-4356-1-git-send-email-timur@freescale.com>
On Thursday 09 June 2011 22:52:06 Timur Tabi wrote:
> Add the drivers/virt directory, which houses drivers that support
> virtualization environments, and add the Freescale hypervisor management
> driver.
>
> The Freescale hypervisor management driver provides several services to
> drivers and applications related to the Freescale hypervisor:
>
> 1. An ioctl interface for querying and managing partitions
>
> 2. A file interface to reading incoming doorbells
>
> 3. An interrupt handler for shutting down the partition upon receiving the
> shutdown doorbell from a manager partition
>
> 4. A kernel interface for receiving callbacks when a managed partition
> shuts down.
>
> Signed-off-by: Timur Tabi <timur@freescale.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* [PATCH 7/7] [v6] drivers/virt: introduce Freescale hypervisor management driver
From: Timur Tabi @ 2011-06-09 20:52 UTC (permalink / raw)
To: alan, arnd, kumar.gala, benh, greg, akpm, cmetcalf, konrad.w
Add the drivers/virt directory, which houses drivers that support
virtualization environments, and add the Freescale hypervisor management
driver.
The Freescale hypervisor management driver provides several services to
drivers and applications related to the Freescale hypervisor:
1. An ioctl interface for querying and managing partitions
2. A file interface to reading incoming doorbells
3. An interrupt handler for shutting down the partition upon receiving the
shutdown doorbell from a manager partition
4. A kernel interface for receiving callbacks when a managed partition
shuts down.
Signed-off-by: Timur Tabi <timur@freescale.com>
---
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/Kconfig | 2 +
drivers/Makefile | 3 +
drivers/virt/Kconfig | 32 ++
drivers/virt/Makefile | 5 +
drivers/virt/fsl_hypervisor.c | 937 ++++++++++++++++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/fsl_hypervisor.h | 241 +++++++++
8 files changed, 1222 insertions(+), 0 deletions(-)
create mode 100644 drivers/virt/Kconfig
create mode 100644 drivers/virt/Makefile
create mode 100644 drivers/virt/fsl_hypervisor.c
create mode 100644 include/linux/fsl_hypervisor.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index a0a5d82..9b8a8bd 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -301,6 +301,7 @@ Code Seq#(hex) Include File Comments
<mailto:rusty@rustcorp.com.au>
0xAE all linux/kvm.h Kernel-based Virtual Machine
<mailto:kvm@vger.kernel.org>
+0xAF 00-1F linux/fsl_hypervisor.h Freescale hypervisor
0xB0 all RATIO devices in development:
<mailto:vgo@ratio.de>
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 557a469..0371680 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -122,4 +122,6 @@ source "drivers/hwspinlock/Kconfig"
source "drivers/clocksource/Kconfig"
+source "drivers/virt/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 3f135b6..bbe2918 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -119,3 +119,6 @@ obj-y += ieee802154/
obj-y += clk/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
+
+# Virtualization drivers
+obj-$(CONFIG_VIRT_DRIVERS) += virt/
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
new file mode 100644
index 0000000..2dcdbc9
--- /dev/null
+++ b/drivers/virt/Kconfig
@@ -0,0 +1,32 @@
+#
+# Virtualization support drivers
+#
+
+menuconfig VIRT_DRIVERS
+ bool "Virtualization drivers"
+ ---help---
+ Say Y here to get to see options for device drivers that support
+ virtualization environments.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if VIRT_DRIVERS
+
+config FSL_HV_MANAGER
+ tristate "Freescale hypervisor management driver"
+ depends on FSL_SOC
+ help
+ The Freescale hypervisor management driver provides several services
+ to drivers and applications related to the Freescale hypervisor:
+
+ 1) An ioctl interface for querying and managing partitions.
+
+ 2) A file interface to reading incoming doorbells.
+
+ 3) An interrupt handler for shutting down the partition upon
+ receiving the shutdown doorbell from a manager partition.
+
+ 4) A kernel interface for receiving callbacks when a managed
+ partition shuts down.
+
+endif
diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
new file mode 100644
index 0000000..c47f04d
--- /dev/null
+++ b/drivers/virt/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for drivers that support virtualization
+#
+
+obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o
diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c
new file mode 100644
index 0000000..1d3b8eb
--- /dev/null
+++ b/drivers/virt/fsl_hypervisor.c
@@ -0,0 +1,937 @@
+/*
+ * Freescale Hypervisor Management Driver
+
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * The Freescale hypervisor management driver provides several services to
+ * drivers and applications related to the Freescale hypervisor:
+ *
+ * 1. An ioctl interface for querying and managing partitions.
+ *
+ * 2. A file interface to reading incoming doorbells.
+ *
+ * 3. An interrupt handler for shutting down the partition upon receiving the
+ * shutdown doorbell from a manager partition.
+ *
+ * 4. A kernel interface for receiving callbacks when a managed partition
+ * shuts down.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/notifier.h>
+
+#include <linux/io.h>
+#include <asm/fsl_hcalls.h>
+
+#include <linux/fsl_hypervisor.h>
+
+static BLOCKING_NOTIFIER_HEAD(failover_subscribers);
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_RESTART
+ *
+ * Restart a running partition
+ */
+static long ioctl_restart(struct fsl_hv_ioctl_restart __user *p)
+{
+ struct fsl_hv_ioctl_restart param;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_restart)))
+ return -EFAULT;
+
+ param.ret = fh_partition_restart(param.partition);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_STATUS
+ *
+ * Query the status of a partition
+ */
+static long ioctl_status(struct fsl_hv_ioctl_status __user *p)
+{
+ struct fsl_hv_ioctl_status param;
+ u32 status;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_status)))
+ return -EFAULT;
+
+ param.ret = fh_partition_get_status(param.partition, &status);
+ if (!param.ret)
+ param.status = status;
+
+ if (copy_to_user(p, ¶m, sizeof(struct fsl_hv_ioctl_status)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_START
+ *
+ * Start a stopped partition.
+ */
+static long ioctl_start(struct fsl_hv_ioctl_start __user *p)
+{
+ struct fsl_hv_ioctl_start param;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_start)))
+ return -EFAULT;
+
+ param.ret = fh_partition_start(param.partition, param.entry_point,
+ param.load);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_STOP
+ *
+ * Stop a running partition
+ */
+static long ioctl_stop(struct fsl_hv_ioctl_stop __user *p)
+{
+ struct fsl_hv_ioctl_stop param;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_stop)))
+ return -EFAULT;
+
+ param.ret = fh_partition_stop(param.partition);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_MEMCPY
+ *
+ * The FH_MEMCPY hypercall takes an array of address/address/size structures
+ * to represent the data being copied. As a convenience to the user, this
+ * ioctl takes a user-create buffer and a pointer to a guest physically
+ * contiguous buffer in the remote partition, and creates the
+ * address/address/size array for the hypercall.
+ */
+static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
+{
+ struct fsl_hv_ioctl_memcpy param;
+
+ struct page **pages = NULL;
+ void *sg_list_unaligned = NULL;
+ struct fh_sg_list *sg_list = NULL;
+
+ unsigned int num_pages;
+ unsigned long lb_offset; /* Offset within a page of the local buffer */
+
+ unsigned int i;
+ long ret = 0;
+ int num_pinned; /* return value from get_user_pages() */
+ phys_addr_t remote_paddr; /* The next address in the remote buffer */
+ uint32_t count; /* The number of bytes left to copy */
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_memcpy)))
+ return -EFAULT;
+
+ /*
+ * One partition must be local, the other must be remote. In other
+ * words, if source and target are both -1, or are both not -1, then
+ * return an error.
+ */
+ if ((param.source == -1) == (param.target == -1))
+ return -EINVAL;
+
+ /*
+ * The array of pages returned by get_user_pages() covers only
+ * page-aligned memory. Since the user buffer is probably not
+ * page-aligned, we need to handle the discrepancy.
+ *
+ * We calculate the offset within a page of the S/G list, and make
+ * adjustments accordingly. This will result in a page list that looks
+ * like this:
+ *
+ * ---- <-- first page starts before the buffer
+ * | |
+ * |////|-> ----
+ * |////| | |
+ * ---- | |
+ * | |
+ * ---- | |
+ * |////| | |
+ * |////| | |
+ * |////| | |
+ * ---- | |
+ * | |
+ * ---- | |
+ * |////| | |
+ * |////| | |
+ * |////| | |
+ * ---- | |
+ * | |
+ * ---- | |
+ * |////| | |
+ * |////|-> ----
+ * | | <-- last page ends after the buffer
+ * ----
+ *
+ * The distance between the start of the first page and the start of the
+ * buffer is lb_offset. The hashed (///) areas are the parts of the
+ * page list that contain the actual buffer.
+ *
+ * The advantage of this approach is that the number of pages is
+ * equal to the number of entries in the S/G list that we give to the
+ * hypervisor.
+ */
+ lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
+ num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ /* Allocate the buffers we need */
+
+ /*
+ * 'pages' is an array of struct page pointers that's initialized by
+ * get_user_pages().
+ */
+ pages = kzalloc(num_pages * sizeof(struct page *), GFP_KERNEL);
+ if (!pages) {
+ pr_debug("fsl-hv: could not allocate page list\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * sg_list is the list of fh_sg_list objects that we pass to the
+ * hypervisor.
+ */
+ sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
+ sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
+ if (!sg_list_unaligned) {
+ pr_debug("fsl-hv: could not allocate S/G list\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));
+
+ /* Get the physical addresses of the source buffer */
+ down_read(¤t->mm->mmap_sem);
+ num_pinned = get_user_pages(current, current->mm,
+ param.local_vaddr - lb_offset, num_pages,
+ (param.source == -1) ? READ : WRITE,
+ 0, pages, NULL);
+ up_read(¤t->mm->mmap_sem);
+
+ if (num_pinned != num_pages) {
+ /* get_user_pages() failed */
+ pr_debug("fsl-hv: could not lock source buffer\n");
+ ret = (num_pinned < 0) ? num_pinned : -EFAULT;
+ goto exit;
+ }
+
+ /*
+ * Build the fh_sg_list[] array. The first page is special
+ * because it's misaligned.
+ */
+ if (param.source == -1) {
+ sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
+ sg_list[0].target = param.remote_paddr;
+ } else {
+ sg_list[0].source = param.remote_paddr;
+ sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
+ }
+ sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
+
+ remote_paddr = param.remote_paddr + sg_list[0].size;
+ count = param.count - sg_list[0].size;
+
+ for (i = 1; i < num_pages; i++) {
+ if (param.source == -1) {
+ /* local to remote */
+ sg_list[i].source = page_to_phys(pages[i]);
+ sg_list[i].target = remote_paddr;
+ } else {
+ /* remote to local */
+ sg_list[i].source = remote_paddr;
+ sg_list[i].target = page_to_phys(pages[i]);
+ }
+ sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
+
+ remote_paddr += sg_list[i].size;
+ count -= sg_list[i].size;
+ }
+
+ param.ret = fh_partition_memcpy(param.source, param.target,
+ virt_to_phys(sg_list), num_pages);
+
+exit:
+ if (pages) {
+ for (i = 0; i < num_pages; i++)
+ if (pages[i])
+ put_page(pages[i]);
+ }
+
+ kfree(sg_list_unaligned);
+ kfree(pages);
+
+ if (!ret)
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return ret;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_DOORBELL
+ *
+ * Ring a doorbell
+ */
+static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
+{
+ struct fsl_hv_ioctl_doorbell param;
+
+ /* Get the parameters from the user. */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_doorbell)))
+ return -EFAULT;
+
+ param.ret = ev_doorbell_send(param.doorbell);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
+{
+ struct fsl_hv_ioctl_prop param;
+ char __user *upath, *upropname;
+ void __user *upropval;
+ char *path = NULL, *propname = NULL;
+ void *propval = NULL;
+ int ret = 0;
+
+ /* Get the parameters from the user. */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_prop)))
+ return -EFAULT;
+
+ upath = (char __user *)(uintptr_t)param.path;
+ upropname = (char __user *)(uintptr_t)param.propname;
+ upropval = (void __user *)(uintptr_t)param.propval;
+
+ path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ goto out;
+ }
+
+ propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
+ if (IS_ERR(propname)) {
+ ret = PTR_ERR(propname);
+ goto out;
+ }
+
+ if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ propval = kmalloc(param.proplen, GFP_KERNEL);
+ if (!propval) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (set) {
+ if (copy_from_user(propval, upropval, param.proplen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ param.ret = fh_partition_set_dtprop(param.handle,
+ virt_to_phys(path),
+ virt_to_phys(propname),
+ virt_to_phys(propval),
+ param.proplen);
+ } else {
+ param.ret = fh_partition_get_dtprop(param.handle,
+ virt_to_phys(path),
+ virt_to_phys(propname),
+ virt_to_phys(propval),
+ ¶m.proplen);
+
+ if (param.ret == 0) {
+ if (copy_to_user(upropval, propval, param.proplen) ||
+ put_user(param.proplen, &p->proplen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+ }
+
+ if (put_user(param.ret, &p->ret))
+ ret = -EFAULT;
+
+out:
+ kfree(path);
+ kfree(propval);
+ kfree(propname);
+
+ return ret;
+}
+
+/*
+ * Ioctl main entry point
+ */
+static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
+ unsigned long argaddr)
+{
+ void __user *arg = (void __user *)argaddr;
+ long ret;
+
+ switch (cmd) {
+ case FSL_HV_IOCTL_PARTITION_RESTART:
+ ret = ioctl_restart(arg);
+ break;
+ case FSL_HV_IOCTL_PARTITION_GET_STATUS:
+ ret = ioctl_status(arg);
+ break;
+ case FSL_HV_IOCTL_PARTITION_START:
+ ret = ioctl_start(arg);
+ break;
+ case FSL_HV_IOCTL_PARTITION_STOP:
+ ret = ioctl_stop(arg);
+ break;
+ case FSL_HV_IOCTL_MEMCPY:
+ ret = ioctl_memcpy(arg);
+ break;
+ case FSL_HV_IOCTL_DOORBELL:
+ ret = ioctl_doorbell(arg);
+ break;
+ case FSL_HV_IOCTL_GETPROP:
+ ret = ioctl_dtprop(arg, 0);
+ break;
+ case FSL_HV_IOCTL_SETPROP:
+ ret = ioctl_dtprop(arg, 1);
+ break;
+ default:
+ pr_debug("fsl-hv: bad ioctl dir=%u type=%u cmd=%u size=%u\n",
+ _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
+ _IOC_SIZE(cmd));
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+/* Linked list of processes that have us open */
+static struct list_head db_list;
+
+/* spinlock for db_list */
+static DEFINE_SPINLOCK(db_list_lock);
+
+/* The size of the doorbell event queue. This must be a power of two. */
+#define QSIZE 16
+
+/* Returns the next head/tail pointer, wrapping around the queue if necessary */
+#define nextp(x) (((x) + 1) & (QSIZE - 1))
+
+/* Per-open data structure */
+struct doorbell_queue {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ unsigned int head;
+ unsigned int tail;
+ uint32_t q[QSIZE];
+};
+
+/* Linked list of ISRs that we registered */
+struct list_head isr_list;
+
+/* Per-ISR data structure */
+struct doorbell_isr {
+ struct list_head list;
+ unsigned int irq;
+ uint32_t doorbell; /* The doorbell handle */
+ uint32_t partition; /* The partition handle, if used */
+};
+
+/*
+ * Add a doorbell to all of the doorbell queues
+ */
+static void fsl_hv_queue_doorbell(uint32_t doorbell)
+{
+ struct doorbell_queue *dbq;
+ unsigned long flags;
+
+ /* Prevent another core from modifying db_list */
+ spin_lock_irqsave(&db_list_lock, flags);
+
+ list_for_each_entry(dbq, &db_list, list) {
+ if (dbq->head != nextp(dbq->tail)) {
+ dbq->q[dbq->tail] = doorbell;
+ /*
+ * This memory barrier eliminates the need to grab
+ * the spinlock for dbq.
+ */
+ smp_wmb();
+ dbq->tail = nextp(dbq->tail);
+ wake_up_interruptible(&dbq->wait);
+ }
+ }
+
+ spin_unlock_irqrestore(&db_list_lock, flags);
+}
+
+/*
+ * Interrupt handler for all doorbells
+ *
+ * We use the same interrupt handler for all doorbells. Whenever a doorbell
+ * is rung, and we receive an interrupt, we just put the handle for that
+ * doorbell (passed to us as *data) into all of the queues.
+ */
+static irqreturn_t fsl_hv_isr(int irq, void *data)
+{
+ fsl_hv_queue_doorbell((uintptr_t) data);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * State change thread function
+ *
+ * The state change notification arrives in an interrupt, but we can't call
+ * blocking_notifier_call_chain() in an interrupt handler. We could call
+ * atomic_notifier_call_chain(), but that would require the clients' call-back
+ * function to run in interrupt context. Since we don't want to impose that
+ * restriction on the clients, we use a threaded IRQ to process the
+ * notification in kernel context.
+ */
+static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
+{
+ struct doorbell_isr *dbisr = data;
+
+ blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Interrupt handler for state-change doorbells
+ */
+static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
+{
+ unsigned int status;
+ struct doorbell_isr *dbisr = data;
+ int ret;
+
+ /* It's still a doorbell, so add it to all the queues. */
+ fsl_hv_queue_doorbell(dbisr->doorbell);
+
+ /* Determine the new state, and if it's stopped, notify the clients. */
+ ret = fh_partition_get_status(dbisr->partition, &status);
+ if (!ret && (status == FH_PARTITION_STOPPED))
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Returns a bitmask indicating whether a read will block
+ */
+static unsigned int fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
+{
+ struct doorbell_queue *dbq = filp->private_data;
+ unsigned long flags;
+ unsigned int mask;
+
+ spin_lock_irqsave(&dbq->lock, flags);
+
+ poll_wait(filp, &dbq->wait, p);
+ mask = (dbq->head == dbq->tail) ? 0 : (POLLIN | POLLRDNORM);
+
+ spin_unlock_irqrestore(&dbq->lock, flags);
+
+ return mask;
+}
+
+/*
+ * Return the handles for any incoming doorbells
+ *
+ * If there are doorbell handles in the queue for this open instance, then
+ * return them to the caller as an array of 32-bit integers. Otherwise,
+ * block until there is at least one handle to return.
+ */
+static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
+ loff_t *off)
+{
+ struct doorbell_queue *dbq = filp->private_data;
+ uint32_t __user *p = (uint32_t __user *) buf; /* for put_user() */
+ unsigned long flags;
+ ssize_t count = 0;
+
+ /* Make sure we stop when the user buffer is full. */
+ while (len >= sizeof(uint32_t)) {
+ uint32_t dbell; /* Local copy of doorbell queue data */
+
+ spin_lock_irqsave(&dbq->lock, flags);
+
+ /*
+ * If the queue is empty, then either we're done or we need
+ * to block. If the application specified O_NONBLOCK, then
+ * we return the appropriate error code.
+ */
+ if (dbq->head == dbq->tail) {
+ spin_unlock_irqrestore(&dbq->lock, flags);
+ if (count)
+ break;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ if (wait_event_interruptible(dbq->wait,
+ dbq->head != dbq->tail))
+ return -ERESTARTSYS;
+ continue;
+ }
+
+ /*
+ * Even though we have an smp_wmb() in the ISR, the core
+ * might speculatively execute the "dbell = ..." below while
+ * it's evaluating the if-statement above. In that case, the
+ * value put into dbell could be stale if the core accepts the
+ * speculation. To prevent that, we need a read memory barrier
+ * here as well.
+ */
+ smp_rmb();
+
+ /* Copy the data to a temporary local buffer, because
+ * we can't call copy_to_user() from inside a spinlock
+ */
+ dbell = dbq->q[dbq->head];
+ dbq->head = nextp(dbq->head);
+
+ spin_unlock_irqrestore(&dbq->lock, flags);
+
+ if (put_user(dbell, p))
+ return -EFAULT;
+ p++;
+ count += sizeof(uint32_t);
+ len -= sizeof(uint32_t);
+ }
+
+ return count;
+}
+
+/*
+ * Open the driver and prepare for reading doorbells.
+ *
+ * Every time an application opens the driver, we create a doorbell queue
+ * for that file handle. This queue is used for any incoming doorbells.
+ */
+static int fsl_hv_open(struct inode *inode, struct file *filp)
+{
+ struct doorbell_queue *dbq;
+ unsigned long flags;
+ int ret = 0;
+
+ dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
+ if (!dbq) {
+ pr_err("fsl-hv: out of memory\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dbq->lock);
+ init_waitqueue_head(&dbq->wait);
+
+ spin_lock_irqsave(&db_list_lock, flags);
+ list_add(&dbq->list, &db_list);
+ spin_unlock_irqrestore(&db_list_lock, flags);
+
+ filp->private_data = dbq;
+
+ return ret;
+}
+
+/*
+ * Close the driver
+ */
+static int fsl_hv_close(struct inode *inode, struct file *filp)
+{
+ struct doorbell_queue *dbq = filp->private_data;
+ unsigned long flags;
+
+ int ret = 0;
+
+ spin_lock_irqsave(&db_list_lock, flags);
+ list_del(&dbq->list);
+ spin_unlock_irqrestore(&db_list_lock, flags);
+
+ kfree(dbq);
+
+ return ret;
+}
+
+static const struct file_operations fsl_hv_fops = {
+ .owner = THIS_MODULE,
+ .open = fsl_hv_open,
+ .release = fsl_hv_close,
+ .poll = fsl_hv_poll,
+ .read = fsl_hv_read,
+ .unlocked_ioctl = fsl_hv_ioctl,
+};
+
+static struct miscdevice fsl_hv_misc_dev = {
+ MISC_DYNAMIC_MINOR,
+ "fsl-hv",
+ &fsl_hv_fops
+};
+
+static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
+{
+ orderly_poweroff(false);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Returns the handle of the parent of the given node
+ *
+ * The handle is the value of the 'hv-handle' property
+ */
+static int get_parent_handle(struct device_node *np)
+{
+ struct device_node *parent;
+ const uint32_t *prop;
+ uint32_t handle;
+ int len;
+
+ parent = of_get_parent(np);
+ if (!parent)
+ /* It's not really possible for this to fail */
+ return -ENODEV;
+
+ /*
+ * The proper name for the handle property is "hv-handle", but some
+ * older versions of the hypervisor used "reg".
+ */
+ prop = of_get_property(parent, "hv-handle", &len);
+ if (!prop)
+ prop = of_get_property(parent, "reg", &len);
+
+ if (!prop || (len != sizeof(uint32_t))) {
+ /* This can happen only if the node is malformed */
+ of_node_put(parent);
+ return -ENODEV;
+ }
+
+ handle = be32_to_cpup(prop);
+ of_node_put(parent);
+
+ return handle;
+}
+
+/*
+ * Register a callback for failover events
+ *
+ * This function is called by device drivers to register their callback
+ * functions for fail-over events.
+ */
+int fsl_hv_failover_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&failover_subscribers, nb);
+}
+EXPORT_SYMBOL(fsl_hv_failover_register);
+
+/*
+ * Unregister a callback for failover events
+ */
+int fsl_hv_failover_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&failover_subscribers, nb);
+}
+EXPORT_SYMBOL(fsl_hv_failover_unregister);
+
+/*
+ * Return TRUE if we're running under FSL hypervisor
+ *
+ * This function checks to see if we're running under the Freescale
+ * hypervisor, and returns zero if we're not, or non-zero if we are.
+ *
+ * First, it checks if MSR[GS]==1, which means we're running under some
+ * hypervisor. Then it checks if there is a hypervisor node in the device
+ * tree. Currently, that means there needs to be a node in the root called
+ * "hypervisor" and which has a property named "fsl,hv-version".
+ */
+static int has_fsl_hypervisor(void)
+{
+ struct device_node *node;
+ int ret;
+
+ if (!(mfmsr() & MSR_GS))
+ return 0;
+
+ node = of_find_node_by_path("/hypervisor");
+ if (!node)
+ return 0;
+
+ ret = of_find_property(node, "fsl,hv-version", NULL) != NULL;
+
+ of_node_put(node);
+
+ return ret;
+}
+
+/*
+ * Freescale hypervisor management driver init
+ *
+ * This function is called when this module is loaded.
+ *
+ * Register ourselves as a miscellaneous driver. This will register the
+ * fops structure and create the right sysfs entries for udev.
+ */
+static int __init fsl_hypervisor_init(void)
+{
+ struct device_node *np;
+ struct doorbell_isr *dbisr, *n;
+ int ret;
+
+ pr_info("Freescale hypervisor management driver\n");
+
+ if (!has_fsl_hypervisor()) {
+ pr_info("fsl-hv: no hypervisor found\n");
+ return -ENODEV;
+ }
+
+ ret = misc_register(&fsl_hv_misc_dev);
+ if (ret) {
+ pr_err("fsl-hv: cannot register device\n");
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&db_list);
+ INIT_LIST_HEAD(&isr_list);
+
+ for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
+ unsigned int irq;
+ const uint32_t *handle;
+
+ handle = of_get_property(np, "interrupts", NULL);
+ irq = irq_of_parse_and_map(np, 0);
+ if (!handle || (irq == NO_IRQ)) {
+ pr_err("fsl-hv: no 'interrupts' property in %s node\n",
+ np->full_name);
+ continue;
+ }
+
+ dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
+ if (!dbisr)
+ goto out_of_memory;
+
+ dbisr->irq = irq;
+ dbisr->doorbell = be32_to_cpup(handle);
+
+ if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
+ /* The shutdown doorbell gets its own ISR */
+ ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
+ np->name, NULL);
+ } else if (of_device_is_compatible(np,
+ "fsl,hv-state-change-doorbell")) {
+ /*
+ * The state change doorbell triggers a notification if
+ * the state of the managed partition changes to
+ * "stopped". We need a separate interrupt handler for
+ * that, and we also need to know the handle of the
+ * target partition, not just the handle of the
+ * doorbell.
+ */
+ dbisr->partition = ret = get_parent_handle(np);
+ if (ret < 0) {
+ pr_err("fsl-hv: node %s has missing or "
+ "malformed parent\n", np->full_name);
+ kfree(dbisr);
+ continue;
+ }
+ ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
+ fsl_hv_state_change_thread,
+ 0, np->name, dbisr);
+ } else
+ ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
+
+ if (ret < 0) {
+ pr_err("fsl-hv: could not request irq %u for node %s\n",
+ irq, np->full_name);
+ kfree(dbisr);
+ continue;
+ }
+
+ list_add(&dbisr->list, &isr_list);
+
+ pr_info("fsl-hv: registered handler for doorbell %u\n",
+ dbisr->doorbell);
+ }
+
+ return 0;
+
+out_of_memory:
+ list_for_each_entry_safe(dbisr, n, &isr_list, list) {
+ free_irq(dbisr->irq, dbisr);
+ list_del(&dbisr->list);
+ kfree(dbisr);
+ }
+
+ misc_deregister(&fsl_hv_misc_dev);
+
+ return -ENOMEM;
+}
+
+/*
+ * Freescale hypervisor management driver termination
+ *
+ * This function is called when this driver is unloaded.
+ */
+static void __exit fsl_hypervisor_exit(void)
+{
+ struct doorbell_isr *dbisr, *n;
+
+ list_for_each_entry_safe(dbisr, n, &isr_list, list) {
+ free_irq(dbisr->irq, dbisr);
+ list_del(&dbisr->list);
+ kfree(dbisr);
+ }
+
+ misc_deregister(&fsl_hv_misc_dev);
+}
+
+module_init(fsl_hypervisor_init);
+module_exit(fsl_hypervisor_exit);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale hypervisor management driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 75cf611..68c341a 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -134,6 +134,7 @@ header-y += firewire-cdev.h
header-y += firewire-constants.h
header-y += flat.h
header-y += fs.h
+header-y += fsl_hypervisor.h
header-y += fuse.h
header-y += futex.h
header-y += gameport.h
diff --git a/include/linux/fsl_hypervisor.h b/include/linux/fsl_hypervisor.h
new file mode 100644
index 0000000..1cebaee
--- /dev/null
+++ b/include/linux/fsl_hypervisor.h
@@ -0,0 +1,241 @@
+/*
+ * Freescale hypervisor ioctl and kernel interface
+ *
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * This software is provided by Freescale Semiconductor "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 Freescale Semiconductor 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.
+ *
+ * This file is used by the Freescale hypervisor management driver. It can
+ * also be included by applications that need to communicate with the driver
+ * via the ioctl interface.
+ */
+
+#ifndef FSL_HYPERVISOR_H
+#define FSL_HYPERVISOR_H
+
+#include <linux/types.h>
+
+/**
+ * struct fsl_hv_ioctl_restart - restart a partition
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to restart, or -1 for the
+ * calling partition
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_RESTART
+ */
+struct fsl_hv_ioctl_restart {
+ __u32 ret;
+ __u32 partition;
+};
+
+/**
+ * struct fsl_hv_ioctl_status - get a partition's status
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to query, or -1 for the
+ * calling partition
+ * @status: The returned status of the partition
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_GET_STATUS
+ *
+ * Values of 'status':
+ * 0 = Stopped
+ * 1 = Running
+ * 2 = Starting
+ * 3 = Stopping
+ */
+struct fsl_hv_ioctl_status {
+ __u32 ret;
+ __u32 partition;
+ __u32 status;
+};
+
+/**
+ * struct fsl_hv_ioctl_start - start a partition
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to control
+ * @entry_point: The offset within the guest IMA to start execution
+ * @load: If non-zero, reload the partition's images before starting
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_START
+ */
+struct fsl_hv_ioctl_start {
+ __u32 ret;
+ __u32 partition;
+ __u32 entry_point;
+ __u32 load;
+};
+
+/**
+ * struct fsl_hv_ioctl_stop - stop a partition
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to stop, or -1 for the calling
+ * partition
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_STOP
+ */
+struct fsl_hv_ioctl_stop {
+ __u32 ret;
+ __u32 partition;
+};
+
+/**
+ * struct fsl_hv_ioctl_memcpy - copy memory between partitions
+ * @ret: return error code from the hypervisor
+ * @source: the partition ID of the source partition, or -1 for this
+ * partition
+ * @target: the partition ID of the target partition, or -1 for this
+ * partition
+ * @reserved: reserved, must be set to 0
+ * @local_addr: user-space virtual address of a buffer in the local
+ * partition
+ * @remote_addr: guest physical address of a buffer in the
+ * remote partition
+ * @count: the number of bytes to copy. Both the local and remote
+ * buffers must be at least 'count' bytes long
+ *
+ * Used by FSL_HV_IOCTL_MEMCPY
+ *
+ * The 'local' partition is the partition that calls this ioctl. The
+ * 'remote' partition is a different partition. The data is copied from
+ * the 'source' paritition' to the 'target' partition.
+ *
+ * The buffer in the remote partition must be guest physically
+ * contiguous.
+ *
+ * This ioctl does not support copying memory between two remote
+ * partitions or within the same partition, so either 'source' or
+ * 'target' (but not both) must be -1. In other words, either
+ *
+ * source == local and target == remote
+ * or
+ * source == remote and target == local
+ */
+struct fsl_hv_ioctl_memcpy {
+ __u32 ret;
+ __u32 source;
+ __u32 target;
+ __u32 reserved; /* padding to ensure local_vaddr is aligned */
+ __u64 local_vaddr;
+ __u64 remote_paddr;
+ __u64 count;
+};
+
+/**
+ * struct fsl_hv_ioctl_doorbell - ring a doorbell
+ * @ret: return error code from the hypervisor
+ * @doorbell: the handle of the doorbell to ring doorbell
+ *
+ * Used by FSL_HV_IOCTL_DOORBELL
+ */
+struct fsl_hv_ioctl_doorbell {
+ __u32 ret;
+ __u32 doorbell;
+};
+
+/**
+ * struct fsl_hv_ioctl_prop - get/set a device tree property
+ * @ret: return error code from the hypervisor
+ * @handle: handle of partition whose tree to access
+ * @path: virtual address of path name of node to access
+ * @propname: virtual address of name of property to access
+ * @propval: virtual address of property data buffer
+ * @proplen: Size of property data buffer
+ * @reserved: reserved, must be set to 0
+ *
+ * Used by FSL_HV_IOCTL_DOORBELL
+ */
+struct fsl_hv_ioctl_prop {
+ __u32 ret;
+ __u32 handle;
+ __u64 path;
+ __u64 propname;
+ __u64 propval;
+ __u32 proplen;
+ __u32 reserved; /* padding to ensure structure is aligned */
+};
+
+/* The ioctl type, documented in ioctl-number.txt */
+#define FSL_HV_IOCTL_TYPE 0xAF
+
+/* Restart another partition */
+#define FSL_HV_IOCTL_PARTITION_RESTART \
+ _IOWR(FSL_HV_IOCTL_TYPE, 1, struct fsl_hv_ioctl_restart)
+
+/* Get a partition's status */
+#define FSL_HV_IOCTL_PARTITION_GET_STATUS \
+ _IOWR(FSL_HV_IOCTL_TYPE, 2, struct fsl_hv_ioctl_status)
+
+/* Boot another partition */
+#define FSL_HV_IOCTL_PARTITION_START \
+ _IOWR(FSL_HV_IOCTL_TYPE, 3, struct fsl_hv_ioctl_start)
+
+/* Stop this or another partition */
+#define FSL_HV_IOCTL_PARTITION_STOP \
+ _IOWR(FSL_HV_IOCTL_TYPE, 4, struct fsl_hv_ioctl_stop)
+
+/* Copy data from one partition to another */
+#define FSL_HV_IOCTL_MEMCPY \
+ _IOWR(FSL_HV_IOCTL_TYPE, 5, struct fsl_hv_ioctl_memcpy)
+
+/* Ring a doorbell */
+#define FSL_HV_IOCTL_DOORBELL \
+ _IOWR(FSL_HV_IOCTL_TYPE, 6, struct fsl_hv_ioctl_doorbell)
+
+/* Get a property from another guest's device tree */
+#define FSL_HV_IOCTL_GETPROP \
+ _IOWR(FSL_HV_IOCTL_TYPE, 7, struct fsl_hv_ioctl_prop)
+
+/* Set a property in another guest's device tree */
+#define FSL_HV_IOCTL_SETPROP \
+ _IOWR(FSL_HV_IOCTL_TYPE, 8, struct fsl_hv_ioctl_prop)
+
+#ifdef __KERNEL__
+
+/**
+ * fsl_hv_event_register() - register a callback for failover events
+ * @nb: pointer to caller-supplied notifier_block structure
+ *
+ * This function is called by device drivers to register their callback
+ * functions for fail-over events.
+ *
+ * The caller should allocate a notifier_block object and initialize the
+ * 'priority' and 'notifier_call' fields.
+ */
+int fsl_hv_failover_register(struct notifier_block *nb);
+
+/**
+ * fsl_hv_event_unregister() - unregister a callback for failover events
+ * @nb: the same 'nb' used in previous fsl_hv_failover_register call
+ */
+int fsl_hv_failover_unregister(struct notifier_block *nb);
+
+#endif
+
+#endif
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Timur Tabi @ 2011-06-09 20:40 UTC (permalink / raw)
To: Greg KH
Cc: Arnd Bergmann, konrad.wilk, benh, kumar.gala, linux-kernel,
cmetcalf, linux-console, virtualization, linuxppc-dev, alan
In-Reply-To: <20110609203117.GB14235@kroah.com>
Greg KH wrote:
> Why is binary compatibility important? Isn't this a brand new driver
> for a brand new system? What userspace tools are out there in the wild
> for such a thing?
This driver (and the hypervisor it talks to, plus the apps, etc) has been in
internal development for three years. There hasn't been a lot of effort
internally to release this software upstream until recently.
I personally have been complaining about it for quite some time, but I have no
control over our internal release process. Even when the hardware has been
announced and available for purchase, the BSP is sometimes only available under
NDA. Eventually, everything is publicly released, and most of the code is
pushed upstream, but it's a long and painful struggle at times.
My concern has been dealing with the headaches of bug reports from customers,
etc as they upgrade their kernel but not their apps, and then wonder why nothing
works.
But as Arnd pointed out, it really isn't as big of deal as I make it out to be.
I can maintain compatibility internally. I blame my allergy medicine.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Arnd Bergmann @ 2011-06-09 20:33 UTC (permalink / raw)
To: Timur Tabi
Cc: konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
linux-console, greg, virtualization, linuxppc-dev, alan
In-Reply-To: <4DF12A94.6070605@freescale.com>
On Thursday 09 June 2011 22:18:28 Timur Tabi wrote:
> > More importantly, the code you have chose (0) conflicts with existing drivers
> > (frame buffer, scsi and wavefront among others). Please chose a free one and
> > add it to Documentation/ioctl/ioctl-number.txt in the same patch.
>
> Ok, I was really hoping to avoid doing this. Like I said, binary compatibility
> is important, and changing the type will break my existing apps. Are you
> insisting that I pick a new number?
I definitely insist that you have a proper interface in the driver at the
time that it gets merged, and that probably includes a collision-free
ioctl code.
You can probably make the driver support both the traditional and the
new interface, but I would prefer if you kept that as a private patch
on top a clean kernel driver. It's also a good idea to keep the header
file clean and only define the new interface there, to ensure that all
applications that are built in the future have to use the new interface.
When you make the patch to add backwards compat support, just add it
to the driver itself, not to the header.
Arnd
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Greg KH @ 2011-06-09 20:31 UTC (permalink / raw)
To: Timur Tabi
Cc: Arnd Bergmann, konrad.wilk, benh, kumar.gala, linux-kernel,
cmetcalf, linux-console, virtualization, linuxppc-dev, alan
In-Reply-To: <4DF12A94.6070605@freescale.com>
On Thu, Jun 09, 2011 at 03:18:28PM -0500, Timur Tabi wrote:
> > More importantly, the code you have chose (0) conflicts with existing drivers
> > (frame buffer, scsi and wavefront among others). Please chose a free one and
> > add it to Documentation/ioctl/ioctl-number.txt in the same patch.
>
> Ok, I was really hoping to avoid doing this. Like I said, binary compatibility
> is important, and changing the type will break my existing apps. Are you
> insisting that I pick a new number?
Why is binary compatibility important? Isn't this a brand new driver
for a brand new system? What userspace tools are out there in the wild
for such a thing?
confused,
greg k-h
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Arnd Bergmann @ 2011-06-09 20:25 UTC (permalink / raw)
To: linuxppc-dev
Cc: Randy Dunlap, konrad.wilk, greg, kumar.gala, linux-kernel,
cmetcalf, dsaxena, linux-console, virtualization, Timur Tabi,
alan
In-Reply-To: <4DF123AA.6070402@oracle.com>
On Thursday 09 June 2011 21:48:58 Randy Dunlap wrote:
> > So is it okay to stick with 0, or do I need to pick a new number?
>
> I wasn't suggesting that you change the 0, just note that it has conflicts,
> like other ioctls do.
We normally don't try to maintain binary compatibility with out of tree
kernel patches. That only leads to inferior interfaces finding their way
into the kernel.
Arnd
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Timur Tabi @ 2011-06-09 20:18 UTC (permalink / raw)
To: Arnd Bergmann
Cc: konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
linux-console, greg, virtualization, linuxppc-dev, alan
In-Reply-To: <201106092213.13755.arnd@arndb.de>
Arnd Bergmann wrote:
> As mentioned, it would be easier and more readable to just do
>
> switch(cmd) {
> case FSL_HV_IOCTL_PARTITION_RESTART:
> ...
>
> case FSL_HV_IOCTL_PARTITION_GET_STATUS;
> ...
>
> There is no need to check the bits individually when you can simply
> compare the command number.
But this will break backwards compatibility with older applications that used
the union as the size parameter. Although these applications won't compile with
the new header file, older already-compiled applications still work.
I will eventually update the applications to use the new header file, and at
that point I will modify the switch statement as you suggest. But until then,
I'd like to keep the code as-is.
>> > +enum fsl_hv_ioctl_cmd {
>> > + FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct fsl_hv_ioctl_restart),
>> > + FSL_HV_IOCTL_PARTITION_GET_STATUS = _IOWR(0, 2, struct fsl_hv_ioctl_status),
>> > + FSL_HV_IOCTL_PARTITION_START = _IOWR(0, 3, struct fsl_hv_ioctl_start),
>> > + FSL_HV_IOCTL_PARTITION_STOP = _IOWR(0, 4, struct fsl_hv_ioctl_stop),
>> > + FSL_HV_IOCTL_MEMCPY = _IOWR(0, 5, struct fsl_hv_ioctl_memcpy),
>> > + FSL_HV_IOCTL_DOORBELL = _IOWR(0, 6, struct fsl_hv_ioctl_doorbell),
>> > + FSL_HV_IOCTL_GETPROP = _IOWR(0, 7, struct fsl_hv_ioctl_prop),
>> > + FSL_HV_IOCTL_SETPROP = _IOWR(0, 8, struct fsl_hv_ioctl_prop),
>> > +};
> Using a #define here is usually preferred because then you can use #ifdef in a user
> application to check if a given value has been assigned.
You're right -- I had enum on the brain.
> More importantly, the code you have chose (0) conflicts with existing drivers
> (frame buffer, scsi and wavefront among others). Please chose a free one and
> add it to Documentation/ioctl/ioctl-number.txt in the same patch.
Ok, I was really hoping to avoid doing this. Like I said, binary compatibility
is important, and changing the type will break my existing apps. Are you
insisting that I pick a new number?
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Arnd Bergmann @ 2011-06-09 20:13 UTC (permalink / raw)
To: Timur Tabi, linuxppc-dev
Cc: konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
linux-console, greg, virtualization, alan
In-Reply-To: <1307646794-26374-1-git-send-email-timur@freescale.com>
Hi Timur, thanks for addressing the issues I pointed out. Unfortunately, I
have found a few more now:
On Thursday 09 June 2011 21:13:14 Timur Tabi wrote:
> + /* Make sure the application is called the right driver. */
> + if (_IOC_TYPE(cmd) != 0) {
> + pr_debug("fsl-hv: ioctl type %u should be 0\n", _IOC_TYPE(cmd));
> + return -EINVAL;
> + }
> +
> + /* Make sure the application set the direction flag correctly. */
> + if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE)) {
> + pr_debug("fsl-hv: ioctl direction should be _IOWR\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * Make sure the application is passing the right structure to us.
> + * For backwards compatibility with older applications, we only check
> + * if the size is too small, rather than unequal.
> + */
> +
> + switch (_IOC_NR(cmd)) {
> + case (_IOC_NR(FSL_HV_IOCTL_PARTITION_RESTART)):
> + size = sizeof(struct fsl_hv_ioctl_restart);
> + if (_IOC_SIZE(cmd) < size)
> + goto size_error;
> + ret = ioctl_restart(arg);
> + break;
As mentioned, it would be easier and more readable to just do
switch(cmd) {
case FSL_HV_IOCTL_PARTITION_RESTART:
...
case FSL_HV_IOCTL_PARTITION_GET_STATUS;
...
There is no need to check the bits individually when you can simply
compare the command number.
> +/**
> + * enum fsl_hv_ioctl_cmd - ioctl commands
> + * @FSL_HV_IOCTL_PARTITION_RESTART: restart another partition
> + * @FSL_HV_IOCTL_PARTITION_GET_STATUS: get a partition's status
> + * @FSL_HV_IOCTL_PARTITION_START: boot another partition
> + * @FSL_HV_IOCTL_PARTITION_STOP: stop this or another partition
> + * @FSL_HV_IOCTL_MEMCPY: copy data from one partition to another
> + * @FSL_HV_IOCTL_DOORBELL: ring a doorbell
> + * @FSL_HV_IOCTL_GETPROP: get a property from another guest's device tree
> + * @FSL_HV_IOCTL_SETPROP: set a property in another guest's device tree
> + *
> + * This enum lists the available ioctl commands for the Freescale hypervisor
> + * management driver. The meaning
> + */
> +enum fsl_hv_ioctl_cmd {
> + FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct fsl_hv_ioctl_restart),
> + FSL_HV_IOCTL_PARTITION_GET_STATUS = _IOWR(0, 2, struct fsl_hv_ioctl_status),
> + FSL_HV_IOCTL_PARTITION_START = _IOWR(0, 3, struct fsl_hv_ioctl_start),
> + FSL_HV_IOCTL_PARTITION_STOP = _IOWR(0, 4, struct fsl_hv_ioctl_stop),
> + FSL_HV_IOCTL_MEMCPY = _IOWR(0, 5, struct fsl_hv_ioctl_memcpy),
> + FSL_HV_IOCTL_DOORBELL = _IOWR(0, 6, struct fsl_hv_ioctl_doorbell),
> + FSL_HV_IOCTL_GETPROP = _IOWR(0, 7, struct fsl_hv_ioctl_prop),
> + FSL_HV_IOCTL_SETPROP = _IOWR(0, 8, struct fsl_hv_ioctl_prop),
> +};
Using a #define here is usually preferred because then you can use #ifdef in a user
application to check if a given value has been assigned.
More importantly, the code you have chose (0) conflicts with existing drivers
(frame buffer, scsi and wavefront among others). Please chose a free one and
add it to Documentation/ioctl/ioctl-number.txt in the same patch.
Arnd
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Randy Dunlap @ 2011-06-09 19:48 UTC (permalink / raw)
To: Timur Tabi
Cc: arnd, konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
dsaxena, linux-console, greg, virtualization, linuxppc-dev, alan
In-Reply-To: <4DF12345.1040409@freescale.com>
On 06/09/11 12:47, Timur Tabi wrote:
> Randy Dunlap wrote:
>>>> +enum fsl_hv_ioctl_cmd {
>>>> + FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct fsl_hv_ioctl_restart),
>>>> + FSL_HV_IOCTL_PARTITION_GET_STATUS = _IOWR(0, 2, struct fsl_hv_ioctl_status),
>>>> + FSL_HV_IOCTL_PARTITION_START = _IOWR(0, 3, struct fsl_hv_ioctl_start),
>>>> + FSL_HV_IOCTL_PARTITION_STOP = _IOWR(0, 4, struct fsl_hv_ioctl_stop),
>>>> + FSL_HV_IOCTL_MEMCPY = _IOWR(0, 5, struct fsl_hv_ioctl_memcpy),
>>>> + FSL_HV_IOCTL_DOORBELL = _IOWR(0, 6, struct fsl_hv_ioctl_doorbell),
>>>> + FSL_HV_IOCTL_GETPROP = _IOWR(0, 7, struct fsl_hv_ioctl_prop),
>>>> + FSL_HV_IOCTL_SETPROP = _IOWR(0, 8, struct fsl_hv_ioctl_prop),
>>>> +};
>
>> Missing an entry in Documentation/ioctl/ioctl-number.txt for 0 (with conflict!).
>
> If I change it from 0, I'm going to break binary compatibility with our apps. I
> agree that maybe I shouldn't have picked 0, but considering how many conflicts
> there already are, I wonder what the point is. Even if I pick a number that is
> currently not listed in the chart, that doesn't mean that it's actually not
> being used, or that it won't conflict in the future.
Yes, I understood that.
> So is it okay to stick with 0, or do I need to pick a new number?
I wasn't suggesting that you change the 0, just note that it has conflicts,
like other ioctls do.
--
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Timur Tabi @ 2011-06-09 19:47 UTC (permalink / raw)
To: Randy Dunlap
Cc: arnd, konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
dsaxena, linux-console, greg, virtualization, linuxppc-dev, alan
In-Reply-To: <20110609124010.6335559e.randy.dunlap@oracle.com>
Randy Dunlap wrote:
>> > +enum fsl_hv_ioctl_cmd {
>> > + FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct fsl_hv_ioctl_restart),
>> > + FSL_HV_IOCTL_PARTITION_GET_STATUS = _IOWR(0, 2, struct fsl_hv_ioctl_status),
>> > + FSL_HV_IOCTL_PARTITION_START = _IOWR(0, 3, struct fsl_hv_ioctl_start),
>> > + FSL_HV_IOCTL_PARTITION_STOP = _IOWR(0, 4, struct fsl_hv_ioctl_stop),
>> > + FSL_HV_IOCTL_MEMCPY = _IOWR(0, 5, struct fsl_hv_ioctl_memcpy),
>> > + FSL_HV_IOCTL_DOORBELL = _IOWR(0, 6, struct fsl_hv_ioctl_doorbell),
>> > + FSL_HV_IOCTL_GETPROP = _IOWR(0, 7, struct fsl_hv_ioctl_prop),
>> > + FSL_HV_IOCTL_SETPROP = _IOWR(0, 8, struct fsl_hv_ioctl_prop),
>> > +};
> Missing an entry in Documentation/ioctl/ioctl-number.txt for 0 (with conflict!).
If I change it from 0, I'm going to break binary compatibility with our apps. I
agree that maybe I shouldn't have picked 0, but considering how many conflicts
there already are, I wonder what the point is. Even if I pick a number that is
currently not listed in the chart, that doesn't mean that it's actually not
being used, or that it won't conflict in the future.
So is it okay to stick with 0, or do I need to pick a new number?
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Randy Dunlap @ 2011-06-09 19:40 UTC (permalink / raw)
To: Timur Tabi
Cc: arnd, konrad.wilk, benh, kumar.gala, linux-kernel, cmetcalf,
dsaxena, linux-console, greg, virtualization, linuxppc-dev, alan
In-Reply-To: <1307646794-26374-1-git-send-email-timur@freescale.com>
On Thu, 9 Jun 2011 14:13:14 -0500 Timur Tabi wrote:
> Add the drivers/virt directory, which houses drivers that support
> virtualization environments, and add the Freescale hypervisor management
> driver.
>
> The Freescale hypervisor management driver provides several services to
> drivers and applications related to the Freescale hypervisor:
>
> 1. An ioctl interface for querying and managing partitions
>
> 2. A file interface to reading incoming doorbells
>
> 3. An interrupt handler for shutting down the partition upon receiving the
> shutdown doorbell from a manager partition
>
> 4. A kernel interface for receiving callbacks when a managed partition
> shuts down.
>
> Signed-off-by: Timur Tabi <timur@freescale.com>
> ---
> drivers/Kconfig | 2 +
> drivers/Makefile | 3 +
> drivers/virt/Kconfig | 32 ++
> drivers/virt/Makefile | 5 +
> drivers/virt/fsl_hypervisor.c | 983 ++++++++++++++++++++++++++++++++++++++++
> include/linux/Kbuild | 1 +
> include/linux/fsl_hypervisor.h | 231 ++++++++++
> 7 files changed, 1257 insertions(+), 0 deletions(-)
> create mode 100644 drivers/virt/Kconfig
> create mode 100644 drivers/virt/Makefile
> create mode 100644 drivers/virt/fsl_hypervisor.c
> create mode 100644 include/linux/fsl_hypervisor.h
> diff --git a/include/linux/fsl_hypervisor.h b/include/linux/fsl_hypervisor.h
> new file mode 100644
> index 0000000..d1ca2b1
> --- /dev/null
> +++ b/include/linux/fsl_hypervisor.h
> @@ -0,0 +1,231 @@
[snip]
> +/**
> + * enum fsl_hv_ioctl_cmd - ioctl commands
> + * @FSL_HV_IOCTL_PARTITION_RESTART: restart another partition
> + * @FSL_HV_IOCTL_PARTITION_GET_STATUS: get a partition's status
> + * @FSL_HV_IOCTL_PARTITION_START: boot another partition
> + * @FSL_HV_IOCTL_PARTITION_STOP: stop this or another partition
> + * @FSL_HV_IOCTL_MEMCPY: copy data from one partition to another
> + * @FSL_HV_IOCTL_DOORBELL: ring a doorbell
> + * @FSL_HV_IOCTL_GETPROP: get a property from another guest's device tree
> + * @FSL_HV_IOCTL_SETPROP: set a property in another guest's device tree
> + *
> + * This enum lists the available ioctl commands for the Freescale hypervisor
> + * management driver. The meaning
> + */
> +enum fsl_hv_ioctl_cmd {
> + FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct fsl_hv_ioctl_restart),
> + FSL_HV_IOCTL_PARTITION_GET_STATUS = _IOWR(0, 2, struct fsl_hv_ioctl_status),
> + FSL_HV_IOCTL_PARTITION_START = _IOWR(0, 3, struct fsl_hv_ioctl_start),
> + FSL_HV_IOCTL_PARTITION_STOP = _IOWR(0, 4, struct fsl_hv_ioctl_stop),
> + FSL_HV_IOCTL_MEMCPY = _IOWR(0, 5, struct fsl_hv_ioctl_memcpy),
> + FSL_HV_IOCTL_DOORBELL = _IOWR(0, 6, struct fsl_hv_ioctl_doorbell),
> + FSL_HV_IOCTL_GETPROP = _IOWR(0, 7, struct fsl_hv_ioctl_prop),
> + FSL_HV_IOCTL_SETPROP = _IOWR(0, 8, struct fsl_hv_ioctl_prop),
> +};
Missing an entry in Documentation/ioctl/ioctl-number.txt for 0 (with conflict!).
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply
* [PATCH 7/7] [v5] drivers/virt: introduce Freescale hypervisor management driver
From: Timur Tabi @ 2011-06-09 19:13 UTC (permalink / raw)
To: alan, arnd, kumar.gala, benh, greg, akpm, cmetcalf, konrad.w
Add the drivers/virt directory, which houses drivers that support
virtualization environments, and add the Freescale hypervisor management
driver.
The Freescale hypervisor management driver provides several services to
drivers and applications related to the Freescale hypervisor:
1. An ioctl interface for querying and managing partitions
2. A file interface to reading incoming doorbells
3. An interrupt handler for shutting down the partition upon receiving the
shutdown doorbell from a manager partition
4. A kernel interface for receiving callbacks when a managed partition
shuts down.
Signed-off-by: Timur Tabi <timur@freescale.com>
---
drivers/Kconfig | 2 +
drivers/Makefile | 3 +
drivers/virt/Kconfig | 32 ++
drivers/virt/Makefile | 5 +
drivers/virt/fsl_hypervisor.c | 983 ++++++++++++++++++++++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/fsl_hypervisor.h | 231 ++++++++++
7 files changed, 1257 insertions(+), 0 deletions(-)
create mode 100644 drivers/virt/Kconfig
create mode 100644 drivers/virt/Makefile
create mode 100644 drivers/virt/fsl_hypervisor.c
create mode 100644 include/linux/fsl_hypervisor.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 557a469..0371680 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -122,4 +122,6 @@ source "drivers/hwspinlock/Kconfig"
source "drivers/clocksource/Kconfig"
+source "drivers/virt/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 3f135b6..bbe2918 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -119,3 +119,6 @@ obj-y += ieee802154/
obj-y += clk/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
+
+# Virtualization drivers
+obj-$(CONFIG_VIRT_DRIVERS) += virt/
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
new file mode 100644
index 0000000..2dcdbc9
--- /dev/null
+++ b/drivers/virt/Kconfig
@@ -0,0 +1,32 @@
+#
+# Virtualization support drivers
+#
+
+menuconfig VIRT_DRIVERS
+ bool "Virtualization drivers"
+ ---help---
+ Say Y here to get to see options for device drivers that support
+ virtualization environments.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if VIRT_DRIVERS
+
+config FSL_HV_MANAGER
+ tristate "Freescale hypervisor management driver"
+ depends on FSL_SOC
+ help
+ The Freescale hypervisor management driver provides several services
+ to drivers and applications related to the Freescale hypervisor:
+
+ 1) An ioctl interface for querying and managing partitions.
+
+ 2) A file interface to reading incoming doorbells.
+
+ 3) An interrupt handler for shutting down the partition upon
+ receiving the shutdown doorbell from a manager partition.
+
+ 4) A kernel interface for receiving callbacks when a managed
+ partition shuts down.
+
+endif
diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
new file mode 100644
index 0000000..c47f04d
--- /dev/null
+++ b/drivers/virt/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for drivers that support virtualization
+#
+
+obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o
diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c
new file mode 100644
index 0000000..086085f
--- /dev/null
+++ b/drivers/virt/fsl_hypervisor.c
@@ -0,0 +1,983 @@
+/*
+ * Freescale Hypervisor Management Driver
+
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * The Freescale hypervisor management driver provides several services to
+ * drivers and applications related to the Freescale hypervisor:
+ *
+ * 1. An ioctl interface for querying and managing partitions.
+ *
+ * 2. A file interface to reading incoming doorbells.
+ *
+ * 3. An interrupt handler for shutting down the partition upon receiving the
+ * shutdown doorbell from a manager partition.
+ *
+ * 4. A kernel interface for receiving callbacks when a managed partition
+ * shuts down.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/notifier.h>
+
+#include <linux/io.h>
+#include <asm/fsl_hcalls.h>
+
+#include <linux/fsl_hypervisor.h>
+
+static BLOCKING_NOTIFIER_HEAD(failover_subscribers);
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_RESTART
+ *
+ * Restart a running partition
+ */
+static long ioctl_restart(struct fsl_hv_ioctl_restart __user *p)
+{
+ struct fsl_hv_ioctl_restart param;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_restart)))
+ return -EFAULT;
+
+ param.ret = fh_partition_restart(param.partition);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_STATUS
+ *
+ * Query the status of a partition
+ */
+static long ioctl_status(struct fsl_hv_ioctl_status __user *p)
+{
+ struct fsl_hv_ioctl_status param;
+ u32 status;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_status)))
+ return -EFAULT;
+
+ param.ret = fh_partition_get_status(param.partition, &status);
+ if (!param.ret)
+ param.status = status;
+
+ if (copy_to_user(p, ¶m, sizeof(struct fsl_hv_ioctl_status)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_START
+ *
+ * Start a stopped partition.
+ */
+static long ioctl_start(struct fsl_hv_ioctl_start __user *p)
+{
+ struct fsl_hv_ioctl_start param;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_start)))
+ return -EFAULT;
+
+ param.ret = fh_partition_start(param.partition, param.entry_point,
+ param.load);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_PARTITION_STOP
+ *
+ * Stop a running partition
+ */
+static long ioctl_stop(struct fsl_hv_ioctl_stop __user *p)
+{
+ struct fsl_hv_ioctl_stop param;
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_stop)))
+ return -EFAULT;
+
+ param.ret = fh_partition_stop(param.partition);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_MEMCPY
+ *
+ * The FH_MEMCPY hypercall takes an array of address/address/size structures
+ * to represent the data being copied. As a convenience to the user, this
+ * ioctl takes a user-create buffer and a pointer to a guest physically
+ * contiguous buffer in the remote partition, and creates the
+ * address/address/size array for the hypercall.
+ */
+static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
+{
+ struct fsl_hv_ioctl_memcpy param;
+
+ struct page **pages = NULL;
+ void *sg_list_unaligned = NULL;
+ struct fh_sg_list *sg_list = NULL;
+
+ unsigned int num_pages;
+ unsigned long lb_offset; /* Offset within a page of the local buffer */
+
+ unsigned int i;
+ long ret = 0;
+ int num_pinned; /* return value from get_user_pages() */
+ phys_addr_t remote_paddr; /* The next address in the remote buffer */
+ uint32_t count; /* The number of bytes left to copy */
+
+ /* Get the parameters from the user */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_memcpy)))
+ return -EFAULT;
+
+ /*
+ * One partition must be local, the other must be remote. In other
+ * words, if source and target are both -1, or are both not -1, then
+ * return an error.
+ */
+ if ((param.source == -1) == (param.target == -1))
+ return -EINVAL;
+
+ /*
+ * The array of pages returned by get_user_pages() covers only
+ * page-aligned memory. Since the user buffer is probably not
+ * page-aligned, we need to handle the discrepancy.
+ *
+ * We calculate the offset within a page of the S/G list, and make
+ * adjustments accordingly. This will result in a page list that looks
+ * like this:
+ *
+ * ---- <-- first page starts before the buffer
+ * | |
+ * |////|-> ----
+ * |////| | |
+ * ---- | |
+ * | |
+ * ---- | |
+ * |////| | |
+ * |////| | |
+ * |////| | |
+ * ---- | |
+ * | |
+ * ---- | |
+ * |////| | |
+ * |////| | |
+ * |////| | |
+ * ---- | |
+ * | |
+ * ---- | |
+ * |////| | |
+ * |////|-> ----
+ * | | <-- last page ends after the buffer
+ * ----
+ *
+ * The distance between the start of the first page and the start of the
+ * buffer is lb_offset. The hashed (///) areas are the parts of the
+ * page list that contain the actual buffer.
+ *
+ * The advantage of this approach is that the number of pages is
+ * equal to the number of entries in the S/G list that we give to the
+ * hypervisor.
+ */
+ lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
+ num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ /* Allocate the buffers we need */
+
+ /*
+ * 'pages' is an array of struct page pointers that's initialized by
+ * get_user_pages().
+ */
+ pages = kzalloc(num_pages * sizeof(struct page *), GFP_KERNEL);
+ if (!pages) {
+ pr_debug("fsl-hv: could not allocate page list\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * sg_list is the list of fh_sg_list objects that we pass to the
+ * hypervisor.
+ */
+ sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
+ sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
+ if (!sg_list_unaligned) {
+ pr_debug("fsl-hv: could not allocate S/G list\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));
+
+ /* Get the physical addresses of the source buffer */
+ down_read(¤t->mm->mmap_sem);
+ num_pinned = get_user_pages(current, current->mm,
+ param.local_vaddr - lb_offset, num_pages,
+ (param.source == -1) ? READ : WRITE,
+ 0, pages, NULL);
+ up_read(¤t->mm->mmap_sem);
+
+ if (num_pinned != num_pages) {
+ /* get_user_pages() failed */
+ pr_debug("fsl-hv: could not lock source buffer\n");
+ ret = (num_pinned < 0) ? num_pinned : -EFAULT;
+ goto exit;
+ }
+
+ /*
+ * Build the fh_sg_list[] array. The first page is special
+ * because it's misaligned.
+ */
+ if (param.source == -1) {
+ sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
+ sg_list[0].target = param.remote_paddr;
+ } else {
+ sg_list[0].source = param.remote_paddr;
+ sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
+ }
+ sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
+
+ remote_paddr = param.remote_paddr + sg_list[0].size;
+ count = param.count - sg_list[0].size;
+
+ for (i = 1; i < num_pages; i++) {
+ if (param.source == -1) {
+ /* local to remote */
+ sg_list[i].source = page_to_phys(pages[i]);
+ sg_list[i].target = remote_paddr;
+ } else {
+ /* remote to local */
+ sg_list[i].source = remote_paddr;
+ sg_list[i].target = page_to_phys(pages[i]);
+ }
+ sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
+
+ remote_paddr += sg_list[i].size;
+ count -= sg_list[i].size;
+ }
+
+ param.ret = fh_partition_memcpy(param.source, param.target,
+ virt_to_phys(sg_list), num_pages);
+
+exit:
+ if (pages) {
+ for (i = 0; i < num_pages; i++)
+ if (pages[i])
+ put_page(pages[i]);
+ }
+
+ kfree(sg_list_unaligned);
+ kfree(pages);
+
+ if (!ret)
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return ret;
+}
+
+/*
+ * Ioctl interface for FSL_HV_IOCTL_DOORBELL
+ *
+ * Ring a doorbell
+ */
+static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
+{
+ struct fsl_hv_ioctl_doorbell param;
+
+ /* Get the parameters from the user. */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_doorbell)))
+ return -EFAULT;
+
+ param.ret = ev_doorbell_send(param.doorbell);
+
+ if (copy_to_user(&p->ret, ¶m.ret, sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
+{
+ struct fsl_hv_ioctl_prop param;
+ char __user *upath, *upropname;
+ void __user *upropval;
+ char *path = NULL, *propname = NULL;
+ void *propval = NULL;
+ int ret = 0;
+
+ /* Get the parameters from the user. */
+ if (copy_from_user(¶m, p, sizeof(struct fsl_hv_ioctl_prop)))
+ return -EFAULT;
+
+ upath = (char __user *)(uintptr_t)param.path;
+ upropname = (char __user *)(uintptr_t)param.propname;
+ upropval = (void __user *)(uintptr_t)param.propval;
+
+ path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ goto out;
+ }
+
+ propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
+ if (IS_ERR(propname)) {
+ ret = PTR_ERR(propname);
+ goto out;
+ }
+
+ if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ propval = kmalloc(param.proplen, GFP_KERNEL);
+ if (!propval) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (set) {
+ if (copy_from_user(propval, upropval, param.proplen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ param.ret = fh_partition_set_dtprop(param.handle,
+ virt_to_phys(path),
+ virt_to_phys(propname),
+ virt_to_phys(propval),
+ param.proplen);
+ } else {
+ param.ret = fh_partition_get_dtprop(param.handle,
+ virt_to_phys(path),
+ virt_to_phys(propname),
+ virt_to_phys(propval),
+ ¶m.proplen);
+
+ if (param.ret == 0) {
+ if (copy_to_user(upropval, propval, param.proplen) ||
+ put_user(param.proplen, &p->proplen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+ }
+
+ if (put_user(param.ret, &p->ret))
+ ret = -EFAULT;
+
+out:
+ kfree(path);
+ kfree(propval);
+ kfree(propname);
+
+ return ret;
+}
+
+/*
+ * Ioctl main entry point
+ */
+static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
+ unsigned long argaddr)
+{
+ void __user *arg = (void __user *)argaddr;
+ long ret;
+ size_t size;
+
+ /* Make sure the application is called the right driver. */
+ if (_IOC_TYPE(cmd) != 0) {
+ pr_debug("fsl-hv: ioctl type %u should be 0\n", _IOC_TYPE(cmd));
+ return -EINVAL;
+ }
+
+ /* Make sure the application set the direction flag correctly. */
+ if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE)) {
+ pr_debug("fsl-hv: ioctl direction should be _IOWR\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure the application is passing the right structure to us.
+ * For backwards compatibility with older applications, we only check
+ * if the size is too small, rather than unequal.
+ */
+
+ switch (_IOC_NR(cmd)) {
+ case (_IOC_NR(FSL_HV_IOCTL_PARTITION_RESTART)):
+ size = sizeof(struct fsl_hv_ioctl_restart);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_restart(arg);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_PARTITION_GET_STATUS)):
+ size = sizeof(struct fsl_hv_ioctl_status);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_status(arg);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_PARTITION_START)):
+ size = sizeof(struct fsl_hv_ioctl_start);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_start(arg);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_PARTITION_STOP)):
+ size = sizeof(struct fsl_hv_ioctl_stop);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_stop(arg);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_MEMCPY)):
+ size = sizeof(struct fsl_hv_ioctl_memcpy);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_memcpy(arg);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_DOORBELL)):
+ size = sizeof(struct fsl_hv_ioctl_doorbell);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_doorbell(arg);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_GETPROP)):
+ size = sizeof(struct fsl_hv_ioctl_prop);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_dtprop(arg, 0);
+ break;
+ case (_IOC_NR(FSL_HV_IOCTL_SETPROP)):
+ size = sizeof(struct fsl_hv_ioctl_prop);
+ if (_IOC_SIZE(cmd) < size)
+ goto size_error;
+ ret = ioctl_dtprop(arg, 1);
+ break;
+ default:
+ pr_debug("fsl-hv: unknown ioctl %u\n", _IOC_NR(cmd));
+ return -ENOTTY;
+ }
+
+ return ret;
+
+size_error:
+ pr_debug("fsl-hv: ioctl %u parameter size %u should be %zu\n",
+ _IOC_NR(cmd), _IOC_SIZE(cmd), size);
+ return -EINVAL;
+}
+
+/* Linked list of processes that have us open */
+static struct list_head db_list;
+
+/* spinlock for db_list */
+static DEFINE_SPINLOCK(db_list_lock);
+
+/* The size of the doorbell event queue. This must be a power of two. */
+#define QSIZE 16
+
+/* Returns the next head/tail pointer, wrapping around the queue if necessary */
+#define nextp(x) (((x) + 1) & (QSIZE - 1))
+
+/* Per-open data structure */
+struct doorbell_queue {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ unsigned int head;
+ unsigned int tail;
+ uint32_t q[QSIZE];
+};
+
+/* Linked list of ISRs that we registered */
+struct list_head isr_list;
+
+/* Per-ISR data structure */
+struct doorbell_isr {
+ struct list_head list;
+ unsigned int irq;
+ uint32_t doorbell; /* The doorbell handle */
+ uint32_t partition; /* The partition handle, if used */
+};
+
+/*
+ * Add a doorbell to all of the doorbell queues
+ */
+static void fsl_hv_queue_doorbell(uint32_t doorbell)
+{
+ struct doorbell_queue *dbq;
+ unsigned long flags;
+
+ /* Prevent another core from modifying db_list */
+ spin_lock_irqsave(&db_list_lock, flags);
+
+ list_for_each_entry(dbq, &db_list, list) {
+ if (dbq->head != nextp(dbq->tail)) {
+ dbq->q[dbq->tail] = doorbell;
+ /*
+ * This memory barrier eliminates the need to grab
+ * the spinlock for dbq.
+ */
+ smp_wmb();
+ dbq->tail = nextp(dbq->tail);
+ wake_up_interruptible(&dbq->wait);
+ }
+ }
+
+ spin_unlock_irqrestore(&db_list_lock, flags);
+}
+
+/*
+ * Interrupt handler for all doorbells
+ *
+ * We use the same interrupt handler for all doorbells. Whenever a doorbell
+ * is rung, and we receive an interrupt, we just put the handle for that
+ * doorbell (passed to us as *data) into all of the queues.
+ */
+static irqreturn_t fsl_hv_isr(int irq, void *data)
+{
+ fsl_hv_queue_doorbell((uintptr_t) data);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * State change thread function
+ *
+ * The state change notification arrives in an interrupt, but we can't call
+ * blocking_notifier_call_chain() in an interrupt handler. We could call
+ * atomic_notifier_call_chain(), but that would require the clients' call-back
+ * function to run in interrupt context. Since we don't want to impose that
+ * restriction on the clients, we use a threaded IRQ to process the
+ * notification in kernel context.
+ */
+static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
+{
+ struct doorbell_isr *dbisr = data;
+
+ blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
+ NULL);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Interrupt handler for state-change doorbells
+ */
+static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
+{
+ unsigned int status;
+ struct doorbell_isr *dbisr = data;
+ int ret;
+
+ /* It's still a doorbell, so add it to all the queues. */
+ fsl_hv_queue_doorbell(dbisr->doorbell);
+
+ /* Determine the new state, and if it's stopped, notify the clients. */
+ ret = fh_partition_get_status(dbisr->partition, &status);
+ if (!ret && (status == FH_PARTITION_STOPPED))
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Returns a bitmask indicating whether a read will block
+ */
+static unsigned int fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
+{
+ struct doorbell_queue *dbq = filp->private_data;
+ unsigned long flags;
+ unsigned int mask;
+
+ spin_lock_irqsave(&dbq->lock, flags);
+
+ poll_wait(filp, &dbq->wait, p);
+ mask = (dbq->head == dbq->tail) ? 0 : (POLLIN | POLLRDNORM);
+
+ spin_unlock_irqrestore(&dbq->lock, flags);
+
+ return mask;
+}
+
+/*
+ * Return the handles for any incoming doorbells
+ *
+ * If there are doorbell handles in the queue for this open instance, then
+ * return them to the caller as an array of 32-bit integers. Otherwise,
+ * block until there is at least one handle to return.
+ */
+static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
+ loff_t *off)
+{
+ struct doorbell_queue *dbq = filp->private_data;
+ uint32_t __user *p = (uint32_t __user *) buf; /* for put_user() */
+ unsigned long flags;
+ ssize_t count = 0;
+
+ /* Make sure we stop when the user buffer is full. */
+ while (len >= sizeof(uint32_t)) {
+ uint32_t dbell; /* Local copy of doorbell queue data */
+
+ spin_lock_irqsave(&dbq->lock, flags);
+
+ /*
+ * If the queue is empty, then either we're done or we need
+ * to block. If the application specified O_NONBLOCK, then
+ * we return the appropriate error code.
+ */
+ if (dbq->head == dbq->tail) {
+ spin_unlock_irqrestore(&dbq->lock, flags);
+ if (count)
+ break;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ if (wait_event_interruptible(dbq->wait,
+ dbq->head != dbq->tail))
+ return -ERESTARTSYS;
+ continue;
+ }
+
+ /*
+ * Even though we have an smp_wmb() in the ISR, the core
+ * might speculatively execute the "dbell = ..." below while
+ * it's evaluating the if-statement above. In that case, the
+ * value put into dbell could be stale if the core accepts the
+ * speculation. To prevent that, we need a read memory barrier
+ * here as well.
+ */
+ smp_rmb();
+
+ /* Copy the data to a temporary local buffer, because
+ * we can't call copy_to_user() from inside a spinlock
+ */
+ dbell = dbq->q[dbq->head];
+ dbq->head = nextp(dbq->head);
+
+ spin_unlock_irqrestore(&dbq->lock, flags);
+
+ if (put_user(dbell, p))
+ return -EFAULT;
+ p++;
+ count += sizeof(uint32_t);
+ len -= sizeof(uint32_t);
+ }
+
+ return count;
+}
+
+/*
+ * Open the driver and prepare for reading doorbells.
+ *
+ * Every time an application opens the driver, we create a doorbell queue
+ * for that file handle. This queue is used for any incoming doorbells.
+ */
+static int fsl_hv_open(struct inode *inode, struct file *filp)
+{
+ struct doorbell_queue *dbq;
+ unsigned long flags;
+ int ret = 0;
+
+ dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
+ if (!dbq) {
+ pr_err("fsl-hv: out of memory\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dbq->lock);
+ init_waitqueue_head(&dbq->wait);
+
+ spin_lock_irqsave(&db_list_lock, flags);
+ list_add(&dbq->list, &db_list);
+ spin_unlock_irqrestore(&db_list_lock, flags);
+
+ filp->private_data = dbq;
+
+ return ret;
+}
+
+/*
+ * Close the driver
+ */
+static int fsl_hv_close(struct inode *inode, struct file *filp)
+{
+ struct doorbell_queue *dbq = filp->private_data;
+ unsigned long flags;
+
+ int ret = 0;
+
+ spin_lock_irqsave(&db_list_lock, flags);
+ list_del(&dbq->list);
+ spin_unlock_irqrestore(&db_list_lock, flags);
+
+ kfree(dbq);
+
+ return ret;
+}
+
+static const struct file_operations fsl_hv_fops = {
+ .owner = THIS_MODULE,
+ .open = fsl_hv_open,
+ .release = fsl_hv_close,
+ .poll = fsl_hv_poll,
+ .read = fsl_hv_read,
+ .unlocked_ioctl = fsl_hv_ioctl,
+};
+
+static struct miscdevice fsl_hv_misc_dev = {
+ MISC_DYNAMIC_MINOR,
+ "fsl-hv",
+ &fsl_hv_fops
+};
+
+static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
+{
+ orderly_poweroff(false);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Returns the handle of the parent of the given node
+ *
+ * The handle is the value of the 'hv-handle' property
+ */
+static int get_parent_handle(struct device_node *np)
+{
+ struct device_node *parent;
+ const uint32_t *prop;
+ uint32_t handle;
+ int len;
+
+ parent = of_get_parent(np);
+ if (!parent)
+ /* It's not really possible for this to fail */
+ return -ENODEV;
+
+ /*
+ * The proper name for the handle property is "hv-handle", but some
+ * older versions of the hypervisor used "reg".
+ */
+ prop = of_get_property(parent, "hv-handle", &len);
+ if (!prop)
+ prop = of_get_property(parent, "reg", &len);
+
+ if (!prop || (len != sizeof(uint32_t))) {
+ /* This can happen only if the node is malformed */
+ of_node_put(parent);
+ return -ENODEV;
+ }
+
+ handle = be32_to_cpup(prop);
+ of_node_put(parent);
+
+ return handle;
+}
+
+/*
+ * Register a callback for failover events
+ *
+ * This function is called by device drivers to register their callback
+ * functions for fail-over events.
+ */
+int fsl_hv_failover_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&failover_subscribers, nb);
+}
+EXPORT_SYMBOL(fsl_hv_failover_register);
+
+/*
+ * Unregister a callback for failover events
+ */
+int fsl_hv_failover_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&failover_subscribers, nb);
+}
+EXPORT_SYMBOL(fsl_hv_failover_unregister);
+
+/*
+ * Return TRUE if we're running under FSL hypervisor
+ *
+ * This function checks to see if we're running under the Freescale
+ * hypervisor, and returns zero if we're not, or non-zero if we are.
+ *
+ * First, it checks if MSR[GS]==1, which means we're running under some
+ * hypervisor. Then it checks if there is a hypervisor node in the device
+ * tree. Currently, that means there needs to be a node in the root called
+ * "hypervisor" and which has a property named "fsl,hv-version".
+ */
+static int has_fsl_hypervisor(void)
+{
+ struct device_node *node;
+ int ret;
+
+ if (!(mfmsr() & MSR_GS))
+ return 0;
+
+ node = of_find_node_by_path("/hypervisor");
+ if (!node)
+ return 0;
+
+ ret = of_find_property(node, "fsl,hv-version", NULL) != NULL;
+
+ of_node_put(node);
+
+ return ret;
+}
+
+/*
+ * Freescale hypervisor management driver init
+ *
+ * This function is called when this module is loaded.
+ *
+ * Register ourselves as a miscellaneous driver. This will register the
+ * fops structure and create the right sysfs entries for udev.
+ */
+static int __init fsl_hypervisor_init(void)
+{
+ struct device_node *np;
+ struct doorbell_isr *dbisr, *n;
+ int ret;
+
+ pr_info("Freescale hypervisor management driver\n");
+
+ if (!has_fsl_hypervisor()) {
+ pr_info("fsl-hv: no hypervisor found\n");
+ return -ENODEV;
+ }
+
+ ret = misc_register(&fsl_hv_misc_dev);
+ if (ret) {
+ pr_err("fsl-hv: cannot register device\n");
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&db_list);
+ INIT_LIST_HEAD(&isr_list);
+
+ for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
+ unsigned int irq;
+ const uint32_t *handle;
+
+ handle = of_get_property(np, "interrupts", NULL);
+ irq = irq_of_parse_and_map(np, 0);
+ if (!handle || (irq == NO_IRQ)) {
+ pr_err("fsl-hv: no 'interrupts' property in %s node\n",
+ np->full_name);
+ continue;
+ }
+
+ dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
+ if (!dbisr)
+ goto out_of_memory;
+
+ dbisr->irq = irq;
+ dbisr->doorbell = be32_to_cpup(handle);
+
+ if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
+ /* The shutdown doorbell gets its own ISR */
+ ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
+ np->name, NULL);
+ } else if (of_device_is_compatible(np,
+ "fsl,hv-state-change-doorbell")) {
+ /*
+ * The state change doorbell triggers a notification if
+ * the state of the managed partition changes to
+ * "stopped". We need a separate interrupt handler for
+ * that, and we also need to know the handle of the
+ * target partition, not just the handle of the
+ * doorbell.
+ */
+ dbisr->partition = ret = get_parent_handle(np);
+ if (ret < 0) {
+ pr_err("fsl-hv: node %s has missing or "
+ "malformed parent\n", np->full_name);
+ kfree(dbisr);
+ continue;
+ }
+ ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
+ fsl_hv_state_change_thread,
+ 0, np->name, dbisr);
+ } else
+ ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
+
+ if (ret < 0) {
+ pr_err("fsl-hv: could not request irq %u for node %s\n",
+ irq, np->full_name);
+ kfree(dbisr);
+ continue;
+ }
+
+ list_add(&dbisr->list, &isr_list);
+
+ pr_info("fsl-hv: registered handler for doorbell %u\n",
+ dbisr->doorbell);
+ }
+
+ return 0;
+
+out_of_memory:
+ list_for_each_entry_safe(dbisr, n, &isr_list, list) {
+ free_irq(dbisr->irq, dbisr);
+ list_del(&dbisr->list);
+ kfree(dbisr);
+ }
+
+ misc_deregister(&fsl_hv_misc_dev);
+
+ return -ENOMEM;
+}
+
+/*
+ * Freescale hypervisor management driver termination
+ *
+ * This function is called when this driver is unloaded.
+ */
+static void __exit fsl_hypervisor_exit(void)
+{
+ struct doorbell_isr *dbisr, *n;
+
+ list_for_each_entry_safe(dbisr, n, &isr_list, list) {
+ free_irq(dbisr->irq, dbisr);
+ list_del(&dbisr->list);
+ kfree(dbisr);
+ }
+
+ misc_deregister(&fsl_hv_misc_dev);
+}
+
+module_init(fsl_hypervisor_init);
+module_exit(fsl_hypervisor_exit);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale hypervisor management driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 75cf611..68c341a 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -134,6 +134,7 @@ header-y += firewire-cdev.h
header-y += firewire-constants.h
header-y += flat.h
header-y += fs.h
+header-y += fsl_hypervisor.h
header-y += fuse.h
header-y += futex.h
header-y += gameport.h
diff --git a/include/linux/fsl_hypervisor.h b/include/linux/fsl_hypervisor.h
new file mode 100644
index 0000000..d1ca2b1
--- /dev/null
+++ b/include/linux/fsl_hypervisor.h
@@ -0,0 +1,231 @@
+/*
+ * Freescale hypervisor ioctl and kernel interface
+ *
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * This software is provided by Freescale Semiconductor "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 Freescale Semiconductor 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.
+ *
+ * This file is used by the Freescale hypervisor management driver. It can
+ * also be included by applications that need to communicate with the driver
+ * via the ioctl interface.
+ */
+
+#ifndef FSL_HYPERVISOR_H
+#define FSL_HYPERVISOR_H
+
+#include <linux/types.h>
+
+/**
+ * struct fsl_hv_ioctl_restart - restart a partition
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to restart, or -1 for the
+ * calling partition
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_RESTART
+ */
+struct fsl_hv_ioctl_restart {
+ __u32 ret;
+ __u32 partition;
+};
+
+/**
+ * struct fsl_hv_ioctl_status - get a partition's status
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to query, or -1 for the
+ * calling partition
+ * @status: The returned status of the partition
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_GET_STATUS
+ *
+ * Values of 'status':
+ * 0 = Stopped
+ * 1 = Running
+ * 2 = Starting
+ * 3 = Stopping
+ */
+struct fsl_hv_ioctl_status {
+ __u32 ret;
+ __u32 partition;
+ __u32 status;
+};
+
+/**
+ * struct fsl_hv_ioctl_start - start a partition
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to control
+ * @entry_point: The offset within the guest IMA to start execution
+ * @load: If non-zero, reload the partition's images before starting
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_START
+ */
+struct fsl_hv_ioctl_start {
+ __u32 ret;
+ __u32 partition;
+ __u32 entry_point;
+ __u32 load;
+};
+
+/**
+ * struct fsl_hv_ioctl_stop - stop a partition
+ * @ret: return error code from the hypervisor
+ * @partition: the ID of the partition to stop, or -1 for the calling
+ * partition
+ *
+ * Used by FSL_HV_IOCTL_PARTITION_STOP
+ */
+struct fsl_hv_ioctl_stop {
+ __u32 ret;
+ __u32 partition;
+};
+
+/**
+ * struct fsl_hv_ioctl_memcpy - copy memory between partitions
+ * @ret: return error code from the hypervisor
+ * @source: the partition ID of the source partition, or -1 for this
+ * partition
+ * @target: the partition ID of the target partition, or -1 for this
+ * partition
+ * @reserved: reserved, must be set to 0
+ * @local_addr: user-space virtual address of a buffer in the local
+ * partition
+ * @remote_addr: guest physical address of a buffer in the
+ * remote partition
+ * @count: the number of bytes to copy. Both the local and remote
+ * buffers must be at least 'count' bytes long
+ *
+ * Used by FSL_HV_IOCTL_MEMCPY
+ *
+ * The 'local' partition is the partition that calls this ioctl. The
+ * 'remote' partition is a different partition. The data is copied from
+ * the 'source' paritition' to the 'target' partition.
+ *
+ * The buffer in the remote partition must be guest physically
+ * contiguous.
+ *
+ * This ioctl does not support copying memory between two remote
+ * partitions or within the same partition, so either 'source' or
+ * 'target' (but not both) must be -1. In other words, either
+ *
+ * source == local and target == remote
+ * or
+ * source == remote and target == local
+ */
+struct fsl_hv_ioctl_memcpy {
+ __u32 ret;
+ __u32 source;
+ __u32 target;
+ __u32 reserved; /* padding to ensure local_vaddr is aligned */
+ __u64 local_vaddr;
+ __u64 remote_paddr;
+ __u64 count;
+};
+
+/**
+ * struct fsl_hv_ioctl_doorbell - ring a doorbell
+ * @ret: return error code from the hypervisor
+ * @doorbell: the handle of the doorbell to ring doorbell
+ *
+ * Used by FSL_HV_IOCTL_DOORBELL
+ */
+struct fsl_hv_ioctl_doorbell {
+ __u32 ret;
+ __u32 doorbell;
+};
+
+/**
+ * struct fsl_hv_ioctl_prop - get/set a device tree property
+ * @ret: return error code from the hypervisor
+ * @handle: handle of partition whose tree to access
+ * @path: virtual address of path name of node to access
+ * @propname: virtual address of name of property to access
+ * @propval: virtual address of property data buffer
+ * @proplen: Size of property data buffer
+ * @reserved: reserved, must be set to 0
+ *
+ * Used by FSL_HV_IOCTL_DOORBELL
+ */
+struct fsl_hv_ioctl_prop {
+ __u32 ret;
+ __u32 handle;
+ __u64 path;
+ __u64 propname;
+ __u64 propval;
+ __u32 proplen;
+ __u32 reserved; /* padding to ensure structure is aligned */
+};
+
+/**
+ * enum fsl_hv_ioctl_cmd - ioctl commands
+ * @FSL_HV_IOCTL_PARTITION_RESTART: restart another partition
+ * @FSL_HV_IOCTL_PARTITION_GET_STATUS: get a partition's status
+ * @FSL_HV_IOCTL_PARTITION_START: boot another partition
+ * @FSL_HV_IOCTL_PARTITION_STOP: stop this or another partition
+ * @FSL_HV_IOCTL_MEMCPY: copy data from one partition to another
+ * @FSL_HV_IOCTL_DOORBELL: ring a doorbell
+ * @FSL_HV_IOCTL_GETPROP: get a property from another guest's device tree
+ * @FSL_HV_IOCTL_SETPROP: set a property in another guest's device tree
+ *
+ * This enum lists the available ioctl commands for the Freescale hypervisor
+ * management driver. The meaning
+ */
+enum fsl_hv_ioctl_cmd {
+ FSL_HV_IOCTL_PARTITION_RESTART = _IOWR(0, 1, struct fsl_hv_ioctl_restart),
+ FSL_HV_IOCTL_PARTITION_GET_STATUS = _IOWR(0, 2, struct fsl_hv_ioctl_status),
+ FSL_HV_IOCTL_PARTITION_START = _IOWR(0, 3, struct fsl_hv_ioctl_start),
+ FSL_HV_IOCTL_PARTITION_STOP = _IOWR(0, 4, struct fsl_hv_ioctl_stop),
+ FSL_HV_IOCTL_MEMCPY = _IOWR(0, 5, struct fsl_hv_ioctl_memcpy),
+ FSL_HV_IOCTL_DOORBELL = _IOWR(0, 6, struct fsl_hv_ioctl_doorbell),
+ FSL_HV_IOCTL_GETPROP = _IOWR(0, 7, struct fsl_hv_ioctl_prop),
+ FSL_HV_IOCTL_SETPROP = _IOWR(0, 8, struct fsl_hv_ioctl_prop),
+};
+
+#ifdef __KERNEL__
+
+/**
+ * fsl_hv_event_register() - register a callback for failover events
+ * @nb: pointer to caller-supplied notifier_block structure
+ *
+ * This function is called by device drivers to register their callback
+ * functions for fail-over events.
+ *
+ * The caller should allocate a notifier_block object and initialize the
+ * 'priority' and 'notifier_call' fields.
+ */
+int fsl_hv_failover_register(struct notifier_block *nb);
+
+/**
+ * fsl_hv_event_unregister() - unregister a callback for failover events
+ * @nb: the same 'nb' used in previous fsl_hv_failover_register call
+ */
+int fsl_hv_failover_unregister(struct notifier_block *nb);
+
+#endif
+
+#endif
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH 1/3] Staging: hv: netvsc: Fix a bug in accounting transmit slots
From: Greg KH @ 2011-06-09 17:53 UTC (permalink / raw)
To: KY Srinivasan
Cc: Greg KH, linux-kernel@vger.kernel.org,
devel@linuxdriverproject.org, virtualization@lists.osdl.org,
Haiyang Zhang, Abhishek Kane (Mindtree Consulting PVT LTD)
In-Reply-To: <6E21E5352C11B742B20C142EB499E0480814575D@TK5EX14MBXC124.redmond.corp.microsoft.com>
On Thu, Jun 09, 2011 at 05:23:50PM +0000, KY Srinivasan wrote:
> > -----Original Message-----
> > From: Greg KH [mailto:greg@kroah.com]
> > Sent: Thursday, June 09, 2011 1:05 PM
> > To: KY Srinivasan
> > Cc: gregkh@suse.de; linux-kernel@vger.kernel.org;
> > devel@linuxdriverproject.org; virtualization@lists.osdl.org; Haiyang Zhang;
> > Abhishek Kane (Mindtree Consulting PVT LTD)
> > Subject: Re: [PATCH 1/3] Staging: hv: netvsc: Fix a bug in accounting transmit slots
> >
> > On Mon, Jun 06, 2011 at 03:57:35PM -0700, K. Y. Srinivasan wrote:
> >
> > I only got 2 of these three patches, what happened to the third one?
> >
> > Care to resend all 3 of these to ensure that I get them all?
>
> There really were only 2 patches I wanted to send. If you want I could reformat
> and send only the 2 I wanted to send.
Please do, as when you send something that says 1/3 and 2/3 and 3/3
never shows up, we have no idea what is going on...
thanks,
greg k-h
^ permalink raw reply
* RE: [PATCH 1/3] Staging: hv: netvsc: Fix a bug in accounting transmit slots
From: KY Srinivasan @ 2011-06-09 17:23 UTC (permalink / raw)
To: Greg KH
Cc: gregkh@suse.de, linux-kernel@vger.kernel.org,
devel@linuxdriverproject.org, virtualization@lists.osdl.org,
Haiyang Zhang, Abhishek Kane (Mindtree Consulting PVT LTD)
In-Reply-To: <20110609170437.GA24686@kroah.com>
> -----Original Message-----
> From: Greg KH [mailto:greg@kroah.com]
> Sent: Thursday, June 09, 2011 1:05 PM
> To: KY Srinivasan
> Cc: gregkh@suse.de; linux-kernel@vger.kernel.org;
> devel@linuxdriverproject.org; virtualization@lists.osdl.org; Haiyang Zhang;
> Abhishek Kane (Mindtree Consulting PVT LTD)
> Subject: Re: [PATCH 1/3] Staging: hv: netvsc: Fix a bug in accounting transmit slots
>
> On Mon, Jun 06, 2011 at 03:57:35PM -0700, K. Y. Srinivasan wrote:
>
> I only got 2 of these three patches, what happened to the third one?
>
> Care to resend all 3 of these to ensure that I get them all?
There really were only 2 patches I wanted to send. If you want I could reformat
and send only the 2 I wanted to send.
Regards,
K. Y
>
> thanks,
>
> greg k-h
^ permalink raw reply
* Re: [PATCH 1/3] Staging: hv: netvsc: Fix a bug in accounting transmit slots
From: Greg KH @ 2011-06-09 17:04 UTC (permalink / raw)
To: K. Y. Srinivasan
Cc: gregkh, linux-kernel, devel, virtualization, Haiyang Zhang,
Abhishek Kane
In-Reply-To: <1307401055-13608-1-git-send-email-kys@microsoft.com>
On Mon, Jun 06, 2011 at 03:57:35PM -0700, K. Y. Srinivasan wrote:
I only got 2 of these three patches, what happened to the third one?
Care to resend all 3 of these to ensure that I get them all?
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH 7/7] [v4] drivers/virt: introduce Freescale hypervisor management driver
From: Timur Tabi @ 2011-06-09 16:48 UTC (permalink / raw)
To: Randy Dunlap
Cc: Arnd Bergmann, konrad.wilk, greg, kumar.gala, linux-kernel,
cmetcalf, Linux Virtualization, dsaxena, linux-console,
linuxppc-dev, alan
In-Reply-To: <4DF0F7F5.5050107@oracle.com>
Randy Dunlap wrote:
>> > I'm okay with that idea, except there's a consensus that drivers should be in
>> > drivers/.
>> >
> Like sound/ ?
My understanding is that this is something that's considered broken and should
be fixed, but I don't know what the holdup is.
> but what makes it a "driver"?
That's a good point.
Ok, so maybe I don't have any really good answers here. :-)
--
Timur Tabi
Linux kernel developer at Freescale
^ 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