summaryrefslogblamecommitdiffstats
path: root/drivers/scsi/lpfc/lpfc_debugfs.c
blob: 7284533f4df232f0e5d9d374a691b58ff8cba694 (plain) (tree)
1
2
3
4
5
6
7
8
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797


                                                                    

                                                                        
                                                                    
                                                                    
                                                                    















                                                                     
                         



                              
                       


                           




                                   


                                 
 
                     

                     
                      
                    
                      
                 

                      
                       



                         
                        
                         
                     
 
                                
  
                    

                                            
                                            
  
                                           

                                             





                                                                      

                                                                              




                                                                         







                                                                              
   
                                   
                                                

                                                                 
                               
                                     
                                                      


                                             
                               
                                          
                                                           


                                                





                                                     
                                      
                                                       




                                            

                                                          
 


                               
   
                                                                  
















                                                                                




                                                                         
                                     




                                                                
 










                                                                          

                                                                         








                                                                          







                                                                         

                      


                   
   
                                                                       
















                                                                                





                                                                           
                     
 


                                                                
























                                                                          





                                                            

                      


                   
                                      
 
   
                                                                      
















                                                                               



                                                                     
                                     






                                         

                               
 















                                                                
                               
                   
                                                                







                                                                             


                                           


                                                                             
 
                                                                


                                                   


                                                       



















                                                                                
                                                                          

                                                                               
                                                                     



















                                                                             

                                          
   
                                                                 















                                                                               





                                                                         




                                           





                                                          
                                                                         




















                                                                  

                      


                   
   
                                                                   












                                                                               
          
                                                                          

                    
                   





                                            



                                                              
                                            











                                                                  
                                    










                                                                  
                                             















                                                                      






                                                                              



                                        
   
                                                                 












                                                                               
          




                                                                         
                                             
                                   
                              


                                             
                                        
                                           


                                                              
                                                                           























                                                                


                                          











                                           







                                                                      
                                                        

                                                                       
                    
                                                                        

                                                                  
                                    
                                                                            
                                                 
                                                                       
                                                
                                                                      
                                                    
                                                                             

                                                       
                                                                             

                                                                
                                                               
                                                


                                                          
 




















                                                                                



























































































                                                                               
                                        
                                                    
                    
                
 



                                                                              
                                                      

                                                                  
                                                      





                                                                      
                                                              




                                                                            
                                                      





                                                                              
                                                      





                                                                      
                                                              




                                                                             


                                                                  




                                                                     
                                                      




                                                                          
                                                      







                                                                                
                                                                  
                                                                         
 


























                                                                             
                



























































                                                                               

                                                          








                                                              

                                                          








                                                               

                                                          








                                                             

                                                          




                                                      
                                                        

                                                 
                                               


                                                   














                                                                  

                                                          






                                                             

                                                          






                                                             

                                                          






                                                               

                                                          






                                                             

                                                          









                                                                   

                                                                  









                                                                

                                                            






                                                               

                                                            






                                                                 

                                                            






                                                                

                                                            






                                                                

                                                            

                                               




























































































































                                                                               
                             
                              
                               






















                                                                         


























                                                                            

                   
 

      
   
                                                    













                                                                                



                                                                    
                                
                                     















                                                                 





                                                                    
   
                                                         










                                                                            



                                                            
                                














                                                                      




                           

































                                                                            
                                
   
                                                            









                                                                                
                                                                               

               
















                                                                  
                                    
                                                                        















                                                                            
   
                                                                 









                                                                                
                                                                               

               
          















                                                                       
                                    
















                                                                                
   
                                                              









                                                                                
                                                                               

               
          









                                                                 
                                    














                                                                   
   
                                                                        









                                                                                
                                                                               

               
          
                                                                     








                                                    
                                    
                                                                   




                             








                                                                       
   
                                                                          









                                                                                
                                                                               

               










                                                                      
                                    







                                                                        






                                   












                                                                  
                                    
                                                         



























                                                                 
                                    

                                                                




































                                                                         



                                                              
                                                  
                                                   
                      
                         


                                           
                                                                             
                                              
                                                                             
                                              
                                                                             
                                               
                                                                             
                                             
                                                                             
                                             
                                                                             







                                                                                

                                                          

                                                                   
              









                                                                         
                                                  
                                                   
                        
                         

                 
                              



                                              

                                                                          
                                             

         
                                                       







                                                           

                                                           





                                                           





                                                                                











                                                                         
   
                                                              









                                                                                
                                                                               

               
          









                                                                  
                                    














                                                                     
   
                                                   














                                                                                


                                                             

                                                                

 
   
                                          













                                                                              




                                                      
 



                                                                        
   
                                                                            









                                                                             










                                                            










                                                                        





























                                                                     

































                                                                         
                                                        







                                                       




                                                       
                                                      



                                                        



                                                         

                                                          



                      







































































































































































































































































































                                                                            



                                                                 


                                              




                                                                  


















                                                                      
  
                                    
                                    
                                    
  

                                                                        
  
                                         



















                                                                           

                     
























                                                             
                                 

                                                                      
                 



































































                                                                             
                                       








                                                              







                                              
                                                         




                              
























































                                                                              

                                                                



















                                                                   
                                                  






















                                                                        




                                                                     











                                                                           




                                                              











































                                                                              
                   


                                                           


                                                                            
                                                                               

                                                                

                                                     
                                               

                                                       
























                                                                         


                                                                             
                                                                          


                                                                



























































































                                                                                




























































































































































































































































































                                                                              













                                                                             

                                                                     

                                                       
                                                         



















                                                                         







                                                                        

















                                                                        

                                                                   

                                                       
                                                         
















                                                                                
                                                  


                                                                        

                                                                     
                                                                      
                                                                         
                                                                        

                                                                     

                                                             
                                                               




                                                           
                                                            






















                                                                       


















                                                                        
                                          



















                                                                               















                                                                        

                                                                  
                                                                      
                                                                         




                                                                                
   








                                                                              


                                                                              










                                                                              
                      
                                    

                                     




                                                                                
                                                   



                         


                                      
                                                             
 
                                             
                                                                    



                                                     
                                                                      

                                                             
 

                                                                                
                                                                  
 



                                              
 
                                                                     
 

                                       
 

                                     
 
                                                                    
                                                               
                                                  

                                     
 


                                                            
 




                                                                     
 




                                                                     
 







                                                                     
 




                                                                     
 
















                                                              




                                                                          
 
                         
         
 
    


                                           
                                                                     

                                       

                                           
 

                                     


                                           


                                                                     
                                           

                                     


                                           


                                                                     
         
 



                                                                        


                                                                     
                                        


                                                                        












































































































                                                                              

                                                                



























































                                                                              
                                     









                                                         





                                                         
















                                                                  

                                            


                                                                              
                                                          
                                                                           
                                                              

                                                               
                                                               

                                                        





                                        

                                                               








                                                                             

                                                               







                                                                             










                                                                                
                                        
                                            



                                                                         

                                                                        
                                                                  

                                                               
                                                               

                                                        
                         
                 


















                                                                             



                                    

                                                               







                                                                             
                               


                                    

                                                               







                                                                             










                                                                                
                                    
                                            
                                                                       


                                                                         

                                                                        
















                                                                         

                                                               
                                                               

                                                        

                         




















                                                                             



                               

                                                               








                                                                             

                                                               







































































































































                                                                              
                                                                     





































                                                                              
                                                










                                                         

                                                             


























































                                                                   

















                                                                          
 

                         
 









































                                                                         
 























                                                                               
 

                                           
 




                                                                           
 










































































































































































































































































































































































































































































































































































































































                                                                              





                                                                
                                                    
































                                                                  











                                                                







                                                         


                                                               
                                    





                                                     
                                    
                                                                     






                                                        

                                               













                                                            









                                                            







                                                             
                           








                                                            
                           








                                                            





























                                                            

      








































                                                                              
                                                            






                                                              
                                                           






                                                              
                                                             






                                                              
                                                            










                                                             
                                                                 









                                                                               
                                                 















































                                                                               
                                                              




                                                     
                                                                 











                                                                               

                                                 

                                                    
                                                              




                                                     
                                                                 
















                                                                          

                                                 









                                                                
   
                                                           







                                                                                


                                                 
                                


                                              
                                 



                                 
                                       


                                                                     
                                         
                                                                   
                                                                              
                                          
                 
         


                                                  

                                                                 
                                      
                                   

                                                                    
                                              
                                                                   
                                                                             
                                          
                 

                                                          
 







                                                                          
                                                                        


                                          
                                       














                                                                              

                                        













                                                                              
                                                        
 























                                                                          












                                                                          





















                                                                              
































                                                                           










                                                                          





















                                                                          











                                                                          
                                                                              
                                                                          


                         





                                                                          
                                                                   
                                                                      
                                                              







                                                                  
                                                                           
                                                                              
                                                                       






                                                                  
 





















































                                                                                   





                                                                         
                                                 
                                                                   
                                                                        
                                          
                 


                                                       










                                                             
                                                                           
                                                          

                 
 
                                  
                                                                              

                            
                               
                                                           
                                                                         
                                             
                                  

                                            






                                                                   
                                                           
                                                              
                                                      







                                                                   
                                                           
                                                                         

                                  
 
































                                                                           
          







                                                                            































                                                                               













                                                                               












                                                                              

























                                                                               











































                                                                               




               
   
                                                                            








                                                                                


                                                
                                

                                              

                               















                                                                    




                                                                       
 
                                                           
 

                                                                  
 





































                                                                        
 

                                           









                                                          
 



                                                     






























                                                             

                                                                   

                                                         

                 
                                             
                                                                         



                                                            

                                                             

         
               
 















                                                                          
                



                                 

                                              
                                                 
 

                                                            
 


                                                             




                                     

                                              
                                                 
 

                                                            
 


                                                             


                                  
                                                         
                                                  
 
/*******************************************************************
 * This file is part of the Emulex Linux Device Driver for         *
 * Fibre Channel Host Bus Adapters.                                *
 * Copyright (C) 2017 Broadcom. All Rights Reserved. The term      *
 * “Broadcom” refers to Broadcom Limited and/or its subsidiaries.  *
 * Copyright (C) 2007-2015 Emulex.  All rights reserved.           *
 * EMULEX and SLI are trademarks of Emulex.                        *
 * www.broadcom.com                                                *
 *                                                                 *
 * This program is free software; you can redistribute it and/or   *
 * modify it under the terms of version 2 of the GNU General       *
 * Public License as published by the Free Software Foundation.    *
 * This program is distributed in the hope that it will be useful. *
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          *
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  *
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      *
 * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
 * TO BE LEGALLY INVALID.  See the GNU General Public License for  *
 * more details, a copy of which can be found in the file COPYING  *
 * included with this package.                                     *
 *******************************************************************/

#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>

#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>

#include <linux/nvme-fc-driver.h>

#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
#include "lpfc.h"
#include "lpfc_scsi.h"
#include "lpfc_nvme.h"
#include "lpfc_nvmet.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
#include "lpfc_bsg.h"

#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/*
 * debugfs interface
 *
 * To access this interface the user should:
 * # mount -t debugfs none /sys/kernel/debug
 *
 * The lpfc debugfs directory hierarchy is:
 * /sys/kernel/debug/lpfc/fnX/vportY
 * where X is the lpfc hba function unique_id
 * where Y is the vport VPI on that hba
 *
 * Debugging services available per vport:
 * discovery_trace
 * This is an ACSII readable file that contains a trace of the last
 * lpfc_debugfs_max_disc_trc events that happened on a specific vport.
 * See lpfc_debugfs.h for different categories of  discovery events.
 * To enable the discovery trace, the following module parameters must be set:
 * lpfc_debugfs_enable=1         Turns on lpfc debugfs filesystem support
 * lpfc_debugfs_max_disc_trc=X   Where X is the event trace depth for
 *                               EACH vport. X MUST also be a power of 2.
 * lpfc_debugfs_mask_disc_trc=Y  Where Y is an event mask as defined in
 *                               lpfc_debugfs.h .
 *
 * slow_ring_trace
 * This is an ACSII readable file that contains a trace of the last
 * lpfc_debugfs_max_slow_ring_trc events that happened on a specific HBA.
 * To enable the slow ring trace, the following module parameters must be set:
 * lpfc_debugfs_enable=1         Turns on lpfc debugfs filesystem support
 * lpfc_debugfs_max_slow_ring_trc=X   Where X is the event trace depth for
 *                               the HBA. X MUST also be a power of 2.
 */
static int lpfc_debugfs_enable = 1;
module_param(lpfc_debugfs_enable, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_enable, "Enable debugfs services");

/* This MUST be a power of 2 */
static int lpfc_debugfs_max_disc_trc;
module_param(lpfc_debugfs_max_disc_trc, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_max_disc_trc,
	"Set debugfs discovery trace depth");

/* This MUST be a power of 2 */
static int lpfc_debugfs_max_slow_ring_trc;
module_param(lpfc_debugfs_max_slow_ring_trc, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_max_slow_ring_trc,
	"Set debugfs slow ring trace depth");

/* This MUST be a power of 2 */
static int lpfc_debugfs_max_nvmeio_trc;
module_param(lpfc_debugfs_max_nvmeio_trc, int, 0444);
MODULE_PARM_DESC(lpfc_debugfs_max_nvmeio_trc,
		 "Set debugfs NVME IO trace depth");

static int lpfc_debugfs_mask_disc_trc;
module_param(lpfc_debugfs_mask_disc_trc, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_debugfs_mask_disc_trc,
	"Set debugfs discovery trace mask");

#include <linux/debugfs.h>

static atomic_t lpfc_debugfs_seq_trc_cnt = ATOMIC_INIT(0);
static unsigned long lpfc_debugfs_start_time = 0L;

/* iDiag */
static struct lpfc_idiag idiag;

/**
 * lpfc_debugfs_disc_trc_data - Dump discovery logging to a buffer
 * @vport: The vport to gather the log info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine gathers the lpfc discovery debugfs data from the @vport and
 * dumps it to @buf up to @size number of bytes. It will start at the next entry
 * in the log and process the log until the end of the buffer. Then it will
 * gather from the beginning of the log and process until the current entry.
 *
 * Notes:
 * Discovery logging will be disabled while while this routine dumps the log.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
{
	int i, index, len, enable;
	uint32_t ms;
	struct lpfc_debugfs_trc *dtp;
	char *buffer;

	buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
	if (!buffer)
		return 0;

	enable = lpfc_debugfs_enable;
	lpfc_debugfs_enable = 0;

	len = 0;
	index = (atomic_read(&vport->disc_trc_cnt) + 1) &
		(lpfc_debugfs_max_disc_trc - 1);
	for (i = index; i < lpfc_debugfs_max_disc_trc; i++) {
		dtp = vport->disc_trc + i;
		if (!dtp->fmt)
			continue;
		ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
		snprintf(buffer,
			LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
			dtp->seq_cnt, ms, dtp->fmt);
		len +=  snprintf(buf+len, size-len, buffer,
			dtp->data1, dtp->data2, dtp->data3);
	}
	for (i = 0; i < index; i++) {
		dtp = vport->disc_trc + i;
		if (!dtp->fmt)
			continue;
		ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
		snprintf(buffer,
			LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
			dtp->seq_cnt, ms, dtp->fmt);
		len +=  snprintf(buf+len, size-len, buffer,
			dtp->data1, dtp->data2, dtp->data3);
	}

	lpfc_debugfs_enable = enable;
	kfree(buffer);

	return len;
}

/**
 * lpfc_debugfs_slow_ring_trc_data - Dump slow ring logging to a buffer
 * @phba: The HBA to gather the log info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine gathers the lpfc slow ring debugfs data from the @phba and
 * dumps it to @buf up to @size number of bytes. It will start at the next entry
 * in the log and process the log until the end of the buffer. Then it will
 * gather from the beginning of the log and process until the current entry.
 *
 * Notes:
 * Slow ring logging will be disabled while while this routine dumps the log.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
{
	int i, index, len, enable;
	uint32_t ms;
	struct lpfc_debugfs_trc *dtp;
	char *buffer;

	buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
	if (!buffer)
		return 0;

	enable = lpfc_debugfs_enable;
	lpfc_debugfs_enable = 0;

	len = 0;
	index = (atomic_read(&phba->slow_ring_trc_cnt) + 1) &
		(lpfc_debugfs_max_slow_ring_trc - 1);
	for (i = index; i < lpfc_debugfs_max_slow_ring_trc; i++) {
		dtp = phba->slow_ring_trc + i;
		if (!dtp->fmt)
			continue;
		ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
		snprintf(buffer,
			LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
			dtp->seq_cnt, ms, dtp->fmt);
		len +=  snprintf(buf+len, size-len, buffer,
			dtp->data1, dtp->data2, dtp->data3);
	}
	for (i = 0; i < index; i++) {
		dtp = phba->slow_ring_trc + i;
		if (!dtp->fmt)
			continue;
		ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time);
		snprintf(buffer,
			LPFC_DEBUG_TRC_ENTRY_SIZE, "%010d:%010d ms:%s\n",
			dtp->seq_cnt, ms, dtp->fmt);
		len +=  snprintf(buf+len, size-len, buffer,
			dtp->data1, dtp->data2, dtp->data3);
	}

	lpfc_debugfs_enable = enable;
	kfree(buffer);

	return len;
}

static int lpfc_debugfs_last_hbq = -1;

/**
 * lpfc_debugfs_hbqinfo_data - Dump host buffer queue info to a buffer
 * @phba: The HBA to gather host buffer info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the host buffer queue info from the @phba to @buf up to
 * @size number of bytes. A header that describes the current hbq state will be
 * dumped to @buf first and then info on each hbq entry will be dumped to @buf
 * until @size bytes have been dumped or all the hbq info has been dumped.
 *
 * Notes:
 * This routine will rotate through each configured HBQ each time called.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
{
	int len = 0;
	int i, j, found, posted, low;
	uint32_t phys, raw_index, getidx;
	struct lpfc_hbq_init *hip;
	struct hbq_s *hbqs;
	struct lpfc_hbq_entry *hbqe;
	struct lpfc_dmabuf *d_buf;
	struct hbq_dmabuf *hbq_buf;

	if (phba->sli_rev != 3)
		return 0;

	spin_lock_irq(&phba->hbalock);

	/* toggle between multiple hbqs, if any */
	i = lpfc_sli_hbq_count();
	if (i > 1) {
		 lpfc_debugfs_last_hbq++;
		 if (lpfc_debugfs_last_hbq >= i)
			lpfc_debugfs_last_hbq = 0;
	}
	else
		lpfc_debugfs_last_hbq = 0;

	i = lpfc_debugfs_last_hbq;

	len +=  snprintf(buf+len, size-len, "HBQ %d Info\n", i);

	hbqs =  &phba->hbqs[i];
	posted = 0;
	list_for_each_entry(d_buf, &hbqs->hbq_buffer_list, list)
		posted++;

	hip =  lpfc_hbq_defs[i];
	len +=  snprintf(buf+len, size-len,
		"idx:%d prof:%d rn:%d bufcnt:%d icnt:%d acnt:%d posted %d\n",
		hip->hbq_index, hip->profile, hip->rn,
		hip->buffer_count, hip->init_count, hip->add_count, posted);

	raw_index = phba->hbq_get[i];
	getidx = le32_to_cpu(raw_index);
	len +=  snprintf(buf+len, size-len,
		"entrys:%d bufcnt:%d Put:%d nPut:%d localGet:%d hbaGet:%d\n",
		hbqs->entry_count, hbqs->buffer_count, hbqs->hbqPutIdx,
		hbqs->next_hbqPutIdx, hbqs->local_hbqGetIdx, getidx);

	hbqe = (struct lpfc_hbq_entry *) phba->hbqs[i].hbq_virt;
	for (j=0; j<hbqs->entry_count; j++) {
		len +=  snprintf(buf+len, size-len,
			"%03d: %08x %04x %05x ", j,
			le32_to_cpu(hbqe->bde.addrLow),
			le32_to_cpu(hbqe->bde.tus.w),
			le32_to_cpu(hbqe->buffer_tag));
		i = 0;
		found = 0;

		/* First calculate if slot has an associated posted buffer */
		low = hbqs->hbqPutIdx - posted;
		if (low >= 0) {
			if ((j >= hbqs->hbqPutIdx) || (j < low)) {
				len +=  snprintf(buf+len, size-len, "Unused\n");
				goto skipit;
			}
		}
		else {
			if ((j >= hbqs->hbqPutIdx) &&
				(j < (hbqs->entry_count+low))) {
				len +=  snprintf(buf+len, size-len, "Unused\n");
				goto skipit;
			}
		}

		/* Get the Buffer info for the posted buffer */
		list_for_each_entry(d_buf, &hbqs->hbq_buffer_list, list) {
			hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
			phys = ((uint64_t)hbq_buf->dbuf.phys & 0xffffffff);
			if (phys == le32_to_cpu(hbqe->bde.addrLow)) {
				len +=  snprintf(buf+len, size-len,
					"Buf%d: %p %06x\n", i,
					hbq_buf->dbuf.virt, hbq_buf->tag);
				found = 1;
				break;
			}
			i++;
		}
		if (!found) {
			len +=  snprintf(buf+len, size-len, "No DMAinfo?\n");
		}
skipit:
		hbqe++;
		if (len > LPFC_HBQINFO_SIZE - 54)
			break;
	}
	spin_unlock_irq(&phba->hbalock);
	return len;
}

