The rules of an IDAW are fairly simple: Each one can move no more than a defined amount of data, must not cross the boundary defined by that length, and must be aligned to that length as well. The first IDAW in a list is special, in that it does not need to adhere to that alignment, but the other rules still apply. Thus, by reading the first IDAW in a list, the number of IDAWs that will comprise a data transfer of a particular size can be calculated. Let's factor out the reading of that first IDAW with the logic that calculates the length of the list, to simplify the rest of the routine that handles the individual IDAWs. Signed-off-by: Eric Farman <farman@xxxxxxxxxxxxx> Reviewed-by: Matthew Rosato <mjrosato@xxxxxxxxxxxxx> --- drivers/s390/cio/vfio_ccw_cp.c | 39 ++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index f448aa93007f..9d74e0b74da7 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -496,23 +496,25 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw, return -EFAULT; } -static int ccwchain_fetch_ccw(struct ccw1 *ccw, - struct page_array *pa, - struct channel_program *cp) +/* + * ccw_count_idaws() - Calculate the number of IDAWs needed to transfer + * a specified amount of data + * + * @ccw: The Channel Command Word being translated + * @cp: Channel Program being processed + */ +static int ccw_count_idaws(struct ccw1 *ccw, + struct channel_program *cp) { struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; u64 iova; - unsigned long *idaws; int ret; int bytes = 1; - int idaw_nr, idal_len; - int i; if (ccw->count) bytes = ccw->count; - /* Calculate size of IDAL */ if (ccw_is_idal(ccw)) { /* Read first IDAW to see if it's 4K-aligned or not. */ /* All subsequent IDAws will be 4K-aligned. */ @@ -522,7 +524,26 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, } else { iova = ccw->cda; } - idaw_nr = idal_nr_words((void *)iova, bytes); + + return idal_nr_words((void *)iova, bytes); +} + +static int ccwchain_fetch_ccw(struct ccw1 *ccw, + struct page_array *pa, + struct channel_program *cp) +{ + struct vfio_device *vdev = + &container_of(cp, struct vfio_ccw_private, cp)->vdev; + unsigned long *idaws; + int ret; + int idaw_nr, idal_len; + int i; + + /* Calculate size of IDAL */ + idaw_nr = ccw_count_idaws(ccw, cp); + if (idaw_nr < 0) + return idaw_nr; + idal_len = idaw_nr * sizeof(*idaws); /* Allocate an IDAL from host storage */ @@ -555,7 +576,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, for (i = 0; i < idaw_nr; i++) pa->pa_iova[i] = idaws[i]; } else { - pa->pa_iova[0] = iova; + pa->pa_iova[0] = ccw->cda; for (i = 1; i < pa->pa_nr; i++) pa->pa_iova[i] = pa->pa_iova[i - 1] + PAGE_SIZE; } -- 2.34.1