2017-11-14 18:38:00 +01:00
// SPDX-License-Identifier: GPL-2.0
2006-06-29 14:58:12 +02:00
/*
2005-04-16 15:20:36 -07:00
* Author ( s ) . . . . . . : Holger Smolinski < Holger . Smolinski @ de . ibm . com >
* Bugreports . to . . : < Linux390 @ de . ibm . com >
2009-06-16 10:30:25 +02:00
* Copyright IBM Corp . 1999 , 2009
2005-04-16 15:20:36 -07:00
*/
2009-09-11 10:28:30 +02:00
# define KMSG_COMPONENT "dasd-fba"
2009-03-26 15:23:49 +01:00
2005-04-16 15:20:36 -07:00
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <asm/debug.h>
# include <linux/slab.h>
# include <linux/hdreg.h> /* HDIO_GETGEO */
# include <linux/bio.h>
# include <linux/module.h>
# include <linux/init.h>
# include <asm/idals.h>
# include <asm/ebcdic.h>
# include <asm/io.h>
# include <asm/ccwdev.h>
# include "dasd_int.h"
# include "dasd_fba.h"
# ifdef PRINTK_HEADER
# undef PRINTK_HEADER
# endif /* PRINTK_HEADER */
# define PRINTK_HEADER "dasd(fba):"
2013-01-30 09:26:12 +00:00
# define FBA_DEFAULT_RETRIES 32
2005-04-16 15:20:36 -07:00
# define DASD_FBA_CCW_WRITE 0x41
# define DASD_FBA_CCW_READ 0x42
# define DASD_FBA_CCW_LOCATE 0x43
# define DASD_FBA_CCW_DEFINE_EXTENT 0x63
MODULE_LICENSE ( " GPL " ) ;
static struct dasd_discipline dasd_fba_discipline ;
struct dasd_fba_private {
struct dasd_fba_characteristics rdc_data ;
} ;
static struct ccw_device_id dasd_fba_ids [ ] = {
2006-07-12 16:41:55 +02:00
{ CCW_DEVICE_DEVTYPE ( 0x6310 , 0 , 0x9336 , 0 ) , . driver_info = 0x1 } ,
{ CCW_DEVICE_DEVTYPE ( 0x3880 , 0 , 0x3370 , 0 ) , . driver_info = 0x2 } ,
2005-04-16 15:20:36 -07:00
{ /* end of list */ } ,
} ;
MODULE_DEVICE_TABLE ( ccw , dasd_fba_ids ) ;
static struct ccw_driver dasd_fba_driver ; /* see below */
static int
dasd_fba_probe ( struct ccw_device * cdev )
{
2006-06-29 15:08:18 +02:00
return dasd_generic_probe ( cdev , & dasd_fba_discipline ) ;
2005-04-16 15:20:36 -07:00
}
static int
dasd_fba_set_online ( struct ccw_device * cdev )
{
2006-06-29 15:08:18 +02:00
return dasd_generic_set_online ( cdev , & dasd_fba_discipline ) ;
2005-04-16 15:20:36 -07:00
}
static struct ccw_driver dasd_fba_driver = {
2011-03-23 10:16:02 +01:00
. driver = {
. name = " dasd-fba " ,
. owner = THIS_MODULE ,
} ,
2005-04-16 15:20:36 -07:00
. ids = dasd_fba_ids ,
. probe = dasd_fba_probe ,
. remove = dasd_generic_remove ,
. set_offline = dasd_generic_set_offline ,
. set_online = dasd_fba_set_online ,
. notify = dasd_generic_notify ,
2011-01-05 12:48:03 +01:00
. path_event = dasd_generic_path_event ,
2009-06-16 10:30:25 +02:00
. freeze = dasd_generic_pm_freeze ,
. thaw = dasd_generic_restore_device ,
. restore = dasd_generic_restore_device ,
2013-01-02 15:18:18 +01:00
. int_class = IRQIO_DAS ,
2005-04-16 15:20:36 -07:00
} ;
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
define_extent ( struct ccw1 * ccw , struct DE_fba_data * data , int rw ,
int blksize , int beg , int nr )
{
ccw - > cmd_code = DASD_FBA_CCW_DEFINE_EXTENT ;
ccw - > flags = 0 ;
ccw - > count = 16 ;
ccw - > cda = ( __u32 ) __pa ( data ) ;
memset ( data , 0 , sizeof ( struct DE_fba_data ) ) ;
if ( rw = = WRITE )
( data - > mask ) . perm = 0x0 ;
else if ( rw = = READ )
( data - > mask ) . perm = 0x1 ;
else
data - > mask . perm = 0x2 ;
data - > blk_size = blksize ;
data - > ext_loc = beg ;
data - > ext_end = nr - 1 ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
locate_record ( struct ccw1 * ccw , struct LO_fba_data * data , int rw ,
int block_nr , int block_ct )
{
ccw - > cmd_code = DASD_FBA_CCW_LOCATE ;
ccw - > flags = 0 ;
ccw - > count = 8 ;
ccw - > cda = ( __u32 ) __pa ( data ) ;
memset ( data , 0 , sizeof ( struct LO_fba_data ) ) ;
if ( rw = = WRITE )
data - > operation . cmd = 0x5 ;
else if ( rw = = READ )
data - > operation . cmd = 0x6 ;
else
data - > operation . cmd = 0x8 ;
data - > blk_nr = block_nr ;
data - > blk_ct = block_ct ;
}
static int
dasd_fba_check_characteristics ( struct dasd_device * device )
{
2016-03-04 10:34:05 +01:00
struct dasd_fba_private * private = device - > private ;
2006-06-29 14:58:12 +02:00
struct ccw_device * cdev = device - > cdev ;
2016-03-04 10:34:05 +01:00
struct dasd_block * block ;
int readonly , rc ;
2005-04-16 15:20:36 -07:00
2009-06-12 10:26:37 +02:00
if ( ! private ) {
private = kzalloc ( sizeof ( * private ) , GFP_KERNEL | GFP_DMA ) ;
if ( ! private ) {
2009-03-26 15:23:49 +01:00
dev_warn ( & device - > cdev - > dev ,
" Allocating memory for private DASD "
" data failed \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
2016-03-04 10:34:05 +01:00
device - > private = private ;
2009-06-12 10:26:37 +02:00
} else {
memset ( private , 0 , sizeof ( * private ) ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-26 14:11:23 +01:00
block = dasd_alloc_block ( ) ;
if ( IS_ERR ( block ) ) {
2009-12-07 12:51:52 +01:00
DBF_EVENT_DEVID ( DBF_WARNING , cdev , " %s " , " could not allocate "
" dasd block structure " ) ;
2008-07-17 17:16:43 +02:00
device - > private = NULL ;
kfree ( private ) ;
2008-01-26 14:11:23 +01:00
return PTR_ERR ( block ) ;
}
device - > block = block ;
block - > base = device ;
2005-04-16 15:20:36 -07:00
/* Read Device Characteristics */
2009-09-11 10:28:29 +02:00
rc = dasd_generic_read_dev_chars ( device , DASD_FBA_MAGIC ,
& private - > rdc_data , 32 ) ;
2005-04-16 15:20:36 -07:00
if ( rc ) {
2009-12-07 12:51:52 +01:00
DBF_EVENT_DEVID ( DBF_WARNING , cdev , " Read device "
" characteristics returned error %d " , rc ) ;
2008-07-17 17:16:43 +02:00
device - > block = NULL ;
dasd_free_block ( block ) ;
device - > private = NULL ;
kfree ( private ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2010-08-09 18:13:00 +02:00
device - > default_expires = DASD_EXPIRES ;
2013-01-30 09:26:12 +00:00
device - > default_retries = FBA_DEFAULT_RETRIES ;
2016-08-08 15:53:54 +02:00
dasd_path_set_opm ( device , LPM_ANYPATH ) ;
2010-08-09 18:13:00 +02:00
2010-03-08 12:26:24 +01:00
readonly = dasd_device_is_ro ( device ) ;
if ( readonly )
set_bit ( DASD_FLAG_DEVICE_RO , & device - > flags ) ;
2016-06-30 13:28:57 +02:00
/* FBA supports discard, set the according feature bit */
dasd_set_feature ( cdev , DASD_FEATURE_DISCARD , 1 ) ;
2009-03-26 15:23:49 +01:00
dev_info ( & device - > cdev - > dev ,
" New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB "
2010-03-08 12:26:24 +01:00
" and %d B/blk%s \n " ,
2009-03-26 15:23:49 +01:00
cdev - > id . dev_type ,
cdev - > id . dev_model ,
cdev - > id . cu_type ,
cdev - > id . cu_model ,
( ( private - > rdc_data . blk_bdsa *
( private - > rdc_data . blk_size > > 9 ) ) > > 11 ) ,
2010-03-08 12:26:24 +01:00
private - > rdc_data . blk_size ,
readonly ? " , read-only device " : " " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-01-26 14:11:23 +01:00
static int dasd_fba_do_analysis ( struct dasd_block * block )
2005-04-16 15:20:36 -07:00
{
2016-03-04 10:34:05 +01:00
struct dasd_fba_private * private = block - > base - > private ;
2005-04-16 15:20:36 -07:00
int sb , rc ;
rc = dasd_check_blocksize ( private - > rdc_data . blk_size ) ;
if ( rc ) {
2009-03-26 15:23:49 +01:00
DBF_DEV_EVENT ( DBF_WARNING , block - > base , " unknown blocksize %d " ,
2005-04-16 15:20:36 -07:00
private - > rdc_data . blk_size ) ;
return rc ;
}
2008-01-26 14:11:23 +01:00
block - > blocks = private - > rdc_data . blk_bdsa ;
block - > bp_block = private - > rdc_data . blk_size ;
block - > s2b_shift = 0 ; /* bits to shift 512 to get a block */
2005-04-16 15:20:36 -07:00
for ( sb = 512 ; sb < private - > rdc_data . blk_size ; sb = sb < < 1 )
2008-01-26 14:11:23 +01:00
block - > s2b_shift + + ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-01-26 14:11:23 +01:00
static int dasd_fba_fill_geometry ( struct dasd_block * block ,
struct hd_geometry * geo )
2005-04-16 15:20:36 -07:00
{
2008-01-26 14:11:23 +01:00
if ( dasd_check_blocksize ( block - > bp_block ) ! = 0 )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2008-01-26 14:11:23 +01:00
geo - > cylinders = ( block - > blocks < < block - > s2b_shift ) > > 10 ;
2005-04-16 15:20:36 -07:00
geo - > heads = 16 ;
2008-01-26 14:11:23 +01:00
geo - > sectors = 128 > > block - > s2b_shift ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static dasd_erp_fn_t
dasd_fba_erp_action ( struct dasd_ccw_req * cqr )
{
return dasd_default_erp_action ;
}
static dasd_erp_fn_t
dasd_fba_erp_postaction ( struct dasd_ccw_req * cqr )
{
if ( cqr - > function = = dasd_default_erp_action )
return dasd_default_erp_postaction ;
2009-03-26 15:23:49 +01:00
DBF_DEV_EVENT ( DBF_WARNING , cqr - > startdev , " unknown ERP action %p " ,
2005-04-16 15:20:36 -07:00
cqr - > function ) ;
return NULL ;
}
2011-01-05 12:48:04 +01:00
static void dasd_fba_check_for_device_change ( struct dasd_device * device ,
struct dasd_ccw_req * cqr ,
struct irb * irb )
2008-01-26 14:11:23 +01:00
{
char mask ;
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP ;
2011-01-05 12:48:04 +01:00
if ( ( irb - > scsw . cmd . dstat & mask ) = = mask )
2008-01-26 14:11:23 +01:00
dasd_generic_handle_state_change ( device ) ;
} ;
2016-06-30 13:28:57 +02:00
/*
* Builds a CCW with no data payload
*/
static void ccw_write_no_data ( struct ccw1 * ccw )
{
ccw - > cmd_code = DASD_FBA_CCW_WRITE ;
ccw - > flags | = CCW_FLAG_SLI ;
ccw - > count = 0 ;
}
/*
* Builds a CCW that writes only zeroes .
*/
static void ccw_write_zero ( struct ccw1 * ccw , int count )
{
ccw - > cmd_code = DASD_FBA_CCW_WRITE ;
ccw - > flags | = CCW_FLAG_SLI ;
ccw - > count = count ;
ccw - > cda = ( __u32 ) ( addr_t ) page_to_phys ( ZERO_PAGE ( 0 ) ) ;
}
/*
* Helper function to count the amount of necessary CCWs within a given range
* with 4 k alignment and command chaining in mind .
*/
static int count_ccws ( sector_t first_rec , sector_t last_rec ,
unsigned int blocks_per_page )
{
sector_t wz_stop = 0 , d_stop = 0 ;
int cur_pos = 0 ;
int count = 0 ;
if ( first_rec % blocks_per_page ! = 0 ) {
wz_stop = first_rec + blocks_per_page -
( first_rec % blocks_per_page ) - 1 ;
if ( wz_stop > last_rec )
wz_stop = last_rec ;
cur_pos = wz_stop - first_rec + 1 ;
count + + ;
}
if ( last_rec - ( first_rec + cur_pos ) + 1 > = blocks_per_page ) {
if ( ( last_rec - blocks_per_page + 1 ) % blocks_per_page ! = 0 )
d_stop = last_rec - ( ( last_rec - blocks_per_page + 1 ) %
blocks_per_page ) ;
else
d_stop = last_rec ;
cur_pos + = d_stop - ( first_rec + cur_pos ) + 1 ;
count + + ;
}
if ( cur_pos = = 0 | | first_rec + cur_pos - 1 < last_rec )
count + + ;
return count ;
}
/*
* This function builds a CCW request for block layer discard requests .
* Each page in the z / VM hypervisor that represents certain records of an FBA
* device will be padded with zeros . This is a special behaviour of the WRITE
* command which is triggered when no data payload is added to the CCW .
*
* Note : Due to issues in some z / VM versions , we can ' t fully utilise this
* special behaviour . We have to keep a 4 k ( or 8 block ) alignment in mind to
* work around those issues and write actual zeroes to the unaligned parts in
* the request . This workaround might be removed in the future .
*/
static struct dasd_ccw_req * dasd_fba_build_cp_discard (
struct dasd_device * memdev ,
struct dasd_block * block ,
struct request * req )
{
struct LO_fba_data * LO_data ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
sector_t wz_stop = 0 , d_stop = 0 ;
sector_t first_rec , last_rec ;
unsigned int blksize = block - > bp_block ;
unsigned int blocks_per_page ;
int wz_count = 0 ;
int d_count = 0 ;
int cur_pos = 0 ; /* Current position within the extent */
int count = 0 ;
int cplength ;
int datasize ;
int nr_ccws ;
first_rec = blk_rq_pos ( req ) > > block - > s2b_shift ;
last_rec =
( blk_rq_pos ( req ) + blk_rq_sectors ( req ) - 1 ) > > block - > s2b_shift ;
count = last_rec - first_rec + 1 ;
blocks_per_page = BLOCKS_PER_PAGE ( blksize ) ;
nr_ccws = count_ccws ( first_rec , last_rec , blocks_per_page ) ;
/* define extent + nr_ccws * locate record + nr_ccws * single CCW */
cplength = 1 + 2 * nr_ccws ;
datasize = sizeof ( struct DE_fba_data ) +
nr_ccws * ( sizeof ( struct LO_fba_data ) + sizeof ( struct ccw1 ) ) ;
2018-06-04 19:07:39 +02:00
cqr = dasd_smalloc_request ( DASD_FBA_MAGIC , cplength , datasize , memdev ,
blk_mq_rq_to_pdu ( req ) ) ;
2016-06-30 13:28:57 +02:00
if ( IS_ERR ( cqr ) )
return cqr ;
ccw = cqr - > cpaddr ;
define_extent ( ccw + + , cqr - > data , WRITE , blksize , first_rec , count ) ;
LO_data = cqr - > data + sizeof ( struct DE_fba_data ) ;
/* First part is not aligned. Calculate range to write zeroes. */
if ( first_rec % blocks_per_page ! = 0 ) {
wz_stop = first_rec + blocks_per_page -
( first_rec % blocks_per_page ) - 1 ;
if ( wz_stop > last_rec )
wz_stop = last_rec ;
wz_count = wz_stop - first_rec + 1 ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , WRITE , cur_pos , wz_count ) ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw_write_zero ( ccw + + , wz_count * blksize ) ;
cur_pos = wz_count ;
}
/* We can do proper discard when we've got at least blocks_per_page blocks. */
if ( last_rec - ( first_rec + cur_pos ) + 1 > = blocks_per_page ) {
/* is last record at page boundary? */
if ( ( last_rec - blocks_per_page + 1 ) % blocks_per_page ! = 0 )
d_stop = last_rec - ( ( last_rec - blocks_per_page + 1 ) %
blocks_per_page ) ;
else
d_stop = last_rec ;
d_count = d_stop - ( first_rec + cur_pos ) + 1 ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , WRITE , cur_pos , d_count ) ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw_write_no_data ( ccw + + ) ;
cur_pos + = d_count ;
}
/* We might still have some bits left which need to be zeroed. */
if ( cur_pos = = 0 | | first_rec + cur_pos - 1 < last_rec ) {
if ( d_stop ! = 0 )
wz_count = last_rec - d_stop ;
else if ( wz_stop ! = 0 )
wz_count = last_rec - wz_stop ;
else
wz_count = count ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , WRITE , cur_pos , wz_count ) ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw_write_zero ( ccw + + , wz_count * blksize ) ;
}
if ( blk_noretry_request ( req ) | |
block - > base - > features & DASD_FEATURE_FAILFAST )
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
cqr - > startdev = memdev ;
cqr - > memdev = memdev ;
cqr - > block = block ;
cqr - > expires = memdev - > default_expires * HZ ; /* default 5 minutes */
cqr - > retries = memdev - > default_retries ;
cqr - > buildclk = get_tod_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
static struct dasd_ccw_req * dasd_fba_build_cp_regular (
struct dasd_device * memdev ,
struct dasd_block * block ,
struct request * req )
2005-04-16 15:20:36 -07:00
{
2016-03-04 10:34:05 +01:00
struct dasd_fba_private * private = block - > base - > private ;
2005-04-16 15:20:36 -07:00
unsigned long * idaws ;
struct LO_fba_data * LO_data ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
2007-09-25 12:35:59 +02:00
struct req_iterator iter ;
2013-11-23 17:19:00 -08:00
struct bio_vec bv ;
2005-04-16 15:20:36 -07:00
char * dst ;
int count , cidaw , cplength , datasize ;
sector_t recid , first_rec , last_rec ;
unsigned int blksize , off ;
unsigned char cmd ;
if ( rq_data_dir ( req ) = = READ ) {
cmd = DASD_FBA_CCW_READ ;
} else if ( rq_data_dir ( req ) = = WRITE ) {
cmd = DASD_FBA_CCW_WRITE ;
} else
return ERR_PTR ( - EINVAL ) ;
2008-01-26 14:11:23 +01:00
blksize = block - > bp_block ;
2005-04-16 15:20:36 -07:00
/* Calculate record id of first and last block. */
2009-05-07 22:24:39 +09:00
first_rec = blk_rq_pos ( req ) > > block - > s2b_shift ;
last_rec =
( blk_rq_pos ( req ) + blk_rq_sectors ( req ) - 1 ) > > block - > s2b_shift ;
2005-04-16 15:20:36 -07:00
/* Check struct bio and count the number of blocks for the request. */
count = 0 ;
cidaw = 0 ;
2007-09-25 12:35:59 +02:00
rq_for_each_segment ( bv , req , iter ) {
2013-11-23 17:19:00 -08:00
if ( bv . bv_len & ( blksize - 1 ) )
2007-08-16 13:43:12 +02:00
/* Fba can only do full blocks. */
return ERR_PTR ( - EINVAL ) ;
2013-11-23 17:19:00 -08:00
count + = bv . bv_len > > ( block - > s2b_shift + 9 ) ;
if ( idal_is_needed ( page_address ( bv . bv_page ) , bv . bv_len ) )
cidaw + = bv . bv_len / blksize ;
2005-04-16 15:20:36 -07:00
}
/* Paranoia. */
if ( count ! = last_rec - first_rec + 1 )
return ERR_PTR ( - EINVAL ) ;
/* 1x define extent + 1x locate record + number of blocks */
cplength = 2 + count ;
/* 1x define extent + 1x locate record */
datasize = sizeof ( struct DE_fba_data ) + sizeof ( struct LO_fba_data ) +
cidaw * sizeof ( unsigned long ) ;
/*
* Find out number of additional locate record ccws if the device
* can ' t do data chaining .
*/
if ( private - > rdc_data . mode . bits . data_chain = = 0 ) {
cplength + = count - 1 ;
datasize + = ( count - 1 ) * sizeof ( struct LO_fba_data ) ;
}
/* Allocate the ccw request. */
2018-06-04 19:07:39 +02:00
cqr = dasd_smalloc_request ( DASD_FBA_MAGIC , cplength , datasize , memdev ,
blk_mq_rq_to_pdu ( req ) ) ;
2005-04-16 15:20:36 -07:00
if ( IS_ERR ( cqr ) )
return cqr ;
ccw = cqr - > cpaddr ;
/* First ccw is define extent. */
define_extent ( ccw + + , cqr - > data , rq_data_dir ( req ) ,
2009-05-07 22:24:39 +09:00
block - > bp_block , blk_rq_pos ( req ) , blk_rq_sectors ( req ) ) ;
2005-04-16 15:20:36 -07:00
/* Build locate_record + read/write ccws. */
idaws = ( unsigned long * ) ( cqr - > data + sizeof ( struct DE_fba_data ) ) ;
LO_data = ( struct LO_fba_data * ) ( idaws + cidaw ) ;
/* Locate record for all blocks for smart devices. */
if ( private - > rdc_data . mode . bits . data_chain ! = 0 ) {
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , rq_data_dir ( req ) , 0 , count ) ;
}
recid = first_rec ;
2007-09-25 12:35:59 +02:00
rq_for_each_segment ( bv , req , iter ) {
2013-11-23 17:19:00 -08:00
dst = page_address ( bv . bv_page ) + bv . bv_offset ;
2005-04-16 15:20:36 -07:00
if ( dasd_page_cache ) {
char * copy = kmem_cache_alloc ( dasd_page_cache ,
2006-12-06 20:33:19 -08:00
GFP_DMA | __GFP_NOWARN ) ;
2005-04-16 15:20:36 -07:00
if ( copy & & rq_data_dir ( req ) = = WRITE )
2013-11-23 17:19:00 -08:00
memcpy ( copy + bv . bv_offset , dst , bv . bv_len ) ;
2005-04-16 15:20:36 -07:00
if ( copy )
2013-11-23 17:19:00 -08:00
dst = copy + bv . bv_offset ;
2005-04-16 15:20:36 -07:00
}
2013-11-23 17:19:00 -08:00
for ( off = 0 ; off < bv . bv_len ; off + = blksize ) {
2005-04-16 15:20:36 -07:00
/* Locate record for stupid devices. */
if ( private - > rdc_data . mode . bits . data_chain = = 0 ) {
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw , LO_data + + ,
rq_data_dir ( req ) ,
recid - first_rec , 1 ) ;
ccw - > flags = CCW_FLAG_CC ;
ccw + + ;
} else {
if ( recid > first_rec )
ccw [ - 1 ] . flags | = CCW_FLAG_DC ;
else
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
}
ccw - > cmd_code = cmd ;
2008-01-26 14:11:23 +01:00
ccw - > count = block - > bp_block ;
2005-04-16 15:20:36 -07:00
if ( idal_is_needed ( dst , blksize ) ) {
ccw - > cda = ( __u32 ) ( addr_t ) idaws ;
ccw - > flags = CCW_FLAG_IDA ;
idaws = idal_create_words ( idaws , dst , blksize ) ;
} else {
ccw - > cda = ( __u32 ) ( addr_t ) dst ;
ccw - > flags = 0 ;
}
ccw + + ;
dst + = blksize ;
recid + + ;
}
}
2009-01-09 12:14:51 +01:00
if ( blk_noretry_request ( req ) | |
block - > base - > features & DASD_FEATURE_FAILFAST )
2006-01-06 00:19:15 -08:00
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
2008-01-26 14:11:23 +01:00
cqr - > startdev = memdev ;
cqr - > memdev = memdev ;
cqr - > block = block ;
2010-08-09 18:13:00 +02:00
cqr - > expires = memdev - > default_expires * HZ ; /* default 5 minutes */
2013-01-30 09:26:12 +00:00
cqr - > retries = memdev - > default_retries ;
2013-01-30 09:49:40 +01:00
cqr - > buildclk = get_tod_clock ( ) ;
2005-04-16 15:20:36 -07:00
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
2016-06-30 13:28:57 +02:00
static struct dasd_ccw_req * dasd_fba_build_cp ( struct dasd_device * memdev ,
struct dasd_block * block ,
struct request * req )
{
if ( req_op ( req ) = = REQ_OP_DISCARD | | req_op ( req ) = = REQ_OP_WRITE_ZEROES )
return dasd_fba_build_cp_discard ( memdev , block , req ) ;
else
return dasd_fba_build_cp_regular ( memdev , block , req ) ;
}
2005-04-16 15:20:36 -07:00
static int
dasd_fba_free_cp ( struct dasd_ccw_req * cqr , struct request * req )
{
2016-03-04 10:34:05 +01:00
struct dasd_fba_private * private = cqr - > block - > base - > private ;
2005-04-16 15:20:36 -07:00
struct ccw1 * ccw ;
2007-09-25 12:35:59 +02:00
struct req_iterator iter ;
2013-11-23 17:19:00 -08:00
struct bio_vec bv ;
2005-04-16 15:20:36 -07:00
char * dst , * cda ;
unsigned int blksize , off ;
2007-09-25 12:35:59 +02:00
int status ;
2005-04-16 15:20:36 -07:00
if ( ! dasd_page_cache )
goto out ;
2008-01-26 14:11:23 +01:00
blksize = cqr - > block - > bp_block ;
2005-04-16 15:20:36 -07:00
ccw = cqr - > cpaddr ;
/* Skip over define extent & locate record. */
ccw + + ;
if ( private - > rdc_data . mode . bits . data_chain ! = 0 )
ccw + + ;
2007-09-25 12:35:59 +02:00
rq_for_each_segment ( bv , req , iter ) {
2013-11-23 17:19:00 -08:00
dst = page_address ( bv . bv_page ) + bv . bv_offset ;
for ( off = 0 ; off < bv . bv_len ; off + = blksize ) {
2005-04-16 15:20:36 -07:00
/* Skip locate record. */
if ( private - > rdc_data . mode . bits . data_chain = = 0 )
ccw + + ;
if ( dst ) {
if ( ccw - > flags & CCW_FLAG_IDA )
cda = * ( ( char * * ) ( ( addr_t ) ccw - > cda ) ) ;
else
cda = ( char * ) ( ( addr_t ) ccw - > cda ) ;
if ( dst ! = cda ) {
if ( rq_data_dir ( req ) = = READ )
2013-11-23 17:19:00 -08:00
memcpy ( dst , cda , bv . bv_len ) ;
2005-04-16 15:20:36 -07:00
kmem_cache_free ( dasd_page_cache ,
( void * ) ( ( addr_t ) cda & PAGE_MASK ) ) ;
}
dst = NULL ;
}
ccw + + ;
}
}
out :
status = cqr - > status = = DASD_CQR_DONE ;
2008-01-26 14:11:23 +01:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-16 15:20:36 -07:00
return status ;
}
2008-01-26 14:11:23 +01:00
static void dasd_fba_handle_terminated_request ( struct dasd_ccw_req * cqr )
{
2013-01-30 09:26:14 +00:00
if ( cqr - > retries < 0 )
cqr - > status = DASD_CQR_FAILED ;
else
cqr - > status = DASD_CQR_FILLED ;
2008-01-26 14:11:23 +01:00
} ;
2005-04-16 15:20:36 -07:00
static int
dasd_fba_fill_info ( struct dasd_device * device ,
struct dasd_information2_t * info )
{
2016-03-04 10:34:05 +01:00
struct dasd_fba_private * private = device - > private ;
2005-04-16 15:20:36 -07:00
info - > label_block = 1 ;
info - > FBA_layout = 1 ;
info - > format = DASD_FORMAT_LDL ;
2016-03-04 10:34:05 +01:00
info - > characteristics_size = sizeof ( private - > rdc_data ) ;
memcpy ( info - > characteristics , & private - > rdc_data ,
sizeof ( private - > rdc_data ) ) ;
2005-04-16 15:20:36 -07:00
info - > confdata_size = 0 ;
return 0 ;
}
2009-03-26 15:23:49 +01:00
static void
2009-07-07 16:37:06 +02:00
dasd_fba_dump_sense_dbf ( struct dasd_device * device , struct irb * irb ,
char * reason )
2009-03-26 15:23:49 +01:00
{
2009-07-07 16:37:06 +02:00
u64 * sense ;
sense = ( u64 * ) dasd_get_sense ( irb ) ;
if ( sense ) {
DBF_DEV_EVENT ( DBF_EMERG , device ,
" %s: %s %02x%02x%02x %016llx %016llx %016llx "
" %016llx " , reason ,
scsw_is_tm ( & irb - > scsw ) ? " t " : " c " ,
scsw_cc ( & irb - > scsw ) , scsw_cstat ( & irb - > scsw ) ,
scsw_dstat ( & irb - > scsw ) , sense [ 0 ] , sense [ 1 ] ,
sense [ 2 ] , sense [ 3 ] ) ;
2009-03-26 15:23:49 +01:00
} else {
DBF_DEV_EVENT ( DBF_EMERG , device , " %s " ,
" SORRY - NO VALID SENSE AVAILABLE \n " ) ;
}
}
2005-04-16 15:20:36 -07:00
static void
dasd_fba_dump_sense ( struct dasd_device * device , struct dasd_ccw_req * req ,
struct irb * irb )
{
char * page ;
struct ccw1 * act , * end , * last ;
int len , sl , sct , count ;
page = ( char * ) get_zeroed_page ( GFP_ATOMIC ) ;
if ( page = = NULL ) {
2009-03-26 15:23:49 +01:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-16 15:20:36 -07:00
" No memory to dump sense data " ) ;
return ;
}
2012-10-23 20:28:37 +02:00
len = sprintf ( page , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" I/O status report for device %s: \n " ,
2008-10-10 21:33:09 +02:00
dev_name ( & device - > cdev - > dev ) ) ;
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" in req: %p CS: 0x%02X DS: 0x%02X \n " , req ,
2008-07-14 09:58:50 +02:00
irb - > scsw . cmd . cstat , irb - > scsw . cmd . dstat ) ;
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" device %s: Failing CCW: %p \n " ,
2008-10-10 21:33:09 +02:00
dev_name ( & device - > cdev - > dev ) ,
2008-07-14 09:58:50 +02:00
( void * ) ( addr_t ) irb - > scsw . cmd . cpa ) ;
2005-04-16 15:20:36 -07:00
if ( irb - > esw . esw0 . erw . cons ) {
for ( sl = 0 ; sl < 4 ; sl + + ) {
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" Sense(hex) %2d-%2d: " ,
( 8 * sl ) , ( ( 8 * sl ) + 7 ) ) ;
for ( sct = 0 ; sct < 8 ; sct + + ) {
len + = sprintf ( page + len , " %02x " ,
irb - > ecw [ 8 * sl + sct ] ) ;
}
len + = sprintf ( page + len , " \n " ) ;
}
} else {
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" SORRY - NO VALID SENSE AVAILABLE \n " ) ;
}
2009-03-26 15:23:49 +01:00
printk ( KERN_ERR " %s " , page ) ;
2005-04-16 15:20:36 -07:00
/* dump the Channel Program */
/* print first CCWs (maximum 8) */
act = req - > cpaddr ;
for ( last = act ; last - > flags & ( CCW_FLAG_CC | CCW_FLAG_DC ) ; last + + ) ;
end = min ( act + 8 , last ) ;
2012-10-23 20:28:37 +02:00
len = sprintf ( page , PRINTK_HEADER " Related CP in req: %p \n " , req ) ;
2005-04-16 15:20:36 -07:00
while ( act < = end ) {
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" CCW %p: %08X %08X DAT: " ,
act , ( ( int * ) act ) [ 0 ] , ( ( int * ) act ) [ 1 ] ) ;
for ( count = 0 ; count < 32 & & count < act - > count ;
count + = sizeof ( int ) )
len + = sprintf ( page + len , " %08X " ,
( ( int * ) ( addr_t ) act - > cda )
[ ( count > > 2 ) ] ) ;
len + = sprintf ( page + len , " \n " ) ;
act + + ;
}
2009-03-26 15:23:49 +01:00
printk ( KERN_ERR " %s " , page ) ;
2005-04-16 15:20:36 -07:00
/* print failing CCW area */
len = 0 ;
2008-07-14 09:58:50 +02:00
if ( act < ( ( struct ccw1 * ) ( addr_t ) irb - > scsw . cmd . cpa ) - 2 ) {
act = ( ( struct ccw1 * ) ( addr_t ) irb - > scsw . cmd . cpa ) - 2 ;
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER " ...... \n " ) ;
2005-04-16 15:20:36 -07:00
}
2008-07-14 09:58:50 +02:00
end = min ( ( struct ccw1 * ) ( addr_t ) irb - > scsw . cmd . cpa + 2 , last ) ;
2005-04-16 15:20:36 -07:00
while ( act < = end ) {
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" CCW %p: %08X %08X DAT: " ,
act , ( ( int * ) act ) [ 0 ] , ( ( int * ) act ) [ 1 ] ) ;
for ( count = 0 ; count < 32 & & count < act - > count ;
count + = sizeof ( int ) )
len + = sprintf ( page + len , " %08X " ,
( ( int * ) ( addr_t ) act - > cda )
[ ( count > > 2 ) ] ) ;
len + = sprintf ( page + len , " \n " ) ;
act + + ;
}
/* print last CCWs */
if ( act < last - 2 ) {
act = last - 2 ;
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER " ...... \n " ) ;
2005-04-16 15:20:36 -07:00
}
while ( act < = last ) {
2012-10-23 20:28:37 +02:00
len + = sprintf ( page + len , PRINTK_HEADER
2005-04-16 15:20:36 -07:00
" CCW %p: %08X %08X DAT: " ,
act , ( ( int * ) act ) [ 0 ] , ( ( int * ) act ) [ 1 ] ) ;
for ( count = 0 ; count < 32 & & count < act - > count ;
count + = sizeof ( int ) )
len + = sprintf ( page + len , " %08X " ,
( ( int * ) ( addr_t ) act - > cda )
[ ( count > > 2 ) ] ) ;
len + = sprintf ( page + len , " \n " ) ;
act + + ;
}
if ( len > 0 )
2009-03-26 15:23:49 +01:00
printk ( KERN_ERR " %s " , page ) ;
2005-04-16 15:20:36 -07:00
free_page ( ( unsigned long ) page ) ;
}
/*
2018-04-27 16:55:27 +02:00
* Initialize block layer request queue .
2005-04-16 15:20:36 -07:00
*/
2018-04-27 16:55:27 +02:00
static void dasd_fba_setup_blk_queue ( struct dasd_block * block )
{
unsigned int logical_block_size = block - > bp_block ;
struct request_queue * q = block - > request_queue ;
unsigned int max_bytes , max_discard_sectors ;
int max ;
max = DASD_FBA_MAX_BLOCKS < < block - > s2b_shift ;
blk_queue_flag_set ( QUEUE_FLAG_NONROT , q ) ;
q - > limits . max_dev_sectors = max ;
blk_queue_logical_block_size ( q , logical_block_size ) ;
blk_queue_max_hw_sectors ( q , max ) ;
blk_queue_max_segments ( q , USHRT_MAX ) ;
/* With page sized segments each segment can be translated into one idaw/tidaw */
blk_queue_max_segment_size ( q , PAGE_SIZE ) ;
blk_queue_segment_boundary ( q , PAGE_SIZE - 1 ) ;
q - > limits . discard_granularity = logical_block_size ;
q - > limits . discard_alignment = PAGE_SIZE ;
/* Calculate max_discard_sectors and make it PAGE aligned */
max_bytes = USHRT_MAX * logical_block_size ;
2018-05-29 14:11:25 +02:00
max_bytes = ALIGN_DOWN ( max_bytes , PAGE_SIZE ) ;
2018-04-27 16:55:27 +02:00
max_discard_sectors = max_bytes / logical_block_size ;
blk_queue_max_discard_sectors ( q , max_discard_sectors ) ;
blk_queue_max_write_zeroes_sectors ( q , max_discard_sectors ) ;
blk_queue_flag_set ( QUEUE_FLAG_DISCARD , q ) ;
}
2005-04-16 15:20:36 -07:00
static struct dasd_discipline dasd_fba_discipline = {
. owner = THIS_MODULE ,
. name = " FBA " ,
. ebcname = " FBA " ,
. check_device = dasd_fba_check_characteristics ,
. do_analysis = dasd_fba_do_analysis ,
2011-01-05 12:48:03 +01:00
. verify_path = dasd_generic_verify_path ,
2018-04-27 16:55:27 +02:00
. setup_blk_queue = dasd_fba_setup_blk_queue ,
2005-04-16 15:20:36 -07:00
. fill_geometry = dasd_fba_fill_geometry ,
. start_IO = dasd_start_IO ,
. term_IO = dasd_term_IO ,
2008-01-26 14:11:23 +01:00
. handle_terminated_request = dasd_fba_handle_terminated_request ,
2005-04-16 15:20:36 -07:00
. erp_action = dasd_fba_erp_action ,
. erp_postaction = dasd_fba_erp_postaction ,
2011-01-05 12:48:04 +01:00
. check_for_device_change = dasd_fba_check_for_device_change ,
2005-04-16 15:20:36 -07:00
. build_cp = dasd_fba_build_cp ,
. free_cp = dasd_fba_free_cp ,
. dump_sense = dasd_fba_dump_sense ,
2009-03-26 15:23:49 +01:00
. dump_sense_dbf = dasd_fba_dump_sense_dbf ,
2005-04-16 15:20:36 -07:00
. fill_info = dasd_fba_fill_info ,
} ;
static int __init
dasd_fba_init ( void )
{
2009-06-12 10:26:38 +02:00
int ret ;
2005-04-16 15:20:36 -07:00
ASCEBC ( dasd_fba_discipline . ebcname , 4 ) ;
2009-06-12 10:26:38 +02:00
ret = ccw_driver_register ( & dasd_fba_driver ) ;
if ( ! ret )
wait_for_device_probe ( ) ;
return ret ;
2005-04-16 15:20:36 -07:00
}
static void __exit
dasd_fba_cleanup ( void )
{
ccw_driver_unregister ( & dasd_fba_driver ) ;
}
module_init ( dasd_fba_init ) ;
module_exit ( dasd_fba_cleanup ) ;