static int lpfc_debugfs_last_hba_slim_off;

/**
 * lpfc_debugfs_dumpHBASlim_data - Dump HBA SLIM info to a buffer
 * @phba: The HBA to gather SLIM info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the current contents of HBA SLIM for the HBA associated
 * with @phba to @buf up to @size bytes of data. This is the raw HBA SLIM data.
 *
 * Notes:
 * This routine will only dump up to 1024 bytes of data each time called and
 * should be called multiple times to dump the entire HBA SLIM.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
{
	int len = 0;
	int i, off;
	uint32_t *ptr;
	char *buffer;

	buffer = kmalloc(1024, GFP_KERNEL);
	if (!buffer)
		return 0;

	off = 0;
	spin_lock_irq(&phba->hbalock);

	len +=  snprintf(buf+len, size-len, "HBA SLIM\n");
	lpfc_memcpy_from_slim(buffer,
		phba->MBslimaddr + lpfc_debugfs_last_hba_slim_off, 1024);

	ptr = (uint32_t *)&buffer[0];
	off = lpfc_debugfs_last_hba_slim_off;

	/* Set it up for the next time */
	lpfc_debugfs_last_hba_slim_off += 1024;
	if (lpfc_debugfs_last_hba_slim_off >= 4096)
		lpfc_debugfs_last_hba_slim_off = 0;

	i = 1024;
	while (i > 0) {
		len +=  snprintf(buf+len, size-len,
		"%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
		off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
		*(ptr+5), *(ptr+6), *(ptr+7));
		ptr += 8;
		i -= (8 * sizeof(uint32_t));
		off += (8 * sizeof(uint32_t));
	}

	spin_unlock_irq(&phba->hbalock);
	kfree(buffer);

	return len;
}

/**
 * lpfc_debugfs_dumpHostSlim_data - Dump host SLIM info to a buffer
 * @phba: The HBA to gather Host SLIM info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the current contents of host SLIM for the host associated
 * with @phba to @buf up to @size bytes of data. The dump will contain the
 * Mailbox, PCB, Rings, and Registers that are located in host memory.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
{
	int len = 0;
	int i, off;
	uint32_t word0, word1, word2, word3;
	uint32_t *ptr;
	struct lpfc_pgp *pgpp;
	struct lpfc_sli *psli = &phba->sli;
	struct lpfc_sli_ring *pring;

	off = 0;
	spin_lock_irq(&phba->hbalock);

	len +=  snprintf(buf+len, size-len, "SLIM Mailbox\n");
	ptr = (uint32_t *)phba->slim2p.virt;
	i = sizeof(MAILBOX_t);
	while (i > 0) {
		len +=  snprintf(buf+len, size-len,
		"%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
		off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
		*(ptr+5), *(ptr+6), *(ptr+7));
		ptr += 8;
		i -= (8 * sizeof(uint32_t));
		off += (8 * sizeof(uint32_t));
	}

	len +=  snprintf(buf+len, size-len, "SLIM PCB\n");
	ptr = (uint32_t *)phba->pcb;
	i = sizeof(PCB_t);
	while (i > 0) {
		len +=  snprintf(buf+len, size-len,
		"%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
		off, *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4),
		*(ptr+5), *(ptr+6), *(ptr+7));
		ptr += 8;
		i -= (8 * sizeof(uint32_t));
		off += (8 * sizeof(uint32_t));
	}

	if (phba->sli_rev <= LPFC_SLI_REV3) {
		for (i = 0; i < 4; i++) {
			pgpp = &phba->port_gp[i];
			pring = &psli->sli3_ring[i];
			len +=  snprintf(buf+len, size-len,
					 "Ring %d: CMD GetInx:%d "
					 "(Max:%d Next:%d "
					 "Local:%d flg:x%x)  "
					 "RSP PutInx:%d Max:%d\n",
					 i, pgpp->cmdGetInx,
					 pring->sli.sli3.numCiocb,
					 pring->sli.sli3.next_cmdidx,
					 pring->sli.sli3.local_getidx,
					 pring->flag, pgpp->rspPutInx,
					 pring->sli.sli3.numRiocb);
		}

		word0 = readl(phba->HAregaddr);
		word1 = readl(phba->CAregaddr);
		word2 = readl(phba->HSregaddr);
		word3 = readl(phba->HCregaddr);
		len +=  snprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x "
				 "HC:%08x\n", word0, word1, word2, word3);
	}
	spin_unlock_irq(&phba->hbalock);
	return len;
}

/**
 * lpfc_debugfs_nodelist_data - Dump target node list to a buffer
 * @vport: The vport to gather target node info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the current target node list associated with @vport to
 * @buf up to @size bytes of data. Each node entry in the dump will contain a
 * node state, DID, WWPN, WWNN, RPI, flags, type, and other useful fields.
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size)
{
	int len = 0;
	int cnt;
	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
	struct lpfc_hba  *phba = vport->phba;
	struct lpfc_nodelist *ndlp;
	unsigned char *statep;
	struct nvme_fc_local_port *localport;
	struct lpfc_nvme_lport *lport;
	struct lpfc_nvme_rport *rport;
	struct lpfc_nvmet_tgtport *tgtp;
	struct nvme_fc_remote_port *nrport;

	cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE);

	len += snprintf(buf+len, size-len, "\nFCP Nodelist Entries ...\n");
	spin_lock_irq(shost->host_lock);
	list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
		if (!cnt) {
			len +=  snprintf(buf+len, size-len,
				"Missing Nodelist Entries\n");
			break;
		}
		cnt--;
		switch (ndlp->nlp_state) {
		case NLP_STE_UNUSED_NODE:
			statep = "UNUSED";
			break;
		case NLP_STE_PLOGI_ISSUE:
			statep = "PLOGI ";
			break;
		case NLP_STE_ADISC_ISSUE:
			statep = "ADISC ";
			break;
		case NLP_STE_REG_LOGIN_ISSUE:
			statep = "REGLOG";
			break;
		case NLP_STE_PRLI_ISSUE:
			statep = "PRLI  ";
			break;
		case NLP_STE_LOGO_ISSUE:
			statep = "LOGO  ";
			break;
		case NLP_STE_UNMAPPED_NODE:
			statep = "UNMAP ";
			break;
		case NLP_STE_MAPPED_NODE:
			statep = "MAPPED";
			break;
		case NLP_STE_NPR_NODE:
			statep = "NPR   ";
			break;
		default:
			statep = "UNKNOWN";
		}
		len += snprintf(buf+len, size-len, "%s DID:x%06x ",
				statep, ndlp->nlp_DID);
		len += snprintf(buf+len, size-len,
				"WWPN x%llx ",
				wwn_to_u64(ndlp->nlp_portname.u.wwn));
		len += snprintf(buf+len, size-len,
				"WWNN x%llx ",
				wwn_to_u64(ndlp->nlp_nodename.u.wwn));
		if (ndlp->nlp_flag & NLP_RPI_REGISTERED)
			len += snprintf(buf+len, size-len, "RPI:%03d ",
					ndlp->nlp_rpi);
		else
			len += snprintf(buf+len, size-len, "RPI:none ");
		len +=  snprintf(buf+len, size-len, "flag:x%08x ",
			ndlp->nlp_flag);
		if (!ndlp->nlp_type)
			len += snprintf(buf+len, size-len, "UNKNOWN_TYPE ");
		if (ndlp->nlp_type & NLP_FC_NODE)
			len += snprintf(buf+len, size-len, "FC_NODE ");
		if (ndlp->nlp_type & NLP_FABRIC)
			len += snprintf(buf+len, size-len, "FABRIC ");
		if (ndlp->nlp_type & NLP_FCP_TARGET)
			len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ",
				ndlp->nlp_sid);
		if (ndlp->nlp_type & NLP_FCP_INITIATOR)
			len += snprintf(buf+len, size-len, "FCP_INITIATOR ");
		len += snprintf(buf+len, size-len, "usgmap:%x ",
			ndlp->nlp_usg_map);
		len += snprintf(buf+len, size-len, "refcnt:%x",
			kref_read(&ndlp->kref));
		len +=  snprintf(buf+len, size-len, "\n");
	}
	spin_unlock_irq(shost->host_lock);

	if (phba->nvmet_support && phba->targetport && (vport == phba->pport)) {
		tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
		len += snprintf(buf + len, size - len,
				"\nNVME Targetport Entry ...\n");

		/* Port state is only one of two values for now. */
		if (phba->targetport->port_id)
			statep = "REGISTERED";
		else
			statep = "INIT";
		len += snprintf(buf + len, size - len,
				"TGT WWNN x%llx WWPN x%llx State %s\n",
				wwn_to_u64(vport->fc_nodename.u.wwn),
				wwn_to_u64(vport->fc_portname.u.wwn),
				statep);
		len += snprintf(buf + len, size - len,
				"    Targetport DID x%06x\n",
				phba->targetport->port_id);
		goto out_exit;
	}

	len += snprintf(buf + len, size - len,
				"\nNVME Lport/Rport Entries ...\n");

	localport = vport->localport;
	if (!localport)
		goto out_exit;

	spin_lock_irq(shost->host_lock);
	lport = (struct lpfc_nvme_lport *)localport->private;

	/* Port state is only one of two values for now. */
	if (localport->port_id)
		statep = "ONLINE";
	else
		statep = "UNKNOWN ";

	len += snprintf(buf + len, size - len,
			"Lport DID x%06x PortState %s\n",
			localport->port_id, statep);

	len += snprintf(buf + len, size - len, "\tRport List:\n");
	list_for_each_entry(rport, &lport->rport_list, list) {
		/* local short-hand pointer. */
		nrport = rport->remoteport;

		/* Port state is only one of two values for now. */
		switch (nrport->port_state) {
		case FC_OBJSTATE_ONLINE:
			statep = "ONLINE";
			break;
		case FC_OBJSTATE_UNKNOWN:
			statep = "UNKNOWN ";
			break;
		default:
			statep = "UNSUPPORTED";
			break;
		}

		/* Tab in to show lport ownership. */
		len += snprintf(buf + len, size - len,
				"\t%s Port ID:x%06x ",
				statep, nrport->port_id);
		len += snprintf(buf + len, size - len, "WWPN x%llx ",
				nrport->port_name);
		len += snprintf(buf + len, size - len, "WWNN x%llx ",
				nrport->node_name);
		switch (nrport->port_role) {
		case FC_PORT_ROLE_NVME_INITIATOR:
			len +=  snprintf(buf + len, size - len,
					 "NVME INITIATOR ");
			break;
		case FC_PORT_ROLE_NVME_TARGET:
			len +=  snprintf(buf + len, size - len,
					 "NVME TARGET ");
			break;
		case FC_PORT_ROLE_NVME_DISCOVERY:
			len +=  snprintf(buf + len, size - len,
					 "NVME DISCOVERY ");
			break;
		default:
			len +=  snprintf(buf + len, size - len,
					 "UNKNOWN ROLE x%x",
					 nrport->port_role);
			break;
		}

		/* Terminate the string. */
		len +=  snprintf(buf + len, size - len, "\n");
	}

	spin_unlock_irq(shost->host_lock);
 out_exit:
	return len;
}

/**
 * lpfc_debugfs_nvmestat_data - Dump target node list to a buffer
 * @vport: The vport to gather target node info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the NVME statistics associated with @vport
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
{
	struct lpfc_hba   *phba = vport->phba;
	struct lpfc_nvmet_tgtport *tgtp;
	struct lpfc_nvmet_rcv_ctx *ctxp, *next_ctxp;
	int len = 0;
	int cnt;

	if (phba->nvmet_support) {
		if (!phba->targetport)
			return len;
		tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
		len += snprintf(buf + len, size - len,
				"\nNVME Targetport Statistics\n");

		len += snprintf(buf + len, size - len,
				"LS: Rcv %08x Drop %08x Abort %08x\n",
				atomic_read(&tgtp->rcv_ls_req_in),
				atomic_read(&tgtp->rcv_ls_req_drop),
				atomic_read(&tgtp->xmt_ls_abort));
		if (atomic_read(&tgtp->rcv_ls_req_in) !=
		    atomic_read(&tgtp->rcv_ls_req_out)) {
			len += snprintf(buf + len, size - len,
					"Rcv LS: in %08x != out %08x\n",
					atomic_read(&tgtp->rcv_ls_req_in),
					atomic_read(&tgtp->rcv_ls_req_out));
		}

		len += snprintf(buf + len, size - len,
				"LS: Xmt %08x Drop %08x Cmpl %08x Err %08x\n",
				atomic_read(&tgtp->xmt_ls_rsp),
				atomic_read(&tgtp->xmt_ls_drop),
				atomic_read(&tgtp->xmt_ls_rsp_cmpl),
				atomic_read(&tgtp->xmt_ls_rsp_error));

		len += snprintf(buf + len, size - len,
				"FCP: Rcv %08x Drop %08x\n",
				atomic_read(&tgtp->rcv_fcp_cmd_in),
				atomic_read(&tgtp->rcv_fcp_cmd_drop));

		if (atomic_read(&tgtp->rcv_fcp_cmd_in) !=
		    atomic_read(&tgtp->rcv_fcp_cmd_out)) {
			len += snprintf(buf + len, size - len,
					"Rcv FCP: in %08x != out %08x\n",
					atomic_read(&tgtp->rcv_fcp_cmd_in),
					atomic_read(&tgtp->rcv_fcp_cmd_out));
		}

		len += snprintf(buf + len, size - len,
				"FCP Rsp: read %08x readrsp %08x "
				"write %08x rsp %08x\n",
				atomic_read(&tgtp->xmt_fcp_read),
				atomic_read(&tgtp->xmt_fcp_read_rsp),
				atomic_read(&tgtp->xmt_fcp_write),
				atomic_read(&tgtp->xmt_fcp_rsp));

		len += snprintf(buf + len, size - len,
				"FCP Rsp Cmpl: %08x err %08x drop %08x\n",
				atomic_read(&tgtp->xmt_fcp_rsp_cmpl),
				atomic_read(&tgtp->xmt_fcp_rsp_error),
				atomic_read(&tgtp->xmt_fcp_rsp_drop));

		len += snprintf(buf + len, size - len,
				"ABORT: Xmt %08x Cmpl %08x\n",
				atomic_read(&tgtp->xmt_fcp_abort),
				atomic_read(&tgtp->xmt_fcp_abort_cmpl));

		len += snprintf(buf + len, size - len,
				"ABORT: Sol %08x  Usol %08x Err %08x Cmpl %08x",
				atomic_read(&tgtp->xmt_abort_sol),
				atomic_read(&tgtp->xmt_abort_unsol),
				atomic_read(&tgtp->xmt_abort_rsp),
				atomic_read(&tgtp->xmt_abort_rsp_error));

		len +=  snprintf(buf + len, size - len, "\n");

		cnt = 0;
		spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
		list_for_each_entry_safe(ctxp, next_ctxp,
				&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
				list) {
			cnt++;
		}
		spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
		if (cnt) {
			len += snprintf(buf + len, size - len,
					"ABORT: %d ctx entries\n", cnt);
			spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock);
			list_for_each_entry_safe(ctxp, next_ctxp,
				    &phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
				    list) {
				if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ))
					break;
				len += snprintf(buf + len, size - len,
						"Entry: oxid %x state %x "
						"flag %x\n",
						ctxp->oxid, ctxp->state,
						ctxp->flag);
			}
			spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock);
		}
	} else {
		if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME))
			return len;

		len += snprintf(buf + len, size - len,
				"\nNVME Lport Statistics\n");

		len += snprintf(buf + len, size - len,
				"LS: Xmt %016llx Cmpl %016llx\n",
				phba->fc4NvmeLsRequests,
				phba->fc4NvmeLsCmpls);

		len += snprintf(buf + len, size - len,
				"FCP: Rd %016llx Wr %016llx IO %016llx\n",
				phba->fc4NvmeInputRequests,
				phba->fc4NvmeOutputRequests,
				phba->fc4NvmeControlRequests);

		len += snprintf(buf + len, size - len,
				"    Cmpl %016llx\n", phba->fc4NvmeIoCmpls);
	}

	return len;
}


/**
 * lpfc_debugfs_nvmektime_data - Dump target node list to a buffer
 * @vport: The vport to gather target node info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the NVME statistics associated with @vport
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_nvmektime_data(struct lpfc_vport *vport, char *buf, int size)
{
	struct lpfc_hba   *phba = vport->phba;
	int len = 0;

	if (phba->nvmet_support == 0) {
		/* NVME Initiator */
		len += snprintf(buf + len, PAGE_SIZE - len,
				"ktime %s: Total Samples: %lld\n",
				(phba->ktime_on ?  "Enabled" : "Disabled"),
				phba->ktime_data_samples);
		if (phba->ktime_data_samples == 0)
			return len;

		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"Segment 1: Last NVME Cmd cmpl "
			"done -to- Start of next NVME cnd (in driver)\n");
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg1_total,
				phba->ktime_data_samples),
			phba->ktime_seg1_min,
			phba->ktime_seg1_max);
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"Segment 2: Driver start of NVME cmd "
			"-to- Firmware WQ doorbell\n");
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg2_total,
				phba->ktime_data_samples),
			phba->ktime_seg2_min,
			phba->ktime_seg2_max);
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"Segment 3: Firmware WQ doorbell -to- "
			"MSI-X ISR cmpl\n");
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg3_total,
				phba->ktime_data_samples),
			phba->ktime_seg3_min,
			phba->ktime_seg3_max);
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"Segment 4: MSI-X ISR cmpl -to- "
			"NVME cmpl done\n");
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg4_total,
				phba->ktime_data_samples),
			phba->ktime_seg4_min,
			phba->ktime_seg4_max);
		len += snprintf(
			buf + len, PAGE_SIZE - len,
			"Total IO avg time: %08lld\n",
			div_u64(phba->ktime_seg1_total +
			phba->ktime_seg2_total  +
			phba->ktime_seg3_total +
			phba->ktime_seg4_total,
			phba->ktime_data_samples));
		return len;
	}

	/* NVME Target */
	len += snprintf(buf + len, PAGE_SIZE-len,
			"ktime %s: Total Samples: %lld %lld\n",
			(phba->ktime_on ? "Enabled" : "Disabled"),
			phba->ktime_data_samples,
			phba->ktime_status_samples);
	if (phba->ktime_data_samples == 0)
		return len;

	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 1: MSI-X ISR Rcv cmd -to- "
			"cmd pass to NVME Layer\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg1_total,
				phba->ktime_data_samples),
			phba->ktime_seg1_min,
			phba->ktime_seg1_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 2: cmd pass to NVME Layer- "
			"-to- Driver rcv cmd OP (action)\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg2_total,
				phba->ktime_data_samples),
			phba->ktime_seg2_min,
			phba->ktime_seg2_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 3: Driver rcv cmd OP -to- "
			"Firmware WQ doorbell: cmd\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg3_total,
				phba->ktime_data_samples),
			phba->ktime_seg3_min,
			phba->ktime_seg3_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 4: Firmware WQ doorbell: cmd "
			"-to- MSI-X ISR for cmd cmpl\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg4_total,
				phba->ktime_data_samples),
			phba->ktime_seg4_min,
			phba->ktime_seg4_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 5: MSI-X ISR for cmd cmpl "
			"-to- NVME layer passed cmd done\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg5_total,
				phba->ktime_data_samples),
			phba->ktime_seg5_min,
			phba->ktime_seg5_max);

	if (phba->ktime_status_samples == 0) {
		len += snprintf(buf + len, PAGE_SIZE-len,
				"Total: cmd received by MSI-X ISR "
				"-to- cmd completed on wire\n");
		len += snprintf(buf + len, PAGE_SIZE-len,
				"avg:%08lld min:%08lld "
				"max %08lld\n",
				div_u64(phba->ktime_seg10_total,
					phba->ktime_data_samples),
				phba->ktime_seg10_min,
				phba->ktime_seg10_max);
		return len;
	}

	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 6: NVME layer passed cmd done "
			"-to- Driver rcv rsp status OP\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg6_total,
				phba->ktime_status_samples),
			phba->ktime_seg6_min,
			phba->ktime_seg6_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 7: Driver rcv rsp status OP "
			"-to- Firmware WQ doorbell: status\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg7_total,
				phba->ktime_status_samples),
			phba->ktime_seg7_min,
			phba->ktime_seg7_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 8: Firmware WQ doorbell: status"
			" -to- MSI-X ISR for status cmpl\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg8_total,
				phba->ktime_status_samples),
			phba->ktime_seg8_min,
			phba->ktime_seg8_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Segment 9: MSI-X ISR for status cmpl  "
			"-to- NVME layer passed status done\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg9_total,
				phba->ktime_status_samples),
			phba->ktime_seg9_min,
			phba->ktime_seg9_max);
	len += snprintf(buf + len, PAGE_SIZE-len,
			"Total: cmd received by MSI-X ISR -to- "
			"cmd completed on wire\n");
	len += snprintf(buf + len, PAGE_SIZE-len,
			"avg:%08lld min:%08lld max %08lld\n",
			div_u64(phba->ktime_seg10_total,
				phba->ktime_status_samples),
			phba->ktime_seg10_min,
			phba->ktime_seg10_max);
	return len;
}

