/* * alua_stpg - Evaluate SET TARGET GROUP STATES * @sdev: the device to be evaluated * @state: the new target group state * * Send a SET TARGET GROUP STATES command to the device. * We only have to test here if we should resubmit the command; * any other error is assumed as a failure. */ static void stpg_endio(struct request *req, int error) { struct alua_dh_data *h = req->end_io_data; struct scsi_sense_hdr sense_hdr; unsigned err = SCSI_DH_OK; if (host_byte(req->errors) != DID_OK || msg_byte(req->errors) != COMMAND_COMPLETE) { err = SCSI_DH_IO; goto done; } if (req->sense_len > 0) { err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!err) { err = SCSI_DH_IO; goto done; } err = alua_check_sense(h->sdev, &sense_hdr); if (err == ADD_TO_MLQUEUE) { err = SCSI_DH_RETRY; goto done; } sdev_printk(KERN_INFO, h->sdev, "%s: stpg sense code: %02x/%02x/%02x\n", ALUA_DH_NAME, sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq); err = SCSI_DH_IO; } else if (error) err = SCSI_DH_IO; if (err == SCSI_DH_OK) { h->state = TPGS_STATE_OPTIMIZED; sdev_printk(KERN_INFO, h->sdev, "%s: port group %02x switched to state %c\n", ALUA_DH_NAME, h->group_id, print_alua_state(h->state)); } done: req->end_io_data = NULL; __blk_put_request(req->q, req); if (h->callback_fn) { h->callback_fn(h->callback_data, err); h->callback_fn = h->callback_data = NULL; } return; }
/* * stpg_endio - Evaluate SET TARGET GROUP STATES * @sdev: the device to be evaluated * @state: the new target group state * * Evaluate a SET TARGET GROUP STATES command response. */ static void stpg_endio(struct request *req, int error) { struct alua_dh_data *h = req->end_io_data; struct scsi_sense_hdr sense_hdr; unsigned err = SCSI_DH_OK; if (host_byte(req->errors) != DID_OK || msg_byte(req->errors) != COMMAND_COMPLETE) { err = SCSI_DH_IO; goto done; } if (scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr)) { if (sense_hdr.sense_key == NOT_READY && sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) { /* ALUA state transition already in progress */ err = SCSI_DH_OK; goto done; } if (sense_hdr.sense_key == UNIT_ATTENTION) { err = SCSI_DH_RETRY; goto done; } sdev_printk(KERN_INFO, h->sdev, "%s: stpg failed\n", ALUA_DH_NAME); scsi_print_sense_hdr(h->sdev, ALUA_DH_NAME, &sense_hdr); err = SCSI_DH_IO; } else if (error) err = SCSI_DH_IO; if (err == SCSI_DH_OK) { h->state = TPGS_STATE_OPTIMIZED; sdev_printk(KERN_INFO, h->sdev, "%s: port group %02x switched to state %c\n", ALUA_DH_NAME, h->group_id, print_alua_state(h->state)); } done: req->end_io_data = NULL; __blk_put_request(req->q, req); if (h->callback_fn) { h->callback_fn(h->callback_data, err); h->callback_fn = h->callback_data = NULL; } return; }
for (k = tpg_desc_tbl_off, ucp = h->buff + tpg_desc_tbl_off; k < len; k += off, ucp += off) { if (h->group_id == get_unaligned_be16(&ucp[2])) { h->state = ucp[0] & 0x0f; h->pref = ucp[0] >> 7; valid_states = ucp[1]; } off = 8 + (ucp[7] * 4); } sdev_printk(KERN_INFO, sdev, "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n", ALUA_DH_NAME, h->group_id, print_alua_state(h->state), h->pref ? "preferred" : "non-preferred", valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', valid_states&TPGS_SUPPORT_STANDBY?'S':'s', valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); switch (h->state) { case TPGS_STATE_TRANSITIONING: if (wait_for_transition) { if (time_before(jiffies, expiry)) { /* State transition, retry */ interval += 2000;
/* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. * * Evaluate the Target Port Group State. * Returns SCSI_DH_DEV_OFFLINED if the path is * found to be unuseable. */ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) { struct scsi_sense_hdr sense_hdr; int len, k, off, valid_states = 0; unsigned char *ucp; unsigned err; unsigned long expiry, interval = 10; expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT); retry: err = submit_rtpg(sdev, h); if (err == SCSI_DH_IO && h->senselen > 0) { err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!err) return SCSI_DH_IO; err = alua_check_sense(sdev, &sense_hdr); if (err == ADD_TO_MLQUEUE && time_before(jiffies, expiry)) goto retry; sdev_printk(KERN_INFO, sdev, "%s: rtpg sense code %02x/%02x/%02x\n", ALUA_DH_NAME, sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq); err = SCSI_DH_IO; } if (err != SCSI_DH_OK) return err; len = (h->buff[0] << 24) + (h->buff[1] << 16) + (h->buff[2] << 8) + h->buff[3] + 4; if (len > h->bufflen) { /* Resubmit with the correct length */ if (realloc_buffer(h, len)) { sdev_printk(KERN_WARNING, sdev, "%s: kmalloc buffer failed\n",__func__); /* Temporary failure, bypass */ return SCSI_DH_DEV_TEMP_BUSY; } goto retry; } for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { if (h->group_id == (ucp[2] << 8) + ucp[3]) { h->state = ucp[0] & 0x0f; valid_states = ucp[1]; } off = 8 + (ucp[7] * 4); } sdev_printk(KERN_INFO, sdev, "%s: port group %02x state %c supports %c%c%c%c%c%c%c\n", ALUA_DH_NAME, h->group_id, print_alua_state(h->state), valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', valid_states&TPGS_SUPPORT_STANDBY?'S':'s', valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); switch (h->state) { case TPGS_STATE_TRANSITIONING: if (time_before(jiffies, expiry)) { /* State transition, retry */ interval *= 10; msleep(interval); goto retry; } /* Transitioning time exceeded, set port to standby */ err = SCSI_DH_RETRY; h->state = TPGS_STATE_STANDBY; break; case TPGS_STATE_OFFLINE: case TPGS_STATE_UNAVAILABLE: /* Path unuseable for unavailable/offline */ err = SCSI_DH_DEV_OFFLINED; break; default: /* Useable path if active */ err = SCSI_DH_OK; break; } return err; }
/* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. * * Evaluate the Target Port Group State. * Returns SCSI_DH_DEV_OFFLINED if the path is * found to be unuseable. */ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) { struct scsi_sense_hdr sense_hdr; int len, k, off, valid_states = 0; char *ucp; unsigned err; retry: err = submit_rtpg(sdev, h); if (err == SCSI_DH_IO && h->senselen > 0) { err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!err) return SCSI_DH_IO; err = alua_check_sense(sdev, &sense_hdr); if (err == ADD_TO_MLQUEUE) goto retry; sdev_printk(KERN_INFO, sdev, "%s: rtpg sense code %02x/%02x/%02x\n", ALUA_DH_NAME, sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq); err = SCSI_DH_IO; } if (err != SCSI_DH_OK) return err; len = (h->buff[0] << 24) + (h->buff[1] << 16) + (h->buff[2] << 8) + h->buff[3] + 4; if (len > h->bufflen) { /* Resubmit with the correct length */ if (realloc_buffer(h, len)) { sdev_printk(KERN_WARNING, sdev, "%s: kmalloc buffer failed\n",__func__); /* Temporary failure, bypass */ return SCSI_DH_DEV_TEMP_BUSY; } goto retry; } for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { if (h->group_id == (ucp[2] << 8) + ucp[3]) { h->state = ucp[0] & 0x0f; valid_states = ucp[1]; } off = 8 + (ucp[7] * 4); } sdev_printk(KERN_INFO, sdev, "%s: port group %02x state %c supports %c%c%c%c%c%c\n", ALUA_DH_NAME, h->group_id, print_alua_state(h->state), valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', valid_states&TPGS_SUPPORT_STANDBY?'S':'s', valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); if (h->tpgs & TPGS_MODE_EXPLICIT) { switch (h->state) { case TPGS_STATE_TRANSITIONING: /* State transition, retry */ goto retry; break; case TPGS_STATE_OFFLINE: /* Path is offline, fail */ err = SCSI_DH_DEV_OFFLINED; break; default: break; } } else { /* Only Implicit ALUA support */ if (h->state == TPGS_STATE_OPTIMIZED || h->state == TPGS_STATE_NONOPTIMIZED || h->state == TPGS_STATE_STANDBY) /* Useable path if active */ err = SCSI_DH_OK; else /* Path unuseable for unavailable/offline */ err = SCSI_DH_DEV_OFFLINED; } return err; }