target_core_alua: validate ALUA state transition
authorHannes Reinecke <hare@suse.de>
Tue, 17 Dec 2013 08:18:43 +0000 (09:18 +0100)
committerNicholas Bellinger <nab@linux-iscsi.org>
Tue, 17 Dec 2013 18:30:26 +0000 (10:30 -0800)
As we now can modify the list of supported states we need to
validate the requested ALUA state when doing a state transition.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
drivers/target/target_core_alua.c

index fdcee32..292ecce 100644 (file)
 #include "target_core_alua.h"
 #include "target_core_ua.h"
 
-static sense_reason_t core_alua_check_transition(int state, int *primary);
+static sense_reason_t core_alua_check_transition(int state, int valid,
+                                                int *primary);
 static int core_alua_set_tg_pt_secondary_state(
                struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
                struct se_port *port, int explicit, int offline);
 
+static char *core_alua_dump_state(int state);
+
 static u16 alua_lu_gps_counter;
 static u32 alua_lu_gps_count;
 
@@ -210,7 +213,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
        unsigned char *ptr;
        sense_reason_t rc = TCM_NO_SENSE;
        u32 len = 4; /* Skip over RESERVED area in header */
-       int alua_access_state, primary = 0;
+       int alua_access_state, primary = 0, valid_states;
        u16 tg_pt_id, rtpi;
 
        if (!l_port)
@@ -252,6 +255,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
                rc = TCM_UNSUPPORTED_SCSI_OPCODE;
                goto out;
        }
+       valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states;
 
        ptr = &buf[4]; /* Skip over RESERVED area in header */
 
@@ -263,7 +267,8 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)
                 * the state is a primary or secondary target port asymmetric
                 * access state.
                 */
-               rc = core_alua_check_transition(alua_access_state, &primary);
+               rc = core_alua_check_transition(alua_access_state,
+                                               valid_states, &primary);
                if (rc) {
                        /*
                         * If the SET TARGET PORT GROUPS attempts to establish
@@ -618,17 +623,31 @@ out:
  * Check implicit and explicit ALUA state change request.
  */
 static sense_reason_t
-core_alua_check_transition(int state, int *primary)
+core_alua_check_transition(int state, int valid, int *primary)
 {
+       /*
+        * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are
+        * defined as primary target port asymmetric access states.
+        */
        switch (state) {
        case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:
+               if (!(valid & ALUA_AO_SUP))
+                       goto not_supported;
+               *primary = 1;
+               break;
        case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
+               if (!(valid & ALUA_AN_SUP))
+                       goto not_supported;
+               *primary = 1;
+               break;
        case ALUA_ACCESS_STATE_STANDBY:
+               if (!(valid & ALUA_S_SUP))
+                       goto not_supported;
+               *primary = 1;
+               break;
        case ALUA_ACCESS_STATE_UNAVAILABLE:
-               /*
-                * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are
-                * defined as primary target port asymmetric access states.
-                */
+               if (!(valid & ALUA_U_SUP))
+                       goto not_supported;
                *primary = 1;
                break;
        case ALUA_ACCESS_STATE_OFFLINE:
@@ -636,14 +655,27 @@ core_alua_check_transition(int state, int *primary)
                 * OFFLINE state is defined as a secondary target port
                 * asymmetric access state.
                 */
+               if (!(valid & ALUA_O_SUP))
+                       goto not_supported;
                *primary = 0;
                break;
+       case ALUA_ACCESS_STATE_TRANSITION:
+               /*
+                * Transitioning is set internally, and
+                * cannot be selected manually.
+                */
+               goto not_supported;
        default:
                pr_err("Unknown ALUA access state: 0x%02x\n", state);
                return TCM_INVALID_PARAMETER_LIST;
        }
 
        return 0;
+
+not_supported:
+       pr_err("ALUA access state %s not supported",
+              core_alua_dump_state(state));
+       return TCM_INVALID_PARAMETER_LIST;
 }
 
 static char *core_alua_dump_state(int state)
@@ -659,6 +691,8 @@ static char *core_alua_dump_state(int state)
                return "Unavailable";
        case ALUA_ACCESS_STATE_OFFLINE:
                return "Offline";
+       case ALUA_ACCESS_STATE_TRANSITION:
+               return "Transitioning";
        default:
                return "Unknown";
        }
@@ -884,9 +918,10 @@ int core_alua_do_port_transition(
        struct t10_alua_lu_gp_member *lu_gp_mem, *local_lu_gp_mem;
        struct t10_alua_tg_pt_gp *tg_pt_gp;
        unsigned char *md_buf;
-       int primary;
+       int primary, valid_states;
 
-       if (core_alua_check_transition(new_state, &primary) != 0)
+       valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states;
+       if (core_alua_check_transition(new_state, valid_states, &primary) != 0)
                return -EINVAL;
 
        md_buf = kzalloc(l_tg_pt_gp->tg_pt_gp_md_buf_len, GFP_KERNEL);