/**
 * lpfc_debugfs_nvmeio_trc_data - Dump NVME IO trace list to a buffer
 * @phba: The phba to gather target node info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the NVME IO trace associated with @phba
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_nvmeio_trc_data(struct lpfc_hba *phba, char *buf, int size)
{
	struct lpfc_debugfs_nvmeio_trc *dtp;
	int i, state, index, skip;
	int len = 0;

	state = phba->nvmeio_trc_on;

	index = (atomic_read(&phba->nvmeio_trc_cnt) + 1) &
		(phba->nvmeio_trc_size - 1);
	skip = phba->nvmeio_trc_output_idx;

	len += snprintf(buf + len, size - len,
			"%s IO Trace %s: next_idx %d skip %d size %d\n",
			(phba->nvmet_support ? "NVME" : "NVMET"),
			(state ? "Enabled" : "Disabled"),
			index, skip, phba->nvmeio_trc_size);

	if (!phba->nvmeio_trc || state)
		return len;

	/* trace MUST bhe off to continue */

	for (i = index; i < phba->nvmeio_trc_size; i++) {
		if (skip) {
			skip--;
			continue;
		}
		dtp = phba->nvmeio_trc + i;
		phba->nvmeio_trc_output_idx++;

		if (!dtp->fmt)
			continue;

		len +=  snprintf(buf + len, size - len, dtp->fmt,
			dtp->data1, dtp->data2, dtp->data3);

		if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) {
			phba->nvmeio_trc_output_idx = 0;
			len += snprintf(buf + len, size - len,
					"Trace Complete\n");
			goto out;
		}

		if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
			len += snprintf(buf + len, size - len,
					"Trace Continue (%d of %d)\n",
					phba->nvmeio_trc_output_idx,
					phba->nvmeio_trc_size);
			goto out;
		}
	}
	for (i = 0; i < index; i++) {
		if (skip) {
			skip--;
			continue;
		}
		dtp = phba->nvmeio_trc + i;
		phba->nvmeio_trc_output_idx++;

		if (!dtp->fmt)
			continue;

		len +=  snprintf(buf + len, size - len, dtp->fmt,
			dtp->data1, dtp->data2, dtp->data3);

		if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) {
			phba->nvmeio_trc_output_idx = 0;
			len += snprintf(buf + len, size - len,
					"Trace Complete\n");
			goto out;
		}

		if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) {
			len += snprintf(buf + len, size - len,
					"Trace Continue (%d of %d)\n",
					phba->nvmeio_trc_output_idx,
					phba->nvmeio_trc_size);
			goto out;
		}
	}

	len += snprintf(buf + len, size - len,
			"Trace Done\n");
out:
	return len;
}

/**
 * lpfc_debugfs_cpucheck_data - Dump target node list to a buffer
 * @vport: The vport to gather target node info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the NVME statistics associated with @vport
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size)
{
	struct lpfc_hba   *phba = vport->phba;
	int i;
	int len = 0;
	uint32_t tot_xmt = 0;
	uint32_t tot_rcv = 0;
	uint32_t tot_cmpl = 0;
	uint32_t tot_ccmpl = 0;

	if (phba->nvmet_support == 0) {
		/* NVME Initiator */
		len += snprintf(buf + len, PAGE_SIZE - len,
				"CPUcheck %s\n",
				(phba->cpucheck_on & LPFC_CHECK_NVME_IO ?
					"Enabled" : "Disabled"));
		for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
			if (i >= LPFC_CHECK_CPU_CNT)
				break;
			len += snprintf(buf + len, PAGE_SIZE - len,
					"%02d: xmit x%08x cmpl x%08x\n",
					i, phba->cpucheck_xmt_io[i],
					phba->cpucheck_cmpl_io[i]);
			tot_xmt += phba->cpucheck_xmt_io[i];
			tot_cmpl += phba->cpucheck_cmpl_io[i];
		}
		len += snprintf(buf + len, PAGE_SIZE - len,
				"tot:xmit x%08x cmpl x%08x\n",
				tot_xmt, tot_cmpl);
		return len;
	}

	/* NVME Target */
	len += snprintf(buf + len, PAGE_SIZE - len,
			"CPUcheck %s ",
			(phba->cpucheck_on & LPFC_CHECK_NVMET_IO ?
				"IO Enabled - " : "IO Disabled - "));
	len += snprintf(buf + len, PAGE_SIZE - len,
			"%s\n",
			(phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ?
				"Rcv Enabled\n" : "Rcv Disabled\n"));
	for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
		if (i >= LPFC_CHECK_CPU_CNT)
			break;
		len += snprintf(buf + len, PAGE_SIZE - len,
				"%02d: xmit x%08x ccmpl x%08x "
				"cmpl x%08x rcv x%08x\n",
				i, phba->cpucheck_xmt_io[i],
				phba->cpucheck_ccmpl_io[i],
				phba->cpucheck_cmpl_io[i],
				phba->cpucheck_rcv_io[i]);
		tot_xmt += phba->cpucheck_xmt_io[i];
		tot_rcv += phba->cpucheck_rcv_io[i];
		tot_cmpl += phba->cpucheck_cmpl_io[i];
		tot_ccmpl += phba->cpucheck_ccmpl_io[i];
	}
	len += snprintf(buf + len, PAGE_SIZE - len,
			"tot:xmit x%08x ccmpl x%08x cmpl x%08x rcv x%08x\n",
			tot_xmt, tot_ccmpl, tot_cmpl, tot_rcv);
	return len;
}

#endif

/**
 * lpfc_debugfs_disc_trc - Store discovery trace log
 * @vport: The vport to associate this trace string with for retrieval.
 * @mask: Log entry classification.
 * @fmt: Format string to be displayed when dumping the log.
 * @data1: 1st data parameter to be applied to @fmt.
 * @data2: 2nd data parameter to be applied to @fmt.
 * @data3: 3rd data parameter to be applied to @fmt.
 *
 * Description:
 * This routine is used by the driver code to add a debugfs log entry to the
 * discovery trace buffer associated with @vport. Only entries with a @mask that
 * match the current debugfs discovery mask will be saved. Entries that do not
 * match will be thrown away. @fmt, @data1, @data2, and @data3 are used like
 * printf when displaying the log.
 **/
inline void
lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt,
	uint32_t data1, uint32_t data2, uint32_t data3)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	struct lpfc_debugfs_trc *dtp;
	int index;

	if (!(lpfc_debugfs_mask_disc_trc & mask))
		return;

	if (!lpfc_debugfs_enable || !lpfc_debugfs_max_disc_trc ||
		!vport || !vport->disc_trc)
		return;

	index = atomic_inc_return(&vport->disc_trc_cnt) &
		(lpfc_debugfs_max_disc_trc - 1);
	dtp = vport->disc_trc + index;
	dtp->fmt = fmt;
	dtp->data1 = data1;
	dtp->data2 = data2;
	dtp->data3 = data3;
	dtp->seq_cnt = atomic_inc_return(&lpfc_debugfs_seq_trc_cnt);
	dtp->jif = jiffies;
#endif
	return;
}

/**
 * lpfc_debugfs_slow_ring_trc - Store slow ring trace log
 * @phba: The phba to associate this trace string with for retrieval.
 * @fmt: Format string to be displayed when dumping the log.
 * @data1: 1st data parameter to be applied to @fmt.
 * @data2: 2nd data parameter to be applied to @fmt.
 * @data3: 3rd data parameter to be applied to @fmt.
 *
 * Description:
 * This routine is used by the driver code to add a debugfs log entry to the
 * discovery trace buffer associated with @vport. @fmt, @data1, @data2, and
 * @data3 are used like printf when displaying the log.
 **/
inline void
lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
	uint32_t data1, uint32_t data2, uint32_t data3)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	struct lpfc_debugfs_trc *dtp;
	int index;

	if (!lpfc_debugfs_enable || !lpfc_debugfs_max_slow_ring_trc ||
		!phba || !phba->slow_ring_trc)
		return;

	index = atomic_inc_return(&phba->slow_ring_trc_cnt) &
		(lpfc_debugfs_max_slow_ring_trc - 1);
	dtp = phba->slow_ring_trc + index;
	dtp->fmt = fmt;
	dtp->data1 = data1;
	dtp->data2 = data2;
	dtp->data3 = data3;
	dtp->seq_cnt = atomic_inc_return(&lpfc_debugfs_seq_trc_cnt);
	dtp->jif = jiffies;
#endif
	return;
}

/**
 * lpfc_debugfs_nvme_trc - Store NVME/NVMET trace log
 * @phba: The phba to associate this trace string with for retrieval.
 * @fmt: Format string to be displayed when dumping the log.
 * @data1: 1st data parameter to be applied to @fmt.
 * @data2: 2nd data parameter to be applied to @fmt.
 * @data3: 3rd data parameter to be applied to @fmt.
 *
 * Description:
 * This routine is used by the driver code to add a debugfs log entry to the
 * nvme trace buffer associated with @phba. @fmt, @data1, @data2, and
 * @data3 are used like printf when displaying the log.
 **/
inline void
lpfc_debugfs_nvme_trc(struct lpfc_hba *phba, char *fmt,
		      uint16_t data1, uint16_t data2, uint32_t data3)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	struct lpfc_debugfs_nvmeio_trc *dtp;
	int index;

	if (!phba->nvmeio_trc_on || !phba->nvmeio_trc)
		return;

	index = atomic_inc_return(&phba->nvmeio_trc_cnt) &
		(phba->nvmeio_trc_size - 1);
	dtp = phba->nvmeio_trc + index;
	dtp->fmt = fmt;
	dtp->data1 = data1;
	dtp->data2 = data2;
	dtp->data3 = data3;
#endif
}

#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
 * lpfc_debugfs_disc_trc_open - Open the discovery trace log
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_disc_trc_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
	int size;
	int rc = -ENOMEM;

	if (!lpfc_debugfs_max_disc_trc) {
		 rc = -ENOSPC;
		goto out;
	}

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	size =  (lpfc_debugfs_max_disc_trc * LPFC_DEBUG_TRC_ENTRY_SIZE);
	size = PAGE_ALIGN(size);

	debug->buffer = kmalloc(size, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_disc_trc_data(vport, debug->buffer, size);
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

/**
 * lpfc_debugfs_slow_ring_trc_open - Open the Slow Ring trace log
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_slow_ring_trc_open(struct inode *inode, struct file *file)
{
	struct lpfc_hba *phba = inode->i_private;
	struct lpfc_debug *debug;
	int size;
	int rc = -ENOMEM;

	if (!lpfc_debugfs_max_slow_ring_trc) {
		 rc = -ENOSPC;
		goto out;
	}

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	size =  (lpfc_debugfs_max_slow_ring_trc * LPFC_DEBUG_TRC_ENTRY_SIZE);
	size = PAGE_ALIGN(size);

	debug->buffer = kmalloc(size, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_slow_ring_trc_data(phba, debug->buffer, size);
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

/**
 * lpfc_debugfs_hbqinfo_open - Open the hbqinfo debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_hbqinfo_open(struct inode *inode, struct file *file)
{
	struct lpfc_hba *phba = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	debug->buffer = kmalloc(LPFC_HBQINFO_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_hbqinfo_data(phba, debug->buffer,
		LPFC_HBQINFO_SIZE);
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

/**
 * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_dumpHBASlim_open(struct inode *inode, struct file *file)
{
	struct lpfc_hba *phba = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	debug->buffer = kmalloc(LPFC_DUMPHBASLIM_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_dumpHBASlim_data(phba, debug->buffer,
		LPFC_DUMPHBASLIM_SIZE);
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

/**
 * lpfc_debugfs_dumpHostSlim_open - Open the Dump Host SLIM debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_dumpHostSlim_open(struct inode *inode, struct file *file)
{
	struct lpfc_hba *phba = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	debug->buffer = kmalloc(LPFC_DUMPHOSTSLIM_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_dumpHostSlim_data(phba, debug->buffer,
		LPFC_DUMPHOSTSLIM_SIZE);
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static int
lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	if (!_dump_buf_data)
		return -EBUSY;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	pr_err("9059 BLKGRD:  %s: _dump_buf_data=0x%p\n",
			__func__, _dump_buf_data);
	debug->buffer = _dump_buf_data;
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = (1 << _dump_buf_data_order) << PAGE_SHIFT;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static int
lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	if (!_dump_buf_dif)
		return -EBUSY;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	pr_err("9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%pD\n",
			__func__, _dump_buf_dif, file);
	debug->buffer = _dump_buf_dif;
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = (1 << _dump_buf_dif_order) << PAGE_SHIFT;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static ssize_t
lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
		  size_t nbytes, loff_t *ppos)
{
	/*
	 * The Data/DIF buffers only save one failing IO
	 * The write op is used as a reset mechanism after an IO has
	 * already been saved to the next one can be saved
	 */
	spin_lock(&_dump_buf_lock);

	memset((void *)_dump_buf_data, 0,
			((1 << PAGE_SHIFT) << _dump_buf_data_order));
	memset((void *)_dump_buf_dif, 0,
			((1 << PAGE_SHIFT) << _dump_buf_dif_order));

	_dump_buf_done = 0;

	spin_unlock(&_dump_buf_lock);

	return nbytes;
}

static ssize_t
lpfc_debugfs_dif_err_read(struct file *file, char __user *buf,
	size_t nbytes, loff_t *ppos)
{
	struct dentry *dent = file->f_path.dentry;
	struct lpfc_hba *phba = file->private_data;
	char cbuf[32];
	uint64_t tmp = 0;
	int cnt = 0;

	if (dent == phba->debug_writeGuard)
		cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wgrd_cnt);
	else if (dent == phba->debug_writeApp)
		cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wapp_cnt);
	else if (dent == phba->debug_writeRef)
		cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wref_cnt);
	else if (dent == phba->debug_readGuard)
		cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rgrd_cnt);
	else if (dent == phba->debug_readApp)
		cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rapp_cnt);
	else if (dent == phba->debug_readRef)
		cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rref_cnt);
	else if (dent == phba->debug_InjErrNPortID)
		cnt = snprintf(cbuf, 32, "0x%06x\n", phba->lpfc_injerr_nportid);
	else if (dent == phba->debug_InjErrWWPN) {
		memcpy(&tmp, &phba->lpfc_injerr_wwpn, sizeof(struct lpfc_name));
		tmp = cpu_to_be64(tmp);
		cnt = snprintf(cbuf, 32, "0x%016llx\n", tmp);
	} else if (dent == phba->debug_InjErrLBA) {
		if (phba->lpfc_injerr_lba == (sector_t)(-1))
			cnt = snprintf(cbuf, 32, "off\n");
		else
			cnt = snprintf(cbuf, 32, "0x%llx\n",
				 (uint64_t) phba->lpfc_injerr_lba);
	} else
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
			 "0547 Unknown debugfs error injection entry\n");

	return simple_read_from_buffer(buf, nbytes, ppos, &cbuf, cnt);
}

static ssize_t
lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
	size_t nbytes, loff_t *ppos)
{
	struct dentry *dent = file->f_path.dentry;
	struct lpfc_hba *phba = file->private_data;
	char dstbuf[33];
	uint64_t tmp = 0;
	int size;

	memset(dstbuf, 0, 33);
	size = (nbytes < 32) ? nbytes : 32;
	if (copy_from_user(dstbuf, buf, size))
		return 0;

	if (dent == phba->debug_InjErrLBA) {
		if ((buf[0] == 'o') && (buf[1] == 'f') && (buf[2] == 'f'))
			tmp = (uint64_t)(-1);
	}

	if ((tmp == 0) && (kstrtoull(dstbuf, 0, &tmp)))
		return 0;

	if (dent == phba->debug_writeGuard)
		phba->lpfc_injerr_wgrd_cnt = (uint32_t)tmp;
	else if (dent == phba->debug_writeApp)
		phba->lpfc_injerr_wapp_cnt = (uint32_t)tmp;
	else if (dent == phba->debug_writeRef)
		phba->lpfc_injerr_wref_cnt = (uint32_t)tmp;
	else if (dent == phba->debug_readGuard)
		phba->lpfc_injerr_rgrd_cnt = (uint32_t)tmp;
	else if (dent == phba->debug_readApp)
		phba->lpfc_injerr_rapp_cnt = (uint32_t)tmp;
	else if (dent == phba->debug_readRef)
		phba->lpfc_injerr_rref_cnt = (uint32_t)tmp;
	else if (dent == phba->debug_InjErrLBA)
		phba->lpfc_injerr_lba = (sector_t)tmp;
	else if (dent == phba->debug_InjErrNPortID)
		phba->lpfc_injerr_nportid = (uint32_t)(tmp & Mask_DID);
	else if (dent == phba->debug_InjErrWWPN) {
		tmp = cpu_to_be64(tmp);
		memcpy(&phba->lpfc_injerr_wwpn, &tmp, sizeof(struct lpfc_name));
	} else
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
			 "0548 Unknown debugfs error injection entry\n");

	return nbytes;
}

static int
lpfc_debugfs_dif_err_release(struct inode *inode, struct file *file)
{
	return 0;
}

/**
 * lpfc_debugfs_nodelist_open - Open the nodelist debugfs file
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_nodelist_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	/* Round to page boundary */
	debug->buffer = kmalloc(LPFC_NODELIST_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_nodelist_data(vport, debug->buffer,
		LPFC_NODELIST_SIZE);
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

/**
 * lpfc_debugfs_lseek - Seek through a debugfs file
 * @file: The file pointer to seek through.
 * @off: The offset to seek to or the amount to seek by.
 * @whence: Indicates how to seek.
 *
 * Description:
 * This routine is the entry point for the debugfs lseek file operation. The
 * @whence parameter indicates whether @off is the offset to directly seek to,
 * or if it is a value to seek forward or reverse by. This function figures out
 * what the new offset of the debugfs file will be and assigns that value to the
 * f_pos field of @file.
 *
 * Returns:
 * This function returns the new offset if successful and returns a negative
 * error if unable to process the seek.
 **/
static loff_t
lpfc_debugfs_lseek(struct file *file, loff_t off, int whence)
{
	struct lpfc_debug *debug = file->private_data;
	return fixed_size_llseek(file, off, whence, debug->len);
}

/**
 * lpfc_debugfs_read - Read a debugfs file
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from from the buffer indicated in the private_data
 * field of @file. It will start reading at @ppos and copy up to @nbytes of
 * data to @buf.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_debugfs_read(struct file *file, char __user *buf,
		  size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;

	return simple_read_from_buffer(buf, nbytes, ppos, debug->buffer,
				       debug->len);
}

/**
 * lpfc_debugfs_release - Release the buffer used to store debugfs file data
 * @inode: The inode pointer that contains a vport pointer. (unused)
 * @file: The file pointer that contains the buffer to release.
 *
 * Description:
 * This routine frees the buffer that was allocated when the debugfs file was
 * opened.
 *
 * Returns:
 * This function returns zero.
 **/
static int
lpfc_debugfs_release(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug = file->private_data;

	kfree(debug->buffer);
	kfree(debug);

	return 0;
}

static int
lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug = file->private_data;

	debug->buffer = NULL;
	kfree(debug);

	return 0;
}


