plan9fox/sys/src/9/port/sdscsi.c
cinap_lenrek f97798e710 devsd: don't raise Enomem error if sdmalloc() fails, instead wait for the memory to become available
filesystems do not handle i/o errors well (cwfs will abandon the blocks),
and temporary exhaustion of kernel memory (because of too many i/o's in
parallel) causes read and write on the partition to fail.

i think it is better to wait for the memory to become available in
this case. the single allocation is at max SDmaxio bytes, which makes
it likely to become available. if we havnt even enought fo that, then
rebooting the machine would be the best option. (aux/reboot)
2013-05-19 20:59:55 +02:00

462 lines
8.3 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
#include "../port/sd.h"
static int
scsitest(SDreq* r)
{
r->write = 0;
memset(r->cmd, 0, sizeof(r->cmd));
r->cmd[1] = r->lun<<5;
r->clen = 6;
r->data = nil;
r->dlen = 0;
r->flags = 0;
r->status = ~0;
return r->unit->dev->ifc->rio(r);
}
int
scsiverify(SDunit* unit)
{
SDreq *r;
int i, status;
uchar *inquiry;
if((r = malloc(sizeof(SDreq))) == nil)
return 0;
if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
free(r);
return 0;
}
r->unit = unit;
r->lun = 0; /* ??? */
memset(unit->inquiry, 0, sizeof(unit->inquiry));
r->write = 0;
r->cmd[0] = 0x12;
r->cmd[1] = r->lun<<5;
r->cmd[4] = 36;
r->clen = 6;
r->data = inquiry;
r->dlen = 36;
r->flags = 0;
r->status = ~0;
if(unit->dev->ifc->rio(r) != SDok){
free(r);
return 0;
}
memmove(unit->inquiry, inquiry, r->dlen);
free(inquiry);
SET(status);
for(i = 0; i < 3; i++){
while((status = scsitest(r)) == SDbusy)
;
if(status == SDok || status != SDcheck)
break;
if(!(r->flags & SDvalidsense))
break;
if((r->sense[2] & 0x0F) != 0x02)
continue;
/*
* Unit is 'not ready'.
* If it is in the process of becoming ready or needs
* an initialising command, set status so it will be spun-up
* below.
* If there's no medium, that's OK too, but don't
* try to spin it up.
*/
if(r->sense[12] == 0x04){
if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
status = SDok;
break;
}
}
if(r->sense[12] == 0x3A)
break;
}
if(status == SDok){
/*
* Try to ensure a direct-access device is spinning.
* Don't wait for completion, ignore the result.
*/
if((unit->inquiry[0] & 0x1F) == 0){
memset(r->cmd, 0, sizeof(r->cmd));
r->write = 0;
r->cmd[0] = 0x1B;
r->cmd[1] = (r->lun<<5)|0x01;
r->cmd[4] = 1;
r->clen = 6;
r->data = nil;
r->dlen = 0;
r->flags = 0;
r->status = ~0;
unit->dev->ifc->rio(r);
}
}
free(r);
if(status == SDok || status == SDcheck)
return 1;
return 0;
}
static int
scsirio(SDreq* r)
{
/*
* Perform an I/O request, returning
* -1 failure
* 0 ok
* 1 no medium present
* 2 retry
* The contents of r may be altered so the
* caller should re-initialise if necesary.
*/
r->status = ~0;
switch(r->unit->dev->ifc->rio(r)){
default:
break;
case SDcheck:
if(!(r->flags & SDvalidsense))
break;
switch(r->sense[2] & 0x0F){
case 0x00: /* no sense */
case 0x01: /* recovered error */
return 2;
case 0x06: /* check condition */
/*
* 0x28 - not ready to ready transition,
* medium may have changed.
* 0x29 - power on or some type of reset.
*/
if(r->sense[12] == 0x28 && r->sense[13] == 0)
return 2;
if(r->sense[12] == 0x29)
return 2;
break;
case 0x02: /* not ready */
/*
* If no medium present, bail out.
* If unit is becoming ready, rather than not
* not ready, wait a little then poke it again.
*/
if(r->sense[12] == 0x3A)
break;
if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
break;
while(waserror())
;
tsleep(&up->sleep, return0, 0, 500);
poperror();
scsitest(r);
return 2;
default:
break;
}
break;
case SDok:
return 0;
}
return -1;
}
static void
cap10(SDreq *r)
{
r->cmd[0] = 0x25;
r->cmd[1] = r->lun<<5;
r->clen = 10;
r->dlen = 8;
}
static void
cap16(SDreq *r)
{
uint i;
i = 32;
r->cmd[0] = 0x9e;
r->cmd[1] = 0x10;
r->cmd[10] = i>>24;
r->cmd[11] = i>>16;
r->cmd[12] = i>>8;
r->cmd[13] = i;
r->clen = 16;
r->dlen = i;
}
static uint
belong(uchar *u)
{
return u[0]<<24 | u[1]<<16 | u[2]<<8 | u[3];
}
static uvlong
capreply(SDreq *r, ulong *secsize)
{
uchar *u;
ulong ss;
uvlong s;
u = r->data;
if(r->clen == 16){
s = (uvlong)belong(u)<<32 | belong(u + 4);
ss = belong(u + 8);
}else{
s = belong(u);
ss = belong(u + 4);
}
if(secsize)
*secsize = ss;
return s;
}
int
scsionline(SDunit* unit)
{
SDreq *r;
uchar *p;
ulong ss;
uvlong s;
int ok, retries;
void (*cap)(SDreq*);
if((r = malloc(sizeof *r)) == nil)
return 0;
if((p = sdmalloc(32)) == nil){
free(r);
return 0;
}
ok = 0;
cap = cap10;
r->unit = unit;
r->lun = 0; /* ??? */
for(retries = 0; retries < 10; retries++){
/*
* Read-capacity is mandatory for DA, WORM, CD-ROM and
* MO. It may return 'not ready' if type DA is not
* spun up, type MO or type CD-ROM are not loaded or just
* plain slow getting their act together after a reset.
*/
r->write = 0;
r->data = p;
r->flags = 0;
memset(r->cmd, 0, sizeof r->cmd);
cap(r);
switch(scsirio(r)){
default:
/*
* ATAPI returns error and no sense information
* on media change / no media present.
* count as retries.
*/
if(retries < 4)
continue;
break;
case 0:
s = capreply(r, &ss);
if(s == 0xffffffff && cap == cap10){
cap = cap16;
continue;
}
if(s == 0xffffffffffffffffLL)
s = 0;
/*
* Some ATAPI CD readers lie about the block size.
* Since we don't read audio via this interface
* it's okay to always fudge this.
*/
if(ss == 2352)
ss = 2048;
/*
* Devices with removable media may return 0 sectors
* when they have empty media (e.g. sata dvd writers);
* if so, keep the count zero.
*
* Read-capacity returns the LBA of the last sector,
* therefore the number of sectors must be incremented.
*/
if(s != 0)
s++;
ok = (unit->sectors != s) ? 2 : 1;
unit->sectors = s;
unit->secsize = ss;
break;
case 1:
ok = (unit->sectors != 0) ? 2 : 1;
unit->sectors = 0;
break;
case 2:
continue;
}
break;
}
free(p);
free(r);
/*
print("scsionline: %s: ok=%d retries=%d sectors=%llud secsize=%lud\n",
unit->name, ok, retries, unit->sectors, unit->secsize);
*/
if(ok)
return ok+retries;
else
return 0;
}
static void
scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno)
{
uchar *c;
c = r->cmd;
if(write == 0)
c[0] = 0x28;
else
c[0] = 0x2A;
c[1] = lun<<5;
c[2] = bno>>24;
c[3] = bno>>16;
c[4] = bno>>8;
c[5] = bno;
c[6] = 0;
c[7] = nb>>8;
c[8] = nb;
c[9] = 0;
r->clen = 10;
}
static void
scsifmt16(SDreq *r, int write, int lun, ulong nb, uvlong bno)
{
uchar *c;
c = r->cmd;
if(write == 0)
c[0] = 0x88;
else
c[0] = 0x8A;
c[1] = lun<<5; /* so wrong */
c[2] = bno>>56;
c[3] = bno>>48;
c[4] = bno>>40;
c[5] = bno>>32;
c[6] = bno>>24;
c[7] = bno>>16;
c[8] = bno>>8;
c[9] = bno;
c[10] = nb>>24;
c[11] = nb>>16;
c[12] = nb>>8;
c[13] = nb;
c[14] = 0;
c[15] = 0;
r->clen = 16;
}
long
scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
{
SDreq *r;
long rlen;
r = smalloc(sizeof(SDreq));
r->unit = unit;
r->lun = lun;
again:
r->write = write;
if(bno > 0xffffffff)
scsifmt16(r, write, lun, nb, bno);
else
scsifmt10(r, write, lun, nb, bno);
r->data = data;
r->dlen = nb*unit->secsize;
r->flags = 0;
r->status = ~0;
switch(scsirio(r)){
default:
rlen = -1;
break;
case 0:
/*
* scsi allows commands to return successfully
* but return sense data, indicating that the
* operation didn't proceed as expected.
* (confusing, no). this allows the raw commands
* to successfully return errors. but any sense
* data bio sees must be an error. bomb out.
*/
if(r->status == SDok && r->rlen > 0
&& ((r->flags & SDvalidsense) == 0 || r->sense[2] == 0)){
rlen = r->rlen;
break;
}
case 2:
rlen = -1;
if(!(r->flags & SDvalidsense))
break;
switch(r->sense[2] & 0x0F){
default:
break;
case 0x01: /* recovered error */
print("%s: recovered error at sector %llud\n",
unit->name, bno);
rlen = r->rlen;
break;
case 0x06: /* check condition */
/*
* Check for a removeable media change.
* If so, mark it by zapping the geometry info
* to force an online request.
*/
if(r->sense[12] != 0x28 || r->sense[13] != 0)
break;
if(unit->inquiry[1] & 0x80)
unit->sectors = 0;
break;
case 0x02: /* not ready */
/*
* If unit is becoming ready,
* rather than not not ready, try again.
*/
if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
goto again;
break;
}
snprint(up->genbuf, sizeof up->genbuf, "%s %.2ux%.2ux%.2ux %lld",
Eio, r->sense[2], r->sense[12], r->sense[13], bno);
free(r);
error(up->genbuf);
break;
}
free(r);
return rlen;
}