From 1197134c26fa38531c4521dce999cae96875bcef Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Mon, 5 Dec 2016 11:35:56 -0800 Subject: [PATCH] UBUNTU: SAUCE: hio: update to Huawei ES3000_V2 (2.1.0.28) BugLink: http://bugs.launchpad.net/bugs/1646643 Update to latest upstream driver version, from: http://support.huawei.com/enterprise/SoftwareVersionActionNew!showVDetailNew?lang=en&idAbsPath=fixnode01%7C7919749%7C9856522%7C9856629%7C21242728&pid=21242728&vrc=21243470%7C21243471%7C21243473%7C21992501&from=soft&tab=bz&bz_vr=21243471&bz_vrc=&nbz_vr=null Signed-off-by: Kamal Mostafa Signed-off-by: Tim Gardner --- ubuntu/hio/hio.c | 857 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 625 insertions(+), 232 deletions(-) diff --git a/ubuntu/hio/hio.c b/ubuntu/hio/hio.c index fd58f37ffbac..4320262a76ba 100644 --- a/ubuntu/hio/hio.c +++ b/ubuntu/hio/hio.c @@ -60,14 +60,10 @@ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17)) #include #endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0)) -#define bio_endio(bio, errors) \ - do { bio->bi_error = errors; bio_endio(bio); } while (0) -#endif /* driver */ #define MODULE_NAME "hio" -#define DRIVER_VERSION "2.1.0.23" +#define DRIVER_VERSION "2.1.0.28" #define DRIVER_VERSION_LEN 16 #define SSD_FW_MIN 0x1 @@ -1402,6 +1398,7 @@ typedef struct ssd_device { /* debug info */ struct ssd_debug_info db_info; + uint64_t reset_time; } ssd_device_t; @@ -1732,6 +1729,8 @@ static struct sbs_cmd ssd_bm_sbs[] = { #define SSD_CMD_DEBUG _IOW('H', 250, struct ssd_debug_info) #define SSD_CMD_DRV_PARAM_INFO _IOR('H', 251, struct ssd_drv_param_info) +#define SSD_CMD_CLEAR_WARNING _IOW('H', 260, int) + /* log */ #define SSD_LOG_MAX_SZ 4096 @@ -1883,8 +1882,8 @@ static struct ssd_log_desc ssd_log_desc[] = { {0x8c, SSD_LOG_LEVEL_WARNING, SSD_LOG_DATA_NONE, 0, 0, "Warning: P/E cycles close to limit"}, {0x8d, SSD_LOG_LEVEL_ERR, SSD_LOG_DATA_NONE, 0, 0, "Error: P/E cycles over limit"}, - {0x90, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Over temperature"}, //xx - {0x91, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Temperature is OK"}, //xx + {0x90, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Warning: Over temperature"}, //90 + {0x91, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Info: Temperature is OK"}, //80 {0x92, SSD_LOG_LEVEL_WARNING, SSD_LOG_DATA_NONE, 0, 0, "Battery fault"}, {0x93, SSD_LOG_LEVEL_WARNING, SSD_LOG_DATA_NONE, 0, 0, "SEU fault"}, //err {0x94, SSD_LOG_LEVEL_ERR, SSD_LOG_DATA_NONE, 0, 0, "DDR error"}, //err @@ -1893,7 +1892,7 @@ static struct ssd_log_desc ssd_log_desc[] = { {0x97, SSD_LOG_LEVEL_ERR, SSD_LOG_DATA_NONE, 0, 0, "Bridge serdes 2 error"}, //err {0x98, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "SEU fault (corrected)"}, //err {0x99, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Battery is OK"}, - {0x9a, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Temperature close to limit"}, //xx + {0x9a, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Info: Temperature close to limit"}, //85 {0x9b, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_HEX, 0, 0, "SEU fault address (low)"}, {0x9c, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_HEX, 0, 0, "SEU fault address (high)"}, @@ -1914,8 +1913,8 @@ static struct ssd_log_desc ssd_log_desc[] = { {0xaa, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_LOC, 0, 0, "Flash init failure"}, {0xab, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_LOC, 1, 1, "Mapping table recovery failure"}, {0xac, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_LOC, 1, 1, "RAID recovery: ECC failed"}, - {0xb0, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Temperature is up to degree 95"}, - {0xb1, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Temperature is up to degree 100"}, + {0xb0, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Warning: Temperature is 95 degrees centigrade"}, + {0xb1, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Warning: Temperature is 100 degrees centigrade"}, {0x300, SSD_LOG_LEVEL_ERR, SSD_LOG_DATA_HEX, 0, 0, "CMD timeout"}, {0x301, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_HEX, 0, 0, "Power on"}, @@ -1939,10 +1938,10 @@ static struct ssd_log_desc ssd_log_desc[] = { {0x313, SSD_LOG_LEVEL_WARNING,SSD_LOG_DATA_NONE, 0, 0, "CAP: learn fault"}, {0x314, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_HEX, 0, 0, "CAP status"}, {0x315, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_HEX, 0, 0, "Board voltage fault status"}, - {0x316, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Inlet over temperature"}, - {0x317, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Inlet temperature is OK"}, - {0x318, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Flash over temperature"}, - {0x319, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Flash temperature is OK"}, + {0x316, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Info: Inlet temperature is 55 degrees centigrade"}, //55 + {0x317, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Info: Inlet temperature is 50 degrees centigrade"}, //50 + {0x318, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Info: Flash over temperature"}, //70 + {0x319, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Info: Flash temperature is OK"}, //65 {0x31a, SSD_LOG_LEVEL_WARNING,SSD_LOG_DATA_NONE, 0, 0, "CAP: short circuit"}, {0x31b, SSD_LOG_LEVEL_WARNING,SSD_LOG_DATA_HEX, 0, 0, "Sensor fault"}, {0x31c, SSD_LOG_LEVEL_NOTICE, SSD_LOG_DATA_NONE, 0, 0, "Erase all data"}, @@ -2081,6 +2080,53 @@ MODULE_PARM_DESC(ot_protect, "over temperature protect, 0 - disable, 1 - enable" MODULE_PARM_DESC(wmode, "write mode, 0 - write buffer (with risk for the 6xx firmware), 1 - write buffer ex, 2 - write through, 3 - auto, 4 - default"); MODULE_PARM_DESC(finject, "enable fault simulation, 0 - off, 1 - on, for debug purpose only"); +// API adaption layer +static inline void ssd_bio_endio(struct bio *bio, int error) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)) + bio->bi_error = error; + bio_endio(bio); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) + bio_endio(bio, error); +#else + bio_endio(bio, bio->bi_size, error); +#endif +} + +static inline int ssd_bio_has_discard(struct bio *bio) +{ +#ifndef SSD_TRIM + return 0; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) + return bio_op(bio) & REQ_OP_DISCARD; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + return bio->bi_rw & REQ_DISCARD; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) + return bio_rw_flagged(bio, BIO_RW_DISCARD); +#else + return 0; +#endif +} + +static inline int ssd_bio_has_flush(struct bio *bio) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) + return bio_op(bio) & REQ_OP_FLUSH; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) + return bio->bi_rw & REQ_FLUSH; +#else + return 0; +#endif +} + +static inline int ssd_bio_has_fua(struct bio *bio) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) + return bio->bi_opf & REQ_FUA; +#else + return bio->bi_rw & REQ_FUA; +#endif +} #ifndef MODULE static int __init ssd_drv_mode(char *str) @@ -2171,7 +2217,7 @@ static int ssd_proc_read(char *page, char **start, int len = 0; //char type; //xx - if (ssd_exiting) { + if (ssd_exiting || off != 0) { return 0; } @@ -2200,6 +2246,7 @@ static int ssd_proc_read(char *page, char **start, len += snprintf((page + len), (count - len), "HIO %d Device:\t%s\n", idx, dev->name); } + *eof = 1; return len; } @@ -3696,7 +3743,8 @@ static int ssd_lm80_check_event(struct ssd_device *dev, uint8_t saddr) uint32_t volt; uint16_t val = 0, status; uint8_t alarm1 = 0, alarm2 = 0; - int i; + uint32_t low, high; + int i,j=0; int ret = 0; /* read interrupt status to clear interrupt */ @@ -3736,12 +3784,27 @@ static int ssd_lm80_check_event(struct ssd_device *dev, uint8_t saddr) continue; } - ret = ssd_smbus_read_word(dev, saddr, SSD_LM80_REG_IN(i), (uint8_t *)&val); - if (ret) { - goto out; + high = (uint32_t)ssd_lm80_limit[i].high * (uint32_t)10; + low = (uint32_t)ssd_lm80_limit[i].low * (uint32_t)10; + + for (j=0; j<3; j++) { + ret = ssd_smbus_read_word(dev, saddr, SSD_LM80_REG_IN(i), (uint8_t *)&val); + if (ret) { + goto out; + } + volt = SSD_LM80_CONVERT_VOLT(u16_swap(val)); + if ((volt>high) || (volt<=low)) { + if(j<2) { + msleep(SSD_LM80_CONV_INTERVAL); + } + } else { + break; + } } - volt = SSD_LM80_CONVERT_VOLT(u16_swap(val)); + if (j<3) { + continue; + } switch (i) { case SSD_LM80_IN_CAP: { @@ -4136,44 +4199,19 @@ static inline void ssd_end_request(struct ssd_cmd *cmd) int tag = cmd->tag; if (bio) { -#if (defined SSD_TRIM && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) - if (!(bio_op(bio) & REQ_OP_DISCARD)) { -#else - if (!(bio->bi_rw & REQ_DISCARD)) { -#endif - ssd_end_io_acct(cmd); - if (!cmd->flag) { - pci_unmap_sg(dev->pdev, cmd->sgl, cmd->nsegs, - bio_data_dir(bio) == READ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - } - } -#elif (defined SSD_TRIM && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))) - if (!bio_rw_flagged(bio, BIO_RW_DISCARD)) { + if (!ssd_bio_has_discard(bio)) { ssd_end_io_acct(cmd); if (!cmd->flag) { pci_unmap_sg(dev->pdev, cmd->sgl, cmd->nsegs, bio_data_dir(bio) == READ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); } } -#else - ssd_end_io_acct(cmd); - - if (!cmd->flag) { - pci_unmap_sg(dev->pdev, cmd->sgl, cmd->nsegs, - bio_data_dir(bio) == READ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - } -#endif cmd->bio = NULL; ssd_put_tag(dev, tag); if (SSD_INT_MSIX == dev->int_mode || tag < 16 || errors) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, errors); -#else - bio_endio(bio, bio->bi_size, errors); -#endif + ssd_bio_endio(bio, errors); } else /* if (bio->bi_idx >= bio->bi_vcnt)*/ { spin_lock(&dev->doneq_lock); ssd_blist_add(&dev->doneq, bio); @@ -4559,39 +4597,7 @@ static int __ssd_submit_pbio(struct ssd_device *dev, struct bio *bio, int wait) msg = (struct ssd_rw_msg *)cmd->msg; -#if (defined SSD_TRIM && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) - if (bio_op(bio) & REQ_OP_DISCARD) { -#else - if (bio->bi_rw & REQ_DISCARD) { -#endif - unsigned int length = bio_sectors(bio); - - //printk(KERN_WARNING "%s: discard len %u, block %llu\n", dev->name, bio_sectors(bio), block); - msg->tag = tag; - msg->fun = SSD_FUNC_TRIM; - - sge = msg->sge; - for (i=0; i<(dev->hw_info.cmd_max_sg); i++) { - sge->block = block; - sge->length = (length >= dev->hw_info.sg_max_sec) ? dev->hw_info.sg_max_sec : length; - sge->buf = 0; - - block += sge->length; - length -= sge->length; - sge++; - - if (length <= 0) { - break; - } - } - msg->nsegs = cmd->nsegs = (i + 1); - - dev->scmd(cmd); - return 0; - } -#elif (defined SSD_TRIM && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))) - if (bio_rw_flagged(bio, BIO_RW_DISCARD)) { + if (ssd_bio_has_discard(bio)) { unsigned int length = bio_sectors(bio); //printk(KERN_WARNING "%s: discard len %u, block %llu\n", dev->name, bio_sectors(bio), block); @@ -4609,15 +4615,15 @@ static int __ssd_submit_pbio(struct ssd_device *dev, struct bio *bio, int wait) sge++; if (length <= 0) { + ++i; break; } } - msg->nsegs = cmd->nsegs = (i + 1); + msg->nsegs = cmd->nsegs = i; dev->scmd(cmd); return 0; } -#endif //msg->nsegs = cmd->nsegs = ssd_bio_map_sg(dev, bio, sgl); msg->nsegs = cmd->nsegs = bio->bi_vcnt; @@ -4678,39 +4684,7 @@ static inline int ssd_submit_bio(struct ssd_device *dev, struct bio *bio, int wa sgl = cmd->sgl; -#if (defined SSD_TRIM && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) - if (bio_op(bio) & REQ_OP_DISCARD) { -#else - if (bio->bi_rw & REQ_DISCARD) { -#endif - unsigned int length = bio_sectors(bio); - - //printk(KERN_WARNING "%s: discard len %u, block %llu\n", dev->name, bio_sectors(bio), block); - msg->tag = tag; - msg->fun = SSD_FUNC_TRIM; - - sge = msg->sge; - for (i=0; i<(dev->hw_info.cmd_max_sg); i++) { - sge->block = block; - sge->length = (length >= dev->hw_info.sg_max_sec) ? dev->hw_info.sg_max_sec : length; - sge->buf = 0; - - block += sge->length; - length -= sge->length; - sge++; - - if (length <= 0) { - break; - } - } - msg->nsegs = cmd->nsegs = (i + 1); - - dev->scmd(cmd); - return 0; - } -#elif (defined SSD_TRIM && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))) - if (bio_rw_flagged(bio, BIO_RW_DISCARD)) { + if (ssd_bio_has_discard(bio)) { unsigned int length = bio_sectors(bio); //printk(KERN_WARNING "%s: discard len %u, block %llu\n", dev->name, bio_sectors(bio), block); @@ -4728,15 +4702,15 @@ static inline int ssd_submit_bio(struct ssd_device *dev, struct bio *bio, int wa sge++; if (length <= 0) { + ++i; break; } } - msg->nsegs = cmd->nsegs = (i + 1); + msg->nsegs = cmd->nsegs = i; dev->scmd(cmd); return 0; } -#endif msg->nsegs = cmd->nsegs = ssd_bio_map_sg(dev, bio, sgl); @@ -4788,6 +4762,7 @@ static int ssd_done_thread(void *data) } dev = data; + current->flags |= PF_NOFREEZE; //set_user_nice(current, -5); while (!kthread_should_stop()) { @@ -4807,11 +4782,7 @@ static int ssd_done_thread(void *data) while (bio) { next = bio->bi_next; bio->bi_next = NULL; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, 0); -#else - bio_endio(bio, bio->bi_size, 0); -#endif + ssd_bio_endio(bio, 0); atomic_dec(&dev->in_doneq); bio = next; } @@ -4822,11 +4793,12 @@ static int ssd_done_thread(void *data) if (unlikely(smp_processor_id() == dev->irq_cpu)) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) cpumask_var_t new_mask; - alloc_cpumask_var(&new_mask, GFP_ATOMIC); - cpumask_setall(new_mask); - cpumask_clear_cpu(dev->irq_cpu, new_mask); - set_cpus_allowed_ptr(current, new_mask); - free_cpumask_var(new_mask); + if (alloc_cpumask_var(&new_mask, GFP_ATOMIC)) { + cpumask_setall(new_mask); + cpumask_clear_cpu(dev->irq_cpu, new_mask); + set_cpus_allowed_ptr(current, new_mask); + free_cpumask_var(new_mask); + } #else cpumask_t new_mask; cpus_setall(new_mask); @@ -4851,6 +4823,7 @@ static int ssd_send_thread(void *data) } dev = data; + current->flags |= PF_NOFREEZE; //set_user_nice(current, -5); while (!kthread_should_stop()) { @@ -4883,11 +4856,12 @@ static int ssd_send_thread(void *data) if (unlikely(smp_processor_id() == dev->irq_cpu)) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) cpumask_var_t new_mask; - alloc_cpumask_var(&new_mask, GFP_ATOMIC); - cpumask_setall(new_mask); - cpumask_clear_cpu(dev->irq_cpu, new_mask); - set_cpus_allowed_ptr(current, new_mask); - free_cpumask_var(new_mask); + if (alloc_cpumask_var(&new_mask, GFP_ATOMIC)) { + cpumask_setall(new_mask); + cpumask_clear_cpu(dev->irq_cpu, new_mask); + set_cpus_allowed_ptr(current, new_mask); + free_cpumask_var(new_mask); + } #else cpumask_t new_mask; cpus_setall(new_mask); @@ -5735,6 +5709,7 @@ static void ssd_cleanup_log(struct ssd_device *dev) if (dev->internal_log.log) { vfree(dev->internal_log.log); + dev->internal_log.nr_log = 0; dev->internal_log.log = NULL; } } @@ -5757,6 +5732,7 @@ static int ssd_init_log(struct ssd_device *dev) off = dev->rom_info.log_base; size = dev->rom_info.log_sz; + dev->internal_log.nr_log = 0; dev->internal_log.log = vmalloc(size); if (!dev->internal_log.log) { ret = -ENOMEM; @@ -6063,6 +6039,69 @@ out: return ret; } +static int ssd_clear_warning(struct ssd_device *dev) +{ + uint32_t off, size; + int i, ret = 0; + + if (dev->protocol_info.ver <= SSD_PROTOCOL_V3) { + return 0; + } + + /* clear log_info warning */ + memset(&dev->smart.log_info, 0, sizeof(dev->smart.log_info)); + + /* clear io_stat warning */ + dev->smart.io_stat.nr_to = 0; + dev->smart.io_stat.nr_rwerr = 0; + dev->smart.io_stat.nr_ioerr = 0; + + /* clear ecc_info warning */ + memset(&dev->smart.ecc_info, 0, sizeof(dev->smart.ecc_info)); + + /* clear queued warnings */ + for (i=0; inr_queue; i++) { + /* queued io_stat warning */ + dev->queue[i].io_stat.nr_to = 0; + dev->queue[i].io_stat.nr_rwerr = 0; + dev->queue[i].io_stat.nr_ioerr = 0; + + /* queued ecc_info warning */ + memset(&(dev->queue[i].ecc_info), 0, sizeof(dev->queue[i].ecc_info)); + } + + /* write smart back to nor */ + for (i = 0; i < dev->rom_info.nr_smart; i++) { + off = dev->rom_info.smart_base + (dev->rom_info.smart_sz * i); + size = dev->rom_info.smart_sz; + + ret = ssd_spi_erase(dev, off, size); + if (ret) { + hio_warn("%s: warning erase: failed with code 1\n", dev->name); + goto out; + } + + size = sizeof(struct ssd_smart); + + ret = ssd_spi_write(dev, &dev->smart, off, size); + if (ret) { + hio_warn("%s: warning erase: failed with code 2\n", dev->name); + goto out; + } + } + + dev->smart.version++; + + /* clear cmd timeout warning */ + atomic_set(&dev->tocnt, 0); + + /* clear tmp log info */ + memset(&dev->log_info, 0, sizeof(dev->log_info)); + +out: + return ret; +} + static int ssd_save_smart(struct ssd_device *dev) { uint32_t off, size; @@ -7724,6 +7763,7 @@ static void ssd_reset_resp_ptr(struct ssd_device *dev); /* reset flash controller etc */ static int __ssd_reset(struct ssd_device *dev, int type) { + struct timeval tv; if (type < SSD_RST_NOINIT || type > SSD_RST_FULL) { return -EINVAL; } @@ -7757,6 +7797,8 @@ static int __ssd_reset(struct ssd_device *dev, int type) mutex_unlock(&dev->fw_mutex); ssd_gen_swlog(dev, SSD_LOG_RESET, (uint32_t)type); + do_gettimeofday(&tv); + dev->reset_time = tv.tv_sec; return __ssd_check_init_state(dev); } @@ -8163,75 +8205,43 @@ void ssd_submit_pbio(struct request_queue *q, struct bio *bio) #endif if (!test_bit(SSD_ONLINE, &dev->state)) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -ENODEV); -#else - bio_endio(bio, bio->bi_size, -ENODEV); -#endif + ssd_bio_endio(bio, -ENODEV); goto out; } #ifdef SSD_DEBUG_ERR if (atomic_read(&dev->tocnt)) { hio_warn("%s: IO rejected because of IO timeout!\n", dev->name); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EIO); -#else - bio_endio(bio, bio->bi_size, -EIO); -#endif + ssd_bio_endio(bio, -EIO); goto out; } #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) if (unlikely(bio_barrier(bio))) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #else //xx -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) - if (unlikely(bio->bi_opf & REQ_FUA)) { -#else - if (unlikely(bio->bi_rw & REQ_FUA)) { -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + if (unlikely(ssd_bio_has_fua(bio))) { + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #endif if (unlikely(dev->readonly && bio_data_dir(bio) == WRITE)) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EROFS); -#else - bio_endio(bio, bio->bi_size, -EROFS); -#endif + ssd_bio_endio(bio, -EROFS); goto out; } @@ -8264,84 +8274,52 @@ static int ssd_make_request(struct request_queue *q, struct bio *bio) int ret = -EBUSY; if (!test_bit(SSD_ONLINE, &dev->state)) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -ENODEV); -#else - bio_endio(bio, bio->bi_size, -ENODEV); -#endif + ssd_bio_endio(bio, -ENODEV); goto out; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0)) + blk_queue_split(q, &bio, q->bio_split); +#endif + #ifdef SSD_DEBUG_ERR if (atomic_read(&dev->tocnt)) { hio_warn("%s: IO rejected because of IO timeout!\n", dev->name); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EIO); -#else - bio_endio(bio, bio->bi_size, -EIO); -#endif + ssd_bio_endio(bio, -EIO); goto out; } #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) if (unlikely(bio_barrier(bio))) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } #else //xx -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) - if (unlikely(bio->bi_opf & REQ_FUA)) { -#else - if (unlikely(bio->bi_rw & REQ_FUA)) { -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - bio_endio(bio, -EOPNOTSUPP); -#else - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); -#endif + if (unlikely(ssd_bio_has_fua(bio))) { + ssd_bio_endio(bio, -EOPNOTSUPP); goto out; } /* writeback_cache_control.txt: REQ_FLUSH requests without data can be completed successfully without doing any work */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) - if (unlikely((bio_op(bio) & REQ_OP_FLUSH) && !bio_sectors(bio))) { -#else - if (unlikely((bio->bi_rw & REQ_FLUSH) && !bio_sectors(bio))) { -#endif - bio_endio(bio, 0); + if (unlikely(ssd_bio_has_flush(bio) && !bio_sectors(bio))) { + ssd_bio_endio(bio, 0); goto out; } #endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0)) - blk_queue_split(q, &bio, q->bio_split); -#endif - if (0 == atomic_read(&dev->in_sendq)) { ret = ssd_submit_bio(dev, bio, 0); } @@ -8381,6 +8359,8 @@ static int ssd_block_getgeo(struct block_device *bdev, struct hd_geometry *geo) } #endif +static int ssd_init_queue(struct ssd_device *dev); +static void ssd_cleanup_queue(struct ssd_device *dev); static void ssd_cleanup_blkdev(struct ssd_device *dev); static int ssd_init_blkdev(struct ssd_device *dev); static int ssd_ioctl_common(struct ssd_device *dev, unsigned int cmd, unsigned long arg) @@ -9531,6 +9511,7 @@ static int ssd_ioctl_common(struct ssd_device *dev, unsigned int cmd, unsigned l if (test_and_clear_bit(SSD_INIT_BD, &dev->state)) { mutex_lock(&dev->gd_mutex); ssd_cleanup_blkdev(dev); + ssd_cleanup_queue(dev); mutex_unlock(&dev->gd_mutex); } @@ -9550,6 +9531,11 @@ static int ssd_ioctl_common(struct ssd_device *dev, unsigned int cmd, unsigned l break; } + ret = ssd_init_queue(dev); + if (ret) { + hio_warn("%s: init queue failed\n", dev->name); + break; + } ret = ssd_init_blkdev(dev); if (ret) { hio_warn("%s: register block device: failed\n", dev->name); @@ -9700,6 +9686,11 @@ static int ssd_ioctl_common(struct ssd_device *dev, unsigned int cmd, unsigned l break; } + case SSD_CMD_CLEAR_WARNING: { + ret = ssd_clear_warning(dev); + break; + } + case SSD_CMD_SW_LOG: { struct ssd_sw_log_info sw_log; @@ -10258,17 +10249,16 @@ static int ssd_init_blkdev(struct ssd_device *dev) dev->gd->fops = &ssd_fops; dev->gd->queue = dev->rq; dev->gd->private_data = dev; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)) - dev->gd->driverfs_dev = &dev->pdev->dev; -#endif + snprintf (dev->gd->disk_name, sizeof(dev->gd->disk_name), "%s", dev->name); set_capacity(dev->gd, dev->hw_info.size >> 9); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)) - add_disk(dev->gd); -#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) device_add_disk(&dev->pdev->dev, dev->gd); +#else + dev->gd->driverfs_dev = &dev->pdev->dev; + add_disk(dev->gd); #endif return 0; @@ -10350,6 +10340,23 @@ static int ssd_release(struct inode *inode, struct file *file) return 0; } +static int ssd_reload_ssd_ptr(struct ssd_device *dev) +{ + ssd_reset_resp_ptr(dev); + + //update base reg address + if (dev->protocol_info.ver >= SSD_PROTOCOL_V3) { + + ssd_reg_write(dev->ctrlp + SSD_MSG_BASE_REG, dev->msg_base_dma); + } + + //update response base reg address + ssd_reg_write(dev->ctrlp + SSD_RESP_FIFO_REG, dev->resp_msg_base_dma); + ssd_reg_write(dev->ctrlp + SSD_RESP_PTR_REG, dev->resp_ptr_base_dma); + + return 0; +} + static struct file_operations ssd_cfops = { .owner = THIS_MODULE, .open = ssd_open, @@ -11432,7 +11439,7 @@ static void ssd_free_irq(struct ssd_device *dev) static int ssd_init_irq(struct ssd_device *dev) { #if (!defined MODULE) && (defined SSD_MSIX_AFFINITY_FORCE) - const struct cpumask *cpu_mask; + const struct cpumask *cpu_mask = NULL; static int cpu_affinity = 0; #endif #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) || (defined RHEL_MAJOR && RHEL_MAJOR == 6)) @@ -11890,6 +11897,7 @@ ssd_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct ssd_device *dev; + struct timeval tv; int ret = 0; if (!pdev || !ent) { @@ -11929,6 +11937,9 @@ ssd_init_one(struct pci_dev *pdev, dev->cmajor = 0; } + do_gettimeofday(&tv); + dev->reset_time = tv.tv_sec; + atomic_set(&(dev->refcnt), 0); atomic_set(&(dev->tocnt), 0); @@ -12294,17 +12305,382 @@ static struct pci_device_id ssd_pci_tbl[] = { { 0x19e5, 0x000a, PCI_ANY_ID, PCI_ANY_ID, }, /* v2 dp slave*/ { 0, } }; -MODULE_DEVICE_TABLE(pci, ssd_pci_tbl); -static struct pci_driver ssd_driver = { - .name = MODULE_NAME, - .id_table = ssd_pci_tbl, - .probe = ssd_init_one, -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) - .remove = __devexit_p(ssd_remove_one), +/*driver power management handler for pm_ops*/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) +static int ssd_hio_suspend(struct pci_dev *pdev, pm_message_t state) +{ +#else +static int ssd_hio_suspend(struct device *ddev) +{ + struct pci_dev *pdev = to_pci_dev(ddev); +#endif + struct ssd_device *dev; + + + if (!pdev) { + return -EINVAL; + } + + dev = pci_get_drvdata(pdev); + if (!dev) { + return -EINVAL; + } + + hio_warn("%s: suspend disk start.\n", dev->name); + ssd_unregister_sysfs(dev); + + /* offline firstly */ + test_and_clear_bit(SSD_ONLINE, &dev->state); + + /* clean work queue first */ + if (!dev->slave) { + test_and_clear_bit(SSD_INIT_WORKQ, &dev->state); + ssd_cleanup_workq(dev); + } + + /* flush cache */ + (void)ssd_flush(dev); + (void)ssd_save_md(dev); + + /* save smart */ + if (!dev->slave) { + ssd_save_smart(dev); + } + + /* clean routine */ + if (!dev->slave) { + ssd_cleanup_routine(dev); + } + + ssd_cleanup_thread(dev); + + ssd_free_irq(dev); + + if (!dev->slave) { + ssd_cleanup_log(dev); + } + + if (dev->reload_fw) { //reload fw + ssd_reg32_write(dev->ctrlp + SSD_RELOAD_FW_REG, SSD_RELOAD_FW); + } + + /* unmap physical adress */ + if (dev->ctrlp) { +#ifdef LINUX_SUSE_OS + iounmap(dev->ctrlp); +#else + pci_iounmap(pdev, dev->ctrlp); +#endif + dev->ctrlp = NULL; + } + + if (dev->mmio_base) { + release_mem_region(dev->mmio_base, dev->mmio_len); + dev->mmio_base = 0; + } + + pci_disable_device(pdev); + + hio_warn("%s: suspend disk finish.\n", dev->name); + + return 0; +} + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) +static int ssd_hio_resume(struct pci_dev *pdev) +{ +#else +static int ssd_hio_resume(struct device *ddev) +{ + struct pci_dev *pdev = to_pci_dev(ddev); +#endif + struct ssd_device *dev = NULL; + int ret = 0; + + if (!pdev ) { + ret = -EINVAL; + goto out; + } + + dev = pci_get_drvdata(pdev); + if (!dev) { + ret = -ENOMEM; + goto out_alloc_dev; + } + + hio_warn("%s: resume disk start.\n", dev->name); + ret = pci_enable_device(pdev); + if (ret) { + hio_warn("%s: can not enable device\n", dev->name); + goto out_enable_device; + } + + pci_set_master(pdev); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) + ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK); +#else + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); +#endif + if (ret) { + hio_warn("%s: set dma mask: failed\n", dev->name); + goto out_set_dma_mask; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) + ret = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); +#else + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); +#endif + if (ret) { + hio_warn("%s: set consistent dma mask: failed\n", dev->name); + goto out_set_dma_mask; + } + + dev->mmio_base = pci_resource_start(pdev, 0); + dev->mmio_len = pci_resource_len(pdev, 0); + + if (!request_mem_region(dev->mmio_base, dev->mmio_len, SSD_DEV_NAME)) { + hio_warn("%s: can not reserve MMIO region 0\n", dev->name); + ret = -EBUSY; + goto out_request_mem_region; + } + + /* 2.6.9 kernel bug */ + dev->ctrlp = pci_iomap(pdev, 0, 0); + if (!dev->ctrlp) { + hio_warn("%s: can not remap IO region 0\n", dev->name); + ret = -ENOMEM; + goto out_pci_iomap; + } + + ret = ssd_check_hw(dev); + if (ret) { + hio_err("%s: check hardware failed\n", dev->name); + goto out_check_hw; + } + + /* alarm led ? */ + ssd_clear_alarm(dev); + + ret = ssd_init_fw_info(dev); + if (ret) { + hio_err("%s: init firmware info failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_fw_info; + } + + /* slave port ? */ + if (dev->slave) { + goto init_next1; + } + + ret = ssd_init_rom_info(dev); + if (ret) { + hio_err("%s: init rom info failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_rom_info; + } + + ret = ssd_init_label(dev); + if (ret) { + hio_err("%s: init label failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_label; + } + + ret = ssd_init_workq(dev); + if (ret) { + hio_warn("%s: init workq failed\n", dev->name); + goto out_init_workq; + } + (void)test_and_set_bit(SSD_INIT_WORKQ, &dev->state); + + ret = ssd_init_log(dev); + if (ret) { + hio_err("%s: init log failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_log; + } + + ret = ssd_init_smart(dev); + if (ret) { + hio_err("%s: init info failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_smart; + } + +init_next1: + ret = ssd_init_hw_info(dev); + if (ret) { + hio_err("%s: init hardware info failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_hw_info; + } + + /* slave port ? */ + if (dev->slave) { + goto init_next2; + } + + ret = ssd_init_sensor(dev); + if (ret) { + hio_err("%s: init sensor failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_sensor; + } + + ret = ssd_init_pl_cap(dev); + if (ret) { + hio_err("%s: int pl_cap failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_init_pl_cap; + } + +init_next2: + ret = ssd_check_init_state(dev); + if (ret) { + hio_err("%s: check init state failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_check_init_state; + } + + //flush all base pointer to ssd + (void)ssd_reload_ssd_ptr(dev); + + ret = ssd_init_irq(dev); + if (ret) { + hio_warn("%s: init irq failed\n", dev->name); + goto out_init_irq; + } + + ret = ssd_init_thread(dev); + if (ret) { + hio_warn("%s: init thread failed\n", dev->name); + goto out_init_thread; + } + + /* */ + (void)test_and_set_bit(SSD_ONLINE, &dev->state); + + /* slave port ? */ + if (dev->slave) { + goto init_next3; + } + + ret = ssd_init_ot_protect(dev); + if (ret) { + hio_err("%s: int ot_protect failed\n", dev->name); + /* alarm led */ + ssd_set_alarm(dev); + goto out_int_ot_protect; + } + + ret = ssd_init_wmode(dev); + if (ret) { + hio_warn("%s: init write mode\n", dev->name); + goto out_init_wmode; + } + + /* init routine after hw is ready */ + ret = ssd_init_routine(dev); + if (ret) { + hio_warn("%s: init routine\n", dev->name); + goto out_init_routine; + } + +init_next3: + (void)test_and_set_bit(SSD_INIT_BD, &dev->state); + + dev->save_md = 1; + + hio_warn("%s: resume disk finish.\n", dev->name); + + return 0; + +out_init_routine: +out_init_wmode: +out_int_ot_protect: + ssd_cleanup_thread(dev); +out_init_thread: + ssd_free_irq(dev); +out_init_irq: +out_check_init_state: +out_init_pl_cap: +out_init_sensor: +out_init_hw_info: +out_init_smart: + /* slave port ? */ + if (!dev->slave) { + ssd_cleanup_log(dev); + } +out_init_log: + /* slave port ? */ + if (!dev->slave) { + test_and_clear_bit(SSD_INIT_WORKQ, &dev->state); + ssd_cleanup_workq(dev); + } +out_init_workq: +out_init_label: +out_init_rom_info: +out_init_fw_info: +out_check_hw: +#ifdef LINUX_SUSE_OS + iounmap(dev->ctrlp); +#else + pci_iounmap(pdev, dev->ctrlp); +#endif +out_pci_iomap: + release_mem_region(dev->mmio_base, dev->mmio_len); +out_request_mem_region: +out_set_dma_mask: + pci_disable_device(pdev); +out_enable_device: +out_alloc_dev: +out: + + hio_warn("%s: resume disk fail.\n", dev->name); + + return ret; +} + +MODULE_DEVICE_TABLE(pci, ssd_pci_tbl); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) +#else +SIMPLE_DEV_PM_OPS(hio_pm_ops, ssd_hio_suspend, ssd_hio_resume); +#endif + +MODULE_DEVICE_TABLE(pci, ssd_pci_tbl); +struct pci_driver ssd_driver = { + .name = MODULE_NAME, + .id_table = ssd_pci_tbl, + .probe = ssd_init_one, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) + .remove = __devexit_p(ssd_remove_one), #else .remove = ssd_remove_one, #endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)) + .suspend = ssd_hio_suspend, + .resume = ssd_hio_resume, +#else + .driver = { + .pm = &hio_pm_ops, + }, +#endif }; /* notifier block to get a notify on system shutdown/halt/reboot */ @@ -12508,8 +12884,9 @@ int ssd_register_event_notifier(struct block_device *bdev, ssd_event_call event_ { struct ssd_device *dev; struct timeval tv; - struct ssd_log *le; + struct ssd_log *le, *temp_le = NULL; uint64_t cur; + int temp = 0; int log_nr; if (!bdev || !event_call || !(bdev->bd_disk)) { @@ -12527,11 +12904,27 @@ int ssd_register_event_notifier(struct block_device *bdev, ssd_event_call event_ while (log_nr--) { if (le->time <= cur && le->time >= dev->uptime) { + if ((le->le.event == SSD_LOG_SEU_FAULT1) && (le->time < dev->reset_time)) { + le++; + continue; + } + if (le->le.event == SSD_LOG_OVER_TEMP || le->le.event == SSD_LOG_NORMAL_TEMP || le->le.event == SSD_LOG_WARN_TEMP) { + if (!temp_le || le->time >= temp_le->time) { + temp_le = le; + } + le++; + continue; + } (void)dev->event_call(dev->gd, le->le.event, ssd_parse_log(dev, le, 0)); } le++; } + ssd_get_temperature(bdev, &temp); + if (temp_le && (temp >= SSD_OT_TEMP_HYST)) { + (void)dev->event_call(dev->gd, temp_le->le.event, ssd_parse_log(dev, temp_le, 0)); + } + return 0; } -- 2.39.2