static int
lpfc_debugfs_nvmestat_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	 /* Round to page boundary */
	debug->buffer = kmalloc(LPFC_NVMESTAT_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_nvmestat_data(vport, debug->buffer,
		LPFC_NVMESTAT_SIZE);

	debug->i_private = inode->i_private;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static ssize_t
lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf,
			    size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
	struct lpfc_hba   *phba = vport->phba;
	struct lpfc_nvmet_tgtport *tgtp;
	char mybuf[64];
	char *pbuf;

	if (!phba->targetport)
		return -ENXIO;

	if (nbytes > 64)
		nbytes = 64;

	/* Protect copy from user */
	if (!access_ok(VERIFY_READ, buf, nbytes))
		return -EFAULT;

	memset(mybuf, 0, sizeof(mybuf));

	if (copy_from_user(mybuf, buf, nbytes))
		return -EFAULT;
	pbuf = &mybuf[0];

	tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
	if ((strncmp(pbuf, "reset", strlen("reset")) == 0) ||
	    (strncmp(pbuf, "zero", strlen("zero")) == 0)) {
		atomic_set(&tgtp->rcv_ls_req_in, 0);
		atomic_set(&tgtp->rcv_ls_req_out, 0);
		atomic_set(&tgtp->rcv_ls_req_drop, 0);
		atomic_set(&tgtp->xmt_ls_abort, 0);
		atomic_set(&tgtp->xmt_ls_abort_cmpl, 0);
		atomic_set(&tgtp->xmt_ls_rsp, 0);
		atomic_set(&tgtp->xmt_ls_drop, 0);
		atomic_set(&tgtp->xmt_ls_rsp_error, 0);
		atomic_set(&tgtp->xmt_ls_rsp_cmpl, 0);

		atomic_set(&tgtp->rcv_fcp_cmd_in, 0);
		atomic_set(&tgtp->rcv_fcp_cmd_out, 0);
		atomic_set(&tgtp->rcv_fcp_cmd_drop, 0);
		atomic_set(&tgtp->xmt_fcp_drop, 0);
		atomic_set(&tgtp->xmt_fcp_read_rsp, 0);
		atomic_set(&tgtp->xmt_fcp_read, 0);
		atomic_set(&tgtp->xmt_fcp_write, 0);
		atomic_set(&tgtp->xmt_fcp_rsp, 0);
		atomic_set(&tgtp->xmt_fcp_release, 0);
		atomic_set(&tgtp->xmt_fcp_rsp_cmpl, 0);
		atomic_set(&tgtp->xmt_fcp_rsp_error, 0);
		atomic_set(&tgtp->xmt_fcp_rsp_drop, 0);

		atomic_set(&tgtp->xmt_fcp_abort, 0);
		atomic_set(&tgtp->xmt_fcp_abort_cmpl, 0);
		atomic_set(&tgtp->xmt_abort_sol, 0);
		atomic_set(&tgtp->xmt_abort_unsol, 0);
		atomic_set(&tgtp->xmt_abort_rsp, 0);
		atomic_set(&tgtp->xmt_abort_rsp_error, 0);
	}
	return nbytes;
}

static int
lpfc_debugfs_nvmektime_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	 /* Round to page boundary */
	debug->buffer = kmalloc(LPFC_NVMEKTIME_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_nvmektime_data(vport, debug->buffer,
		LPFC_NVMEKTIME_SIZE);

	debug->i_private = inode->i_private;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static ssize_t
lpfc_debugfs_nvmektime_write(struct file *file, const char __user *buf,
			     size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
	struct lpfc_hba   *phba = vport->phba;
	char mybuf[64];
	char *pbuf;

	if (nbytes > 64)
		nbytes = 64;

	/* Protect copy from user */
	if (!access_ok(VERIFY_READ, buf, nbytes))
		return -EFAULT;

	memset(mybuf, 0, sizeof(mybuf));

	if (copy_from_user(mybuf, buf, nbytes))
		return -EFAULT;
	pbuf = &mybuf[0];

	if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
		phba->ktime_data_samples = 0;
		phba->ktime_status_samples = 0;
		phba->ktime_seg1_total = 0;
		phba->ktime_seg1_max = 0;
		phba->ktime_seg1_min = 0xffffffff;
		phba->ktime_seg2_total = 0;
		phba->ktime_seg2_max = 0;
		phba->ktime_seg2_min = 0xffffffff;
		phba->ktime_seg3_total = 0;
		phba->ktime_seg3_max = 0;
		phba->ktime_seg3_min = 0xffffffff;
		phba->ktime_seg4_total = 0;
		phba->ktime_seg4_max = 0;
		phba->ktime_seg4_min = 0xffffffff;
		phba->ktime_seg5_total = 0;
		phba->ktime_seg5_max = 0;
		phba->ktime_seg5_min = 0xffffffff;
		phba->ktime_seg6_total = 0;
		phba->ktime_seg6_max = 0;
		phba->ktime_seg6_min = 0xffffffff;
		phba->ktime_seg7_total = 0;
		phba->ktime_seg7_max = 0;
		phba->ktime_seg7_min = 0xffffffff;
		phba->ktime_seg8_total = 0;
		phba->ktime_seg8_max = 0;
		phba->ktime_seg8_min = 0xffffffff;
		phba->ktime_seg9_total = 0;
		phba->ktime_seg9_max = 0;
		phba->ktime_seg9_min = 0xffffffff;
		phba->ktime_seg10_total = 0;
		phba->ktime_seg10_max = 0;
		phba->ktime_seg10_min = 0xffffffff;

		phba->ktime_on = 1;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "off",
		   sizeof("off") - 1) == 0)) {
		phba->ktime_on = 0;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "zero",
		   sizeof("zero") - 1) == 0)) {
		phba->ktime_data_samples = 0;
		phba->ktime_status_samples = 0;
		phba->ktime_seg1_total = 0;
		phba->ktime_seg1_max = 0;
		phba->ktime_seg1_min = 0xffffffff;
		phba->ktime_seg2_total = 0;
		phba->ktime_seg2_max = 0;
		phba->ktime_seg2_min = 0xffffffff;
		phba->ktime_seg3_total = 0;
		phba->ktime_seg3_max = 0;
		phba->ktime_seg3_min = 0xffffffff;
		phba->ktime_seg4_total = 0;
		phba->ktime_seg4_max = 0;
		phba->ktime_seg4_min = 0xffffffff;
		phba->ktime_seg5_total = 0;
		phba->ktime_seg5_max = 0;
		phba->ktime_seg5_min = 0xffffffff;
		phba->ktime_seg6_total = 0;
		phba->ktime_seg6_max = 0;
		phba->ktime_seg6_min = 0xffffffff;
		phba->ktime_seg7_total = 0;
		phba->ktime_seg7_max = 0;
		phba->ktime_seg7_min = 0xffffffff;
		phba->ktime_seg8_total = 0;
		phba->ktime_seg8_max = 0;
		phba->ktime_seg8_min = 0xffffffff;
		phba->ktime_seg9_total = 0;
		phba->ktime_seg9_max = 0;
		phba->ktime_seg9_min = 0xffffffff;
		phba->ktime_seg10_total = 0;
		phba->ktime_seg10_max = 0;
		phba->ktime_seg10_min = 0xffffffff;
		return strlen(pbuf);
	}
	return -EINVAL;
}

static int
lpfc_debugfs_nvmeio_trc_open(struct inode *inode, struct file *file)
{
	struct lpfc_hba *phba = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	 /* Round to page boundary */
	debug->buffer = kmalloc(LPFC_NVMEIO_TRC_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_nvmeio_trc_data(phba, debug->buffer,
		LPFC_NVMEIO_TRC_SIZE);

	debug->i_private = inode->i_private;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static ssize_t
lpfc_debugfs_nvmeio_trc_write(struct file *file, const char __user *buf,
			      size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	int i;
	unsigned long sz;
	char mybuf[64];
	char *pbuf;

	if (nbytes > 64)
		nbytes = 64;

	/* Protect copy from user */
	if (!access_ok(VERIFY_READ, buf, nbytes))
		return -EFAULT;

	memset(mybuf, 0, sizeof(mybuf));

	if (copy_from_user(mybuf, buf, nbytes))
		return -EFAULT;
	pbuf = &mybuf[0];

	if ((strncmp(pbuf, "off", sizeof("off") - 1) == 0)) {
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
				"0570 nvmeio_trc_off\n");
		phba->nvmeio_trc_output_idx = 0;
		phba->nvmeio_trc_on = 0;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
				"0571 nvmeio_trc_on\n");
		phba->nvmeio_trc_output_idx = 0;
		phba->nvmeio_trc_on = 1;
		return strlen(pbuf);
	}

	/* We must be off to allocate the trace buffer */
	if (phba->nvmeio_trc_on != 0)
		return -EINVAL;

	/* If not on or off, the parameter is the trace buffer size */
	i = kstrtoul(pbuf, 0, &sz);
	if (i)
		return -EINVAL;
	phba->nvmeio_trc_size = (uint32_t)sz;

	/* It must be a power of 2 - round down */
	i = 0;
	while (sz > 1) {
		sz = sz >> 1;
		i++;
	}
	sz = (1 << i);
	if (phba->nvmeio_trc_size != sz)
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
				"0572 nvmeio_trc_size changed to %ld\n",
				sz);
	phba->nvmeio_trc_size = (uint32_t)sz;

	/* If one previously exists, free it */
	kfree(phba->nvmeio_trc);

	/* Allocate new trace buffer and initialize */
	phba->nvmeio_trc = kmalloc((sizeof(struct lpfc_debugfs_nvmeio_trc) *
				    sz), GFP_KERNEL);
	if (!phba->nvmeio_trc) {
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
				"0573 Cannot create debugfs "
				"nvmeio_trc buffer\n");
		return -ENOMEM;
	}
	memset(phba->nvmeio_trc, 0,
	       (sizeof(struct lpfc_debugfs_nvmeio_trc) * sz));
	atomic_set(&phba->nvmeio_trc_cnt, 0);
	phba->nvmeio_trc_on = 0;
	phba->nvmeio_trc_output_idx = 0;

	return strlen(pbuf);
}

static int
lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	 /* Round to page boundary */
	debug->buffer = kmalloc(LPFC_CPUCHECK_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_cpucheck_data(vport, debug->buffer,
		LPFC_NVMEKTIME_SIZE);

	debug->i_private = inode->i_private;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static ssize_t
lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,
			    size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
	struct lpfc_hba   *phba = vport->phba;
	char mybuf[64];
	char *pbuf;
	int i;

	if (nbytes > 64)
		nbytes = 64;

	/* Protect copy from user */
	if (!access_ok(VERIFY_READ, buf, nbytes))
		return -EFAULT;

	memset(mybuf, 0, sizeof(mybuf));

	if (copy_from_user(mybuf, buf, nbytes))
		return -EFAULT;
	pbuf = &mybuf[0];

	if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
		if (phba->nvmet_support)
			phba->cpucheck_on |= LPFC_CHECK_NVMET_IO;
		else
			phba->cpucheck_on |= LPFC_CHECK_NVME_IO;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "rcv",
		   sizeof("rcv") - 1) == 0)) {
		if (phba->nvmet_support)
			phba->cpucheck_on |= LPFC_CHECK_NVMET_RCV;
		else
			return -EINVAL;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "off",
		   sizeof("off") - 1) == 0)) {
		phba->cpucheck_on = LPFC_CHECK_OFF;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "zero",
		   sizeof("zero") - 1) == 0)) {
		for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
			if (i >= LPFC_CHECK_CPU_CNT)
				break;
			phba->cpucheck_rcv_io[i] = 0;
			phba->cpucheck_xmt_io[i] = 0;
			phba->cpucheck_cmpl_io[i] = 0;
			phba->cpucheck_ccmpl_io[i] = 0;
		}
		return strlen(pbuf);
	}
	return -EINVAL;
}

/*
 * ---------------------------------
 * iDiag debugfs file access methods
 * ---------------------------------
 *
 * All access methods are through the proper SLI4 PCI function's debugfs
 * iDiag directory:
 *
 *     /sys/kernel/debug/lpfc/fn<#>/iDiag
 */

/**
 * lpfc_idiag_cmd_get - Get and parse idiag debugfs comands from user space
 * @buf: The pointer to the user space buffer.
 * @nbytes: The number of bytes in the user space buffer.
 * @idiag_cmd: pointer to the idiag command struct.
 *
 * This routine reads data from debugfs user space buffer and parses the
 * buffer for getting the idiag command and arguments. The while space in
 * between the set of data is used as the parsing separator.
 *
 * This routine returns 0 when successful, it returns proper error code
 * back to the user space in error conditions.
 */
static int lpfc_idiag_cmd_get(const char __user *buf, size_t nbytes,
			      struct lpfc_idiag_cmd *idiag_cmd)
{
	char mybuf[64];
	char *pbuf, *step_str;
	int i;
	size_t bsize;

	/* Protect copy from user */
	if (!access_ok(VERIFY_READ, buf, nbytes))
		return -EFAULT;

	memset(mybuf, 0, sizeof(mybuf));
	memset(idiag_cmd, 0, sizeof(*idiag_cmd));
	bsize = min(nbytes, (sizeof(mybuf)-1));

	if (copy_from_user(mybuf, buf, bsize))
		return -EFAULT;
	pbuf = &mybuf[0];
	step_str = strsep(&pbuf, "\t ");

	/* The opcode must present */
	if (!step_str)
		return -EINVAL;

	idiag_cmd->opcode = simple_strtol(step_str, NULL, 0);
	if (idiag_cmd->opcode == 0)
		return -EINVAL;

	for (i = 0; i < LPFC_IDIAG_CMD_DATA_SIZE; i++) {
		step_str = strsep(&pbuf, "\t ");
		if (!step_str)
			return i;
		idiag_cmd->data[i] = simple_strtol(step_str, NULL, 0);
	}
	return i;
}

/**
 * lpfc_idiag_open - idiag open debugfs
 * @inode: The inode pointer that contains a pointer to phba.
 * @file: The file pointer to attach the file operation.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It
 * gets the reference to phba from the i_private field in @inode, it then
 * allocates buffer for the file operation, performs the necessary PCI config
 * space read into the allocated buffer according to the idiag user command
 * setup, and then returns a pointer to buffer in the private_data field in
 * @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return an
 * negative error value.
 **/
static int
lpfc_idiag_open(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		return -ENOMEM;

	debug->i_private = inode->i_private;
	debug->buffer = NULL;
	file->private_data = debug;

	return 0;
}

/**
 * lpfc_idiag_release - Release idiag access file operation
 * @inode: The inode pointer that contains a vport pointer. (unused)
 * @file: The file pointer that contains the buffer to release.
 *
 * Description:
 * This routine is the generic release routine for the idiag access file
 * operation, it frees the buffer that was allocated when the debugfs file
 * was opened.
 *
 * Returns:
 * This function returns zero.
 **/
static int
lpfc_idiag_release(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug = file->private_data;

	/* Free the buffers to the file operation */
	kfree(debug->buffer);
	kfree(debug);

	return 0;
}

/**
 * lpfc_idiag_cmd_release - Release idiag cmd access file operation
 * @inode: The inode pointer that contains a vport pointer. (unused)
 * @file: The file pointer that contains the buffer to release.
 *
 * Description:
 * This routine frees the buffer that was allocated when the debugfs file
 * was opened. It also reset the fields in the idiag command struct in the
 * case of command for write operation.
 *
 * Returns:
 * This function returns zero.
 **/
static int
lpfc_idiag_cmd_release(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug = file->private_data;

	if (debug->op == LPFC_IDIAG_OP_WR) {
		switch (idiag.cmd.opcode) {
		case LPFC_IDIAG_CMD_PCICFG_WR:
		case LPFC_IDIAG_CMD_PCICFG_ST:
		case LPFC_IDIAG_CMD_PCICFG_CL:
		case LPFC_IDIAG_CMD_QUEACC_WR:
		case LPFC_IDIAG_CMD_QUEACC_ST:
		case LPFC_IDIAG_CMD_QUEACC_CL:
			memset(&idiag, 0, sizeof(idiag));
			break;
		default:
			break;
		}
	}

	/* Free the buffers to the file operation */
	kfree(debug->buffer);
	kfree(debug);

	return 0;
}

/**
 * lpfc_idiag_pcicfg_read - idiag debugfs read pcicfg
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba pci config space according to the
 * idiag command, and copies to user @buf. Depending on the PCI config space
 * read command setup, it does either a single register read of a byte
 * (8 bits), a word (16 bits), or a dword (32 bits) or browsing through all
 * registers from the 4K extended PCI config space.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_pcicfg_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	int offset_label, offset, len = 0, index = LPFC_PCI_CFG_RD_SIZE;
	int where, count;
	char *pbuffer;
	struct pci_dev *pdev;
	uint32_t u32val;
	uint16_t u16val;
	uint8_t u8val;

	pdev = phba->pcidev;
	if (!pdev)
		return 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_PCI_CFG_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;

	if (*ppos)
		return 0;

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) {
		where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
		count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
	} else
		return 0;

	/* Read single PCI config space register */
	switch (count) {
	case SIZE_U8: /* byte (8 bits) */
		pci_read_config_byte(pdev, where, &u8val);
		len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
				"%03x: %02x\n", where, u8val);
		break;
	case SIZE_U16: /* word (16 bits) */
		pci_read_config_word(pdev, where, &u16val);
		len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
				"%03x: %04x\n", where, u16val);
		break;
	case SIZE_U32: /* double word (32 bits) */
		pci_read_config_dword(pdev, where, &u32val);
		len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
				"%03x: %08x\n", where, u32val);
		break;
	case LPFC_PCI_CFG_BROWSE: /* browse all */
		goto pcicfg_browse;
		break;
	default:
		/* illegal count */
		len = 0;
		break;
	}
	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);

pcicfg_browse:

	/* Browse all PCI config space registers */
	offset_label = idiag.offset.last_rd;
	offset = offset_label;

	/* Read PCI config space */
	len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
			"%03x: ", offset_label);
	while (index > 0) {
		pci_read_config_dword(pdev, offset, &u32val);
		len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
				"%08x ", u32val);
		offset += sizeof(uint32_t);
		if (offset >= LPFC_PCI_CFG_SIZE) {
			len += snprintf(pbuffer+len,
					LPFC_PCI_CFG_SIZE-len, "\n");
			break;
		}
		index -= sizeof(uint32_t);
		if (!index)
			len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
					"\n");
		else if (!(index % (8 * sizeof(uint32_t)))) {
			offset_label += (8 * sizeof(uint32_t));
			len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
					"\n%03x: ", offset_label);
		}
	}

	/* Set up the offset for next portion of pci cfg read */
	if (index == 0) {
		idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
		if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
			idiag.offset.last_rd = 0;
	} else
		idiag.offset.last_rd = 0;

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_pcicfg_write - Syntax check and set up idiag pcicfg commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and
 * then perform the syntax check for PCI config space read or write command
 * accordingly. In the case of PCI config space read command, it sets up
 * the command in the idiag command struct for the debugfs read operation.
 * In the case of PCI config space write operation, it executes the write
 * operation into the PCI config space accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 */
static ssize_t
lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t where, value, count;
	uint32_t u32val;
	uint16_t u16val;
	uint8_t u8val;
	struct pci_dev *pdev;
	int rc;

	pdev = phba->pcidev;
	if (!pdev)
		return -EFAULT;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) {
		/* Sanity check on PCI config read command line arguments */
		if (rc != LPFC_PCI_CFG_RD_CMD_ARG)
			goto error_out;
		/* Read command from PCI config space, set up command fields */
		where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
		count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
		if (count == LPFC_PCI_CFG_BROWSE) {
			if (where % sizeof(uint32_t))
				goto error_out;
			/* Starting offset to browse */
			idiag.offset.last_rd = where;
		} else if ((count != sizeof(uint8_t)) &&
			   (count != sizeof(uint16_t)) &&
			   (count != sizeof(uint32_t)))
			goto error_out;
		if (count == sizeof(uint8_t)) {
			if (where > LPFC_PCI_CFG_SIZE - sizeof(uint8_t))
				goto error_out;
			if (where % sizeof(uint8_t))
				goto error_out;
		}
		if (count == sizeof(uint16_t)) {
			if (where > LPFC_PCI_CFG_SIZE - sizeof(uint16_t))
				goto error_out;
			if (where % sizeof(uint16_t))
				goto error_out;
		}
		if (count == sizeof(uint32_t)) {
			if (where > LPFC_PCI_CFG_SIZE - sizeof(uint32_t))
				goto error_out;
			if (where % sizeof(uint32_t))
				goto error_out;
		}
	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_WR ||
		   idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_ST ||
		   idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_CL) {
		/* Sanity check on PCI config write command line arguments */
		if (rc != LPFC_PCI_CFG_WR_CMD_ARG)
			goto error_out;
		/* Write command to PCI config space, read-modify-write */
		where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
		count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
		value = idiag.cmd.data[IDIAG_PCICFG_VALUE_INDX];
		/* Sanity checks */
		if ((count != sizeof(uint8_t)) &&
		    (count != sizeof(uint16_t)) &&
		    (count != sizeof(uint32_t)))
			goto error_out;
		if (count == sizeof(uint8_t)) {
			if (where > LPFC_PCI_CFG_SIZE - sizeof(uint8_t))
				goto error_out;
			if (where % sizeof(uint8_t))
				goto error_out;
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_WR)
				pci_write_config_byte(pdev, where,
						      (uint8_t)value);
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_ST) {
				rc = pci_read_config_byte(pdev, where, &u8val);
				if (!rc) {
					u8val |= (uint8_t)value;
					pci_write_config_byte(pdev, where,
							      u8val);
				}
			}
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_CL) {
				rc = pci_read_config_byte(pdev, where, &u8val);
				if (!rc) {
					u8val &= (uint8_t)(~value);
					pci_write_config_byte(pdev, where,
							      u8val);
				}
			}
		}
		if (count == sizeof(uint16_t)) {
			if (where > LPFC_PCI_CFG_SIZE - sizeof(uint16_t))
				goto error_out;
			if (where % sizeof(uint16_t))
				goto error_out;
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_WR)
				pci_write_config_word(pdev, where,
						      (uint16_t)value);
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_ST) {
				rc = pci_read_config_word(pdev, where, &u16val);
				if (!rc) {
					u16val |= (uint16_t)value;
					pci_write_config_word(pdev, where,
							      u16val);
				}
			}
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_CL) {
				rc = pci_read_config_word(pdev, where, &u16val);
				if (!rc) {
					u16val &= (uint16_t)(~value);
					pci_write_config_word(pdev, where,
							      u16val);
				}
			}
		}
		if (count == sizeof(uint32_t)) {
			if (where > LPFC_PCI_CFG_SIZE - sizeof(uint32_t))
				goto error_out;
			if (where % sizeof(uint32_t))
				goto error_out;
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_WR)
				pci_write_config_dword(pdev, where, value);
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_ST) {
				rc = pci_read_config_dword(pdev, where,
							   &u32val);
				if (!rc) {
					u32val |= value;
					pci_write_config_dword(pdev, where,
							       u32val);
				}
			}
			if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_CL) {
				rc = pci_read_config_dword(pdev, where,
							   &u32val);
				if (!rc) {
					u32val &= ~value;
					pci_write_config_dword(pdev, where,
							       u32val);
				}
			}
		}
	} else
		/* All other opecodes are illegal for now */
		goto error_out;

	return nbytes;
