Brian King <brk...@linux.vnet.ibm.com> writes:

> This converts ipr's queuecommand path to use percpu refcounts
> rather than atomics.
>
> Signed-off-by: Brian King <brk...@linux.vnet.ibm.com>
> ---
>
>  drivers/scsi/ipr.c |  111 
> ++++++++++++++++++++++++++++++++++++++++++-----------
>  drivers/scsi/ipr.h |   53 +++----------------------
>  2 files changed, 98 insertions(+), 66 deletions(-)
>
> diff -puN drivers/scsi/ipr.h~ipr_lockless_percpu drivers/scsi/ipr.h
> --- linux-2.6.git/drivers/scsi/ipr.h~ipr_lockless_percpu      2016-09-06 
> 21:08:18.268234535 -0500
> +++ linux-2.6.git-bjking1/drivers/scsi/ipr.h  2016-09-06 21:08:18.278234468 
> -0500
> @@ -33,6 +33,7 @@
>  #include <linux/list.h>
>  #include <linux/kref.h>
>  #include <linux/irq_poll.h>
> +#include <linux/percpu-refcount.h>
>  #include <scsi/scsi.h>
>  #include <scsi/scsi_cmnd.h>
>
> @@ -512,7 +513,6 @@ struct ipr_hrr_queue {
>       struct list_head hrrq_error_q;
>       spinlock_t _lock;
>       spinlock_t *lock;
> -     atomic_t active;
>
>       volatile u32 toggle_bit;
>       u32 size;
> @@ -522,7 +522,6 @@ struct ipr_hrr_queue {
>       u8 ioa_is_dead:1;
>       u8 allow_cmds:1;
>       u8 removing_ioa:1;
> -     u8 is_active:1;
>
>       struct irq_poll iopoll;
>  };
> @@ -1472,6 +1471,12 @@ enum ipr_sdt_state {
>       DUMP_OBTAINED
>  };
>
> +struct ipr_ioa_ref_cnt {
> +     struct percpu_ref       ref;
> +     int released;
> +     struct ipr_ioa_cfg *ioa_cfg;
> +};

Do we really need the pointer back to ioa_cfg?  The released variable
could be inferred if with keep track of the first reference.  If we
handle these two points, we don't need the ipr_ioa_ref_cnt structure and
can encapsulate the percpu_ref counter directly in the main structure.

> +
>  /* Per-controller data */
>  struct ipr_ioa_cfg {
>       char eye_catcher[8];
> @@ -1547,7 +1552,6 @@ struct ipr_ioa_cfg {
>       struct ipr_hrr_queue hrrq[IPR_MAX_HRRQ_NUM];
>       u32 hrrq_num;
>       atomic_t  hrrq_index;
> -     atomic_t  hrrq_active;
>       u16 identify_hrrq_index;
>
>       struct ipr_bus_attributes bus_attr[IPR_MAX_NUM_BUSES];
> @@ -1592,6 +1596,7 @@ struct ipr_ioa_cfg {
>       int (*reset) (struct ipr_cmnd *);
>
>       struct ata_host ata_host;
> +     struct ipr_ioa_ref_cnt  *ioa_usage_counter;
>       char ipr_cmd_label[8];
>  #define IPR_CMD_LABEL                "ipr_cmd"
>       u32 max_cmds;
> @@ -1872,48 +1877,6 @@ static inline void ipr_free_cmd(struct i
>                 ipr_cmd->hrrq->active_map);
>  }
>
> -static inline void ipr_hrrq_activate(struct ipr_hrr_queue *hrrq)
> -{
> -     if (!hrrq->is_active) {
> -             atomic_inc(&hrrq->active);
> -             atomic_inc(&hrrq->ioa_cfg->hrrq_active);
> -             hrrq->is_active = 1;
> -     }
> -}
> -
> -static inline void ipr_hrrq_deactivate(struct ipr_hrr_queue *hrrq)
> -{
> -     if (hrrq->is_active) {
> -             hrrq->is_active = 0;
> -
> -             if (!atomic_dec_return(&hrrq->active))
> -                     atomic_dec(&hrrq->ioa_cfg->hrrq_active);
> -     }
> -}
> -
> -static inline int ipr_hrrq_enter(struct ipr_hrr_queue *hrrq)
> -{
> -     return atomic_inc_not_zero(&hrrq->active);
> -}
> -
> -static void ipr_reset_ioa_job(struct ipr_cmnd *);
> -
> -static inline void ipr_hrrq_exit(struct ipr_hrr_queue *hrrq)
> -{
> -     unsigned long flags;
> -     struct ipr_ioa_cfg *ioa_cfg;
> -
> -     if (!atomic_dec_return(&hrrq->active)) {
> -             ioa_cfg = hrrq->ioa_cfg;
> -
> -             if (!atomic_dec_return(&ioa_cfg->hrrq_active)) {
> -                     spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
> -                     ipr_reset_ioa_job(ioa_cfg->reset_cmd);
> -                     spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
> -             }
> -     }
> -}
> -
>  static inline struct ipr_cmnd* ipr_first_active_cmd(struct ipr_hrr_queue 
> *hrrq)
>  {
>       int i = find_first_bit(hrrq->active_map, hrrq->size);
> diff -puN drivers/scsi/ipr.c~ipr_lockless_percpu drivers/scsi/ipr.c
> --- linux-2.6.git/drivers/scsi/ipr.c~ipr_lockless_percpu      2016-09-06 
> 21:08:18.272234509 -0500
> +++ linux-2.6.git-bjking1/drivers/scsi/ipr.c  2016-09-06 21:08:18.280234455 
> -0500
> @@ -6511,7 +6511,7 @@ static int ipr_queuecommand(struct Scsi_
>
>       hrrq = &ioa_cfg->hrrq[hrrq_id];
>
> -     if (!ipr_hrrq_enter(hrrq)) {
> +     if (!percpu_ref_tryget_live(&ioa_cfg->ioa_usage_counter->ref)) {

I'd like to see these calls encapsulated inside ipr_ioa_enter,ipr_ioa_exit or
something like that.  Just cosmetic, though.

>               dev_info(&ioa_cfg->pdev->dev, "Failed to get a ref\n");
>
>               spin_lock_irqsave(hrrq->lock, hrrq_flags);
> @@ -6529,7 +6529,7 @@ static int ipr_queuecommand(struct Scsi_
>
>       ipr_cmd = __ipr_get_free_ipr_cmnd(hrrq);
>       if (ipr_cmd == NULL) {
> -             ipr_hrrq_exit(hrrq);
> +             percpu_ref_put(&ioa_cfg->ioa_usage_counter->ref);
>               return SCSI_MLQUEUE_HOST_BUSY;
>       }
>
> @@ -6591,7 +6591,7 @@ static int ipr_queuecommand(struct Scsi_
>                       ipr_free_cmd(ipr_cmd);
>                       scsi_dma_unmap(scsi_cmd);
>               }
> -             ipr_hrrq_exit(hrrq);
> +             percpu_ref_put(&ioa_cfg->ioa_usage_counter->ref);
>               return SCSI_MLQUEUE_HOST_BUSY;
>       }
>
> @@ -6599,7 +6599,7 @@ static int ipr_queuecommand(struct Scsi_
>
>       ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_RES_PHYS_LOC(res));
>       ipr_send_command(ipr_cmd);
> -     ipr_hrrq_exit(hrrq);
> +     percpu_ref_put(&ioa_cfg->ioa_usage_counter->ref);
>       return 0;
>  }
>
> @@ -7161,14 +7161,17 @@ static int ipr_ioa_reset_done(struct ipr
>  {
>       struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
>       struct ipr_resource_entry *res;
> +     struct ipr_hrr_queue *hrrq;
>       int j;
>
>       ENTER;
>       ioa_cfg->in_reset_reload = 0;
> +     hrrq = &ioa_cfg->hrrq[IPR_INIT_HRRQ];
> +     if (!hrrq->ioa_is_dead && !hrrq->removing_ioa)
> +             ioa_cfg->allow_cmds = 1;
>       for (j = 0; j < ioa_cfg->hrrq_num; j++) {
>               spin_lock(&ioa_cfg->hrrq[j]._lock);
>               ioa_cfg->hrrq[j].allow_cmds = 1;
> -             ipr_hrrq_activate(&ioa_cfg->hrrq[j]);
>               spin_unlock(&ioa_cfg->hrrq[j]._lock);
>       }
>       wmb();
> @@ -7203,6 +7206,8 @@ static int ipr_ioa_reset_done(struct ipr
>       wake_up_all(&ioa_cfg->reset_wait_q);
>
>       spin_unlock_irq(ioa_cfg->host->host_lock);
> +     if (ioa_cfg->allow_cmds)
> +             percpu_ref_reinit(&ioa_cfg->ioa_usage_counter->ref);
>       scsi_unblock_requests(ioa_cfg->host);
>       ipr_send_back_failed_ops(ioa_cfg);
>       spin_lock_irq(ioa_cfg->host->host_lock);
> @@ -9119,9 +9124,6 @@ static void ipr_reset_ioa_job(struct ipr
>                       return;
>               }
>
> -             if (atomic_read(&ioa_cfg->hrrq_active))
> -                     return;
> -
>               if (IPR_IOASC_SENSE_KEY(ioasc)) {
>                       rc = ipr_cmd->job_step_failed(ipr_cmd);
>                       if (rc == IPR_RC_JOB_RETURN)
> @@ -9134,6 +9136,48 @@ static void ipr_reset_ioa_job(struct ipr
>       } while (rc == IPR_RC_JOB_CONTINUE);
>  }
>
> +static void ipr_ioa_usage_counter_release(struct percpu_ref *ref)
> +{
> +     struct ipr_ioa_ref_cnt *ioa_ref = (struct ipr_ioa_ref_cnt *)ref;
> +     struct ipr_ioa_cfg *ioa_cfg = ioa_ref->ioa_cfg;
> +     unsigned long lock_flags;
> +
> +     ENTER;
> +     spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
> +     if (ioa_cfg->reset_cmd)
> +             ipr_reset_ioa_job(ioa_cfg->reset_cmd);
> +     spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
> +     LEAVE;
> +}
> +
> +/**
> + * ipr_reset_quiesce_work - Quiesce queuecommand
> + * @work:    work struct
> + *
> + * Description: Quiesce queuecommand.
> + *
> + **/
> +static void ipr_reset_quiesce_work(struct work_struct *work)
> +{
> +     struct ipr_cmnd *ipr_cmd = container_of(work, struct ipr_cmnd, work);
> +     struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
> +     unsigned long lock_flags = 0;
> +     int allow_cmds;
> +     ENTER;
> +
> +     spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
> +     allow_cmds = ioa_cfg->allow_cmds;
> +     ioa_cfg->allow_cmds = 0;
> +     spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
> +
> +     /* xxx add a timeout here? */

Wouldn't a timeout indicate a bug in the code?  I think we don't need an
explicit timeout, since it should always converge to zero, unless
there's a bug.

> +     if (allow_cmds)
> +             percpu_ref_kill(&ioa_cfg->ioa_usage_counter->ref);
> +     else
> +             ipr_ioa_usage_counter_release(&ioa_cfg->ioa_usage_counter->ref);
> +     LEAVE;
> +}
> +
>  /**
>   * _ipr_initiate_ioa_reset - Initiate an adapter reset
>   * @ioa_cfg:         ioa config struct
> @@ -9159,7 +9203,6 @@ static void _ipr_initiate_ioa_reset(stru
>       for (i = 0; i < ioa_cfg->hrrq_num; i++) {
>               spin_lock(&ioa_cfg->hrrq[i]._lock);
>               ioa_cfg->hrrq[i].allow_cmds = 0;
> -             ipr_hrrq_deactivate(&ioa_cfg->hrrq[i]);
>               spin_unlock(&ioa_cfg->hrrq[i]._lock);
>       }
>       wmb();
> @@ -9171,7 +9214,8 @@ static void _ipr_initiate_ioa_reset(stru
>       ipr_cmd->job_step = job_step;
>       ipr_cmd->u.shutdown_type = shutdown_type;
>
> -     ipr_reset_ioa_job(ipr_cmd);
> +     INIT_WORK(&ipr_cmd->work, ipr_reset_quiesce_work);
> +     queue_work(ioa_cfg->reset_work_q, &ipr_cmd->work);
>  }
>
>  /**
> @@ -9346,7 +9390,6 @@ static void ipr_pci_perm_failure(struct
>               for (i = 0; i < ioa_cfg->hrrq_num; i++) {
>                       spin_lock(&ioa_cfg->hrrq[i]._lock);
>                       ioa_cfg->hrrq[i].allow_cmds = 0;
> -                     ipr_hrrq_deactivate(&ioa_cfg->hrrq[i]);
>                       spin_unlock(&ioa_cfg->hrrq[i]._lock);
>               }
>               wmb();
> @@ -9403,6 +9446,7 @@ static int ipr_probe_ioa_part2(struct ip
>       spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
>       dev_dbg(&ioa_cfg->pdev->dev, "ioa_cfg adx: 0x%p\n", ioa_cfg);
>       ioa_cfg->probe_done = 1;
> +     ioa_cfg->allow_cmds = 1;
>       if (ioa_cfg->needs_hard_reset) {
>               ioa_cfg->needs_hard_reset = 0;
>               ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
> @@ -9540,6 +9584,8 @@ static void ipr_free_all_resources(struc
>       iounmap(ioa_cfg->hdw_dma_regs);
>       pci_release_regions(pdev);
>       ipr_free_mem(ioa_cfg);
> +     percpu_ref_exit(&ioa_cfg->ioa_usage_counter->ref);
> +     kfree(ioa_cfg->ioa_usage_counter);
>       scsi_host_put(ioa_cfg->host);
>       pci_disable_device(pdev);
>       LEAVE;
> @@ -10146,11 +10192,21 @@ static int ipr_probe_ioa(struct pci_dev
>               goto out_scsi_host_put;
>       }
>
> +     ioa_cfg->ioa_usage_counter = 
> kzalloc(sizeof(*ioa_cfg->ioa_usage_counter),
> +                                          GFP_KERNEL);
> +
> +     if (!ioa_cfg->ioa_usage_counter) {
> +             dev_err(&pdev->dev, "Failed to allocate ioa_usage_counter\n");
> +             rc = -ENOMEM;
> +             goto out_scsi_host_put;
> +     }
> +
>       /* set SIS 32 or SIS 64 */
>       ioa_cfg->sis64 = ioa_cfg->ipr_chip->sis_type == IPR_SIS64 ? 1 : 0;
>       ioa_cfg->chip_cfg = ioa_cfg->ipr_chip->cfg;
>       ioa_cfg->clear_isr = ioa_cfg->chip_cfg->clear_isr;
>       ioa_cfg->max_cmds = ioa_cfg->chip_cfg->max_cmds;
> +     ioa_cfg->ioa_usage_counter->ioa_cfg = ioa_cfg;
>
>       if (ipr_transop_timeout)
>               ioa_cfg->transop_timeout = ipr_transop_timeout;
> @@ -10163,13 +10219,24 @@ static int ipr_probe_ioa(struct pci_dev
>
>       ipr_init_ioa_cfg(ioa_cfg, host, pdev);
>
> +     rc = percpu_ref_init(&ioa_cfg->ioa_usage_counter->ref,
> +                          ipr_ioa_usage_counter_release,
> +                          0, GFP_KERNEL);
> +
> +     if (rc) {
> +             dev_err(&pdev->dev,
> +                     "Couldn't initialize percpu counter\n");
> +             kfree(ioa_cfg->ioa_usage_counter);
> +             goto out_scsi_host_put;
> +     }
> +
>       ipr_regs_pci = pci_resource_start(pdev, 0);
>
>       rc = pci_request_regions(pdev, IPR_NAME);
>       if (rc < 0) {
>               dev_err(&pdev->dev,
>                       "Couldn't register memory range of registers\n");
> -             goto out_scsi_host_put;
> +             goto out_percpu_ref;
>       }
>
>       rc = pci_enable_device(pdev);
> @@ -10373,18 +10440,18 @@ static int ipr_probe_ioa(struct pci_dev
>           (dev_id->device == PCI_DEVICE_ID_IBM_OBSIDIAN_E && 
> !ioa_cfg->revid)) {
>               ioa_cfg->needs_warm_reset = 1;
>               ioa_cfg->reset = ipr_reset_slot_reset;
> -
> -             ioa_cfg->reset_work_q = alloc_ordered_workqueue("ipr_reset_%d",
> -                                                             WQ_MEM_RECLAIM, 
> host->host_no);
> -
> -             if (!ioa_cfg->reset_work_q) {
> -                     dev_err(&pdev->dev, "Couldn't register reset 
> workqueue\n");
> -                     rc = -ENOMEM;
> -                     goto out_free_sata_work_q;
> -             }
>       } else
>               ioa_cfg->reset = ipr_reset_start_bist;
>
> +     ioa_cfg->reset_work_q = alloc_ordered_workqueue("ipr_reset_%d",
> +                                                     WQ_MEM_RECLAIM, 
> host->host_no);
> +
> +     if (!ioa_cfg->reset_work_q) {
> +             dev_err(&pdev->dev, "Couldn't register reset workqueue\n");
> +             rc = -ENOMEM;
> +             goto out_free_sata_work_q;
> +     }
> +
>       spin_lock_irqsave(&ipr_driver_lock, driver_lock_flags);
>       list_add_tail(&ioa_cfg->queue, &ipr_ioa_head);
>       spin_unlock_irqrestore(&ipr_driver_lock, driver_lock_flags);
> @@ -10411,6 +10478,8 @@ out_disable:
>       pci_disable_device(pdev);
>  out_release_regions:
>       pci_release_regions(pdev);
> +out_percpu_ref:
> +     percpu_ref_exit(&ioa_cfg->ioa_usage_counter->ref);
>  out_scsi_host_put:
>       scsi_host_put(host);
>       goto out;
> _
>
>
> ------------------------------------------------------------------------------
> _______________________________________________
> Iprdd-devel mailing list
> Iprdd-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/iprdd-devel
>

-- 
Gabriel Krisman Bertazi


------------------------------------------------------------------------------
_______________________________________________
Iprdd-devel mailing list
Iprdd-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/iprdd-devel

Reply via email to