@ -41,6 +41,7 @@
# include <linux/devfreq.h>
# include <linux/nls.h>
# include <linux/of.h>
# include <linux/blkdev.h>
# include "ufshcd.h"
# include "ufs_quirks.h"
# include "unipro.h"
@ -2340,6 +2341,17 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
clear_bit_unlock ( tag , & hba - > lrb_in_use ) ;
goto out ;
}
/* IO svc time latency histogram */
if ( hba ! = NULL & & cmd - > request ! = NULL ) {
if ( hba - > latency_hist_enabled & &
( cmd - > request - > cmd_type = = REQ_TYPE_FS ) ) {
cmd - > request - > lat_hist_io_start = ktime_get ( ) ;
cmd - > request - > lat_hist_enabled = 1 ;
} else
cmd - > request - > lat_hist_enabled = 0 ;
}
WARN_ON ( hba - > clk_gating . state ! = CLKS_ON ) ;
lrbp = & hba - > lrb [ tag ] ;
@ -4602,6 +4614,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
struct scsi_cmnd * cmd ;
int result ;
int index ;
struct request * req ;
for_each_set_bit ( index , & completed_reqs , hba - > nutrs ) {
lrbp = & hba - > lrb [ index ] ;
@ -4614,6 +4627,22 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
/* Mark completed command as NULL in LRB */
lrbp - > cmd = NULL ;
clear_bit_unlock ( index , & hba - > lrb_in_use ) ;
req = cmd - > request ;
if ( req ) {
/* Update IO svc time latency histogram */
if ( req - > lat_hist_enabled ) {
ktime_t completion ;
u_int64_t delta_us ;
completion = ktime_get ( ) ;
delta_us = ktime_us_delta ( completion ,
req - > lat_hist_io_start ) ;
/* rq_data_dir() => true if WRITE */
blk_update_latency_hist ( & hba - > io_lat_s ,
( rq_data_dir ( req ) = = READ ) ,
delta_us ) ;
}
}
/* Do not touch lrbp after scsi done */
cmd - > scsi_done ( cmd ) ;
__ufshcd_release ( hba ) ;
@ -7735,6 +7764,54 @@ out:
}
EXPORT_SYMBOL ( ufshcd_shutdown ) ;
/*
* Values permitted 0 , 1 , 2.
* 0 - > Disable IO latency histograms ( default )
* 1 - > Enable IO latency histograms
* 2 - > Zero out IO latency histograms
*/
static ssize_t
latency_hist_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ufs_hba * hba = dev_get_drvdata ( dev ) ;
long value ;
if ( kstrtol ( buf , 0 , & value ) )
return - EINVAL ;
if ( value = = BLK_IO_LAT_HIST_ZERO )
blk_zero_latency_hist ( & hba - > io_lat_s ) ;
else if ( value = = BLK_IO_LAT_HIST_ENABLE | |
value = = BLK_IO_LAT_HIST_DISABLE )
hba - > latency_hist_enabled = value ;
return count ;
}
ssize_t
latency_hist_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct ufs_hba * hba = dev_get_drvdata ( dev ) ;
return blk_latency_hist_show ( & hba - > io_lat_s , buf ) ;
}
static DEVICE_ATTR ( latency_hist , S_IRUGO | S_IWUSR ,
latency_hist_show , latency_hist_store ) ;
static void
ufshcd_init_latency_hist ( struct ufs_hba * hba )
{
if ( device_create_file ( hba - > dev , & dev_attr_latency_hist ) )
dev_err ( hba - > dev , " Failed to create latency_hist sysfs entry \n " ) ;
}
static void
ufshcd_exit_latency_hist ( struct ufs_hba * hba )
{
device_create_file ( hba - > dev , & dev_attr_latency_hist ) ;
}
/**
* ufshcd_remove - de - allocate SCSI host and host memory space
* data structure memory
@ -7751,6 +7828,7 @@ void ufshcd_remove(struct ufs_hba *hba)
ufshcd_exit_clk_gating ( hba ) ;
if ( ufshcd_is_clkscaling_supported ( hba ) )
device_remove_file ( hba - > dev , & hba - > clk_scaling . enable_attr ) ;
ufshcd_exit_latency_hist ( hba ) ;
ufshcd_hba_exit ( hba ) ;
}
EXPORT_SYMBOL_GPL ( ufshcd_remove ) ;
@ -7980,6 +8058,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync ( dev ) ;
ufshcd_init_latency_hist ( hba ) ;
/*
* We are assuming that device wasn ' t put in sleep / power - down
* state exclusively during the boot stage before kernel .
@ -7997,6 +8077,7 @@ out_remove_scsi_host:
scsi_remove_host ( hba - > host ) ;
exit_gating :
ufshcd_exit_clk_gating ( hba ) ;
ufshcd_exit_latency_hist ( hba ) ;
out_disable :
hba - > is_irq_enabled = false ;
ufshcd_hba_exit ( hba ) ;