error_out:
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

/**
 * lpfc_idiag_baracc_read - idiag debugfs pci bar access read
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba pci bar memory mapped space
 * according to the idiag command, and copies to user @buf.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_baracc_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	int offset_label, offset, offset_run, len = 0, index;
	int bar_num, acc_range, bar_size;
	char *pbuffer;
	void __iomem *mem_mapped_bar;
	uint32_t if_type;
	struct pci_dev *pdev;
	uint32_t u32val;

	pdev = phba->pcidev;
	if (!pdev)
		return 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_PCI_BAR_RD_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;

	if (*ppos)
		return 0;

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
		bar_num   = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
		offset    = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
		acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
		bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
	} else
		return 0;

	if (acc_range == 0)
		return 0;

	if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
	if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
		if (bar_num == IDIAG_BARACC_BAR_0)
			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
		else if (bar_num == IDIAG_BARACC_BAR_1)
			mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
		else if (bar_num == IDIAG_BARACC_BAR_2)
			mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
		else
			return 0;
	} else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
		if (bar_num == IDIAG_BARACC_BAR_0)
			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
		else
			return 0;
	} else
		return 0;

	/* Read single PCI bar space register */
	if (acc_range == SINGLE_WORD) {
		offset_run = offset;
		u32val = readl(mem_mapped_bar + offset_run);
		len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
				"%05x: %08x\n", offset_run, u32val);
	} else
		goto baracc_browse;

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);

baracc_browse:

	/* Browse all PCI bar space registers */
	offset_label = idiag.offset.last_rd;
	offset_run = offset_label;

	/* Read PCI bar memory mapped space */
	len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
			"%05x: ", offset_label);
	index = LPFC_PCI_BAR_RD_SIZE;
	while (index > 0) {
		u32val = readl(mem_mapped_bar + offset_run);
		len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
				"%08x ", u32val);
		offset_run += sizeof(uint32_t);
		if (acc_range == LPFC_PCI_BAR_BROWSE) {
			if (offset_run >= bar_size) {
				len += snprintf(pbuffer+len,
					LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
				break;
			}
		} else {
			if (offset_run >= offset +
			    (acc_range * sizeof(uint32_t))) {
				len += snprintf(pbuffer+len,
					LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
				break;
			}
		}
		index -= sizeof(uint32_t);
		if (!index)
			len += snprintf(pbuffer+len,
					LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
		else if (!(index % (8 * sizeof(uint32_t)))) {
			offset_label += (8 * sizeof(uint32_t));
			len += snprintf(pbuffer+len,
					LPFC_PCI_BAR_RD_BUF_SIZE-len,
					"\n%05x: ", offset_label);
		}
	}

	/* Set up the offset for next portion of pci bar read */
	if (index == 0) {
		idiag.offset.last_rd += LPFC_PCI_BAR_RD_SIZE;
		if (acc_range == LPFC_PCI_BAR_BROWSE) {
			if (idiag.offset.last_rd >= bar_size)
				idiag.offset.last_rd = 0;
		} else {
			if (offset_run >= offset +
			    (acc_range * sizeof(uint32_t)))
				idiag.offset.last_rd = offset;
		}
	} else {
		if (acc_range == LPFC_PCI_BAR_BROWSE)
			idiag.offset.last_rd = 0;
		else
			idiag.offset.last_rd = offset;
	}

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_baracc_write - Syntax check and set up idiag bar access commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and
 * then perform the syntax check for PCI bar memory mapped space read or
 * write command accordingly. In the case of PCI bar memory mapped space
 * read command, it sets up the command in the idiag command struct for
 * the debugfs read operation. In the case of PCI bar memorpy mapped space
 * write operation, it executes the write operation into the PCI bar memory
 * mapped space accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 */
static ssize_t
lpfc_idiag_baracc_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t bar_num, bar_size, offset, value, acc_range;
	struct pci_dev *pdev;
	void __iomem *mem_mapped_bar;
	uint32_t if_type;
	uint32_t u32val;
	int rc;

	pdev = phba->pcidev;
	if (!pdev)
		return -EFAULT;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
	bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];

	if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
		if ((bar_num != IDIAG_BARACC_BAR_0) &&
		    (bar_num != IDIAG_BARACC_BAR_1) &&
		    (bar_num != IDIAG_BARACC_BAR_2))
			goto error_out;
	} else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
		if (bar_num != IDIAG_BARACC_BAR_0)
			goto error_out;
	} else
		goto error_out;

	if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
		if (bar_num == IDIAG_BARACC_BAR_0) {
			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
				LPFC_PCI_IF0_BAR0_SIZE;
			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
		} else if (bar_num == IDIAG_BARACC_BAR_1) {
			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
				LPFC_PCI_IF0_BAR1_SIZE;
			mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
		} else if (bar_num == IDIAG_BARACC_BAR_2) {
			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
				LPFC_PCI_IF0_BAR2_SIZE;
			mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
		} else
			goto error_out;
	} else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
		if (bar_num == IDIAG_BARACC_BAR_0) {
			idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
				LPFC_PCI_IF2_BAR0_SIZE;
			mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
		} else
			goto error_out;
	} else
		goto error_out;

	offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
	if (offset % sizeof(uint32_t))
		goto error_out;

	bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
		/* Sanity check on PCI config read command line arguments */
		if (rc != LPFC_PCI_BAR_RD_CMD_ARG)
			goto error_out;
		acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
		if (acc_range == LPFC_PCI_BAR_BROWSE) {
			if (offset > bar_size - sizeof(uint32_t))
				goto error_out;
			/* Starting offset to browse */
			idiag.offset.last_rd = offset;
		} else if (acc_range > SINGLE_WORD) {
			if (offset + acc_range * sizeof(uint32_t) > bar_size)
				goto error_out;
			/* Starting offset to browse */
			idiag.offset.last_rd = offset;
		} else if (acc_range != SINGLE_WORD)
			goto error_out;
	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR ||
		   idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST ||
		   idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
		/* Sanity check on PCI bar write command line arguments */
		if (rc != LPFC_PCI_BAR_WR_CMD_ARG)
			goto error_out;
		/* Write command to PCI bar space, read-modify-write */
		acc_range = SINGLE_WORD;
		value = idiag.cmd.data[IDIAG_BARACC_REG_VAL_INDX];
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR) {
			writel(value, mem_mapped_bar + offset);
			readl(mem_mapped_bar + offset);
		}
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST) {
			u32val = readl(mem_mapped_bar + offset);
			u32val |= value;
			writel(u32val, mem_mapped_bar + offset);
			readl(mem_mapped_bar + offset);
		}
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
			u32val = readl(mem_mapped_bar + offset);
			u32val &= ~value;
			writel(u32val, mem_mapped_bar + offset);
			readl(mem_mapped_bar + offset);
		}
	} else
		/* All other opecodes are illegal for now */
		goto error_out;

	return nbytes;
error_out:
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

static int
__lpfc_idiag_print_wq(struct lpfc_queue *qp, char *wqtype,
			char *pbuffer, int len)
{
	if (!qp)
		return len;

	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\t\t%s WQ info: ", wqtype);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"AssocCQID[%04d]: WQ-STAT[oflow:x%x posted:x%llx]\n",
			qp->assoc_qid, qp->q_cnt_1,
			(unsigned long long)qp->q_cnt_4);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\t\tWQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
			"HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
			qp->queue_id, qp->entry_count,
			qp->entry_size, qp->host_index,
			qp->hba_index, qp->entry_repost);
	len +=  snprintf(pbuffer + len,
			LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");
	return len;
}

static int
lpfc_idiag_wqs_for_cq(struct lpfc_hba *phba, char *wqtype, char *pbuffer,
		int *len, int max_cnt, int cq_id)
{
	struct lpfc_queue *qp;
	int qidx;

	for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) {
		qp = phba->sli4_hba.fcp_wq[qidx];
		if (qp->assoc_qid != cq_id)
			continue;
		*len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len);
		if (*len >= max_cnt)
			return 1;
	}
	for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
		qp = phba->sli4_hba.nvme_wq[qidx];
		if (qp->assoc_qid != cq_id)
			continue;
		*len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len);
		if (*len >= max_cnt)
			return 1;
	}
	return 0;
}

static int
__lpfc_idiag_print_cq(struct lpfc_queue *qp, char *cqtype,
			char *pbuffer, int len)
{
	if (!qp)
		return len;

	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\t%s CQ info: ", cqtype);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"AssocEQID[%02d]: CQ STAT[max:x%x relw:x%x "
			"xabt:x%x wq:x%llx]\n",
			qp->assoc_qid, qp->q_cnt_1, qp->q_cnt_2,
			qp->q_cnt_3, (unsigned long long)qp->q_cnt_4);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\tCQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
			"HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
			qp->queue_id, qp->entry_count,
			qp->entry_size, qp->host_index,
			qp->hba_index, qp->entry_repost);

	len +=  snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");

	return len;
}

static int
__lpfc_idiag_print_rqpair(struct lpfc_queue *qp, struct lpfc_queue *datqp,
			char *rqtype, char *pbuffer, int len)
{
	if (!qp || !datqp)
		return len;

	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\t\t%s RQ info: ", rqtype);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"AssocCQID[%02d]: RQ-STAT[nopost:x%x nobuf:x%x "
			"posted:x%x rcv:x%llx]\n",
			qp->assoc_qid, qp->q_cnt_1, qp->q_cnt_2,
			qp->q_cnt_3, (unsigned long long)qp->q_cnt_4);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\t\tHQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
			"HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]\n",
			qp->queue_id, qp->entry_count, qp->entry_size,
			qp->host_index, qp->hba_index, qp->entry_repost);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\t\tDQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
			"HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]\n",
			datqp->queue_id, datqp->entry_count,
			datqp->entry_size, datqp->host_index,
			datqp->hba_index, datqp->entry_repost);
	return len;
}

static int
lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer,
		int *len, int max_cnt, int eqidx, int eq_id)
{
	struct lpfc_queue *qp;
	int qidx, rc;

	for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) {
		qp = phba->sli4_hba.fcp_cq[qidx];
		if (qp->assoc_qid != eq_id)
			continue;

		*len = __lpfc_idiag_print_cq(qp, "FCP", pbuffer, *len);

		/* Reset max counter */
		qp->CQ_max_cqe = 0;

		if (*len >= max_cnt)
			return 1;

		rc = lpfc_idiag_wqs_for_cq(phba, "FCP", pbuffer, len,
				max_cnt, qp->queue_id);
		if (rc)
			return 1;
	}

	for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
		qp = phba->sli4_hba.nvme_cq[qidx];
		if (qp->assoc_qid != eq_id)
			continue;

		*len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len);

		/* Reset max counter */
		qp->CQ_max_cqe = 0;

		if (*len >= max_cnt)
			return 1;

		rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len,
				max_cnt, qp->queue_id);
		if (rc)
			return 1;
	}

	if (eqidx < phba->cfg_nvmet_mrq) {
		/* NVMET CQset */
		qp = phba->sli4_hba.nvmet_cqset[eqidx];
		*len = __lpfc_idiag_print_cq(qp, "NVMET CQset", pbuffer, *len);

		/* Reset max counter */
		qp->CQ_max_cqe = 0;

		if (*len >= max_cnt)
			return 1;

		/* RQ header */
		qp = phba->sli4_hba.nvmet_mrq_hdr[eqidx];
		*len = __lpfc_idiag_print_rqpair(qp,
				phba->sli4_hba.nvmet_mrq_data[eqidx],
				"NVMET MRQ", pbuffer, *len);

		if (*len >= max_cnt)
			return 1;
	}

	return 0;
}

static int
__lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype,
			char *pbuffer, int len)
{
	if (!qp)
		return len;

	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"\n%s EQ info: EQ-STAT[max:x%x noE:x%x "
			"bs:x%x proc:x%llx]\n",
			eqtype, qp->q_cnt_1, qp->q_cnt_2, qp->q_cnt_3,
			(unsigned long long)qp->q_cnt_4);
	len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
			"EQID[%02d], QE-CNT[%04d], QE-SZ[%04d], "
			"HST-IDX[%04d], PRT-IDX[%04d], PST[%03d]",
			qp->queue_id, qp->entry_count, qp->entry_size,
			qp->host_index, qp->hba_index, qp->entry_repost);
	len +=  snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n");

	return len;
}

/**
 * lpfc_idiag_queinfo_read - idiag debugfs read queue information
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba SLI4 PCI function queue information,
 * and copies to user @buf.
 * This routine only returns 1 EQs worth of information. It remembers the last
 * EQ read and jumps to the next EQ. Thus subsequent calls to queInfo will
 * retrieve all EQs allocated for the phba.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
			loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	char *pbuffer;
	int max_cnt, rc, x, len = 0;
	struct lpfc_queue *qp = NULL;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_QUE_INFO_GET_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;
	max_cnt = LPFC_QUE_INFO_GET_BUF_SIZE - 256;

	if (*ppos)
		return 0;

	spin_lock_irq(&phba->hbalock);

	/* Fast-path event queue */
	if (phba->sli4_hba.hba_eq && phba->io_channel_irqs) {

		x = phba->lpfc_idiag_last_eq;
		if (phba->cfg_fof && (x >= phba->io_channel_irqs)) {
			phba->lpfc_idiag_last_eq = 0;
			goto fof;
		}
		phba->lpfc_idiag_last_eq++;
		if (phba->lpfc_idiag_last_eq >= phba->io_channel_irqs)
			if (phba->cfg_fof == 0)
				phba->lpfc_idiag_last_eq = 0;

		len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len,
					"EQ %d out of %d HBA EQs\n",
					x, phba->io_channel_irqs);

		/* Fast-path EQ */
		qp = phba->sli4_hba.hba_eq[x];
		if (!qp)
			goto out;

		len = __lpfc_idiag_print_eq(qp, "HBA", pbuffer, len);

		/* Reset max counter */
		qp->EQ_max_eqe = 0;

		if (len >= max_cnt)
			goto too_big;

		/* will dump both fcp and nvme cqs/wqs for the eq */
		rc = lpfc_idiag_cqs_for_eq(phba, pbuffer, &len,
			max_cnt, x, qp->queue_id);
		if (rc)
			goto too_big;

		/* Only EQ 0 has slow path CQs configured */
		if (x)
			goto out;

		/* Slow-path mailbox CQ */
		qp = phba->sli4_hba.mbx_cq;
		len = __lpfc_idiag_print_cq(qp, "MBX", pbuffer, len);
		if (len >= max_cnt)
			goto too_big;

		/* Slow-path MBOX MQ */
		qp = phba->sli4_hba.mbx_wq;
		len = __lpfc_idiag_print_wq(qp, "MBX", pbuffer, len);
		if (len >= max_cnt)
			goto too_big;

		/* Slow-path ELS response CQ */
		qp = phba->sli4_hba.els_cq;
		len = __lpfc_idiag_print_cq(qp, "ELS", pbuffer, len);
		/* Reset max counter */
		if (qp)
			qp->CQ_max_cqe = 0;
		if (len >= max_cnt)
			goto too_big;

		/* Slow-path ELS WQ */
		qp = phba->sli4_hba.els_wq;
		len = __lpfc_idiag_print_wq(qp, "ELS", pbuffer, len);
		if (len >= max_cnt)
			goto too_big;

		/* Slow-path NVME LS response CQ */
		qp = phba->sli4_hba.nvmels_cq;
		len = __lpfc_idiag_print_cq(qp, "NVME LS",
						pbuffer, len);
		/* Reset max counter */
		if (qp)
			qp->CQ_max_cqe = 0;
		if (len >= max_cnt)
			goto too_big;

		/* Slow-path NVME LS WQ */
		qp = phba->sli4_hba.nvmels_wq;
		len = __lpfc_idiag_print_wq(qp, "NVME LS",
						pbuffer, len);
		if (len >= max_cnt)
			goto too_big;

		qp = phba->sli4_hba.hdr_rq;
		len = __lpfc_idiag_print_rqpair(qp, phba->sli4_hba.dat_rq,
				"RQpair", pbuffer, len);
		if (len >= max_cnt)
			goto too_big;

		goto out;
	}

fof:
	if (phba->cfg_fof) {
		/* FOF EQ */
		qp = phba->sli4_hba.fof_eq;
		len = __lpfc_idiag_print_eq(qp, "FOF", pbuffer, len);

		/* Reset max counter */
		if (qp)
			qp->EQ_max_eqe = 0;

		if (len >= max_cnt)
			goto too_big;

		/* OAS CQ */
		qp = phba->sli4_hba.oas_cq;
		len = __lpfc_idiag_print_cq(qp, "OAS", pbuffer, len);
		/* Reset max counter */
		if (qp)
			qp->CQ_max_cqe = 0;
		if (len >= max_cnt)
			goto too_big;

		/* OAS WQ */
		qp = phba->sli4_hba.oas_wq;
		len = __lpfc_idiag_print_wq(qp, "OAS", pbuffer, len);
		if (len >= max_cnt)
			goto too_big;
	}

	spin_unlock_irq(&phba->hbalock);
	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);

too_big:
	len +=  snprintf(pbuffer + len,
		LPFC_QUE_INFO_GET_BUF_SIZE - len, "Truncated ...\n");
out:
	spin_unlock_irq(&phba->hbalock);
	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_que_param_check - queue access command parameter sanity check
 * @q: The pointer to queue structure.
 * @index: The index into a queue entry.
 * @count: The number of queue entries to access.
 *
 * Description:
 * The routine performs sanity check on device queue access method commands.
 *
 * Returns:
 * This function returns -EINVAL when fails the sanity check, otherwise, it
 * returns 0.
 **/
static int
lpfc_idiag_que_param_check(struct lpfc_queue *q, int index, int count)
{
	/* Only support single entry read or browsing */
	if ((count != 1) && (count != LPFC_QUE_ACC_BROWSE))
		return -EINVAL;
	if (index > q->entry_count - 1)
		return -EINVAL;
	return 0;
}

/**
 * lpfc_idiag_queacc_read_qe - read a single entry from the given queue index
 * @pbuffer: The pointer to buffer to copy the read data into.
 * @pque: The pointer to the queue to be read.
 * @index: The index into the queue entry.
 *
 * Description:
 * This routine reads out a single entry from the given queue's index location
 * and copies it into the buffer provided.
 *
 * Returns:
 * This function returns 0 when it fails, otherwise, it returns the length of
 * the data read into the buffer provided.
 **/
static int
lpfc_idiag_queacc_read_qe(char *pbuffer, int len, struct lpfc_queue *pque,
			  uint32_t index)
{
	int offset, esize;
	uint32_t *pentry;

	if (!pbuffer || !pque)
		return 0;

	esize = pque->entry_size;
	len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
			"QE-INDEX[%04d]:\n", index);

	offset = 0;
	pentry = pque->qe[index].address;
	while (esize > 0) {
		len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
				"%08x ", *pentry);
		pentry++;
		offset += sizeof(uint32_t);
		esize -= sizeof(uint32_t);
		if (esize > 0 && !(offset % (4 * sizeof(uint32_t))))
			len += snprintf(pbuffer+len,
					LPFC_QUE_ACC_BUF_SIZE-len, "\n");
	}
	len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len, "\n");

	return len;
}

