2006-09-20 22:36:48 +04:00
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
2005-04-17 02:20:36 +04:00
/*
* aoecmd . c
* Filesystem request handling methods
*/
# include <linux/hdreg.h>
# include <linux/blkdev.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
2006-01-19 21:46:19 +03:00
# include <linux/genhd.h>
2005-09-29 20:47:40 +04:00
# include <asm/unaligned.h>
2005-04-17 02:20:36 +04:00
# include "aoe.h"
# define TIMERTICK (HZ / 10)
# define MINTIMER (2 * TIMERTICK)
# define MAXTIMER (HZ << 1)
# define MAXWAIT (60 * 3) /* After MAXWAIT seconds, give up and fail dev */
2006-09-20 22:36:49 +04:00
struct sk_buff *
new_skb ( ulong len )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
skb = alloc_skb ( len , GFP_ATOMIC ) ;
if ( skb ) {
skb - > nh . raw = skb - > mac . raw = skb - > data ;
skb - > protocol = __constant_htons ( ETH_P_AOE ) ;
skb - > priority = 0 ;
skb_put ( skb , len ) ;
2006-01-19 20:37:24 +03:00
memset ( skb - > head , 0 , len ) ;
2005-04-17 02:20:36 +04:00
skb - > next = skb - > prev = NULL ;
/* tell the network layer not to perform IP checksums
* or to get the NIC to do it
*/
skb - > ip_summed = CHECKSUM_NONE ;
}
return skb ;
}
static struct frame *
getframe ( struct aoedev * d , int tag )
{
struct frame * f , * e ;
f = d - > frames ;
e = f + d - > nframes ;
for ( ; f < e ; f + + )
if ( f - > tag = = tag )
return f ;
return NULL ;
}
/*
* Leave the top bit clear so we have tagspace for userland .
* The bottom 16 bits are the xmit tick for rexmit / rttavg processing .
* This driver reserves tag - 1 to mean " unused frame. "
*/
static int
newtag ( struct aoedev * d )
{
register ulong n ;
n = jiffies & 0xffff ;
return n | = ( + + d - > lasttag & 0x7fff ) < < 16 ;
}
static int
aoehdr_atainit ( struct aoedev * d , struct aoe_hdr * h )
{
u32 host_tag = newtag ( d ) ;
memcpy ( h - > src , d - > ifp - > dev_addr , sizeof h - > src ) ;
memcpy ( h - > dst , d - > addr , sizeof h - > dst ) ;
2005-04-19 09:00:20 +04:00
h - > type = __constant_cpu_to_be16 ( ETH_P_AOE ) ;
2005-04-17 02:20:36 +04:00
h - > verfl = AOE_HVER ;
2005-04-19 09:00:20 +04:00
h - > major = cpu_to_be16 ( d - > aoemajor ) ;
2005-04-17 02:20:36 +04:00
h - > minor = d - > aoeminor ;
h - > cmd = AOECMD_ATA ;
2005-04-19 09:00:20 +04:00
h - > tag = cpu_to_be32 ( host_tag ) ;
2005-04-17 02:20:36 +04:00
return host_tag ;
}
2006-09-20 22:36:49 +04:00
static inline void
put_lba ( struct aoe_atahdr * ah , sector_t lba )
{
ah - > lba0 = lba ;
ah - > lba1 = lba > > = 8 ;
ah - > lba2 = lba > > = 8 ;
ah - > lba3 = lba > > = 8 ;
ah - > lba4 = lba > > = 8 ;
ah - > lba5 = lba > > = 8 ;
}
2005-04-17 02:20:36 +04:00
static void
aoecmd_ata_rw ( struct aoedev * d , struct frame * f )
{
struct aoe_hdr * h ;
struct aoe_atahdr * ah ;
struct buf * buf ;
struct sk_buff * skb ;
ulong bcnt ;
register sector_t sector ;
char writebit , extbit ;
writebit = 0x10 ;
extbit = 0x4 ;
buf = d - > inprocess ;
sector = buf - > sector ;
bcnt = buf - > bv_resid ;
2006-09-20 22:36:49 +04:00
if ( bcnt > d - > maxbcnt )
bcnt = d - > maxbcnt ;
2005-04-17 02:20:36 +04:00
/* initialize the headers & frame */
2006-09-20 22:36:49 +04:00
skb = f - > skb ;
h = ( struct aoe_hdr * ) skb - > mac . raw ;
2005-04-17 02:20:36 +04:00
ah = ( struct aoe_atahdr * ) ( h + 1 ) ;
2006-09-20 22:36:49 +04:00
skb - > len = sizeof * h + sizeof * ah ;
memset ( h , 0 , skb - > len ) ;
2005-04-17 02:20:36 +04:00
f - > tag = aoehdr_atainit ( d , h ) ;
f - > waited = 0 ;
f - > buf = buf ;
f - > bufaddr = buf - > bufaddr ;
2006-09-20 22:36:49 +04:00
f - > bcnt = bcnt ;
f - > lba = sector ;
2005-04-17 02:20:36 +04:00
/* set up ata header */
ah - > scnt = bcnt > > 9 ;
2006-09-20 22:36:49 +04:00
put_lba ( ah , sector ) ;
2005-04-17 02:20:36 +04:00
if ( d - > flags & DEVFL_EXT ) {
ah - > aflags | = AOEAFL_EXT ;
} else {
extbit = 0 ;
ah - > lba3 & = 0x0f ;
ah - > lba3 | = 0xe0 ; /* LBA bit + obsolete 0xa0 */
}
if ( bio_data_dir ( buf - > bio ) = = WRITE ) {
2006-09-20 22:36:49 +04:00
skb_fill_page_desc ( skb , 0 , virt_to_page ( f - > bufaddr ) ,
offset_in_page ( f - > bufaddr ) , bcnt ) ;
2005-04-17 02:20:36 +04:00
ah - > aflags | = AOEAFL_WRITE ;
} else {
2006-09-20 22:36:49 +04:00
skb_shinfo ( skb ) - > nr_frags = 0 ;
skb - > len = ETH_ZLEN ;
2005-04-17 02:20:36 +04:00
writebit = 0 ;
}
ah - > cmdstat = WIN_READ | writebit | extbit ;
/* mark all tracking fields and load out */
buf - > nframesout + = 1 ;
buf - > bufaddr + = bcnt ;
buf - > bv_resid - = bcnt ;
2006-09-20 22:36:49 +04:00
/* dprintk("bv_resid=%ld\n", buf->bv_resid); */
2005-04-17 02:20:36 +04:00
buf - > resid - = bcnt ;
buf - > sector + = bcnt > > 9 ;
if ( buf - > resid = = 0 ) {
d - > inprocess = NULL ;
} else if ( buf - > bv_resid = = 0 ) {
buf - > bv + + ;
buf - > bv_resid = buf - > bv - > bv_len ;
buf - > bufaddr = page_address ( buf - > bv - > bv_page ) + buf - > bv - > bv_offset ;
}
2006-09-20 22:36:49 +04:00
skb - > dev = d - > ifp ;
skb_get ( skb ) ;
skb - > next = NULL ;
if ( d - > sendq_hd )
d - > sendq_tl - > next = skb ;
else
d - > sendq_hd = skb ;
d - > sendq_tl = skb ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 21:46:19 +03:00
/* some callers cannot sleep, and they can call this function,
* transmitting the packets later , when interrupts are on
*/
static struct sk_buff *
aoecmd_cfg_pkts ( ushort aoemajor , unsigned char aoeminor , struct sk_buff * * tail )
{
struct aoe_hdr * h ;
struct aoe_cfghdr * ch ;
struct sk_buff * skb , * sl , * sl_tail ;
struct net_device * ifp ;
sl = sl_tail = NULL ;
read_lock ( & dev_base_lock ) ;
for ( ifp = dev_base ; ifp ; dev_put ( ifp ) , ifp = ifp - > next ) {
dev_hold ( ifp ) ;
if ( ! is_aoe_netif ( ifp ) )
continue ;
2006-09-20 22:36:49 +04:00
skb = new_skb ( sizeof * h + sizeof * ch ) ;
2006-01-19 21:46:19 +03:00
if ( skb = = NULL ) {
2006-09-20 22:36:49 +04:00
iprintk ( " skb alloc failure \n " ) ;
2006-01-19 21:46:19 +03:00
continue ;
}
2006-09-20 22:36:49 +04:00
skb - > dev = ifp ;
2006-01-19 21:46:19 +03:00
if ( sl_tail = = NULL )
sl_tail = skb ;
h = ( struct aoe_hdr * ) skb - > mac . raw ;
memset ( h , 0 , sizeof * h + sizeof * ch ) ;
memset ( h - > dst , 0xff , sizeof h - > dst ) ;
memcpy ( h - > src , ifp - > dev_addr , sizeof h - > src ) ;
h - > type = __constant_cpu_to_be16 ( ETH_P_AOE ) ;
h - > verfl = AOE_HVER ;
h - > major = cpu_to_be16 ( aoemajor ) ;
h - > minor = aoeminor ;
h - > cmd = AOECMD_CFG ;
skb - > next = sl ;
sl = skb ;
}
read_unlock ( & dev_base_lock ) ;
if ( tail ! = NULL )
* tail = sl_tail ;
return sl ;
}
2005-04-17 02:20:36 +04:00
/* enters with d->lock held */
void
aoecmd_work ( struct aoedev * d )
{
struct frame * f ;
struct buf * buf ;
2006-01-19 21:46:19 +03:00
if ( d - > flags & DEVFL_PAUSE ) {
if ( ! aoedev_isbusy ( d ) )
d - > sendq_hd = aoecmd_cfg_pkts ( d - > aoemajor ,
d - > aoeminor , & d - > sendq_tl ) ;
return ;
}
2005-04-17 02:20:36 +04:00
loop :
f = getframe ( d , FREETAG ) ;
if ( f = = NULL )
return ;
if ( d - > inprocess = = NULL ) {
if ( list_empty ( & d - > bufq ) )
return ;
buf = container_of ( d - > bufq . next , struct buf , bufs ) ;
list_del ( d - > bufq . next ) ;
2006-09-20 22:36:49 +04:00
/*dprintk("bi_size=%ld\n", buf->bio->bi_size); */
2005-04-17 02:20:36 +04:00
d - > inprocess = buf ;
}
aoecmd_ata_rw ( d , f ) ;
goto loop ;
}
static void
rexmit ( struct aoedev * d , struct frame * f )
{
struct sk_buff * skb ;
struct aoe_hdr * h ;
2006-09-20 22:36:49 +04:00
struct aoe_atahdr * ah ;
2005-04-17 02:20:36 +04:00
char buf [ 128 ] ;
u32 n ;
n = newtag ( d ) ;
snprintf ( buf , sizeof buf ,
" %15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x \n " ,
" retransmit " ,
d - > aoemajor , d - > aoeminor , f - > tag , jiffies , n ) ;
aoechr_error ( buf ) ;
2006-09-20 22:36:49 +04:00
skb = f - > skb ;
h = ( struct aoe_hdr * ) skb - > mac . raw ;
2006-09-20 22:36:49 +04:00
ah = ( struct aoe_atahdr * ) ( h + 1 ) ;
2005-04-17 02:20:36 +04:00
f - > tag = n ;
2005-04-19 09:00:20 +04:00
h - > tag = cpu_to_be32 ( n ) ;
2006-01-19 21:46:25 +03:00
memcpy ( h - > dst , d - > addr , sizeof h - > dst ) ;
memcpy ( h - > src , d - > ifp - > dev_addr , sizeof h - > src ) ;
2005-04-17 02:20:36 +04:00
2006-09-20 22:36:49 +04:00
n = DEFAULTBCNT / 512 ;
if ( ah - > scnt > n ) {
ah - > scnt = n ;
if ( ah - > aflags & AOEAFL_WRITE )
skb_fill_page_desc ( skb , 0 , virt_to_page ( f - > bufaddr ) ,
offset_in_page ( f - > bufaddr ) , DEFAULTBCNT ) ;
if ( + + d - > lostjumbo > ( d - > nframes < < 1 ) )
if ( d - > maxbcnt ! = DEFAULTBCNT ) {
2006-09-20 22:36:49 +04:00
iprintk ( " too many lost jumbo - using 1KB frames. \n " ) ;
2006-09-20 22:36:49 +04:00
d - > maxbcnt = DEFAULTBCNT ;
d - > flags | = DEVFL_MAXBCNT ;
}
}
2006-09-20 22:36:49 +04:00
skb - > dev = d - > ifp ;
skb_get ( skb ) ;
skb - > next = NULL ;
if ( d - > sendq_hd )
d - > sendq_tl - > next = skb ;
else
d - > sendq_hd = skb ;
d - > sendq_tl = skb ;
2005-04-17 02:20:36 +04:00
}
static int
tsince ( int tag )
{
int n ;
n = jiffies & 0xffff ;
n - = tag & 0xffff ;
if ( n < 0 )
n + = 1 < < 16 ;
return n ;
}
static void
rexmit_timer ( ulong vp )
{
struct aoedev * d ;
struct frame * f , * e ;
struct sk_buff * sl ;
register long timeout ;
ulong flags , n ;
d = ( struct aoedev * ) vp ;
sl = NULL ;
/* timeout is always ~150% of the moving average */
timeout = d - > rttavg ;
timeout + = timeout > > 1 ;
spin_lock_irqsave ( & d - > lock , flags ) ;
if ( d - > flags & DEVFL_TKILL ) {
2006-01-25 21:54:44 +03:00
spin_unlock_irqrestore ( & d - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
return ;
}
f = d - > frames ;
e = f + d - > nframes ;
for ( ; f < e ; f + + ) {
if ( f - > tag ! = FREETAG & & tsince ( f - > tag ) > = timeout ) {
n = f - > waited + = timeout ;
n / = HZ ;
if ( n > MAXWAIT ) { /* waited too long. device failure. */
aoedev_downdev ( d ) ;
2006-01-25 21:54:44 +03:00
break ;
2005-04-17 02:20:36 +04:00
}
rexmit ( d , f ) ;
}
}
2005-04-19 09:00:22 +04:00
sl = d - > sendq_hd ;
d - > sendq_hd = d - > sendq_tl = NULL ;
2005-04-17 02:20:36 +04:00
if ( sl ) {
n = d - > rttavg < < = 1 ;
if ( n > MAXTIMER )
d - > rttavg = MAXTIMER ;
}
d - > timer . expires = jiffies + TIMERTICK ;
add_timer ( & d - > timer ) ;
spin_unlock_irqrestore ( & d - > lock , flags ) ;
aoenet_xmit ( sl ) ;
}
2006-01-19 21:46:19 +03:00
/* this function performs work that has been deferred until sleeping is OK
*/
void
aoecmd_sleepwork ( void * vp )
{
struct aoedev * d = ( struct aoedev * ) vp ;
if ( d - > flags & DEVFL_GDALLOC )
aoeblk_gdalloc ( d ) ;
if ( d - > flags & DEVFL_NEWSIZE ) {
struct block_device * bd ;
unsigned long flags ;
u64 ssize ;
ssize = d - > gd - > capacity ;
bd = bdget_disk ( d - > gd , 0 ) ;
if ( bd ) {
mutex_lock ( & bd - > bd_inode - > i_mutex ) ;
i_size_write ( bd - > bd_inode , ( loff_t ) ssize < < 9 ) ;
mutex_unlock ( & bd - > bd_inode - > i_mutex ) ;
bdput ( bd ) ;
}
spin_lock_irqsave ( & d - > lock , flags ) ;
d - > flags | = DEVFL_UP ;
d - > flags & = ~ DEVFL_NEWSIZE ;
spin_unlock_irqrestore ( & d - > lock , flags ) ;
}
}
2005-04-17 02:20:36 +04:00
static void
ataid_complete ( struct aoedev * d , unsigned char * id )
{
u64 ssize ;
u16 n ;
/* word 83: command set supported */
2005-09-29 20:47:40 +04:00
n = le16_to_cpu ( get_unaligned ( ( __le16 * ) & id [ 83 < < 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
/* word 86: command set/feature enabled */
2005-09-29 20:47:40 +04:00
n | = le16_to_cpu ( get_unaligned ( ( __le16 * ) & id [ 86 < < 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
if ( n & ( 1 < < 10 ) ) { /* bit 10: LBA 48 */
d - > flags | = DEVFL_EXT ;
/* word 100: number lba48 sectors */
2005-09-29 20:47:40 +04:00
ssize = le64_to_cpu ( get_unaligned ( ( __le64 * ) & id [ 100 < < 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
/* set as in ide-disk.c:init_idedisk_capacity */
d - > geo . cylinders = ssize ;
d - > geo . cylinders / = ( 255 * 63 ) ;
d - > geo . heads = 255 ;
d - > geo . sectors = 63 ;
} else {
d - > flags & = ~ DEVFL_EXT ;
/* number lba28 sectors */
2005-09-29 20:47:40 +04:00
ssize = le32_to_cpu ( get_unaligned ( ( __le32 * ) & id [ 60 < < 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
/* NOTE: obsolete in ATA 6 */
2005-09-29 20:47:40 +04:00
d - > geo . cylinders = le16_to_cpu ( get_unaligned ( ( __le16 * ) & id [ 54 < < 1 ] ) ) ;
d - > geo . heads = le16_to_cpu ( get_unaligned ( ( __le16 * ) & id [ 55 < < 1 ] ) ) ;
d - > geo . sectors = le16_to_cpu ( get_unaligned ( ( __le16 * ) & id [ 56 < < 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 21:46:19 +03:00
if ( d - > ssize ! = ssize )
2006-09-20 22:36:49 +04:00
iprintk ( " %012llx e%lu.%lu v%04x has %llu sectors \n " ,
( unsigned long long ) mac_addr ( d - > addr ) ,
2006-01-19 21:46:19 +03:00
d - > aoemajor , d - > aoeminor ,
d - > fw_ver , ( long long ) ssize ) ;
2005-04-17 02:20:36 +04:00
d - > ssize = ssize ;
d - > geo . start = 0 ;
if ( d - > gd ! = NULL ) {
d - > gd - > capacity = ssize ;
2006-01-19 21:46:19 +03:00
d - > flags | = DEVFL_NEWSIZE ;
} else {
if ( d - > flags & DEVFL_GDALLOC ) {
2006-09-20 22:36:49 +04:00
eprintk ( " can't schedule work for e%lu.%lu, %s \n " ,
2006-01-19 21:46:19 +03:00
d - > aoemajor , d - > aoeminor ,
2006-09-20 22:36:49 +04:00
" it's already on! This shouldn't happen. \n " ) ;
2006-01-19 21:46:19 +03:00
return ;
}
d - > flags | = DEVFL_GDALLOC ;
2005-04-17 02:20:36 +04:00
}
schedule_work ( & d - > work ) ;
}
static void
calc_rttavg ( struct aoedev * d , int rtt )
{
register long n ;
n = rtt ;
if ( n < MINTIMER )
n = MINTIMER ;
else if ( n > MAXTIMER )
n = MAXTIMER ;
/* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
n - = d - > rttavg ;
d - > rttavg + = n > > 2 ;
}
void
aoecmd_ata_rsp ( struct sk_buff * skb )
{
struct aoedev * d ;
struct aoe_hdr * hin ;
struct aoe_atahdr * ahin , * ahout ;
struct frame * f ;
struct buf * buf ;
struct sk_buff * sl ;
register long n ;
ulong flags ;
char ebuf [ 128 ] ;
2005-04-19 09:00:18 +04:00
u16 aoemajor ;
2005-04-17 02:20:36 +04:00
hin = ( struct aoe_hdr * ) skb - > mac . raw ;
2005-04-19 09:00:20 +04:00
aoemajor = be16_to_cpu ( hin - > major ) ;
2005-04-19 09:00:18 +04:00
d = aoedev_by_aoeaddr ( aoemajor , hin - > minor ) ;
2005-04-17 02:20:36 +04:00
if ( d = = NULL ) {
snprintf ( ebuf , sizeof ebuf , " aoecmd_ata_rsp: ata response "
" for unknown device %d.%d \n " ,
2005-04-19 09:00:18 +04:00
aoemajor , hin - > minor ) ;
2005-04-17 02:20:36 +04:00
aoechr_error ( ebuf ) ;
return ;
}
spin_lock_irqsave ( & d - > lock , flags ) ;
2005-04-19 09:00:20 +04:00
f = getframe ( d , be32_to_cpu ( hin - > tag ) ) ;
2005-04-17 02:20:36 +04:00
if ( f = = NULL ) {
spin_unlock_irqrestore ( & d - > lock , flags ) ;
snprintf ( ebuf , sizeof ebuf ,
" %15s e%d.%d tag=%08x@%08lx \n " ,
" unexpected rsp " ,
2005-04-19 09:00:20 +04:00
be16_to_cpu ( hin - > major ) ,
2005-04-17 02:20:36 +04:00
hin - > minor ,
2005-04-19 09:00:20 +04:00
be32_to_cpu ( hin - > tag ) ,
2005-04-17 02:20:36 +04:00
jiffies ) ;
aoechr_error ( ebuf ) ;
return ;
}
calc_rttavg ( d , tsince ( f - > tag ) ) ;
ahin = ( struct aoe_atahdr * ) ( hin + 1 ) ;
2006-09-20 22:36:49 +04:00
ahout = ( struct aoe_atahdr * ) ( f - > skb - > mac . raw + sizeof ( struct aoe_hdr ) ) ;
2005-04-17 02:20:36 +04:00
buf = f - > buf ;
2006-02-07 19:37:24 +03:00
if ( ahout - > cmdstat = = WIN_IDENTIFY )
d - > flags & = ~ DEVFL_PAUSE ;
2005-04-17 02:20:36 +04:00
if ( ahin - > cmdstat & 0xa9 ) { /* these bits cleared on success */
2006-09-20 22:36:49 +04:00
eprintk ( " ata error cmd=%2.2Xh stat=%2.2Xh from e%ld.%ld \n " ,
2005-04-17 02:20:36 +04:00
ahout - > cmdstat , ahin - > cmdstat ,
d - > aoemajor , d - > aoeminor ) ;
if ( buf )
buf - > flags | = BUFFL_FAIL ;
} else {
2006-09-20 22:36:49 +04:00
n = ahout - > scnt < < 9 ;
2005-04-17 02:20:36 +04:00
switch ( ahout - > cmdstat ) {
case WIN_READ :
case WIN_READ_EXT :
if ( skb - > len - sizeof * hin - sizeof * ahin < n ) {
2006-09-20 22:36:49 +04:00
eprintk ( " runt data size in read. skb->len=%d \n " ,
2005-04-17 02:20:36 +04:00
skb - > len ) ;
/* fail frame f? just returning will rexmit. */
spin_unlock_irqrestore ( & d - > lock , flags ) ;
return ;
}
memcpy ( f - > bufaddr , ahin + 1 , n ) ;
case WIN_WRITE :
case WIN_WRITE_EXT :
2006-09-20 22:36:49 +04:00
if ( f - > bcnt - = n ) {
f - > bufaddr + = n ;
put_lba ( ahout , f - > lba + = ahout - > scnt ) ;
2006-09-20 22:36:49 +04:00
n = f - > bcnt ;
if ( n > DEFAULTBCNT )
n = DEFAULTBCNT ;
2006-09-20 22:36:49 +04:00
ahout - > scnt = n > > 9 ;
if ( ahout - > aflags & AOEAFL_WRITE )
2006-09-20 22:36:49 +04:00
skb_fill_page_desc ( f - > skb , 0 ,
virt_to_page ( f - > bufaddr ) ,
2006-09-20 22:36:49 +04:00
offset_in_page ( f - > bufaddr ) , n ) ;
skb_get ( f - > skb ) ;
f - > skb - > next = NULL ;
spin_unlock_irqrestore ( & d - > lock , flags ) ;
aoenet_xmit ( f - > skb ) ;
return ;
}
if ( n > DEFAULTBCNT )
d - > lostjumbo = 0 ;
2005-04-17 02:20:36 +04:00
break ;
case WIN_IDENTIFY :
if ( skb - > len - sizeof * hin - sizeof * ahin < 512 ) {
2006-09-20 22:36:49 +04:00
iprintk ( " runt data size in ataid. skb->len=%d \n " ,
skb - > len ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & d - > lock , flags ) ;
return ;
}
ataid_complete ( d , ( char * ) ( ahin + 1 ) ) ;
break ;
default :
2006-09-20 22:36:49 +04:00
iprintk ( " unrecognized ata command %2.2Xh for %d.%d \n " ,
ahout - > cmdstat ,
be16_to_cpu ( hin - > major ) ,
hin - > minor ) ;
2005-04-17 02:20:36 +04:00
}
}
if ( buf ) {
buf - > nframesout - = 1 ;
if ( buf - > nframesout = = 0 & & buf - > resid = = 0 ) {
2005-04-19 09:00:22 +04:00
unsigned long duration = jiffies - buf - > start_time ;
unsigned long n_sect = buf - > bio - > bi_size > > 9 ;
struct gendisk * disk = d - > gd ;
2005-11-01 11:54:23 +03:00
const int rw = bio_data_dir ( buf - > bio ) ;
2005-04-19 09:00:22 +04:00
2005-11-01 11:54:23 +03:00
disk_stat_inc ( disk , ios [ rw ] ) ;
disk_stat_add ( disk , ticks [ rw ] , duration ) ;
disk_stat_add ( disk , sectors [ rw ] , n_sect ) ;
2005-04-19 09:00:22 +04:00
disk_stat_add ( disk , io_ticks , duration ) ;
2005-04-17 02:20:36 +04:00
n = ( buf - > flags & BUFFL_FAIL ) ? - EIO : 0 ;
bio_endio ( buf - > bio , buf - > bio - > bi_size , n ) ;
mempool_free ( buf , d - > bufpool ) ;
}
}
f - > buf = NULL ;
f - > tag = FREETAG ;
aoecmd_work ( d ) ;
2005-04-19 09:00:22 +04:00
sl = d - > sendq_hd ;
d - > sendq_hd = d - > sendq_tl = NULL ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & d - > lock , flags ) ;
aoenet_xmit ( sl ) ;
}
void
aoecmd_cfg ( ushort aoemajor , unsigned char aoeminor )
{
2006-01-19 21:46:19 +03:00
struct sk_buff * sl ;
2005-04-17 02:20:36 +04:00
2006-01-19 21:46:19 +03:00
sl = aoecmd_cfg_pkts ( aoemajor , aoeminor , NULL ) ;
2005-04-17 02:20:36 +04:00
aoenet_xmit ( sl ) ;
}
/*
* Since we only call this in one place ( and it only prepares one frame )
2005-04-19 09:00:22 +04:00
* we just return the skb . Usually we ' d chain it up to the aoedev sendq .
2005-04-17 02:20:36 +04:00
*/
static struct sk_buff *
aoecmd_ata_id ( struct aoedev * d )
{
struct aoe_hdr * h ;
struct aoe_atahdr * ah ;
struct frame * f ;
struct sk_buff * skb ;
f = getframe ( d , FREETAG ) ;
if ( f = = NULL ) {
2006-09-20 22:36:49 +04:00
eprintk ( " can't get a frame. This shouldn't happen. \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
/* initialize the headers & frame */
2006-09-20 22:36:49 +04:00
skb = f - > skb ;
h = ( struct aoe_hdr * ) skb - > mac . raw ;
2005-04-17 02:20:36 +04:00
ah = ( struct aoe_atahdr * ) ( h + 1 ) ;
2006-09-20 22:36:49 +04:00
skb - > len = sizeof * h + sizeof * ah ;
memset ( h , 0 , skb - > len ) ;
2005-04-17 02:20:36 +04:00
f - > tag = aoehdr_atainit ( d , h ) ;
f - > waited = 0 ;
/* set up ata header */
ah - > scnt = 1 ;
ah - > cmdstat = WIN_IDENTIFY ;
ah - > lba3 = 0xa0 ;
2006-09-20 22:36:49 +04:00
skb - > dev = d - > ifp ;
skb_get ( skb ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 21:46:19 +03:00
d - > rttavg = MAXTIMER ;
2005-04-17 02:20:36 +04:00
d - > timer . function = rexmit_timer ;
return skb ;
}
void
aoecmd_cfg_rsp ( struct sk_buff * skb )
{
struct aoedev * d ;
struct aoe_hdr * h ;
struct aoe_cfghdr * ch ;
2005-04-19 09:00:20 +04:00
ulong flags , sysminor , aoemajor ;
2005-04-17 02:20:36 +04:00
struct sk_buff * sl ;
2006-01-19 21:46:20 +03:00
enum { MAXFRAMES = 16 } ;
2006-09-20 22:36:49 +04:00
u16 n ;
2005-04-17 02:20:36 +04:00
h = ( struct aoe_hdr * ) skb - > mac . raw ;
ch = ( struct aoe_cfghdr * ) ( h + 1 ) ;
/*
* Enough people have their dip switches set backwards to
* warrant a loud message for this special case .
*/
2005-04-19 09:00:20 +04:00
aoemajor = be16_to_cpu ( h - > major ) ;
2005-04-17 02:20:36 +04:00
if ( aoemajor = = 0xfff ) {
2006-09-20 22:36:49 +04:00
eprintk ( " Warning: shelf address is all ones. "
" Check shelf dip switches. \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
sysminor = SYSMINOR ( aoemajor , h - > minor ) ;
2005-04-19 09:00:17 +04:00
if ( sysminor * AOE_PARTITIONS + AOE_PARTITIONS > MINORMASK ) {
2006-09-20 22:36:49 +04:00
iprintk ( " e%ld.%d: minor number too large \n " ,
2005-04-19 09:00:17 +04:00
aoemajor , ( int ) h - > minor ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2006-09-20 22:36:49 +04:00
n = be16_to_cpu ( ch - > bufcnt ) ;
if ( n > MAXFRAMES ) /* keep it reasonable */
n = MAXFRAMES ;
2005-04-17 02:20:36 +04:00
2006-09-20 22:36:49 +04:00
d = aoedev_by_sysminor_m ( sysminor , n ) ;
2005-04-17 02:20:36 +04:00
if ( d = = NULL ) {
2006-09-20 22:36:49 +04:00
iprintk ( " device sysminor_m failure \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
spin_lock_irqsave ( & d - > lock , flags ) ;
2006-01-19 21:46:19 +03:00
/* permit device to migrate mac and network interface */
d - > ifp = skb - > dev ;
memcpy ( d - > addr , h - > src , sizeof d - > addr ) ;
2006-09-20 22:36:49 +04:00
if ( ! ( d - > flags & DEVFL_MAXBCNT ) ) {
n = d - > ifp - > mtu ;
n - = sizeof ( struct aoe_hdr ) + sizeof ( struct aoe_atahdr ) ;
n / = 512 ;
if ( n > ch - > scnt )
n = ch - > scnt ;
d - > maxbcnt = n ? n * 512 : DEFAULTBCNT ;
}
2006-01-19 21:46:19 +03:00
/* don't change users' perspective */
if ( d - > nopen & & ! ( d - > flags & DEVFL_PAUSE ) ) {
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & d - > lock , flags ) ;
return ;
}
2006-01-19 21:46:19 +03:00
d - > flags | = DEVFL_PAUSE ; /* force pause */
2005-04-19 09:00:20 +04:00
d - > fw_ver = be16_to_cpu ( ch - > fwver ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 21:46:19 +03:00
/* check for already outstanding ataid */
sl = aoedev_isbusy ( d ) = = 0 ? aoecmd_ata_id ( d ) : NULL ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & d - > lock , flags ) ;
aoenet_xmit ( sl ) ;
}