2010-02-17 11:18:50 +01:00
/*
* zfcp device driver
*
* Data structure and helper functions for tracking pending FSF
* requests .
*
scsi: zfcp: fix use-after-"free" in FC ingress path after TMF
When SCSI EH invokes zFCP's callbacks for eh_device_reset_handler() and
eh_target_reset_handler(), it expects us to relent the ownership over
the given scsi_cmnd and all other scsi_cmnds within the same scope - LUN
or target - when returning with SUCCESS from the callback ('release'
them). SCSI EH can then reuse those commands.
We did not follow this rule to release commands upon SUCCESS; and if
later a reply arrived for one of those supposed to be released commands,
we would still make use of the scsi_cmnd in our ingress tasklet. This
will at least result in undefined behavior or a kernel panic because of
a wrong kernel pointer dereference.
To fix this, we NULLify all pointers to scsi_cmnds (struct zfcp_fsf_req
*)->data in the matching scope if a TMF was successful. This is done
under the locks (struct zfcp_adapter *)->abort_lock and (struct
zfcp_reqlist *)->lock to prevent the requests from being removed from
the request-hashtable, and the ingress tasklet from making use of the
scsi_cmnd-pointer in zfcp_fsf_fcp_cmnd_handler().
For cases where a reply arrives during SCSI EH, but before we get a
chance to NULLify the pointer - but before we return from the callback
-, we assume that the code is protected from races via the CAS operation
in blk_complete_request() that is called in scsi_done().
The following stacktrace shows an example for a crash resulting from the
previous behavior:
Unable to handle kernel pointer dereference at virtual kernel address fffffee17a672000
Oops: 0038 [#1] SMP
CPU: 2 PID: 0 Comm: swapper/2 Not tainted
task: 00000003f7ff5be0 ti: 00000003f3d38000 task.ti: 00000003f3d38000
Krnl PSW : 0404d00180000000 00000000001156b0 (smp_vcpu_scheduled+0x18/0x40)
R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:1 PM:0 EA:3
Krnl GPRS: 000000200000007e 0000000000000000 fffffee17a671fd8 0000000300000015
ffffffff80000000 00000000005dfde8 07000003f7f80e00 000000004fa4e800
000000036ce8d8f8 000000036ce8d9c0 00000003ece8fe00 ffffffff969c9e93
00000003fffffffd 000000036ce8da10 00000000003bf134 00000003f3b07918
Krnl Code: 00000000001156a2: a7190000 lghi %r1,0
00000000001156a6: a7380015 lhi %r3,21
#00000000001156aa: e32050000008 ag %r2,0(%r5)
>00000000001156b0: 482022b0 lh %r2,688(%r2)
00000000001156b4: ae123000 sigp %r1,%r2,0(%r3)
00000000001156b8: b2220020 ipm %r2
00000000001156bc: 8820001c srl %r2,28
00000000001156c0: c02700000001 xilf %r2,1
Call Trace:
([<0000000000000000>] 0x0)
[<000003ff807bdb8e>] zfcp_fsf_fcp_cmnd_handler+0x3de/0x490 [zfcp]
[<000003ff807be30a>] zfcp_fsf_req_complete+0x252/0x800 [zfcp]
[<000003ff807c0a48>] zfcp_fsf_reqid_check+0xe8/0x190 [zfcp]
[<000003ff807c194e>] zfcp_qdio_int_resp+0x66/0x188 [zfcp]
[<000003ff80440c64>] qdio_kick_handler+0xdc/0x310 [qdio]
[<000003ff804463d0>] __tiqdio_inbound_processing+0xf8/0xcd8 [qdio]
[<0000000000141fd4>] tasklet_action+0x9c/0x170
[<0000000000141550>] __do_softirq+0xe8/0x258
[<000000000010ce0a>] do_softirq+0xba/0xc0
[<000000000014187c>] irq_exit+0xc4/0xe8
[<000000000046b526>] do_IRQ+0x146/0x1d8
[<00000000005d6a3c>] io_return+0x0/0x8
[<00000000005d6422>] vtime_stop_cpu+0x4a/0xa0
([<0000000000000000>] 0x0)
[<0000000000103d8a>] arch_cpu_idle+0xa2/0xb0
[<0000000000197f94>] cpu_startup_entry+0x13c/0x1f8
[<0000000000114782>] smp_start_secondary+0xda/0xe8
[<00000000005d6efe>] restart_int_handler+0x56/0x6c
[<0000000000000000>] 0x0
Last Breaking-Event-Address:
[<00000000003bf12e>] arch_spin_lock_wait+0x56/0xb0
Suggested-by: Steffen Maier <maier@linux.vnet.ibm.com>
Signed-off-by: Benjamin Block <bblock@linux.vnet.ibm.com>
Fixes: ea127f9754 ("[PATCH] s390 (7/7): zfcp host adapter.") (tglx/history.git)
Cc: <stable@vger.kernel.org> #2.6.32+
Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2016-12-09 17:16:31 +01:00
* Copyright IBM Corp . 2009 , 2016
2010-02-17 11:18:50 +01:00
*/
# ifndef ZFCP_REQLIST_H
# define ZFCP_REQLIST_H
/* number of hash buckets */
# define ZFCP_REQ_LIST_BUCKETS 128
/**
* struct zfcp_reqlist - Container for request list ( reqlist )
* @ lock : Spinlock for protecting the hash list
* @ list : Array of hashbuckets , each is a list of requests in this bucket
*/
struct zfcp_reqlist {
spinlock_t lock ;
struct list_head buckets [ ZFCP_REQ_LIST_BUCKETS ] ;
} ;
static inline int zfcp_reqlist_hash ( unsigned long req_id )
{
return req_id % ZFCP_REQ_LIST_BUCKETS ;
}
/**
* zfcp_reqlist_alloc - Allocate and initialize reqlist
*
* Returns pointer to allocated reqlist on success , or NULL on
* allocation failure .
*/
static inline struct zfcp_reqlist * zfcp_reqlist_alloc ( void )
{
unsigned int i ;
struct zfcp_reqlist * rl ;
rl = kzalloc ( sizeof ( struct zfcp_reqlist ) , GFP_KERNEL ) ;
if ( ! rl )
return NULL ;
spin_lock_init ( & rl - > lock ) ;
for ( i = 0 ; i < ZFCP_REQ_LIST_BUCKETS ; i + + )
INIT_LIST_HEAD ( & rl - > buckets [ i ] ) ;
return rl ;
}
/**
* zfcp_reqlist_isempty - Check whether the request list empty
* @ rl : pointer to reqlist
*
* Returns : 1 if list is empty , 0 if not
*/
static inline int zfcp_reqlist_isempty ( struct zfcp_reqlist * rl )
{
unsigned int i ;
for ( i = 0 ; i < ZFCP_REQ_LIST_BUCKETS ; i + + )
if ( ! list_empty ( & rl - > buckets [ i ] ) )
return 0 ;
return 1 ;
}
/**
* zfcp_reqlist_free - Free allocated memory for reqlist
* @ rl : The reqlist where to free memory
*/
static inline void zfcp_reqlist_free ( struct zfcp_reqlist * rl )
{
/* sanity check */
BUG_ON ( ! zfcp_reqlist_isempty ( rl ) ) ;
kfree ( rl ) ;
}
static inline struct zfcp_fsf_req *
_zfcp_reqlist_find ( struct zfcp_reqlist * rl , unsigned long req_id )
{
struct zfcp_fsf_req * req ;
unsigned int i ;
i = zfcp_reqlist_hash ( req_id ) ;
list_for_each_entry ( req , & rl - > buckets [ i ] , list )
if ( req - > req_id = = req_id )
return req ;
return NULL ;
}
/**
* zfcp_reqlist_find - Lookup FSF request by its request id
* @ rl : The reqlist where to lookup the FSF request
* @ req_id : The request id to look for
*
* Returns a pointer to the FSF request with the specified request id
* or NULL if there is no known FSF request with this id .
*/
static inline struct zfcp_fsf_req *
zfcp_reqlist_find ( struct zfcp_reqlist * rl , unsigned long req_id )
{
unsigned long flags ;
struct zfcp_fsf_req * req ;
spin_lock_irqsave ( & rl - > lock , flags ) ;
req = _zfcp_reqlist_find ( rl , req_id ) ;
spin_unlock_irqrestore ( & rl - > lock , flags ) ;
return req ;
}
/**
* zfcp_reqlist_find_rm - Lookup request by id and remove it from reqlist
* @ rl : reqlist where to search and remove entry
* @ req_id : The request id of the request to look for
*
* This functions tries to find the FSF request with the specified
* id and then removes it from the reqlist . The reqlist lock is held
* during both steps of the operation .
*
* Returns : Pointer to the FSF request if the request has been found ,
* NULL if it has not been found .
*/
static inline struct zfcp_fsf_req *
zfcp_reqlist_find_rm ( struct zfcp_reqlist * rl , unsigned long req_id )
{
unsigned long flags ;
struct zfcp_fsf_req * req ;
spin_lock_irqsave ( & rl - > lock , flags ) ;
req = _zfcp_reqlist_find ( rl , req_id ) ;
if ( req )
list_del ( & req - > list ) ;
spin_unlock_irqrestore ( & rl - > lock , flags ) ;
return req ;
}
/**
* zfcp_reqlist_add - Add entry to reqlist
* @ rl : reqlist where to add the entry
* @ req : The entry to add
*
* The request id always increases . As an optimization new requests
* are added here with list_add_tail at the end of the bucket lists
* while old requests are looked up starting at the beginning of the
* lists .
*/
static inline void zfcp_reqlist_add ( struct zfcp_reqlist * rl ,
struct zfcp_fsf_req * req )
{
unsigned int i ;
unsigned long flags ;
i = zfcp_reqlist_hash ( req - > req_id ) ;
spin_lock_irqsave ( & rl - > lock , flags ) ;
list_add_tail ( & req - > list , & rl - > buckets [ i ] ) ;
spin_unlock_irqrestore ( & rl - > lock , flags ) ;
}
/**
* zfcp_reqlist_move - Move all entries from reqlist to simple list
* @ rl : The zfcp_reqlist where to remove all entries
* @ list : The list where to move all entries
*/
static inline void zfcp_reqlist_move ( struct zfcp_reqlist * rl ,
struct list_head * list )
{
unsigned int i ;
unsigned long flags ;
spin_lock_irqsave ( & rl - > lock , flags ) ;
for ( i = 0 ; i < ZFCP_REQ_LIST_BUCKETS ; i + + )
list_splice_init ( & rl - > buckets [ i ] , list ) ;
spin_unlock_irqrestore ( & rl - > lock , flags ) ;
}
scsi: zfcp: fix use-after-"free" in FC ingress path after TMF
When SCSI EH invokes zFCP's callbacks for eh_device_reset_handler() and
eh_target_reset_handler(), it expects us to relent the ownership over
the given scsi_cmnd and all other scsi_cmnds within the same scope - LUN
or target - when returning with SUCCESS from the callback ('release'
them). SCSI EH can then reuse those commands.
We did not follow this rule to release commands upon SUCCESS; and if
later a reply arrived for one of those supposed to be released commands,
we would still make use of the scsi_cmnd in our ingress tasklet. This
will at least result in undefined behavior or a kernel panic because of
a wrong kernel pointer dereference.
To fix this, we NULLify all pointers to scsi_cmnds (struct zfcp_fsf_req
*)->data in the matching scope if a TMF was successful. This is done
under the locks (struct zfcp_adapter *)->abort_lock and (struct
zfcp_reqlist *)->lock to prevent the requests from being removed from
the request-hashtable, and the ingress tasklet from making use of the
scsi_cmnd-pointer in zfcp_fsf_fcp_cmnd_handler().
For cases where a reply arrives during SCSI EH, but before we get a
chance to NULLify the pointer - but before we return from the callback
-, we assume that the code is protected from races via the CAS operation
in blk_complete_request() that is called in scsi_done().
The following stacktrace shows an example for a crash resulting from the
previous behavior:
Unable to handle kernel pointer dereference at virtual kernel address fffffee17a672000
Oops: 0038 [#1] SMP
CPU: 2 PID: 0 Comm: swapper/2 Not tainted
task: 00000003f7ff5be0 ti: 00000003f3d38000 task.ti: 00000003f3d38000
Krnl PSW : 0404d00180000000 00000000001156b0 (smp_vcpu_scheduled+0x18/0x40)
R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:1 PM:0 EA:3
Krnl GPRS: 000000200000007e 0000000000000000 fffffee17a671fd8 0000000300000015
ffffffff80000000 00000000005dfde8 07000003f7f80e00 000000004fa4e800
000000036ce8d8f8 000000036ce8d9c0 00000003ece8fe00 ffffffff969c9e93
00000003fffffffd 000000036ce8da10 00000000003bf134 00000003f3b07918
Krnl Code: 00000000001156a2: a7190000 lghi %r1,0
00000000001156a6: a7380015 lhi %r3,21
#00000000001156aa: e32050000008 ag %r2,0(%r5)
>00000000001156b0: 482022b0 lh %r2,688(%r2)
00000000001156b4: ae123000 sigp %r1,%r2,0(%r3)
00000000001156b8: b2220020 ipm %r2
00000000001156bc: 8820001c srl %r2,28
00000000001156c0: c02700000001 xilf %r2,1
Call Trace:
([<0000000000000000>] 0x0)
[<000003ff807bdb8e>] zfcp_fsf_fcp_cmnd_handler+0x3de/0x490 [zfcp]
[<000003ff807be30a>] zfcp_fsf_req_complete+0x252/0x800 [zfcp]
[<000003ff807c0a48>] zfcp_fsf_reqid_check+0xe8/0x190 [zfcp]
[<000003ff807c194e>] zfcp_qdio_int_resp+0x66/0x188 [zfcp]
[<000003ff80440c64>] qdio_kick_handler+0xdc/0x310 [qdio]
[<000003ff804463d0>] __tiqdio_inbound_processing+0xf8/0xcd8 [qdio]
[<0000000000141fd4>] tasklet_action+0x9c/0x170
[<0000000000141550>] __do_softirq+0xe8/0x258
[<000000000010ce0a>] do_softirq+0xba/0xc0
[<000000000014187c>] irq_exit+0xc4/0xe8
[<000000000046b526>] do_IRQ+0x146/0x1d8
[<00000000005d6a3c>] io_return+0x0/0x8
[<00000000005d6422>] vtime_stop_cpu+0x4a/0xa0
([<0000000000000000>] 0x0)
[<0000000000103d8a>] arch_cpu_idle+0xa2/0xb0
[<0000000000197f94>] cpu_startup_entry+0x13c/0x1f8
[<0000000000114782>] smp_start_secondary+0xda/0xe8
[<00000000005d6efe>] restart_int_handler+0x56/0x6c
[<0000000000000000>] 0x0
Last Breaking-Event-Address:
[<00000000003bf12e>] arch_spin_lock_wait+0x56/0xb0
Suggested-by: Steffen Maier <maier@linux.vnet.ibm.com>
Signed-off-by: Benjamin Block <bblock@linux.vnet.ibm.com>
Fixes: ea127f9754 ("[PATCH] s390 (7/7): zfcp host adapter.") (tglx/history.git)
Cc: <stable@vger.kernel.org> #2.6.32+
Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2016-12-09 17:16:31 +01:00
/**
* zfcp_reqlist_apply_for_all ( ) - apply a function to every request .
* @ rl : the requestlist that contains the target requests .
* @ f : the function to apply to each request ; the first parameter of the
* function will be the target - request ; the second parameter is the same
* pointer as given with the argument @ data .
* @ data : freely chosen argument ; passed through to @ f as second parameter .
*
* Uses : c : macro : ` list_for_each_entry ` to iterate over the lists in the hash -
* table ( not a ' safe ' variant , so don ' t modify the list ) .
*
* Holds @ rl - > lock over the entire request - iteration .
*/
static inline void
zfcp_reqlist_apply_for_all ( struct zfcp_reqlist * rl ,
void ( * f ) ( struct zfcp_fsf_req * , void * ) , void * data )
{
struct zfcp_fsf_req * req ;
unsigned long flags ;
unsigned int i ;
spin_lock_irqsave ( & rl - > lock , flags ) ;
for ( i = 0 ; i < ZFCP_REQ_LIST_BUCKETS ; i + + )
list_for_each_entry ( req , & rl - > buckets [ i ] , list )
f ( req , data ) ;
spin_unlock_irqrestore ( & rl - > lock , flags ) ;
}
2010-02-17 11:18:50 +01:00
# endif /* ZFCP_REQLIST_H */