/**
 * lpfc_idiag_queacc_read - idiag debugfs read port queue
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba device queue memory according to the
 * idiag command, and copies to user @buf. Depending on the queue dump read
 * command setup, it does either a single queue entry read or browing through
 * all entries of the queue.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_queacc_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	uint32_t last_index, index, count;
	struct lpfc_queue *pque = NULL;
	char *pbuffer;
	int len = 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_QUE_ACC_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;

	if (*ppos)
		return 0;

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
		index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
		count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
		pque = (struct lpfc_queue *)idiag.ptr_private;
	} else
		return 0;

	/* Browse the queue starting from index */
	if (count == LPFC_QUE_ACC_BROWSE)
		goto que_browse;

	/* Read a single entry from the queue */
	len = lpfc_idiag_queacc_read_qe(pbuffer, len, pque, index);

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);

que_browse:

	/* Browse all entries from the queue */
	last_index = idiag.offset.last_rd;
	index = last_index;

	while (len < LPFC_QUE_ACC_SIZE - pque->entry_size) {
		len = lpfc_idiag_queacc_read_qe(pbuffer, len, pque, index);
		index++;
		if (index > pque->entry_count - 1)
			break;
	}

	/* Set up the offset for next portion of pci cfg read */
	if (index > pque->entry_count - 1)
		index = 0;
	idiag.offset.last_rd = index;

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_queacc_write - Syntax check and set up idiag queacc commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and then
 * perform the syntax check for port queue read (dump) or write (set) command
 * accordingly. In the case of port queue read command, it sets up the command
 * in the idiag command struct for the following debugfs read operation. In
 * the case of port queue write operation, it executes the write operation
 * into the port queue entry accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 **/
static ssize_t
lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t qidx, quetp, queid, index, count, offset, value;
	uint32_t *pentry;
	struct lpfc_queue *pque, *qp;
	int rc;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	/* Get and sanity check on command feilds */
	quetp  = idiag.cmd.data[IDIAG_QUEACC_QUETP_INDX];
	queid  = idiag.cmd.data[IDIAG_QUEACC_QUEID_INDX];
	index  = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
	count  = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
	offset = idiag.cmd.data[IDIAG_QUEACC_OFFST_INDX];
	value  = idiag.cmd.data[IDIAG_QUEACC_VALUE_INDX];

	/* Sanity check on command line arguments */
	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_ST ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_CL) {
		if (rc != LPFC_QUE_ACC_WR_CMD_ARG)
			goto error_out;
		if (count != 1)
			goto error_out;
	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
		if (rc != LPFC_QUE_ACC_RD_CMD_ARG)
			goto error_out;
	} else
		goto error_out;

	switch (quetp) {
	case LPFC_IDIAG_EQ:
		/* HBA event queue */
		if (phba->sli4_hba.hba_eq) {
			for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) {
				qp = phba->sli4_hba.hba_eq[qidx];
				if (qp && qp->queue_id == queid) {
					/* Sanity check */
					rc = lpfc_idiag_que_param_check(qp,
						index, count);
					if (rc)
						goto error_out;
					idiag.ptr_private = qp;
					goto pass_check;
				}
			}
		}
		goto error_out;
		break;
	case LPFC_IDIAG_CQ:
		/* MBX complete queue */
		if (phba->sli4_hba.mbx_cq &&
		    phba->sli4_hba.mbx_cq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.mbx_cq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.mbx_cq;
			goto pass_check;
		}
		/* ELS complete queue */
		if (phba->sli4_hba.els_cq &&
		    phba->sli4_hba.els_cq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.els_cq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.els_cq;
			goto pass_check;
		}
		/* NVME LS complete queue */
		if (phba->sli4_hba.nvmels_cq &&
		    phba->sli4_hba.nvmels_cq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.nvmels_cq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.nvmels_cq;
			goto pass_check;
		}
		/* FCP complete queue */
		if (phba->sli4_hba.fcp_cq) {
			for (qidx = 0; qidx < phba->cfg_fcp_io_channel;
								qidx++) {
				qp = phba->sli4_hba.fcp_cq[qidx];
				if (qp && qp->queue_id == queid) {
					/* Sanity check */
					rc = lpfc_idiag_que_param_check(
						qp, index, count);
					if (rc)
						goto error_out;
					idiag.ptr_private = qp;
					goto pass_check;
				}
			}
		}
		/* NVME complete queue */
		if (phba->sli4_hba.nvme_cq) {
			qidx = 0;
			do {
				if (phba->sli4_hba.nvme_cq[qidx] &&
				    phba->sli4_hba.nvme_cq[qidx]->queue_id ==
				    queid) {
					/* Sanity check */
					rc = lpfc_idiag_que_param_check(
						phba->sli4_hba.nvme_cq[qidx],
						index, count);
					if (rc)
						goto error_out;
					idiag.ptr_private =
						phba->sli4_hba.nvme_cq[qidx];
					goto pass_check;
				}
			} while (++qidx < phba->cfg_nvme_io_channel);
		}
		goto error_out;
		break;
	case LPFC_IDIAG_MQ:
		/* MBX work queue */
		if (phba->sli4_hba.mbx_wq &&
		    phba->sli4_hba.mbx_wq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.mbx_wq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.mbx_wq;
			goto pass_check;
		}
		goto error_out;
		break;
	case LPFC_IDIAG_WQ:
		/* ELS work queue */
		if (phba->sli4_hba.els_wq &&
		    phba->sli4_hba.els_wq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.els_wq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.els_wq;
			goto pass_check;
		}
		/* NVME LS work queue */
		if (phba->sli4_hba.nvmels_wq &&
		    phba->sli4_hba.nvmels_wq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.nvmels_wq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.nvmels_wq;
			goto pass_check;
		}
		/* FCP work queue */
		if (phba->sli4_hba.fcp_wq) {
			for (qidx = 0; qidx < phba->cfg_fcp_io_channel;
								qidx++) {
				qp = phba->sli4_hba.fcp_wq[qidx];
				if (qp && qp->queue_id == queid) {
					/* Sanity check */
					rc = lpfc_idiag_que_param_check(
						qp, index, count);
					if (rc)
						goto error_out;
					idiag.ptr_private = qp;
					goto pass_check;
				}
			}
		}
		/* NVME work queue */
		if (phba->sli4_hba.nvme_wq) {
			for (qidx = 0; qidx < phba->cfg_nvme_io_channel;
								qidx++) {
				qp = phba->sli4_hba.nvme_wq[qidx];
				if (qp && qp->queue_id == queid) {
					/* Sanity check */
					rc = lpfc_idiag_que_param_check(
						qp, index, count);
					if (rc)
						goto error_out;
					idiag.ptr_private = qp;
					goto pass_check;
				}
			}
		}

		/* NVME work queues */
		if (phba->sli4_hba.nvme_wq) {
			for (qidx = 0; qidx < phba->cfg_nvme_io_channel;
				qidx++) {
				if (!phba->sli4_hba.nvme_wq[qidx])
					continue;
				if (phba->sli4_hba.nvme_wq[qidx]->queue_id ==
				    queid) {
					/* Sanity check */
					rc = lpfc_idiag_que_param_check(
						phba->sli4_hba.nvme_wq[qidx],
						index, count);
					if (rc)
						goto error_out;
					idiag.ptr_private =
						phba->sli4_hba.nvme_wq[qidx];
					goto pass_check;
				}
			}
		}
		goto error_out;
		break;
	case LPFC_IDIAG_RQ:
		/* HDR queue */
		if (phba->sli4_hba.hdr_rq &&
		    phba->sli4_hba.hdr_rq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.hdr_rq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.hdr_rq;
			goto pass_check;
		}
		/* DAT queue */
		if (phba->sli4_hba.dat_rq &&
		    phba->sli4_hba.dat_rq->queue_id == queid) {
			/* Sanity check */
			rc = lpfc_idiag_que_param_check(
					phba->sli4_hba.dat_rq, index, count);
			if (rc)
				goto error_out;
			idiag.ptr_private = phba->sli4_hba.dat_rq;
			goto pass_check;
		}
		goto error_out;
		break;
	default:
		goto error_out;
		break;
	}

pass_check:

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
		if (count == LPFC_QUE_ACC_BROWSE)
			idiag.offset.last_rd = index;
	}

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_ST ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_CL) {
		/* Additional sanity checks on write operation */
		pque = (struct lpfc_queue *)idiag.ptr_private;
		if (offset > pque->entry_size/sizeof(uint32_t) - 1)
			goto error_out;
		pentry = pque->qe[index].address;
		pentry += offset;
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR)
			*pentry = value;
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_ST)
			*pentry |= value;
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_CL)
			*pentry &= ~value;
	}
	return nbytes;

error_out:
	/* Clean out command structure on command error out */
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

/**
 * lpfc_idiag_drbacc_read_reg - idiag debugfs read a doorbell register
 * @phba: The pointer to hba structure.
 * @pbuffer: The pointer to the buffer to copy the data to.
 * @len: The lenght of bytes to copied.
 * @drbregid: The id to doorbell registers.
 *
 * Description:
 * This routine reads a doorbell register and copies its content to the
 * user buffer pointed to by @pbuffer.
 *
 * Returns:
 * This function returns the amount of data that was copied into @pbuffer.
 **/
static int
lpfc_idiag_drbacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
			   int len, uint32_t drbregid)
{

	if (!pbuffer)
		return 0;

	switch (drbregid) {
	case LPFC_DRB_EQCQ:
		len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
				"EQCQ-DRB-REG: 0x%08x\n",
				readl(phba->sli4_hba.EQCQDBregaddr));
		break;
	case LPFC_DRB_MQ:
		len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
				"MQ-DRB-REG:   0x%08x\n",
				readl(phba->sli4_hba.MQDBregaddr));
		break;
	case LPFC_DRB_WQ:
		len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
				"WQ-DRB-REG:   0x%08x\n",
				readl(phba->sli4_hba.WQDBregaddr));
		break;
	case LPFC_DRB_RQ:
		len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
				"RQ-DRB-REG:   0x%08x\n",
				readl(phba->sli4_hba.RQDBregaddr));
		break;
	default:
		break;
	}

	return len;
}

/**
 * lpfc_idiag_drbacc_read - idiag debugfs read port doorbell
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba device doorbell register according
 * to the idiag command, and copies to user @buf. Depending on the doorbell
 * register read command setup, it does either a single doorbell register
 * read or dump all doorbell registers.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_drbacc_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t drb_reg_id, i;
	char *pbuffer;
	int len = 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_DRB_ACC_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;

	if (*ppos)
		return 0;

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD)
		drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
	else
		return 0;

	if (drb_reg_id == LPFC_DRB_ACC_ALL)
		for (i = 1; i <= LPFC_DRB_MAX; i++)
			len = lpfc_idiag_drbacc_read_reg(phba,
							 pbuffer, len, i);
	else
		len = lpfc_idiag_drbacc_read_reg(phba,
						 pbuffer, len, drb_reg_id);

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_drbacc_write - Syntax check and set up idiag drbacc commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and then
 * perform the syntax check for port doorbell register read (dump) or write
 * (set) command accordingly. In the case of port queue read command, it sets
 * up the command in the idiag command struct for the following debugfs read
 * operation. In the case of port doorbell register write operation, it
 * executes the write operation into the port doorbell register accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 **/
static ssize_t
lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t drb_reg_id, value, reg_val = 0;
	void __iomem *drb_reg;
	int rc;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	/* Sanity check on command line arguments */
	drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
	value = idiag.cmd.data[IDIAG_DRBACC_VALUE_INDX];

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
		if (rc != LPFC_DRB_ACC_WR_CMD_ARG)
			goto error_out;
		if (drb_reg_id > LPFC_DRB_MAX)
			goto error_out;
	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD) {
		if (rc != LPFC_DRB_ACC_RD_CMD_ARG)
			goto error_out;
		if ((drb_reg_id > LPFC_DRB_MAX) &&
		    (drb_reg_id != LPFC_DRB_ACC_ALL))
			goto error_out;
	} else
		goto error_out;

	/* Perform the write access operation */
	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
		switch (drb_reg_id) {
		case LPFC_DRB_EQCQ:
			drb_reg = phba->sli4_hba.EQCQDBregaddr;
			break;
		case LPFC_DRB_MQ:
			drb_reg = phba->sli4_hba.MQDBregaddr;
			break;
		case LPFC_DRB_WQ:
			drb_reg = phba->sli4_hba.WQDBregaddr;
			break;
		case LPFC_DRB_RQ:
			drb_reg = phba->sli4_hba.RQDBregaddr;
			break;
		default:
			goto error_out;
		}

		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR)
			reg_val = value;
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST) {
			reg_val = readl(drb_reg);
			reg_val |= value;
		}
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
			reg_val = readl(drb_reg);
			reg_val &= ~value;
		}
		writel(reg_val, drb_reg);
		readl(drb_reg); /* flush */
	}
	return nbytes;

error_out:
	/* Clean out command structure on command error out */
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

/**
 * lpfc_idiag_ctlacc_read_reg - idiag debugfs read a control registers
 * @phba: The pointer to hba structure.
 * @pbuffer: The pointer to the buffer to copy the data to.
 * @len: The lenght of bytes to copied.
 * @drbregid: The id to doorbell registers.
 *
 * Description:
 * This routine reads a control register and copies its content to the
 * user buffer pointed to by @pbuffer.
 *
 * Returns:
 * This function returns the amount of data that was copied into @pbuffer.
 **/
static int
lpfc_idiag_ctlacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
			   int len, uint32_t ctlregid)
{

	if (!pbuffer)
		return 0;

	switch (ctlregid) {
	case LPFC_CTL_PORT_SEM:
		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
				"Port SemReg:   0x%08x\n",
				readl(phba->sli4_hba.conf_regs_memmap_p +
				      LPFC_CTL_PORT_SEM_OFFSET));
		break;
	case LPFC_CTL_PORT_STA:
		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
				"Port StaReg:   0x%08x\n",
				readl(phba->sli4_hba.conf_regs_memmap_p +
				      LPFC_CTL_PORT_STA_OFFSET));
		break;
	case LPFC_CTL_PORT_CTL:
		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
				"Port CtlReg:   0x%08x\n",
				readl(phba->sli4_hba.conf_regs_memmap_p +
				      LPFC_CTL_PORT_CTL_OFFSET));
		break;
	case LPFC_CTL_PORT_ER1:
		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
				"Port Er1Reg:   0x%08x\n",
				readl(phba->sli4_hba.conf_regs_memmap_p +
				      LPFC_CTL_PORT_ER1_OFFSET));
		break;
	case LPFC_CTL_PORT_ER2:
		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
				"Port Er2Reg:   0x%08x\n",
				readl(phba->sli4_hba.conf_regs_memmap_p +
				      LPFC_CTL_PORT_ER2_OFFSET));
		break;
	case LPFC_CTL_PDEV_CTL:
		len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
				"PDev CtlReg:   0x%08x\n",
				readl(phba->sli4_hba.conf_regs_memmap_p +
				      LPFC_CTL_PDEV_CTL_OFFSET));
		break;
	default:
		break;
	}
	return len;
}

/**
 * lpfc_idiag_ctlacc_read - idiag debugfs read port and device control register
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba port and device registers according
 * to the idiag command, and copies to user @buf.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_ctlacc_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t ctl_reg_id, i;
	char *pbuffer;
	int len = 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_CTL_ACC_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;

	if (*ppos)
		return 0;

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD)
		ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
	else
		return 0;

	if (ctl_reg_id == LPFC_CTL_ACC_ALL)
		for (i = 1; i <= LPFC_CTL_MAX; i++)
			len = lpfc_idiag_ctlacc_read_reg(phba,
							 pbuffer, len, i);
	else
		len = lpfc_idiag_ctlacc_read_reg(phba,
						 pbuffer, len, ctl_reg_id);

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_ctlacc_write - Syntax check and set up idiag ctlacc commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and then
 * perform the syntax check for port and device control register read (dump)
 * or write (set) command accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 **/
static ssize_t
lpfc_idiag_ctlacc_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	uint32_t ctl_reg_id, value, reg_val = 0;
	void __iomem *ctl_reg;
	int rc;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	/* Sanity check on command line arguments */
	ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
	value = idiag.cmd.data[IDIAG_CTLACC_VALUE_INDX];

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
		if (rc != LPFC_CTL_ACC_WR_CMD_ARG)
			goto error_out;
		if (ctl_reg_id > LPFC_CTL_MAX)
			goto error_out;
	} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD) {
		if (rc != LPFC_CTL_ACC_RD_CMD_ARG)
			goto error_out;
		if ((ctl_reg_id > LPFC_CTL_MAX) &&
		    (ctl_reg_id != LPFC_CTL_ACC_ALL))
			goto error_out;
	} else
		goto error_out;

	/* Perform the write access operation */
	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
	    idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
		switch (ctl_reg_id) {
		case LPFC_CTL_PORT_SEM:
			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
					LPFC_CTL_PORT_SEM_OFFSET;
			break;
		case LPFC_CTL_PORT_STA:
			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
					LPFC_CTL_PORT_STA_OFFSET;
			break;
		case LPFC_CTL_PORT_CTL:
			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
					LPFC_CTL_PORT_CTL_OFFSET;
			break;
		case LPFC_CTL_PORT_ER1:
			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
					LPFC_CTL_PORT_ER1_OFFSET;
			break;
		case LPFC_CTL_PORT_ER2:
			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
					LPFC_CTL_PORT_ER2_OFFSET;
			break;
		case LPFC_CTL_PDEV_CTL:
			ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
					LPFC_CTL_PDEV_CTL_OFFSET;
			break;
		default:
			goto error_out;
		}

		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR)
			reg_val = value;
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST) {
			reg_val = readl(ctl_reg);
			reg_val |= value;
		}
		if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
			reg_val = readl(ctl_reg);
			reg_val &= ~value;
		}
		writel(reg_val, ctl_reg);
		readl(ctl_reg); /* flush */
	}
	return nbytes;

error_out:
	/* Clean out command structure on command error out */
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

/**
 * lpfc_idiag_mbxacc_get_setup - idiag debugfs get mailbox access setup
 * @phba: Pointer to HBA context object.
 * @pbuffer: Pointer to data buffer.
 *
 * Description:
 * This routine gets the driver mailbox access debugfs setup information.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static int
lpfc_idiag_mbxacc_get_setup(struct lpfc_hba *phba, char *pbuffer)
{
	uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
	int len = 0;

	mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
	mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
	mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
	mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];

	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
			"mbx_dump_map: 0x%08x\n", mbx_dump_map);
	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
			"mbx_dump_cnt: %04d\n", mbx_dump_cnt);
	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
			"mbx_word_cnt: %04d\n", mbx_word_cnt);
	len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
			"mbx_mbox_cmd: 0x%02x\n", mbx_mbox_cmd);

	return len;
}

/**
 * lpfc_idiag_mbxacc_read - idiag debugfs read on mailbox access
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the @phba driver mailbox access debugfs setup
 * information.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_mbxacc_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	char *pbuffer;
	int len = 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_MBX_ACC_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;

	if (*ppos)
		return 0;

	if ((idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP) &&
	    (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP))
		return 0;

	len = lpfc_idiag_mbxacc_get_setup(phba, pbuffer);

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

/**
 * lpfc_idiag_mbxacc_write - Syntax check and set up idiag mbxacc commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and then
 * perform the syntax check for driver mailbox command (dump) and sets up the
 * necessary states in the idiag command struct accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 **/
static ssize_t
lpfc_idiag_mbxacc_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
	int rc;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	/* Sanity check on command line arguments */
	mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
	mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
	mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
	mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];

	if (idiag.cmd.opcode == LPFC_IDIAG_CMD_MBXACC_DP) {
		if (!(mbx_dump_map & LPFC_MBX_DMP_MBX_ALL))
			goto error_out;
		if ((mbx_dump_map & ~LPFC_MBX_DMP_MBX_ALL) &&
		    (mbx_dump_map != LPFC_MBX_DMP_ALL))
			goto error_out;
		if (mbx_word_cnt > sizeof(MAILBOX_t))
			goto error_out;
	} else if (idiag.cmd.opcode == LPFC_IDIAG_BSG_MBXACC_DP) {
		if (!(mbx_dump_map & LPFC_BSG_DMP_MBX_ALL))
			goto error_out;
		if ((mbx_dump_map & ~LPFC_BSG_DMP_MBX_ALL) &&
		    (mbx_dump_map != LPFC_MBX_DMP_ALL))
			goto error_out;
		if (mbx_word_cnt > (BSG_MBOX_SIZE)/4)
			goto error_out;
		if (mbx_mbox_cmd != 0x9b)
			goto error_out;
	} else
		goto error_out;

	if (mbx_word_cnt == 0)
		goto error_out;
	if (rc != LPFC_MBX_DMP_ARG)
		goto error_out;
	if (mbx_mbox_cmd & ~0xff)
		goto error_out;

	/* condition for stop mailbox dump */
	if (mbx_dump_cnt == 0)
		goto reset_out;

	return nbytes;

