Merge tag 'usb-6.6-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
[platform/kernel/linux-rpi.git] / drivers / target / target_core_pr.c
index d19ec4e..49d9167 100644 (file)
@@ -3538,6 +3538,37 @@ out_put_pr_reg:
        return ret;
 }
 
+static sense_reason_t
+target_try_pr_out_pt(struct se_cmd *cmd, u8 sa, u64 res_key, u64 sa_res_key,
+                    u8 type, bool aptpl, bool all_tg_pt, bool spec_i_pt)
+{
+       struct exec_cmd_ops *ops = cmd->protocol_data;
+
+       if (!cmd->se_sess || !cmd->se_lun) {
+               pr_err("SPC-3 PR: se_sess || struct se_lun is NULL!\n");
+               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+       }
+
+       if (!ops->execute_pr_out) {
+               pr_err("SPC-3 PR: Device has been configured for PR passthrough but it's not supported by the backend.\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       switch (sa) {
+       case PRO_REGISTER_AND_MOVE:
+       case PRO_REPLACE_LOST_RESERVATION:
+               pr_err("SPC-3 PR: PRO_REGISTER_AND_MOVE and PRO_REPLACE_LOST_RESERVATION are not supported by PR passthrough.\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       if (spec_i_pt || all_tg_pt) {
+               pr_err("SPC-3 PR: SPEC_I_PT and ALL_TG_PT are not supported by PR passthrough.\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       return ops->execute_pr_out(cmd, sa, res_key, sa_res_key, type, aptpl);
+}
+
 /*
  * See spc4r17 section 6.14 Table 170
  */
@@ -3641,6 +3672,12 @@ target_scsi3_emulate_pr_out(struct se_cmd *cmd)
                return TCM_PARAMETER_LIST_LENGTH_ERROR;
        }
 
+       if (dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR) {
+               ret = target_try_pr_out_pt(cmd, sa, res_key, sa_res_key, type,
+                                          aptpl, all_tg_pt, spec_i_pt);
+               goto done;
+       }
+
        /*
         * (core_scsi3_emulate_pro_* function parameters
         * are defined by spc4r17 Table 174:
@@ -3682,6 +3719,7 @@ target_scsi3_emulate_pr_out(struct se_cmd *cmd)
                return TCM_INVALID_CDB_FIELD;
        }
 
+done:
        if (!ret)
                target_complete_cmd(cmd, SAM_STAT_GOOD);
        return ret;
@@ -4039,9 +4077,42 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd)
        return 0;
 }
 
+static sense_reason_t target_try_pr_in_pt(struct se_cmd *cmd, u8 sa)
+{
+       struct exec_cmd_ops *ops = cmd->protocol_data;
+       unsigned char *buf;
+       sense_reason_t ret;
+
+       if (cmd->data_length < 8) {
+               pr_err("PRIN SA SCSI Data Length: %u too small\n",
+                      cmd->data_length);
+               return TCM_INVALID_CDB_FIELD;
+       }
+
+       if (!ops->execute_pr_in) {
+               pr_err("SPC-3 PR: Device has been configured for PR passthrough but it's not supported by the backend.\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       if (sa == PRI_READ_FULL_STATUS) {
+               pr_err("SPC-3 PR: PRI_READ_FULL_STATUS is not supported by PR passthrough.\n");
+               return TCM_UNSUPPORTED_SCSI_OPCODE;
+       }
+
+       buf = transport_kmap_data_sg(cmd);
+       if (!buf)
+               return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+       ret = ops->execute_pr_in(cmd, sa, buf);
+
+       transport_kunmap_data_sg(cmd);
+       return ret;
+}
+
 sense_reason_t
 target_scsi3_emulate_pr_in(struct se_cmd *cmd)
 {
+       u8 sa = cmd->t_task_cdb[1] & 0x1f;
        sense_reason_t ret;
 
        /*
@@ -4060,7 +4131,12 @@ target_scsi3_emulate_pr_in(struct se_cmd *cmd)
                return TCM_RESERVATION_CONFLICT;
        }
 
-       switch (cmd->t_task_cdb[1] & 0x1f) {
+       if (cmd->se_dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR) {
+               ret = target_try_pr_in_pt(cmd, sa);
+               goto done;
+       }
+
+       switch (sa) {
        case PRI_READ_KEYS:
                ret = core_scsi3_pri_read_keys(cmd);
                break;
@@ -4079,6 +4155,7 @@ target_scsi3_emulate_pr_in(struct se_cmd *cmd)
                return TCM_INVALID_CDB_FIELD;
        }
 
+done:
        if (!ret)
                target_complete_cmd(cmd, SAM_STAT_GOOD);
        return ret;