/*
* Helpers to operate ccwchain.
*/
+#define ccw_is_read(_ccw) (((_ccw)->cmd_code & 0x03) == 0x02)
+#define ccw_is_read_backward(_ccw) (((_ccw)->cmd_code & 0x0F) == 0x0C)
+#define ccw_is_sense(_ccw) (((_ccw)->cmd_code & 0x0F) == CCW_CMD_BASIC_SENSE)
+
#define ccw_is_test(_ccw) (((_ccw)->cmd_code & 0x0F) == 0)
#define ccw_is_noop(_ccw) ((_ccw)->cmd_code == CCW_CMD_NOOP)
#define ccw_is_tic(_ccw) ((_ccw)->cmd_code == CCW_CMD_TIC)
#define ccw_is_idal(_ccw) ((_ccw)->flags & CCW_FLAG_IDA)
-
+#define ccw_is_skip(_ccw) ((_ccw)->flags & CCW_FLAG_SKIP)
#define ccw_is_chain(_ccw) ((_ccw)->flags & (CCW_FLAG_CC | CCW_FLAG_DC))
+/*
+ * ccw_does_data_transfer()
+ *
+ * Determine whether a CCW will move any data, such that the guest pages
+ * would need to be pinned before performing the I/O.
+ *
+ * Returns 1 if yes, 0 if no.
+ */
+static inline int ccw_does_data_transfer(struct ccw1 *ccw)
+{
+ /* If the skip flag is off, then data will be transferred */
+ if (!ccw_is_skip(ccw))
+ return 1;
+
+ /*
+ * If the skip flag is on, it is only meaningful if the command
+ * code is a read, read backward, sense, or sense ID. In those
+ * cases, no data will be transferred.
+ */
+ if (ccw_is_read(ccw) || ccw_is_read_backward(ccw))
+ return 0;
+
+ if (ccw_is_sense(ccw))
+ return 0;
+
+ /* The skip flag is on, but it is ignored for this command code. */
+ return 1;
+}
+
/*
* is_cpa_within_range()
*
struct pfn_array_table *pat;
unsigned long *idaws;
int ret;
+ int idaw_nr = 1;
ccw = chain->ch_ccw + idx;
*/
ccw->flags |= CCW_FLAG_IDA;
return 0;
+ } else {
+ idaw_nr = idal_nr_words((void *)(u64)ccw->cda, ccw->count);
}
/*
if (ret < 0)
goto out_unpin;
- ret = pfn_array_pin(pat->pat_pa, cp->mdev);
- if (ret < 0)
- goto out_unpin;
+ if (ccw_does_data_transfer(ccw)) {
+ ret = pfn_array_pin(pat->pat_pa, cp->mdev);
+ if (ret < 0)
+ goto out_unpin;
+ } else {
+ pat->pat_pa->pa_nr = 0;
+ }
/* Translate this direct ccw to a idal ccw. */
- idaws = kcalloc(ret, sizeof(*idaws), GFP_DMA | GFP_KERNEL);
+ idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL);
if (!idaws) {
ret = -ENOMEM;
goto out_unpin;
if (ret < 0)
goto out_free_idaws;
+ if (!ccw_does_data_transfer(ccw)) {
+ pa->pa_nr = 0;
+ continue;
+ }
+
ret = pfn_array_pin(pa, cp->mdev);
if (ret < 0)
goto out_free_idaws;