reset_out:
	/* Clean out command structure on command error out */
	memset(&idiag, 0, sizeof(idiag));
	return nbytes;

error_out:
	/* Clean out command structure on command error out */
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

/**
 * lpfc_idiag_extacc_avail_get - get the available extents information
 * @phba: pointer to lpfc hba data structure.
 * @pbuffer: pointer to internal buffer.
 * @len: length into the internal buffer data has been copied.
 *
 * Description:
 * This routine is to get the available extent information.
 *
 * Returns:
 * overall lenth of the data read into the internal buffer.
 **/
static int
lpfc_idiag_extacc_avail_get(struct lpfc_hba *phba, char *pbuffer, int len)
{
	uint16_t ext_cnt, ext_size;

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\nAvailable Extents Information:\n");

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tPort Available VPI extents: ");
	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VPI,
				       &ext_cnt, &ext_size);
	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"Count %3d, Size %3d\n", ext_cnt, ext_size);

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tPort Available VFI extents: ");
	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VFI,
				       &ext_cnt, &ext_size);
	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"Count %3d, Size %3d\n", ext_cnt, ext_size);

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tPort Available RPI extents: ");
	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_RPI,
				       &ext_cnt, &ext_size);
	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"Count %3d, Size %3d\n", ext_cnt, ext_size);

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tPort Available XRI extents: ");
	lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_XRI,
				       &ext_cnt, &ext_size);
	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"Count %3d, Size %3d\n", ext_cnt, ext_size);

	return len;
}

/**
 * lpfc_idiag_extacc_alloc_get - get the allocated extents information
 * @phba: pointer to lpfc hba data structure.
 * @pbuffer: pointer to internal buffer.
 * @len: length into the internal buffer data has been copied.
 *
 * Description:
 * This routine is to get the allocated extent information.
 *
 * Returns:
 * overall lenth of the data read into the internal buffer.
 **/
static int
lpfc_idiag_extacc_alloc_get(struct lpfc_hba *phba, char *pbuffer, int len)
{
	uint16_t ext_cnt, ext_size;
	int rc;

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\nAllocated Extents Information:\n");

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tHost Allocated VPI extents: ");
	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VPI,
					    &ext_cnt, &ext_size);
	if (!rc)
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"Port %d Extent %3d, Size %3d\n",
				phba->brd_no, ext_cnt, ext_size);
	else
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"N/A\n");

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tHost Allocated VFI extents: ");
	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VFI,
					    &ext_cnt, &ext_size);
	if (!rc)
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"Port %d Extent %3d, Size %3d\n",
				phba->brd_no, ext_cnt, ext_size);
	else
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"N/A\n");

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tHost Allocated RPI extents: ");
	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_RPI,
					    &ext_cnt, &ext_size);
	if (!rc)
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"Port %d Extent %3d, Size %3d\n",
				phba->brd_no, ext_cnt, ext_size);
	else
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"N/A\n");

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tHost Allocated XRI extents: ");
	rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_XRI,
					    &ext_cnt, &ext_size);
	if (!rc)
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"Port %d Extent %3d, Size %3d\n",
				phba->brd_no, ext_cnt, ext_size);
	else
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"N/A\n");

	return len;
}

/**
 * lpfc_idiag_extacc_drivr_get - get driver extent information
 * @phba: pointer to lpfc hba data structure.
 * @pbuffer: pointer to internal buffer.
 * @len: length into the internal buffer data has been copied.
 *
 * Description:
 * This routine is to get the driver extent information.
 *
 * Returns:
 * overall lenth of the data read into the internal buffer.
 **/
static int
lpfc_idiag_extacc_drivr_get(struct lpfc_hba *phba, char *pbuffer, int len)
{
	struct lpfc_rsrc_blks *rsrc_blks;
	int index;

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\nDriver Extents Information:\n");

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tVPI extents:\n");
	index = 0;
	list_for_each_entry(rsrc_blks, &phba->lpfc_vpi_blk_list, list) {
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"\t\tBlock %3d: Start %4d, Count %4d\n",
				index, rsrc_blks->rsrc_start,
				rsrc_blks->rsrc_size);
		index++;
	}
	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tVFI extents:\n");
	index = 0;
	list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_vfi_blk_list,
			    list) {
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"\t\tBlock %3d: Start %4d, Count %4d\n",
				index, rsrc_blks->rsrc_start,
				rsrc_blks->rsrc_size);
		index++;
	}

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tRPI extents:\n");
	index = 0;
	list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_rpi_blk_list,
			    list) {
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"\t\tBlock %3d: Start %4d, Count %4d\n",
				index, rsrc_blks->rsrc_start,
				rsrc_blks->rsrc_size);
		index++;
	}

	len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
			"\tXRI extents:\n");
	index = 0;
	list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_xri_blk_list,
			    list) {
		len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
				"\t\tBlock %3d: Start %4d, Count %4d\n",
				index, rsrc_blks->rsrc_start,
				rsrc_blks->rsrc_size);
		index++;
	}

	return len;
}

/**
 * lpfc_idiag_extacc_write - Syntax check and set up idiag extacc commands
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the user data from.
 * @nbytes: The number of bytes to get.
 * @ppos: The position in the file to start reading from.
 *
 * This routine get the debugfs idiag command struct from user space and then
 * perform the syntax check for extent information access commands and sets
 * up the necessary states in the idiag command struct accordingly.
 *
 * It returns the @nbytges passing in from debugfs user space when successful.
 * In case of error conditions, it returns proper error code back to the user
 * space.
 **/
static ssize_t
lpfc_idiag_extacc_write(struct file *file, const char __user *buf,
			size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	uint32_t ext_map;
	int rc;

	/* This is a user write operation */
	debug->op = LPFC_IDIAG_OP_WR;

	rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
	if (rc < 0)
		return rc;

	ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];

	if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
		goto error_out;
	if (rc != LPFC_EXT_ACC_CMD_ARG)
		goto error_out;
	if (!(ext_map & LPFC_EXT_ACC_ALL))
		goto error_out;

	return nbytes;
error_out:
	/* Clean out command structure on command error out */
	memset(&idiag, 0, sizeof(idiag));
	return -EINVAL;
}

/**
 * lpfc_idiag_extacc_read - idiag debugfs read access to extent information
 * @file: The file pointer to read from.
 * @buf: The buffer to copy the data to.
 * @nbytes: The number of bytes to read.
 * @ppos: The position in the file to start reading from.
 *
 * Description:
 * This routine reads data from the proper extent information according to
 * the idiag command, and copies to user @buf.
 *
 * Returns:
 * This function returns the amount of data that was read (this could be less
 * than @nbytes if the end of the file was reached) or a negative error value.
 **/
static ssize_t
lpfc_idiag_extacc_read(struct file *file, char __user *buf, size_t nbytes,
		       loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	char *pbuffer;
	uint32_t ext_map;
	int len = 0;

	/* This is a user read operation */
	debug->op = LPFC_IDIAG_OP_RD;

	if (!debug->buffer)
		debug->buffer = kmalloc(LPFC_EXT_ACC_BUF_SIZE, GFP_KERNEL);
	if (!debug->buffer)
		return 0;
	pbuffer = debug->buffer;
	if (*ppos)
		return 0;
	if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
		return 0;

	ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
	if (ext_map & LPFC_EXT_ACC_AVAIL)
		len = lpfc_idiag_extacc_avail_get(phba, pbuffer, len);
	if (ext_map & LPFC_EXT_ACC_ALLOC)
		len = lpfc_idiag_extacc_alloc_get(phba, pbuffer, len);
	if (ext_map & LPFC_EXT_ACC_DRIVR)
		len = lpfc_idiag_extacc_drivr_get(phba, pbuffer, len);

	return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}

#undef lpfc_debugfs_op_disc_trc
static const struct file_operations lpfc_debugfs_op_disc_trc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_disc_trc_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_nodelist
static const struct file_operations lpfc_debugfs_op_nodelist = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_nodelist_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_hbqinfo
static const struct file_operations lpfc_debugfs_op_hbqinfo = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_hbqinfo_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_dumpHBASlim
static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_dumpHBASlim_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_dumpHostSlim
static const struct file_operations lpfc_debugfs_op_dumpHostSlim = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_dumpHostSlim_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_nvmestat
static const struct file_operations lpfc_debugfs_op_nvmestat = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_nvmestat_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_nvmestat_write,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_nvmektime
static const struct file_operations lpfc_debugfs_op_nvmektime = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_nvmektime_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_nvmektime_write,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_nvmeio_trc
static const struct file_operations lpfc_debugfs_op_nvmeio_trc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_nvmeio_trc_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_nvmeio_trc_write,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_cpucheck
static const struct file_operations lpfc_debugfs_op_cpucheck = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_cpucheck_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_cpucheck_write,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_dumpData
static const struct file_operations lpfc_debugfs_op_dumpData = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_dumpData_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_dumpDataDif_write,
	.release =      lpfc_debugfs_dumpDataDif_release,
};

#undef lpfc_debugfs_op_dumpDif
static const struct file_operations lpfc_debugfs_op_dumpDif = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_dumpDif_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_dumpDataDif_write,
	.release =      lpfc_debugfs_dumpDataDif_release,
};

#undef lpfc_debugfs_op_dif_err
static const struct file_operations lpfc_debugfs_op_dif_err = {
	.owner =	THIS_MODULE,
	.open =		simple_open,
	.llseek =	lpfc_debugfs_lseek,
	.read =		lpfc_debugfs_dif_err_read,
	.write =	lpfc_debugfs_dif_err_write,
	.release =	lpfc_debugfs_dif_err_release,
};

#undef lpfc_debugfs_op_slow_ring_trc
static const struct file_operations lpfc_debugfs_op_slow_ring_trc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_slow_ring_trc_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_release,
};

static struct dentry *lpfc_debugfs_root = NULL;
static atomic_t lpfc_debugfs_hba_count;

/*
 * File operations for the iDiag debugfs
 */
#undef lpfc_idiag_op_pciCfg
static const struct file_operations lpfc_idiag_op_pciCfg = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_pcicfg_read,
	.write =        lpfc_idiag_pcicfg_write,
	.release =      lpfc_idiag_cmd_release,
};

#undef lpfc_idiag_op_barAcc
static const struct file_operations lpfc_idiag_op_barAcc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_baracc_read,
	.write =        lpfc_idiag_baracc_write,
	.release =      lpfc_idiag_cmd_release,
};

#undef lpfc_idiag_op_queInfo
static const struct file_operations lpfc_idiag_op_queInfo = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.read =         lpfc_idiag_queinfo_read,
	.release =      lpfc_idiag_release,
};

#undef lpfc_idiag_op_queAcc
static const struct file_operations lpfc_idiag_op_queAcc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_queacc_read,
	.write =        lpfc_idiag_queacc_write,
	.release =      lpfc_idiag_cmd_release,
};

#undef lpfc_idiag_op_drbAcc
static const struct file_operations lpfc_idiag_op_drbAcc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_drbacc_read,
	.write =        lpfc_idiag_drbacc_write,
	.release =      lpfc_idiag_cmd_release,
};

#undef lpfc_idiag_op_ctlAcc
static const struct file_operations lpfc_idiag_op_ctlAcc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_ctlacc_read,
	.write =        lpfc_idiag_ctlacc_write,
	.release =      lpfc_idiag_cmd_release,
};

#undef lpfc_idiag_op_mbxAcc
static const struct file_operations lpfc_idiag_op_mbxAcc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_mbxacc_read,
	.write =        lpfc_idiag_mbxacc_write,
	.release =      lpfc_idiag_cmd_release,
};

#undef lpfc_idiag_op_extAcc
static const struct file_operations lpfc_idiag_op_extAcc = {
	.owner =        THIS_MODULE,
	.open =         lpfc_idiag_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_idiag_extacc_read,
	.write =        lpfc_idiag_extacc_write,
	.release =      lpfc_idiag_cmd_release,
};

#endif

/* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command
 * @phba: Pointer to HBA context object.
 * @dmabuf: Pointer to a DMA buffer descriptor.
 *
 * Description:
 * This routine dump a bsg pass-through non-embedded mailbox command with
 * external buffer.
 **/
void
lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp,
				enum mbox_type mbox_tp, enum dma_type dma_tp,
				enum sta_type sta_tp,
				struct lpfc_dmabuf *dmabuf, uint32_t ext_buf)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	uint32_t *mbx_mbox_cmd, *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt;
	char line_buf[LPFC_MBX_ACC_LBUF_SZ];
	int len = 0;
	uint32_t do_dump = 0;
	uint32_t *pword;
	uint32_t i;

	if (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP)
		return;

	mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
	mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
	mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
	mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];

	if (!(*mbx_dump_map & LPFC_MBX_DMP_ALL) ||
	    (*mbx_dump_cnt == 0) ||
	    (*mbx_word_cnt == 0))
		return;

	if (*mbx_mbox_cmd != 0x9B)
		return;

	if ((mbox_tp == mbox_rd) && (dma_tp == dma_mbox)) {
		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_MBX) {
			do_dump |= LPFC_BSG_DMP_MBX_RD_MBX;
			pr_err("\nRead mbox command (x%x), "
			       "nemb:0x%x, extbuf_cnt:%d:\n",
			       sta_tp, nemb_tp, ext_buf);
		}
	}
	if ((mbox_tp == mbox_rd) && (dma_tp == dma_ebuf)) {
		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_BUF) {
			do_dump |= LPFC_BSG_DMP_MBX_RD_BUF;
			pr_err("\nRead mbox buffer (x%x), "
			       "nemb:0x%x, extbuf_seq:%d:\n",
			       sta_tp, nemb_tp, ext_buf);
		}
	}
	if ((mbox_tp == mbox_wr) && (dma_tp == dma_mbox)) {
		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_MBX) {
			do_dump |= LPFC_BSG_DMP_MBX_WR_MBX;
			pr_err("\nWrite mbox command (x%x), "
			       "nemb:0x%x, extbuf_cnt:%d:\n",
			       sta_tp, nemb_tp, ext_buf);
		}
	}
	if ((mbox_tp == mbox_wr) && (dma_tp == dma_ebuf)) {
		if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_BUF) {
			do_dump |= LPFC_BSG_DMP_MBX_WR_BUF;
			pr_err("\nWrite mbox buffer (x%x), "
			       "nemb:0x%x, extbuf_seq:%d:\n",
			       sta_tp, nemb_tp, ext_buf);
		}
	}

	/* dump buffer content */
	if (do_dump) {
		pword = (uint32_t *)dmabuf->virt;
		for (i = 0; i < *mbx_word_cnt; i++) {
			if (!(i % 8)) {
				if (i != 0)
					pr_err("%s\n", line_buf);
				len = 0;
				len += snprintf(line_buf+len,
						LPFC_MBX_ACC_LBUF_SZ-len,
						"%03d: ", i);
			}
			len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
					"%08x ", (uint32_t)*pword);
			pword++;
		}
		if ((i - 1) % 8)
			pr_err("%s\n", line_buf);
		(*mbx_dump_cnt)--;
	}

	/* Clean out command structure on reaching dump count */
	if (*mbx_dump_cnt == 0)
		memset(&idiag, 0, sizeof(idiag));
	return;
#endif
}

/* lpfc_idiag_mbxacc_dump_issue_mbox - idiag debugfs dump issue mailbox command
 * @phba: Pointer to HBA context object.
 * @dmabuf: Pointer to a DMA buffer descriptor.
 *
 * Description:
 * This routine dump a pass-through non-embedded mailbox command from issue
 * mailbox command.
 **/
void
lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	uint32_t *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt, *mbx_mbox_cmd;
	char line_buf[LPFC_MBX_ACC_LBUF_SZ];
	int len = 0;
	uint32_t *pword;
	uint8_t *pbyte;
	uint32_t i, j;

	if (idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP)
		return;

	mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
	mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
	mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
	mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];

	if (!(*mbx_dump_map & LPFC_MBX_DMP_MBX_ALL) ||
	    (*mbx_dump_cnt == 0) ||
	    (*mbx_word_cnt == 0))
		return;

	if ((*mbx_mbox_cmd != LPFC_MBX_ALL_CMD) &&
	    (*mbx_mbox_cmd != pmbox->mbxCommand))
		return;

	/* dump buffer content */
	if (*mbx_dump_map & LPFC_MBX_DMP_MBX_WORD) {
		pr_err("Mailbox command:0x%x dump by word:\n",
		       pmbox->mbxCommand);
		pword = (uint32_t *)pmbox;
		for (i = 0; i < *mbx_word_cnt; i++) {
			if (!(i % 8)) {
				if (i != 0)
					pr_err("%s\n", line_buf);
				len = 0;
				memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
				len += snprintf(line_buf+len,
						LPFC_MBX_ACC_LBUF_SZ-len,
						"%03d: ", i);
			}
			len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
					"%08x ",
					((uint32_t)*pword) & 0xffffffff);
			pword++;
		}
		if ((i - 1) % 8)
			pr_err("%s\n", line_buf);
		pr_err("\n");
	}
	if (*mbx_dump_map & LPFC_MBX_DMP_MBX_BYTE) {
		pr_err("Mailbox command:0x%x dump by byte:\n",
		       pmbox->mbxCommand);
		pbyte = (uint8_t *)pmbox;
		for (i = 0; i < *mbx_word_cnt; i++) {
			if (!(i % 8)) {
				if (i != 0)
					pr_err("%s\n", line_buf);
				len = 0;
				memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
				len += snprintf(line_buf+len,
						LPFC_MBX_ACC_LBUF_SZ-len,
						"%03d: ", i);
			}
			for (j = 0; j < 4; j++) {
				len += snprintf(line_buf+len,
						LPFC_MBX_ACC_LBUF_SZ-len,
						"%02x",
						((uint8_t)*pbyte) & 0xff);
				pbyte++;
			}
			len += snprintf(line_buf+len,
					LPFC_MBX_ACC_LBUF_SZ-len, " ");
		}
		if ((i - 1) % 8)
			pr_err("%s\n", line_buf);
		pr_err("\n");
	}
	(*mbx_dump_cnt)--;

	/* Clean out command structure on reaching dump count */
	if (*mbx_dump_cnt == 0)
		memset(&idiag, 0, sizeof(idiag));
	return;
#endif
}

/**
 * lpfc_debugfs_initialize - Initialize debugfs for a vport
 * @vport: The vport pointer to initialize.
 *
 * Description:
 * When Debugfs is configured this routine sets up the lpfc debugfs file system.
 * If not already created, this routine will create the lpfc directory, and
 * lpfcX directory (for this HBA), and vportX directory for this vport. It will
 * also create each file used to access lpfc specific debugfs information.
 **/
