@ -21,6 +21,8 @@
# include "hw/sysbus.h"
# include "qemu/bitops.h"
# include "hw/virtio/virtio-bus.h"
# include "hw/s390x/adapter.h"
# include "hw/s390x/s390_flic.h"
# include "ioinst.h"
# include "css.h"
@ -48,7 +50,16 @@ static IndAddr *get_indicator(hwaddr ind_addr, int len)
return indicator ;
}
static void release_indicator ( IndAddr * indicator )
static int s390_io_adapter_map ( AdapterInfo * adapter , uint64_t map_addr ,
bool do_map )
{
S390FLICState * fs = s390_get_flic ( ) ;
S390FLICStateClass * fsc = S390_FLIC_COMMON_GET_CLASS ( fs ) ;
return fsc - > io_adapter_map ( fs , adapter - > adapter_id , map_addr , do_map ) ;
}
static void release_indicator ( AdapterInfo * adapter , IndAddr * indicator )
{
assert ( indicator - > refcnt > 0 ) ;
indicator - > refcnt - - ;
@ -56,9 +67,31 @@ static void release_indicator(IndAddr *indicator)
return ;
}
QTAILQ_REMOVE ( & indicator_addresses , indicator , sibling ) ;
if ( indicator - > map ) {
s390_io_adapter_map ( adapter , indicator - > map , false ) ;
}
g_free ( indicator ) ;
}
static int map_indicator ( AdapterInfo * adapter , IndAddr * indicator )
{
int ret ;
if ( indicator - > map ) {
return 0 ; /* already mapped is not an error */
}
indicator - > map = indicator - > addr ;
ret = s390_io_adapter_map ( adapter , indicator - > map , true ) ;
if ( ( ret ! = 0 ) & & ( ret ! = - ENOSYS ) ) {
goto out_err ;
}
return 0 ;
out_err :
indicator - > map = 0 ;
return ret ;
}
static void virtio_ccw_bus_new ( VirtioBusState * bus , size_t bus_size ,
VirtioCcwDevice * dev ) ;
@ -554,11 +587,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
dev - > indicators = get_indicator ( thinint - > device_indicator ,
thinint - > ind_bit / 8 + 1 ) ;
dev - > thinint_isc = thinint - > isc ;
dev - > ind_bit = thinint - > ind_bit ;
dev - > routes . adapter . ind_offset = thinint - > ind_bit ;
dev - > routes . adapter . summary_offset = 7 ;
cpu_physical_memory_unmap ( thinint , hw_len , 0 , hw_len ) ;
ret = css_register_io_adapter ( CSS_IO_ADAPTER_VIRTIO ,
dev - > thinint_isc , true , false ,
& dev - > adapter_id ) ;
& dev - > routes . adapter . adapter_id ) ;
assert ( ret = = 0 ) ;
sch - > thinint_active = ( ( dev - > indicators ! = NULL ) & &
( dev - > summary_indicator ! = NULL ) ) ;
@ -732,7 +766,7 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
g_free ( sch ) ;
}
if ( dev - > indicators ) {
release_indicator ( dev - > indicators ) ;
release_indicator ( & dev - > routes . adapter , dev - > indicators ) ;
dev - > indicators = NULL ;
}
return 0 ;
@ -991,9 +1025,11 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
* ind_bit indicates the start of the indicators in a big
* endian notation .
*/
uint64_t ind_bit = dev - > routes . adapter . ind_offset ;
virtio_set_ind_atomic ( sch , dev - > indicators - > addr +
( dev - > ind_bit + vector ) / 8 ,
0x80 > > ( ( dev - > ind_bit + vector ) % 8 ) ) ;
( ind_bit + vector ) / 8 ,
0x80 > > ( ( ind_bit + vector ) % 8 ) ) ;
if ( ! virtio_set_ind_atomic ( sch , dev - > summary_indicator - > addr ,
0x01 ) ) {
css_adapter_interrupt ( dev - > thinint_isc ) ;
@ -1033,15 +1069,15 @@ static void virtio_ccw_reset(DeviceState *d)
virtio_reset ( vdev ) ;
css_reset_sch ( dev - > sch ) ;
if ( dev - > indicators ) {
release_indicator ( dev - > indicators ) ;
release_indicator ( & dev - > routes . adapter , dev - > indicators ) ;
dev - > indicators = NULL ;
}
if ( dev - > indicators2 ) {
release_indicator ( dev - > indicators2 ) ;
release_indicator ( & dev - > routes . adapter , dev - > indicators2 ) ;
dev - > indicators2 = NULL ;
}
if ( dev - > summary_indicator ) {
release_indicator ( dev - > summary_indicator ) ;
release_indicator ( & dev - > routes . adapter , dev - > summary_indicator ) ;
dev - > summary_indicator = NULL ;
}
}
@ -1077,6 +1113,79 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
return virtio_ccw_set_guest2host_notifier ( dev , n , assign , false ) ;
}
static int virtio_ccw_get_mappings ( VirtioCcwDevice * dev )
{
int r ;
if ( ! dev - > sch - > thinint_active ) {
return - EINVAL ;
}
r = map_indicator ( & dev - > routes . adapter , dev - > summary_indicator ) ;
if ( r ) {
return r ;
}
r = map_indicator ( & dev - > routes . adapter , dev - > indicators ) ;
if ( r ) {
return r ;
}
dev - > routes . adapter . summary_addr = dev - > summary_indicator - > map ;
dev - > routes . adapter . ind_addr = dev - > indicators - > map ;
return 0 ;
}
static int virtio_ccw_setup_irqroutes ( VirtioCcwDevice * dev , int nvqs )
{
int i ;
VirtIODevice * vdev = virtio_bus_get_device ( & dev - > bus ) ;
int ret ;
S390FLICState * fs = s390_get_flic ( ) ;
S390FLICStateClass * fsc = S390_FLIC_COMMON_GET_CLASS ( fs ) ;
ret = virtio_ccw_get_mappings ( dev ) ;
if ( ret ) {
return ret ;
}
for ( i = 0 ; i < nvqs ; i + + ) {
if ( ! virtio_queue_get_num ( vdev , i ) ) {
break ;
}
}
dev - > routes . num_routes = i ;
return fsc - > add_adapter_routes ( fs , & dev - > routes ) ;
}
static void virtio_ccw_release_irqroutes ( VirtioCcwDevice * dev , int nvqs )
{
S390FLICState * fs = s390_get_flic ( ) ;
S390FLICStateClass * fsc = S390_FLIC_COMMON_GET_CLASS ( fs ) ;
fsc - > release_adapter_routes ( fs , & dev - > routes ) ;
}
static int virtio_ccw_add_irqfd ( VirtioCcwDevice * dev , int n )
{
VirtIODevice * vdev = virtio_bus_get_device ( & dev - > bus ) ;
VirtQueue * vq = virtio_get_queue ( vdev , n ) ;
EventNotifier * notifier = virtio_queue_get_guest_notifier ( vq ) ;
return kvm_irqchip_add_irqfd_notifier ( kvm_state , notifier , NULL ,
dev - > routes . gsi [ n ] ) ;
}
static void virtio_ccw_remove_irqfd ( VirtioCcwDevice * dev , int n )
{
VirtIODevice * vdev = virtio_bus_get_device ( & dev - > bus ) ;
VirtQueue * vq = virtio_get_queue ( vdev , n ) ;
EventNotifier * notifier = virtio_queue_get_guest_notifier ( vq ) ;
int ret ;
ret = kvm_irqchip_remove_irqfd_notifier ( kvm_state , notifier ,
dev - > routes . gsi [ n ] ) ;
assert ( ret = = 0 ) ;
}
static int virtio_ccw_set_guest_notifier ( VirtioCcwDevice * dev , int n ,
bool assign , bool with_irqfd )
{
@ -1092,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
return r ;
}
virtio_queue_set_guest_notifier_fd_handler ( vq , true , with_irqfd ) ;
/* We do not support irqfd for classic I/O interrupts, because the
* classic interrupts are intermixed with the subchannel status , that
* is queried with test subchannel . We want to use vhost , though .
* Lets make sure to have vhost running and wire up the irq fd to
* land in qemu ( and only the irq fd ) in this code .
if ( with_irqfd ) {
r = virtio_ccw_add_irqfd ( dev , n ) ;
if ( r ) {
virtio_queue_set_guest_notifier_fd_handler ( vq , false ,
with_irqfd ) ;
return r ;
}
}
/*
* We do not support individual masking for channel devices , so we
* need to manually trigger any guest masking callbacks here .
*/
if ( k - > guest_notifier_mask ) {
k - > guest_notifier_mask ( vdev , n , false ) ;
@ -1110,6 +1225,9 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
if ( k - > guest_notifier_mask ) {
k - > guest_notifier_mask ( vdev , n , true ) ;
}
if ( with_irqfd ) {
virtio_ccw_remove_irqfd ( dev , n ) ;
}
virtio_queue_set_guest_notifier_fd_handler ( vq , false , with_irqfd ) ;
event_notifier_cleanup ( notifier ) ;
}
@ -1121,24 +1239,39 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
{
VirtioCcwDevice * dev = VIRTIO_CCW_DEVICE ( d ) ;
VirtIODevice * vdev = virtio_bus_get_device ( & dev - > bus ) ;
bool with_irqfd = dev - > sch - > thinint_active & & kvm_irqfds_enabled ( ) ;
int r , n ;
if ( with_irqfd & & assigned ) {
/* irq routes need to be set up before assigning irqfds */
r = virtio_ccw_setup_irqroutes ( dev , nvqs ) ;
if ( r < 0 ) {
goto irqroute_error ;
}
}
for ( n = 0 ; n < nvqs ; n + + ) {
if ( ! virtio_queue_get_num ( vdev , n ) ) {
break ;
}
/* false -> true, as soon as irqfd works */
r = virtio_ccw_set_guest_notifier ( dev , n , assigned , false ) ;
r = virtio_ccw_set_guest_notifier ( dev , n , assigned , with_irqfd ) ;
if ( r < 0 ) {
goto assign_error ;
}
}
if ( with_irqfd & & ! assigned ) {
/* release irq routes after irqfds have been released */
virtio_ccw_release_irqroutes ( dev , nvqs ) ;
}
return 0 ;
assign_error :
while ( - - n > = 0 ) {
virtio_ccw_set_guest_notifier ( dev , n , ! assigned , false ) ;
}
irqroute_error :
if ( with_irqfd & & assigned ) {
virtio_ccw_release_irqroutes ( dev , nvqs ) ;
}
return r ;
}