[PATCH 3/3] staging/rdma/hfi1: check for ARMED->ACTIVE transition in receive interrupt
Jubin John
jubin.john at intel.com
Wed Dec 23 22:27:17 UTC 2015
On Fri, Dec 18, 2015 at 09:31:39AM +0300, Dan Carpenter wrote:
> Possible off by one, but mostly the whitespace makes me itch.
>
> Jim was not on the CC list.
>
> On Thu, Dec 17, 2015 at 07:24:15PM -0500, Jubin John wrote:
> > From: Jim Snow <jim.m.snow at intel.com>
> >
> > The link state will transition from ARMED to ACTIVE when a non-SC15
> > packet arrives, but the driver might not notice the change. With this
> > fix, if the slowpath receive interrupt handler sees a non-SC15 packet
> > while in the ARMED state, we queue work to call linkstate_active_work
> > from process context to promote it to ACTIVE.
> >
> > Signed-off-by: Jim Snow <jim.m.snow at intel.com>
> > Signed-off-by: Brendan Cunningham <brendan.cunningham at intel.com>
> > Reviewed-by: Dean Luick <dean.luick at intel.com>
> > Signed-off-by: Jubin John <jubin.john at intel.com>
> > ---
> > drivers/staging/rdma/hfi1/chip.c | 5 ++-
> > drivers/staging/rdma/hfi1/chip.h | 2 +
> > drivers/staging/rdma/hfi1/driver.c | 66 ++++++++++++++++++++++++++++++++++++
> > drivers/staging/rdma/hfi1/hfi.h | 12 ++++++
> > drivers/staging/rdma/hfi1/init.c | 1 +
> > 5 files changed, 84 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/staging/rdma/hfi1/chip.c b/drivers/staging/rdma/hfi1/chip.c
> > index 78a5f08..f82b848 100644
> > --- a/drivers/staging/rdma/hfi1/chip.c
> > +++ b/drivers/staging/rdma/hfi1/chip.c
> > @@ -4606,7 +4606,7 @@ static inline void clear_recv_intr(struct hfi1_ctxtdata *rcd)
> > }
> >
> > /* force the receive interrupt */
> > -static inline void force_recv_intr(struct hfi1_ctxtdata *rcd)
> > +void force_recv_intr(struct hfi1_ctxtdata *rcd)
> > {
> > write_csr(rcd->dd, CCE_INT_FORCE + (8 * rcd->ireg), rcd->imask);
> > }
> > @@ -4705,7 +4705,7 @@ u32 read_physical_state(struct hfi1_devdata *dd)
> > & DC_DC8051_STS_CUR_STATE_PORT_MASK;
> > }
> >
> > -static u32 read_logical_state(struct hfi1_devdata *dd)
> > +u32 read_logical_state(struct hfi1_devdata *dd)
> > {
> > u64 reg;
> >
> > @@ -6658,6 +6658,7 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state)
> > ppd->link_enabled = 1;
> > }
> >
> > + set_all_slowpath(ppd->dd);
> > ret = set_local_link_attributes(ppd);
> > if (ret)
> > break;
> > diff --git a/drivers/staging/rdma/hfi1/chip.h b/drivers/staging/rdma/hfi1/chip.h
> > index d74aed8..620cf08 100644
> > --- a/drivers/staging/rdma/hfi1/chip.h
> > +++ b/drivers/staging/rdma/hfi1/chip.h
> > @@ -691,6 +691,8 @@ u64 read_dev_cntr(struct hfi1_devdata *dd, int index, int vl);
> > u64 write_dev_cntr(struct hfi1_devdata *dd, int index, int vl, u64 data);
> > u64 read_port_cntr(struct hfi1_pportdata *ppd, int index, int vl);
> > u64 write_port_cntr(struct hfi1_pportdata *ppd, int index, int vl, u64 data);
> > +u32 read_logical_state(struct hfi1_devdata *dd);
> > +void force_recv_intr(struct hfi1_ctxtdata *rcd);
> >
> > /* Per VL indexes */
> > enum {
> > diff --git a/drivers/staging/rdma/hfi1/driver.c b/drivers/staging/rdma/hfi1/driver.c
> > index 4c52e78..16b1be1 100644
> > --- a/drivers/staging/rdma/hfi1/driver.c
> > +++ b/drivers/staging/rdma/hfi1/driver.c
> > @@ -862,6 +862,15 @@ static inline void set_all_dma_rtail(struct hfi1_devdata *dd)
> > &handle_receive_interrupt_dma_rtail;
> > }
> >
> > +void set_all_slowpath(struct hfi1_devdata *dd)
> > +{
> > + int i;
> > +
> > + for (i = HFI1_CTRL_CTXT + 1; i < dd->first_user_ctxt; i++)
> > + dd->rcd[i]->do_interrupt =
> > + &handle_receive_interrupt;
>
> This fits within the 80 character limit.
Will fix in v2.
>
> We start counting from HFI1_CTRL_CTXT + 1 but in receive_interrupt_work()
> we start counting from HFI1_CTRL_CTXT. What's the story? It's either a
> bug or needs much better documentation.
In set_all_slowpath(), we exclude HFI1_CTRL_CTXT because HFI1_CTRL_CTXT
(the SC15, error, and multicast context) must always use the slowpath
handler. We skip HFI1_CTRL_CTXT in set_all_nodma_rtail() and
set_all_dma_rtail() as well.
receive_interrupt_work() starts at HFI1_CTRL_CTXT because we want to
trigger interrupts on all of the receive contexts, including
HFI1_CTRL_CTXT in case any packets were received on any of the contexts
between the interrupt and when the work function is called.
>
> > +}
> > +
> > /*
> > * handle_receive_interrupt - receive a packet
> > * @rcd: the context
> > @@ -929,6 +938,27 @@ int handle_receive_interrupt(struct hfi1_ctxtdata *rcd, int thread)
> > last = skip_rcv_packet(&packet, thread);
> > skip_pkt = 0;
> > } else {
> > + /* Auto activate link on non-SC15 packet receive */
> > + if (unlikely(rcd->ppd->host_link_state ==
> > + HLS_UP_ARMED)) {
> > + struct work_struct *lsaw =
> > + &rcd->ppd->linkstate_active_work;
> > + struct hfi1_message_header *hdr =
> > + hfi1_get_msgheader(packet.rcd->dd,
> > + packet.rhf_addr);
> > + if (hdr2sc(hdr, packet.rhf) != 0xf) {
> > + int hwstate = read_logical_state(dd);
> > +
> > + if (hwstate != LSTATE_ACTIVE) {
> > + dd_dev_info(dd, "Unexpected link state %d\n",
> > + hwstate);
> > + } else {
> > + queue_work(
> > + rcd->ppd->hfi1_wq, lsaw);
> > + goto bail;
> > + }
> > + }
> > + }
>
> Can we make this an inline function?
Yes, will make that change in v2.
>
> > last = process_rcv_packet(&packet, thread);
> > }
> >
> > @@ -984,6 +1014,42 @@ bail:
> > }
> >
> > /*
> > + * We may discover in the interrupt that the hardware link state has
> > + * changed from ARMED to ACTIVE (due to the arrival of a non-SC15 packet),
> > + * and we need to update the driver's notion of the link state. We cannot
> > + * run set_link_state from interrupt context, so we queue this function on
> > + * a workqueue.
> > + *
> > + * We delay the regular interrupt processing until after the state changes
> > + * so that the link will be in the correct state by the time any application
> > + * we wake up attempts to send a reply to any message it received.
> > + * (Subsequent receive interrupts may possibly force the wakeup before we
> > + * update the link state.)
> > + *
> > + * The rcd is freed in hfi1_free_ctxtdata after hfi1_postinit_cleanup invokes
> > + * dd->f_cleanup(dd) to disable the interrupt handler and flush workqueues,
> > + * so we're safe from use-after-free of the rcd.
> > + */
> > +void receive_interrupt_work(struct work_struct *work)
> > +{
> > + struct hfi1_pportdata *ppd =
> > + container_of(work, struct hfi1_pportdata, linkstate_active_work);
>
> Normally we would put mostly on the first line.
>
> struct hfi1_pportdata *ppd = container_of(work, struct hfi1_pportdata,
> linkstate_active_work);
Fixed in v2.
>
> > + struct hfi1_devdata *dd = ppd->dd;
> > + int i;
> > +
> > + /* Received non-SC15 packet implies neighbor_normal */
> > + ppd->neighbor_normal = 1;
> > + set_link_state(ppd, HLS_UP_ACTIVE);
> > +
> > + /*
> > + * Interrupt all kernel contexts that could have had an
> > + * interrupt during auto activation.
>
> Remove the extra space before "interrupt".
Fixed in v2.
>
> > + */
> > + for (i = HFI1_CTRL_CTXT; i < dd->first_user_ctxt; i++)
> > + force_recv_intr(dd->rcd[i]);
> > +}
> > +
> > +/*
> > * Convert a given MTU size to the on-wire MAD packet enumeration.
> > * Return -1 if the size is invalid.
> > */
> > diff --git a/drivers/staging/rdma/hfi1/hfi.h b/drivers/staging/rdma/hfi1/hfi.h
> > index 54ed6b3..bfd9723 100644
> > --- a/drivers/staging/rdma/hfi1/hfi.h
> > +++ b/drivers/staging/rdma/hfi1/hfi.h
> > @@ -712,6 +712,7 @@ struct hfi1_pportdata {
> > u8 remote_link_down_reason;
> > /* Error events that will cause a port bounce. */
> > u32 port_error_action;
> > + struct work_struct linkstate_active_work;
> > };
> >
> > typedef int (*rhf_rcv_function_ptr)(struct hfi1_packet *packet);
> > @@ -1128,6 +1129,7 @@ void hfi1_free_ctxtdata(struct hfi1_devdata *, struct hfi1_ctxtdata *);
> > int handle_receive_interrupt(struct hfi1_ctxtdata *, int);
> > int handle_receive_interrupt_nodma_rtail(struct hfi1_ctxtdata *, int);
> > int handle_receive_interrupt_dma_rtail(struct hfi1_ctxtdata *, int);
> > +void set_all_slowpath(struct hfi1_devdata *dd);
> >
> > /* receive packet handler dispositions */
> > #define RCV_PKT_OK 0x0 /* keep going */
> > @@ -1148,6 +1150,16 @@ static inline u32 driver_lstate(struct hfi1_pportdata *ppd)
> > return ppd->lstate; /* use the cached value */
> > }
> >
> > +void receive_interrupt_work(struct work_struct *work);
> > +
> > +/* extract service channel from header and rhf */
> > +static inline int hdr2sc(struct hfi1_message_header *hdr, u64 rhf)
> > +{
> > + return
> > + ((be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf) |
> > + ((!!(rhf & RHF_DC_INFO_MASK)) << 4);
>
> This should be:
>
> return ((be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf) |
> ((!!(rhf & RHF_DC_INFO_MASK)) << 4);
Fixed in v2.
>
> > +}
> > +
> > static inline u16 generate_jkey(kuid_t uid)
> > {
> > return from_kuid(current_user_ns(), uid) & 0xffff;
> > diff --git a/drivers/staging/rdma/hfi1/init.c b/drivers/staging/rdma/hfi1/init.c
> > index 8f13d53..ee206ae 100644
> > --- a/drivers/staging/rdma/hfi1/init.c
> > +++ b/drivers/staging/rdma/hfi1/init.c
> > @@ -498,6 +498,7 @@ void hfi1_init_pportdata(struct pci_dev *pdev, struct hfi1_pportdata *ppd,
> > INIT_WORK(&ppd->link_downgrade_work, handle_link_downgrade);
> > INIT_WORK(&ppd->sma_message_work, handle_sma_message);
> > INIT_WORK(&ppd->link_bounce_work, handle_link_bounce);
> > + INIT_WORK(&ppd->linkstate_active_work, receive_interrupt_work);
> > mutex_init(&ppd->hls_lock);
> > spin_lock_init(&ppd->sdma_alllock);
> > spin_lock_init(&ppd->qsfp_info.qsfp_lock);
> > --
> > 1.7.1
> >
> > _______________________________________________
> > devel mailing list
> > devel at linuxdriverproject.org
> > http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
More information about the devel
mailing list