inline void
lpfc_debugfs_initialize(struct lpfc_vport *vport)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	struct lpfc_hba   *phba = vport->phba;
	char name[64];
	uint32_t num, i;
	bool pport_setup = false;

	if (!lpfc_debugfs_enable)
		return;

	/* Setup lpfc root directory */
	if (!lpfc_debugfs_root) {
		lpfc_debugfs_root = debugfs_create_dir("lpfc", NULL);
		atomic_set(&lpfc_debugfs_hba_count, 0);
		if (!lpfc_debugfs_root) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "0408 Cannot create debugfs root\n");
			goto debug_failed;
		}
	}
	if (!lpfc_debugfs_start_time)
		lpfc_debugfs_start_time = jiffies;

	/* Setup funcX directory for specific HBA PCI function */
	snprintf(name, sizeof(name), "fn%d", phba->brd_no);
	if (!phba->hba_debugfs_root) {
		pport_setup = true;
		phba->hba_debugfs_root =
			debugfs_create_dir(name, lpfc_debugfs_root);
		if (!phba->hba_debugfs_root) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "0412 Cannot create debugfs hba\n");
			goto debug_failed;
		}
		atomic_inc(&lpfc_debugfs_hba_count);
		atomic_set(&phba->debugfs_vport_count, 0);

		/* Setup hbqinfo */
		snprintf(name, sizeof(name), "hbqinfo");
		phba->debug_hbqinfo =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				 phba->hba_debugfs_root,
				 phba, &lpfc_debugfs_op_hbqinfo);
		if (!phba->debug_hbqinfo) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0411 Cannot create debugfs hbqinfo\n");
			goto debug_failed;
		}

		/* Setup dumpHBASlim */
		if (phba->sli_rev < LPFC_SLI_REV4) {
			snprintf(name, sizeof(name), "dumpHBASlim");
			phba->debug_dumpHBASlim =
				debugfs_create_file(name,
					S_IFREG|S_IRUGO|S_IWUSR,
					phba->hba_debugfs_root,
					phba, &lpfc_debugfs_op_dumpHBASlim);
			if (!phba->debug_dumpHBASlim) {
				lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
						 "0413 Cannot create debugfs "
						"dumpHBASlim\n");
				goto debug_failed;
			}
		} else
			phba->debug_dumpHBASlim = NULL;

		/* Setup dumpHostSlim */
		if (phba->sli_rev < LPFC_SLI_REV4) {
			snprintf(name, sizeof(name), "dumpHostSlim");
			phba->debug_dumpHostSlim =
				debugfs_create_file(name,
					S_IFREG|S_IRUGO|S_IWUSR,
					phba->hba_debugfs_root,
					phba, &lpfc_debugfs_op_dumpHostSlim);
			if (!phba->debug_dumpHostSlim) {
				lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
						 "0414 Cannot create debugfs "
						 "dumpHostSlim\n");
				goto debug_failed;
			}
		} else
			phba->debug_dumpHostSlim = NULL;

		/* Setup dumpData */
		snprintf(name, sizeof(name), "dumpData");
		phba->debug_dumpData =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				 phba->hba_debugfs_root,
				 phba, &lpfc_debugfs_op_dumpData);
		if (!phba->debug_dumpData) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0800 Cannot create debugfs dumpData\n");
			goto debug_failed;
		}

		/* Setup dumpDif */
		snprintf(name, sizeof(name), "dumpDif");
		phba->debug_dumpDif =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				 phba->hba_debugfs_root,
				 phba, &lpfc_debugfs_op_dumpDif);
		if (!phba->debug_dumpDif) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0801 Cannot create debugfs dumpDif\n");
			goto debug_failed;
		}

		/* Setup DIF Error Injections */
		snprintf(name, sizeof(name), "InjErrLBA");
		phba->debug_InjErrLBA =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_InjErrLBA) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0807 Cannot create debugfs InjErrLBA\n");
			goto debug_failed;
		}
		phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;

		snprintf(name, sizeof(name), "InjErrNPortID");
		phba->debug_InjErrNPortID =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_InjErrNPortID) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0809 Cannot create debugfs InjErrNPortID\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "InjErrWWPN");
		phba->debug_InjErrWWPN =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_InjErrWWPN) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0810 Cannot create debugfs InjErrWWPN\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "writeGuardInjErr");
		phba->debug_writeGuard =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_writeGuard) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0802 Cannot create debugfs writeGuard\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "writeAppInjErr");
		phba->debug_writeApp =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_writeApp) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0803 Cannot create debugfs writeApp\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "writeRefInjErr");
		phba->debug_writeRef =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_writeRef) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0804 Cannot create debugfs writeRef\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "readGuardInjErr");
		phba->debug_readGuard =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_readGuard) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0808 Cannot create debugfs readGuard\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "readAppInjErr");
		phba->debug_readApp =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_readApp) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0805 Cannot create debugfs readApp\n");
			goto debug_failed;
		}

		snprintf(name, sizeof(name), "readRefInjErr");
		phba->debug_readRef =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
			phba->hba_debugfs_root,
			phba, &lpfc_debugfs_op_dif_err);
		if (!phba->debug_readRef) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				"0806 Cannot create debugfs readApp\n");
			goto debug_failed;
		}

		/* Setup slow ring trace */
		if (lpfc_debugfs_max_slow_ring_trc) {
			num = lpfc_debugfs_max_slow_ring_trc - 1;
			if (num & lpfc_debugfs_max_slow_ring_trc) {
				/* Change to be a power of 2 */
				num = lpfc_debugfs_max_slow_ring_trc;
				i = 0;
				while (num > 1) {
					num = num >> 1;
					i++;
				}
				lpfc_debugfs_max_slow_ring_trc = (1 << i);
				pr_err("lpfc_debugfs_max_disc_trc changed to "
				       "%d\n", lpfc_debugfs_max_disc_trc);
			}
		}

		snprintf(name, sizeof(name), "slow_ring_trace");
		phba->debug_slow_ring_trc =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				 phba->hba_debugfs_root,
				 phba, &lpfc_debugfs_op_slow_ring_trc);
		if (!phba->debug_slow_ring_trc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "0415 Cannot create debugfs "
					 "slow_ring_trace\n");
			goto debug_failed;
		}
		if (!phba->slow_ring_trc) {
			phba->slow_ring_trc = kmalloc(
				(sizeof(struct lpfc_debugfs_trc) *
				lpfc_debugfs_max_slow_ring_trc),
				GFP_KERNEL);
			if (!phba->slow_ring_trc) {
				lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
						 "0416 Cannot create debugfs "
						 "slow_ring buffer\n");
				goto debug_failed;
			}
			atomic_set(&phba->slow_ring_trc_cnt, 0);
			memset(phba->slow_ring_trc, 0,
				(sizeof(struct lpfc_debugfs_trc) *
				lpfc_debugfs_max_slow_ring_trc));
		}

		snprintf(name, sizeof(name), "nvmeio_trc");
		phba->debug_nvmeio_trc =
			debugfs_create_file(name, 0644,
					    phba->hba_debugfs_root,
					    phba, &lpfc_debugfs_op_nvmeio_trc);
		if (!phba->debug_nvmeio_trc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "0574 No create debugfs nvmeio_trc\n");
			goto debug_failed;
		}

		atomic_set(&phba->nvmeio_trc_cnt, 0);
		if (lpfc_debugfs_max_nvmeio_trc) {
			num = lpfc_debugfs_max_nvmeio_trc - 1;
			if (num & lpfc_debugfs_max_disc_trc) {
				/* Change to be a power of 2 */
				num = lpfc_debugfs_max_nvmeio_trc;
				i = 0;
				while (num > 1) {
					num = num >> 1;
					i++;
				}
				lpfc_debugfs_max_nvmeio_trc = (1 << i);
				lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
						"0575 lpfc_debugfs_max_nvmeio_trc "
						"changed to %d\n",
						lpfc_debugfs_max_nvmeio_trc);
			}
			phba->nvmeio_trc_size = lpfc_debugfs_max_nvmeio_trc;

			/* Allocate trace buffer and initialize */
			phba->nvmeio_trc = kmalloc(
				(sizeof(struct lpfc_debugfs_nvmeio_trc) *
				phba->nvmeio_trc_size), GFP_KERNEL);

			if (!phba->nvmeio_trc) {
				lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
						"0576 Cannot create debugfs "
						"nvmeio_trc buffer\n");
				goto nvmeio_off;
			}
			memset(phba->nvmeio_trc, 0,
			       (sizeof(struct lpfc_debugfs_nvmeio_trc) *
			       phba->nvmeio_trc_size));
			phba->nvmeio_trc_on = 1;
			phba->nvmeio_trc_output_idx = 0;
			phba->nvmeio_trc = NULL;
		} else {
nvmeio_off:
			phba->nvmeio_trc_size = 0;
			phba->nvmeio_trc_on = 0;
			phba->nvmeio_trc_output_idx = 0;
			phba->nvmeio_trc = NULL;
		}
	}

	snprintf(name, sizeof(name), "vport%d", vport->vpi);
	if (!vport->vport_debugfs_root) {
		vport->vport_debugfs_root =
			debugfs_create_dir(name, phba->hba_debugfs_root);
		if (!vport->vport_debugfs_root) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "0417 Can't create debugfs\n");
			goto debug_failed;
		}
		atomic_inc(&phba->debugfs_vport_count);
	}

	if (lpfc_debugfs_max_disc_trc) {
		num = lpfc_debugfs_max_disc_trc - 1;
		if (num & lpfc_debugfs_max_disc_trc) {
			/* Change to be a power of 2 */
			num = lpfc_debugfs_max_disc_trc;
			i = 0;
			while (num > 1) {
				num = num >> 1;
				i++;
			}
			lpfc_debugfs_max_disc_trc = (1 << i);
			pr_err("lpfc_debugfs_max_disc_trc changed to %d\n",
			       lpfc_debugfs_max_disc_trc);
		}
	}

	vport->disc_trc = kzalloc(
		(sizeof(struct lpfc_debugfs_trc) * lpfc_debugfs_max_disc_trc),
		GFP_KERNEL);

	if (!vport->disc_trc) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "0418 Cannot create debugfs disc trace "
				 "buffer\n");
		goto debug_failed;
	}
	atomic_set(&vport->disc_trc_cnt, 0);

	snprintf(name, sizeof(name), "discovery_trace");
	vport->debug_disc_trc =
		debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				 vport->vport_debugfs_root,
				 vport, &lpfc_debugfs_op_disc_trc);
	if (!vport->debug_disc_trc) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "0419 Cannot create debugfs "
				 "discovery_trace\n");
		goto debug_failed;
	}
	snprintf(name, sizeof(name), "nodelist");
	vport->debug_nodelist =
		debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				 vport->vport_debugfs_root,
				 vport, &lpfc_debugfs_op_nodelist);
	if (!vport->debug_nodelist) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "2985 Can't create debugfs nodelist\n");
		goto debug_failed;
	}

	snprintf(name, sizeof(name), "nvmestat");
	vport->debug_nvmestat =
		debugfs_create_file(name, 0644,
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_nvmestat);
	if (!vport->debug_nvmestat) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "0811 Cannot create debugfs nvmestat\n");
		goto debug_failed;
	}

	snprintf(name, sizeof(name), "nvmektime");
	vport->debug_nvmektime =
		debugfs_create_file(name, 0644,
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_nvmektime);
	if (!vport->debug_nvmektime) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "0815 Cannot create debugfs nvmektime\n");
		goto debug_failed;
	}

	snprintf(name, sizeof(name), "cpucheck");
	vport->debug_cpucheck =
		debugfs_create_file(name, 0644,
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_cpucheck);
	if (!vport->debug_cpucheck) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "0819 Cannot create debugfs cpucheck\n");
		goto debug_failed;
	}

	/*
	 * The following section is for additional directories/files for the
	 * physical port.
	 */

	if (!pport_setup)
		goto debug_failed;

	/*
	 * iDiag debugfs root entry points for SLI4 device only
	 */
	if (phba->sli_rev < LPFC_SLI_REV4)
		goto debug_failed;

	snprintf(name, sizeof(name), "iDiag");
	if (!phba->idiag_root) {
		phba->idiag_root =
			debugfs_create_dir(name, phba->hba_debugfs_root);
		if (!phba->idiag_root) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "2922 Can't create idiag debugfs\n");
			goto debug_failed;
		}
		/* Initialize iDiag data structure */
		memset(&idiag, 0, sizeof(idiag));
	}

	/* iDiag read PCI config space */
	snprintf(name, sizeof(name), "pciCfg");
	if (!phba->idiag_pci_cfg) {
		phba->idiag_pci_cfg =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				phba->idiag_root, phba, &lpfc_idiag_op_pciCfg);
		if (!phba->idiag_pci_cfg) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "2923 Can't create idiag debugfs\n");
			goto debug_failed;
		}
		idiag.offset.last_rd = 0;
	}

	/* iDiag PCI BAR access */
	snprintf(name, sizeof(name), "barAcc");
	if (!phba->idiag_bar_acc) {
		phba->idiag_bar_acc =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				phba->idiag_root, phba, &lpfc_idiag_op_barAcc);
		if (!phba->idiag_bar_acc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					"3056 Can't create idiag debugfs\n");
			goto debug_failed;
		}
		idiag.offset.last_rd = 0;
	}

	/* iDiag get PCI function queue information */
	snprintf(name, sizeof(name), "queInfo");
	if (!phba->idiag_que_info) {
		phba->idiag_que_info =
			debugfs_create_file(name, S_IFREG|S_IRUGO,
			phba->idiag_root, phba, &lpfc_idiag_op_queInfo);
		if (!phba->idiag_que_info) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "2924 Can't create idiag debugfs\n");
			goto debug_failed;
		}
	}

	/* iDiag access PCI function queue */
	snprintf(name, sizeof(name), "queAcc");
	if (!phba->idiag_que_acc) {
		phba->idiag_que_acc =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				phba->idiag_root, phba, &lpfc_idiag_op_queAcc);
		if (!phba->idiag_que_acc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "2926 Can't create idiag debugfs\n");
			goto debug_failed;
		}
	}

	/* iDiag access PCI function doorbell registers */
	snprintf(name, sizeof(name), "drbAcc");
	if (!phba->idiag_drb_acc) {
		phba->idiag_drb_acc =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				phba->idiag_root, phba, &lpfc_idiag_op_drbAcc);
		if (!phba->idiag_drb_acc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "2927 Can't create idiag debugfs\n");
			goto debug_failed;
		}
	}

	/* iDiag access PCI function control registers */
	snprintf(name, sizeof(name), "ctlAcc");
	if (!phba->idiag_ctl_acc) {
		phba->idiag_ctl_acc =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				phba->idiag_root, phba, &lpfc_idiag_op_ctlAcc);
		if (!phba->idiag_ctl_acc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "2981 Can't create idiag debugfs\n");
			goto debug_failed;
		}
	}

	/* iDiag access mbox commands */
	snprintf(name, sizeof(name), "mbxAcc");
	if (!phba->idiag_mbx_acc) {
		phba->idiag_mbx_acc =
			debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
				phba->idiag_root, phba, &lpfc_idiag_op_mbxAcc);
		if (!phba->idiag_mbx_acc) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					"2980 Can't create idiag debugfs\n");
			goto debug_failed;
		}
	}

	/* iDiag extents access commands */
	if (phba->sli4_hba.extents_in_use) {
		snprintf(name, sizeof(name), "extAcc");
		if (!phba->idiag_ext_acc) {
			phba->idiag_ext_acc =
				debugfs_create_file(name,
						    S_IFREG|S_IRUGO|S_IWUSR,
						    phba->idiag_root, phba,
						    &lpfc_idiag_op_extAcc);
			if (!phba->idiag_ext_acc) {
				lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
						"2986 Cant create "
						"idiag debugfs\n");
				goto debug_failed;
			}
		}
	}

debug_failed:
	return;
#endif
}

/**
 * lpfc_debugfs_terminate -  Tear down debugfs infrastructure for this vport
 * @vport: The vport pointer to remove from debugfs.
 *
 * Description:
 * When Debugfs is configured this routine removes debugfs file system elements
 * that are specific to this vport. It also checks to see if there are any
 * users left for the debugfs directories associated with the HBA and driver. If
 * this is the last user of the HBA directory or driver directory then it will
 * remove those from the debugfs infrastructure as well.
 **/
inline void
lpfc_debugfs_terminate(struct lpfc_vport *vport)
{
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	struct lpfc_hba   *phba = vport->phba;

	kfree(vport->disc_trc);
	vport->disc_trc = NULL;

	debugfs_remove(vport->debug_disc_trc); /* discovery_trace */
	vport->debug_disc_trc = NULL;

	debugfs_remove(vport->debug_nodelist); /* nodelist */
	vport->debug_nodelist = NULL;

	debugfs_remove(vport->debug_nvmestat); /* nvmestat */
	vport->debug_nvmestat = NULL;

	debugfs_remove(vport->debug_nvmektime); /* nvmektime */
	vport->debug_nvmektime = NULL;

	debugfs_remove(vport->debug_cpucheck); /* cpucheck */
	vport->debug_cpucheck = NULL;

	if (vport->vport_debugfs_root) {
		debugfs_remove(vport->vport_debugfs_root); /* vportX */
		vport->vport_debugfs_root = NULL;
		atomic_dec(&phba->debugfs_vport_count);
	}

	if (atomic_read(&phba->debugfs_vport_count) == 0) {

		debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */
		phba->debug_hbqinfo = NULL;

		debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */
		phba->debug_dumpHBASlim = NULL;

		debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
		phba->debug_dumpHostSlim = NULL;

		debugfs_remove(phba->debug_dumpData); /* dumpData */
		phba->debug_dumpData = NULL;

		debugfs_remove(phba->debug_dumpDif); /* dumpDif */
		phba->debug_dumpDif = NULL;

		debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */
		phba->debug_InjErrLBA = NULL;

		debugfs_remove(phba->debug_InjErrNPortID);
		phba->debug_InjErrNPortID = NULL;

		debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */
		phba->debug_InjErrWWPN = NULL;

		debugfs_remove(phba->debug_writeGuard); /* writeGuard */
		phba->debug_writeGuard = NULL;

		debugfs_remove(phba->debug_writeApp); /* writeApp */
		phba->debug_writeApp = NULL;

		debugfs_remove(phba->debug_writeRef); /* writeRef */
		phba->debug_writeRef = NULL;

		debugfs_remove(phba->debug_readGuard); /* readGuard */
		phba->debug_readGuard = NULL;

		debugfs_remove(phba->debug_readApp); /* readApp */
		phba->debug_readApp = NULL;

		debugfs_remove(phba->debug_readRef); /* readRef */
		phba->debug_readRef = NULL;

		kfree(phba->slow_ring_trc);
		phba->slow_ring_trc = NULL;

		/* slow_ring_trace */
		debugfs_remove(phba->debug_slow_ring_trc);
		phba->debug_slow_ring_trc = NULL;

		debugfs_remove(phba->debug_nvmeio_trc);
		phba->debug_nvmeio_trc = NULL;

		kfree(phba->nvmeio_trc);
		phba->nvmeio_trc = NULL;

		/*
		 * iDiag release
		 */
		if (phba->sli_rev == LPFC_SLI_REV4) {
			/* iDiag extAcc */
			debugfs_remove(phba->idiag_ext_acc);
			phba->idiag_ext_acc = NULL;

			/* iDiag mbxAcc */
			debugfs_remove(phba->idiag_mbx_acc);
			phba->idiag_mbx_acc = NULL;

			/* iDiag ctlAcc */
			debugfs_remove(phba->idiag_ctl_acc);
			phba->idiag_ctl_acc = NULL;

			/* iDiag drbAcc */
			debugfs_remove(phba->idiag_drb_acc);
			phba->idiag_drb_acc = NULL;

			/* iDiag queAcc */
			debugfs_remove(phba->idiag_que_acc);
			phba->idiag_que_acc = NULL;

			/* iDiag queInfo */
			debugfs_remove(phba->idiag_que_info);
			phba->idiag_que_info = NULL;

			/* iDiag barAcc */
			debugfs_remove(phba->idiag_bar_acc);
			phba->idiag_bar_acc = NULL;

			/* iDiag pciCfg */
			debugfs_remove(phba->idiag_pci_cfg);
			phba->idiag_pci_cfg = NULL;

			/* Finally remove the iDiag debugfs root */
			debugfs_remove(phba->idiag_root);
			phba->idiag_root = NULL;
		}

		if (phba->hba_debugfs_root) {
			debugfs_remove(phba->hba_debugfs_root); /* fnX */
			phba->hba_debugfs_root = NULL;
			atomic_dec(&lpfc_debugfs_hba_count);
		}

		debugfs_remove(lpfc_debugfs_root); /* lpfc */
		lpfc_debugfs_root = NULL;
	}
#endif
	return;
}

/*
 * Driver debug utility routines outside of debugfs. The debug utility
 * routines implemented here is intended to be used in the instrumented
 * debug driver for debugging host or port issues.
 */

/**
 * lpfc_debug_dump_all_queues - dump all the queues with a hba
 * @phba: Pointer to HBA context object.
 *
 * This function dumps entries of all the queues asociated with the @phba.
 **/
void
lpfc_debug_dump_all_queues(struct lpfc_hba *phba)
{
	int idx;

	/*
	 * Dump Work Queues (WQs)
	 */
	lpfc_debug_dump_wq(phba, DUMP_MBX, 0);
	lpfc_debug_dump_wq(phba, DUMP_ELS, 0);
	lpfc_debug_dump_wq(phba, DUMP_NVMELS, 0);

	for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++)
		lpfc_debug_dump_wq(phba, DUMP_FCP, idx);

	for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++)
		lpfc_debug_dump_wq(phba, DUMP_NVME, idx);

	lpfc_debug_dump_hdr_rq(phba);
	lpfc_debug_dump_dat_rq(phba);
	/*
	 * Dump Complete Queues (CQs)
	 */
	lpfc_debug_dump_cq(phba, DUMP_MBX, 0);
	lpfc_debug_dump_cq(phba, DUMP_ELS, 0);
	lpfc_debug_dump_cq(phba, DUMP_NVMELS, 0);

	for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++)
		lpfc_debug_dump_cq(phba, DUMP_FCP, idx);

	for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++)
		lpfc_debug_dump_cq(phba, DUMP_NVME, idx);

	/*
	 * Dump Event Queues (EQs)
	 */
	for (idx = 0; idx < phba->io_channel_irqs; idx++)
		lpfc_debug_dump_hba_eq(phba, idx);
}