switch (oob_status) { case CURRENT_LOSS_OF_SIGNAL: /* directly attached device was removed */
ASD_DPRINTK("phy%d: device unplugged\n", phy_id);
asd_turn_led(asd_ha, phy_id, 0);
sas_phy_disconnected(&phy->sas_phy);
sas_notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL,
GFP_ATOMIC); break; case CURRENT_OOB_DONE: /* hot plugged device */
asd_turn_led(asd_ha, phy_id, 1);
get_lrate_mode(phy, oob_mode);
ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",
phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);
sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE, GFP_ATOMIC); break; case CURRENT_SPINUP_HOLD: /* hot plug SATA, no COMWAKE sent */
asd_turn_led(asd_ha, phy_id, 1);
sas_notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD,
GFP_ATOMIC); break; case CURRENT_GTO_TIMEOUT: case CURRENT_OOB_ERROR:
ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id,
dl->status_block[1]);
asd_turn_led(asd_ha, phy_id, 0);
sas_phy_disconnected(&phy->sas_phy);
sas_notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR, GFP_ATOMIC); break;
}
}
/* If phys are enabled sparsely, this will do the right thing. */ staticunsigned ord_phy(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
{
u8 enabled_mask = asd_ha->hw_prof.enabled_phys; int i, k = 0;
/** * asd_get_attached_sas_addr -- extract/generate attached SAS address * @phy: pointer to asd_phy * @sas_addr: pointer to buffer where the SAS address is to be written * * This function extracts the SAS address from an IDENTIFY frame * received. If OOB is SATA, then a SAS address is generated from the * HA tables. * * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame * buffer.
*/ staticvoid asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr)
{ if (phy->sas_phy.frame_rcvd[0] == 0x34
&& phy->sas_phy.oob_mode == SATA_OOB_MODE) { struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; /* FIS device-to-host */
u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr);
spin_lock_irqsave(&asd_ha->asd_ports_lock, flags); if (!phy->asd_port) { for (i = 0; i < ASD_MAX_PHYS; i++) {
port = &asd_ha->asd_ports[i];
/* Check for wide port */ if (port->num_phys > 0 &&
memcmp(port->sas_addr, sas_phy->sas_addr,
SAS_ADDR_SIZE) == 0 &&
memcmp(port->attached_sas_addr,
sas_phy->attached_sas_addr,
SAS_ADDR_SIZE) == 0) { break;
}
/* Find a free port */ if (port->num_phys == 0 && free_port == NULL) {
free_port = port;
}
}
/* Use a free port if this doesn't form a wide port */ if (i >= ASD_MAX_PHYS) {
port = free_port;
BUG_ON(!port);
memcpy(port->sas_addr, sas_phy->sas_addr,
SAS_ADDR_SIZE);
memcpy(port->attached_sas_addr,
sas_phy->attached_sas_addr,
SAS_ADDR_SIZE);
}
port->num_phys++;
port->phy_mask |= (1U << sas_phy->id);
phy->asd_port = port;
}
ASD_DPRINTK("%s: updating phy_mask 0x%x for phy%d\n",
__func__, phy->asd_port->phy_mask, sas_phy->id);
asd_update_port_links(asd_ha, phy);
spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
}
switch (lr_error) { case 0:
ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id); break; case 1:
ASD_DPRINTK("phy%d: Loss of signal\n", phy_id); break; case 2:
ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id); break; case 3:
ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id); break; default:
ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n",
phy_id, lr_error); break;
}
reg &= ~3; switch (reg) { case LmPRMSTAT0BYTE0: switch (cont) { case LmBROADCH: case LmBROADRVCH0: case LmBROADRVCH1: case LmBROADSES:
ASD_DPRINTK("phy%d: BROADCAST change received:%d\n",
phy_id, cont);
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
sas_phy->sas_prim = ffs(cont);
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
sas_notify_port_event(sas_phy, PORTE_BROADCAST_RCVD,
GFP_ATOMIC); break;
case LmUNKNOWNP:
ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id); break;
default:
ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
phy_id, reg, cont); break;
} break; case LmPRMSTAT1BYTE0: switch (cont) { case LmHARDRST:
ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n",
phy_id); /* The sequencer disables all phys on that port.
* We have to re-enable the phys ourselves. */
asd_deform_port(asd_ha, phy);
sas_notify_port_event(sas_phy, PORTE_HARD_RESET,
GFP_ATOMIC); break;
/** * asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB * @ascb: pointer to Empty SCB * @edb_id: index [0,6] to the empty data buffer which is to be invalidated * * After an EDB has been invalidated, if all EDBs in this ESCB have been * invalidated, the ESCB is posted back to the sequencer. * Context is tasklet/IRQ.
*/ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
{ struct asd_seq_data *seq = &ascb->ha->seq; struct empty_scb *escb = &ascb->scb->escb; struct sg_el *eb = &escb->eb[edb_id]; struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id];
/* Catch these before we mask off the sb_opcode bits */ switch (sb_opcode) { case REQ_TASK_ABORT: { struct asd_ascb *a, *b;
u16 tc_abort; struct domain_device *failed_dev = NULL;
/* * Find the task that caused the abort and abort it first. * The sequencer won't put anything on the done list until * that happens.
*/
tc_abort = *((u16*)(&dl->status_block[1]));
tc_abort = le16_to_cpu(tc_abort);
if (task) {
failed_dev = task->dev;
sas_task_abort(task);
} else {
ASD_DPRINTK("R_T_A for non TASK scb 0x%x\n",
a->scb->header.opcode);
} break;
}
if (!failed_dev) {
ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n",
__func__, tc_abort); goto out;
}
/* * Now abort everything else for that device (hba?) so * that the EH will wake up and do something.
*/
list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { struct sas_task *task = a->uldd_task;
/* Find the last pending task for the device... */
list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
u16 x; struct domain_device *dev; struct sas_task *task = a->uldd_task;
if (!task) continue;
dev = task->dev;
x = (unsignedlong)dev->lldd_dev; if (x == conn_handle)
last_dev_task = task;
}
if (!last_dev_task) {
ASD_DPRINTK("%s: Device reset for idle device %d?\n",
__func__, conn_handle); goto out;
}
/* ...and set the reset flag */
spin_lock_irqsave(&last_dev_task->task_state_lock, flags);
last_dev_task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
spin_unlock_irqrestore(&last_dev_task->task_state_lock, flags);
/* Kill all pending tasks for the device */
list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
u16 x; struct domain_device *dev; struct sas_task *task = a->uldd_task;
if (!task) continue;
dev = task->dev;
x = (unsignedlong)dev->lldd_dev; if (x == conn_handle)
sas_task_abort(task);
}
goto out;
} case SIGNAL_NCQ_ERROR:
ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __func__); goto out; case CLEAR_NCQ_ERROR:
ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __func__); goto out;
}
/** * control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb * @ascb: pointer to an ascb * @dl: pointer to the done list entry * * This function completes a CONTROL PHY scb and frees the ascb. * A note on LEDs: * - an LED blinks if there is IO though it, * - if a device is connected to the LED, it is lit, * - if no device is connected to the LED, is is dimmed (off).
*/ staticvoid control_phy_tasklet_complete(struct asd_ascb *ascb, struct done_list_struct *dl)
{ struct asd_ha_struct *asd_ha = ascb->ha; struct scb *scb = ascb->scb; struct control_phy *control_phy = &scb->control_phy;
u8 phy_id = control_phy->phy_id; struct asd_phy *phy = &ascb->ha->phys[phy_id];
switch (pd->max_sas_lrate) { case SAS_LINK_RATE_6_0_GBPS:
*speed_mask &= ~SAS_SPEED_60_DIS;
fallthrough; default: case SAS_LINK_RATE_3_0_GBPS:
*speed_mask &= ~SAS_SPEED_30_DIS;
fallthrough; case SAS_LINK_RATE_1_5_GBPS:
*speed_mask &= ~SAS_SPEED_15_DIS;
}
switch (pd->min_sas_lrate) { case SAS_LINK_RATE_6_0_GBPS:
*speed_mask |= SAS_SPEED_30_DIS;
fallthrough; case SAS_LINK_RATE_3_0_GBPS:
*speed_mask |= SAS_SPEED_15_DIS;
fallthrough; default: case SAS_LINK_RATE_1_5_GBPS: /* nothing to do */
;
}
switch (pd->max_sata_lrate) { case SAS_LINK_RATE_3_0_GBPS:
*speed_mask &= ~SATA_SPEED_30_DIS;
fallthrough; default: case SAS_LINK_RATE_1_5_GBPS:
*speed_mask &= ~SATA_SPEED_15_DIS;
}
switch (pd->min_sata_lrate) { case SAS_LINK_RATE_3_0_GBPS:
*speed_mask |= SATA_SPEED_15_DIS;
fallthrough; default: case SAS_LINK_RATE_1_5_GBPS: /* nothing to do */
;
}
}
/** * asd_build_control_phy -- build a CONTROL PHY SCB * @ascb: pointer to an ascb * @phy_id: phy id to control, integer * @subfunc: subfunction, what to actually to do the phy * * This function builds a CONTROL PHY scb. No allocation of any kind * is performed. @ascb is allocated with the list function. * The caller can override the ascb->tasklet_complete to point * to its own callback function. It must call asd_ascb_free() * at its tasklet complete function. * See the default implementation.
*/ void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
{ struct asd_phy *phy = &ascb->ha->phys[phy_id]; struct scb *scb = ascb->scb; struct control_phy *control_phy = &scb->control_phy;
/** * asd_ascb_timedout -- called when a pending SCB's timer has expired * @t: Timer context used to fetch the SCB * * This is the default timeout function which does the most necessary. * Upper layers can implement their own timeout function, say to free * resources they have with this SCB, and then call this one at the * end of their timeout function. To do this, one should initialize * the ascb->timer.{function, expires} prior to calling the post * function. The timer is started by the post function.
*/ void asd_ascb_timedout(struct timer_list *t)
{ struct asd_ascb *ascb = timer_container_of(ascb, t, timer); struct asd_seq_data *seq = &ascb->ha->seq; unsignedlong flags;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.