Commit f2a3bc00 authored by Michael J. Ruhl's avatar Michael J. Ruhl Committed by Doug Ledford

IB/hfi1: Protect context array set/clear with spinlock

The rcd array can be accessed from user context or during interrupts.
Protecting this with a mutex isn't a good idea because the mutex should
not be used from an IRQ.

Protect the allocation and freeing of rcd array elements with a
spinlock.
Reviewed-by: default avatarMike Marciniszyn <mike.marciniszyn@intel.com>
Reviewed-by: default avatarSebastian Sanchez <sebastian.sanchez@intel.com>
Signed-off-by: default avatarMichael J. Ruhl <michael.j.ruhl@intel.com>
Signed-off-by: default avatarDennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 64a296f5
...@@ -15054,10 +15054,16 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev, ...@@ -15054,10 +15054,16 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev,
if (ret) if (ret)
goto bail_cleanup; goto bail_cleanup;
ret = hfi1_create_ctxts(dd); ret = hfi1_create_kctxts(dd);
if (ret) if (ret)
goto bail_cleanup; goto bail_cleanup;
/*
* Initialize aspm, to be done after gen3 transition and setting up
* contexts and before enabling interrupts
*/
aspm_init(dd);
dd->rcvhdrsize = DEFAULT_RCVHDRSIZE; dd->rcvhdrsize = DEFAULT_RCVHDRSIZE;
/* /*
* rcd[0] is guaranteed to be valid by this point. Also, all * rcd[0] is guaranteed to be valid by this point. Also, all
...@@ -15076,7 +15082,7 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev, ...@@ -15076,7 +15082,7 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev,
goto bail_cleanup; goto bail_cleanup;
} }
/* use contexts created by hfi1_create_ctxts */ /* use contexts created by hfi1_create_kctxts */
ret = set_up_interrupts(dd); ret = set_up_interrupts(dd);
if (ret) if (ret)
goto bail_cleanup; goto bail_cleanup;
......
...@@ -79,8 +79,8 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma); ...@@ -79,8 +79,8 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma);
static u64 kvirt_to_phys(void *addr); static u64 kvirt_to_phys(void *addr);
static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo); static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo);
static int init_subctxts(struct hfi1_ctxtdata *uctxt, static void init_subctxts(struct hfi1_ctxtdata *uctxt,
const struct hfi1_user_info *uinfo); const struct hfi1_user_info *uinfo);
static int init_user_ctxt(struct hfi1_filedata *fd, static int init_user_ctxt(struct hfi1_filedata *fd,
struct hfi1_ctxtdata *uctxt); struct hfi1_ctxtdata *uctxt);
static void user_init(struct hfi1_ctxtdata *uctxt); static void user_init(struct hfi1_ctxtdata *uctxt);
...@@ -758,7 +758,6 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) ...@@ -758,7 +758,6 @@ static int hfi1_file_close(struct inode *inode, struct file *fp)
goto done; goto done;
hfi1_cdbg(PROC, "freeing ctxt %u:%u", uctxt->ctxt, fdata->subctxt); hfi1_cdbg(PROC, "freeing ctxt %u:%u", uctxt->ctxt, fdata->subctxt);
mutex_lock(&hfi1_mutex);
flush_wc(); flush_wc();
/* drain user sdma queue */ /* drain user sdma queue */
...@@ -778,6 +777,7 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) ...@@ -778,6 +777,7 @@ static int hfi1_file_close(struct inode *inode, struct file *fp)
HFI1_MAX_SHARED_CTXTS) + fdata->subctxt; HFI1_MAX_SHARED_CTXTS) + fdata->subctxt;
*ev = 0; *ev = 0;
mutex_lock(&hfi1_mutex);
__clear_bit(fdata->subctxt, uctxt->in_use_ctxts); __clear_bit(fdata->subctxt, uctxt->in_use_ctxts);
fdata->uctxt = NULL; fdata->uctxt = NULL;
hfi1_rcd_put(uctxt); /* fdata reference */ hfi1_rcd_put(uctxt); /* fdata reference */
...@@ -844,6 +844,38 @@ static u64 kvirt_to_phys(void *addr) ...@@ -844,6 +844,38 @@ static u64 kvirt_to_phys(void *addr)
return paddr; return paddr;
} }
static int complete_subctxt(struct hfi1_filedata *fd)
{
int ret;
/*
* sub-context info can only be set up after the base context
* has been completed.
*/
ret = wait_event_interruptible(
fd->uctxt->wait,
!test_bit(HFI1_CTXT_BASE_UNINIT, &fd->uctxt->event_flags));
if (test_bit(HFI1_CTXT_BASE_FAILED, &fd->uctxt->event_flags))
ret = -ENOMEM;
/* The only thing a sub context needs is the user_xxx stuff */
if (!ret) {
fd->rec_cpu_num = hfi1_get_proc_affinity(fd->uctxt->numa_id);
ret = init_user_ctxt(fd, fd->uctxt);
}
if (ret) {
hfi1_rcd_put(fd->uctxt);
fd->uctxt = NULL;
mutex_lock(&hfi1_mutex);
__clear_bit(fd->subctxt, fd->uctxt->in_use_ctxts);
mutex_unlock(&hfi1_mutex);
}
return ret;
}
static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo) static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo)
{ {
int ret; int ret;
...@@ -854,24 +886,25 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo) ...@@ -854,24 +886,25 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo)
if (swmajor != HFI1_USER_SWMAJOR) if (swmajor != HFI1_USER_SWMAJOR)
return -ENODEV; return -ENODEV;
if (uinfo->subctxt_cnt > HFI1_MAX_SHARED_CTXTS)
return -EINVAL;
swminor = uinfo->userversion & 0xffff; swminor = uinfo->userversion & 0xffff;
/*
* Acquire the mutex to protect against multiple creations of what
* could be a shared base context.
*/
mutex_lock(&hfi1_mutex); mutex_lock(&hfi1_mutex);
/* /*
* Get a sub context if necessary. * Get a sub context if available (fd->uctxt will be set).
* ret < 0 error, 0 no context, 1 sub-context found * ret < 0 error, 0 no context, 1 sub-context found
*/ */
ret = 0; ret = find_sub_ctxt(fd, uinfo);
if (uinfo->subctxt_cnt) {
ret = find_sub_ctxt(fd, uinfo);
if (ret > 0)
fd->rec_cpu_num =
hfi1_get_proc_affinity(fd->uctxt->numa_id);
}
/* /*
* Allocate a base context if context sharing is not required or we * Allocate a base context if context sharing is not required or a
* couldn't find a sub context. * sub context wasn't found.
*/ */
if (!ret) if (!ret)
ret = allocate_ctxt(fd, fd->dd, uinfo, &uctxt); ret = allocate_ctxt(fd, fd->dd, uinfo, &uctxt);
...@@ -879,31 +912,10 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo) ...@@ -879,31 +912,10 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo)
mutex_unlock(&hfi1_mutex); mutex_unlock(&hfi1_mutex);
/* Depending on the context type, do the appropriate init */ /* Depending on the context type, do the appropriate init */
if (ret > 0) { switch (ret) {
/* case 0:
* sub-context info can only be set up after the base
* context has been completed.
*/
ret = wait_event_interruptible(fd->uctxt->wait, !test_bit(
HFI1_CTXT_BASE_UNINIT,
&fd->uctxt->event_flags));
if (test_bit(HFI1_CTXT_BASE_FAILED, &fd->uctxt->event_flags))
ret = -ENOMEM;
/* The only thing a sub context needs is the user_xxx stuff */
if (!ret)
ret = init_user_ctxt(fd, fd->uctxt);
if (ret)
clear_bit(fd->subctxt, fd->uctxt->in_use_ctxts);
} else if (!ret) {
ret = setup_base_ctxt(fd, uctxt); ret = setup_base_ctxt(fd, uctxt);
if (uctxt->subctxt_cnt) { if (uctxt->subctxt_cnt) {
/* If there is an error, set the failed bit. */
if (ret)
set_bit(HFI1_CTXT_BASE_FAILED,
&uctxt->event_flags);
/* /*
* Base context is done, notify anybody using a * Base context is done, notify anybody using a
* sub-context that is waiting for this completion * sub-context that is waiting for this completion
...@@ -911,14 +923,12 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo) ...@@ -911,14 +923,12 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo)
clear_bit(HFI1_CTXT_BASE_UNINIT, &uctxt->event_flags); clear_bit(HFI1_CTXT_BASE_UNINIT, &uctxt->event_flags);
wake_up(&uctxt->wait); wake_up(&uctxt->wait);
} }
if (ret) break;
deallocate_ctxt(uctxt); case 1:
} ret = complete_subctxt(fd);
break;
/* If an error occurred, clear the reference */ default:
if (ret && fd->uctxt) { break;
hfi1_rcd_put(fd->uctxt);
fd->uctxt = NULL;
} }
return ret; return ret;
...@@ -926,7 +936,7 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo) ...@@ -926,7 +936,7 @@ static int assign_ctxt(struct hfi1_filedata *fd, struct hfi1_user_info *uinfo)
/* /*
* The hfi1_mutex must be held when this function is called. It is * The hfi1_mutex must be held when this function is called. It is
* necessary to ensure serialized access to the bitmask in_use_ctxts. * necessary to ensure serialized creation of shared contexts.
*/ */
static int find_sub_ctxt(struct hfi1_filedata *fd, static int find_sub_ctxt(struct hfi1_filedata *fd,
const struct hfi1_user_info *uinfo) const struct hfi1_user_info *uinfo)
...@@ -935,6 +945,9 @@ static int find_sub_ctxt(struct hfi1_filedata *fd, ...@@ -935,6 +945,9 @@ static int find_sub_ctxt(struct hfi1_filedata *fd,
struct hfi1_devdata *dd = fd->dd; struct hfi1_devdata *dd = fd->dd;
u16 subctxt; u16 subctxt;
if (!uinfo->subctxt_cnt)
return 0;
for (i = dd->first_dyn_alloc_ctxt; i < dd->num_rcv_contexts; i++) { for (i = dd->first_dyn_alloc_ctxt; i < dd->num_rcv_contexts; i++) {
struct hfi1_ctxtdata *uctxt = dd->rcd[i]; struct hfi1_ctxtdata *uctxt = dd->rcd[i];
...@@ -983,7 +996,6 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, ...@@ -983,7 +996,6 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd,
struct hfi1_ctxtdata **cd) struct hfi1_ctxtdata **cd)
{ {
struct hfi1_ctxtdata *uctxt; struct hfi1_ctxtdata *uctxt;
u16 ctxt;
int ret, numa; int ret, numa;
if (dd->flags & HFI1_FROZEN) { if (dd->flags & HFI1_FROZEN) {
...@@ -997,22 +1009,9 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, ...@@ -997,22 +1009,9 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd,
return -EIO; return -EIO;
} }
/*
* This check is sort of redundant to the next EBUSY error. It would
* also indicate an inconsistancy in the driver if this value was
* zero, but there were still contexts available.
*/
if (!dd->freectxts) if (!dd->freectxts)
return -EBUSY; return -EBUSY;
for (ctxt = dd->first_dyn_alloc_ctxt;
ctxt < dd->num_rcv_contexts; ctxt++)
if (!dd->rcd[ctxt])
break;
if (ctxt == dd->num_rcv_contexts)
return -EBUSY;
/* /*
* If we don't have a NUMA node requested, preference is towards * If we don't have a NUMA node requested, preference is towards
* device NUMA node. * device NUMA node.
...@@ -1022,11 +1021,10 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, ...@@ -1022,11 +1021,10 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd,
numa = cpu_to_node(fd->rec_cpu_num); numa = cpu_to_node(fd->rec_cpu_num);
else else
numa = numa_node_id(); numa = numa_node_id();
uctxt = hfi1_create_ctxtdata(dd->pport, ctxt, numa); ret = hfi1_create_ctxtdata(dd->pport, numa, &uctxt);
if (!uctxt) { if (ret < 0) {
dd_dev_err(dd, dd_dev_err(dd, "user ctxtdata allocation failed\n");
"Unable to allocate ctxtdata memory, failing open\n"); return ret;
return -ENOMEM;
} }
hfi1_cdbg(PROC, "[%u:%u] pid %u assigned to CPU %d (NUMA %u)", hfi1_cdbg(PROC, "[%u:%u] pid %u assigned to CPU %d (NUMA %u)",
uctxt->ctxt, fd->subctxt, current->pid, fd->rec_cpu_num, uctxt->ctxt, fd->subctxt, current->pid, fd->rec_cpu_num,
...@@ -1035,8 +1033,7 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, ...@@ -1035,8 +1033,7 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd,
/* /*
* Allocate and enable a PIO send context. * Allocate and enable a PIO send context.
*/ */
uctxt->sc = sc_alloc(dd, SC_USER, uctxt->rcvhdrqentsize, uctxt->sc = sc_alloc(dd, SC_USER, uctxt->rcvhdrqentsize, dd->node);
uctxt->dd->node);
if (!uctxt->sc) { if (!uctxt->sc) {
ret = -ENOMEM; ret = -ENOMEM;
goto ctxdata_free; goto ctxdata_free;
...@@ -1048,20 +1045,13 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, ...@@ -1048,20 +1045,13 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd,
goto ctxdata_free; goto ctxdata_free;
/* /*
* Setup sub context resources if the user-level has requested * Setup sub context information if the user-level has requested
* sub contexts. * sub contexts.
* This has to be done here so the rest of the sub-contexts find the * This has to be done here so the rest of the sub-contexts find the
* proper master. * proper base context.
*/ */
if (uinfo->subctxt_cnt) { if (uinfo->subctxt_cnt)
ret = init_subctxts(uctxt, uinfo); init_subctxts(uctxt, uinfo);
/*
* On error, we don't need to disable and de-allocate the
* send context because it will be done during file close
*/
if (ret)
goto ctxdata_free;
}
uctxt->userversion = uinfo->userversion; uctxt->userversion = uinfo->userversion;
uctxt->flags = hfi1_cap_mask; /* save current flag state */ uctxt->flags = hfi1_cap_mask; /* save current flag state */
init_waitqueue_head(&uctxt->wait); init_waitqueue_head(&uctxt->wait);
...@@ -1081,9 +1071,7 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd, ...@@ -1081,9 +1071,7 @@ static int allocate_ctxt(struct hfi1_filedata *fd, struct hfi1_devdata *dd,
return 0; return 0;
ctxdata_free: ctxdata_free:
*cd = NULL; hfi1_free_ctxt(dd, uctxt);
dd->rcd[ctxt] = NULL;
hfi1_rcd_put(uctxt);
return ret; return ret;
} }
...@@ -1093,28 +1081,17 @@ static void deallocate_ctxt(struct hfi1_ctxtdata *uctxt) ...@@ -1093,28 +1081,17 @@ static void deallocate_ctxt(struct hfi1_ctxtdata *uctxt)
hfi1_stats.sps_ctxts--; hfi1_stats.sps_ctxts--;
if (++uctxt->dd->freectxts == uctxt->dd->num_user_contexts) if (++uctxt->dd->freectxts == uctxt->dd->num_user_contexts)
aspm_enable_all(uctxt->dd); aspm_enable_all(uctxt->dd);
/* _rcd_put() should be done after releasing mutex */
uctxt->dd->rcd[uctxt->ctxt] = NULL;
mutex_unlock(&hfi1_mutex); mutex_unlock(&hfi1_mutex);
hfi1_rcd_put(uctxt); /* dd reference */
hfi1_free_ctxt(uctxt->dd, uctxt);
} }
static int init_subctxts(struct hfi1_ctxtdata *uctxt, static void init_subctxts(struct hfi1_ctxtdata *uctxt,
const struct hfi1_user_info *uinfo) const struct hfi1_user_info *uinfo)
{ {
u16 num_subctxts;
num_subctxts = uinfo->subctxt_cnt;
if (num_subctxts > HFI1_MAX_SHARED_CTXTS)
return -EINVAL;
uctxt->subctxt_cnt = uinfo->subctxt_cnt; uctxt->subctxt_cnt = uinfo->subctxt_cnt;
uctxt->subctxt_id = uinfo->subctxt_id; uctxt->subctxt_id = uinfo->subctxt_id;
uctxt->redirect_seq_cnt = 1;
set_bit(HFI1_CTXT_BASE_UNINIT, &uctxt->event_flags); set_bit(HFI1_CTXT_BASE_UNINIT, &uctxt->event_flags);
return 0;
} }
static int setup_subctxt(struct hfi1_ctxtdata *uctxt) static int setup_subctxt(struct hfi1_ctxtdata *uctxt)
...@@ -1302,8 +1279,8 @@ static int setup_base_ctxt(struct hfi1_filedata *fd, ...@@ -1302,8 +1279,8 @@ static int setup_base_ctxt(struct hfi1_filedata *fd,
return 0; return 0;
setup_failed: setup_failed:
/* Call _free_ctxtdata, not _rcd_put(). We still need the context. */ set_bit(HFI1_CTXT_BASE_FAILED, &uctxt->event_flags);
hfi1_free_ctxtdata(dd, uctxt); deallocate_ctxt(uctxt);
return ret; return ret;
} }
......
...@@ -273,7 +273,6 @@ struct hfi1_ctxtdata { ...@@ -273,7 +273,6 @@ struct hfi1_ctxtdata {
u16 poll_type; u16 poll_type;
/* receive packet sequence counter */ /* receive packet sequence counter */
u8 seq_cnt; u8 seq_cnt;
u8 redirect_seq_cnt;
/* ctxt rcvhdrq head offset */ /* ctxt rcvhdrq head offset */
u32 head; u32 head;
/* QPs waiting for context processing */ /* QPs waiting for context processing */
...@@ -1263,9 +1262,10 @@ void handle_user_interrupt(struct hfi1_ctxtdata *rcd); ...@@ -1263,9 +1262,10 @@ void handle_user_interrupt(struct hfi1_ctxtdata *rcd);
int hfi1_create_rcvhdrq(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd); int hfi1_create_rcvhdrq(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd);
int hfi1_setup_eagerbufs(struct hfi1_ctxtdata *rcd); int hfi1_setup_eagerbufs(struct hfi1_ctxtdata *rcd);
int hfi1_create_ctxts(struct hfi1_devdata *dd); int hfi1_create_kctxts(struct hfi1_devdata *dd);
struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt, int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa,
int numa); struct hfi1_ctxtdata **rcd);
void hfi1_free_ctxt(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd);
void hfi1_init_pportdata(struct pci_dev *pdev, struct hfi1_pportdata *ppd, void hfi1_init_pportdata(struct pci_dev *pdev, struct hfi1_pportdata *ppd,
struct hfi1_devdata *dd, u8 hw_pidx, u8 port); struct hfi1_devdata *dd, u8 hw_pidx, u8 port);
void hfi1_free_ctxtdata(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd); void hfi1_free_ctxtdata(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd);
......
...@@ -126,71 +126,67 @@ static struct idr hfi1_unit_table; ...@@ -126,71 +126,67 @@ static struct idr hfi1_unit_table;
u32 hfi1_cpulist_count; u32 hfi1_cpulist_count;
unsigned long *hfi1_cpulist; unsigned long *hfi1_cpulist;
/* static int hfi1_create_kctxt(struct hfi1_devdata *dd,
* Common code for creating the receive context array. struct hfi1_pportdata *ppd)
*/
int hfi1_create_ctxts(struct hfi1_devdata *dd)
{ {
u16 i; struct hfi1_ctxtdata *rcd;
int ret; int ret;
/* Control context has to be always 0 */ /* Control context has to be always 0 */
BUILD_BUG_ON(HFI1_CTRL_CTXT != 0); BUILD_BUG_ON(HFI1_CTRL_CTXT != 0);
ret = hfi1_create_ctxtdata(ppd, dd->node, &rcd);
if (ret < 0) {
dd_dev_err(dd, "Kernel receive context allocation failed\n");
return ret;
}
/*
* Set up the kernel context flags here and now because they use
* default values for all receive side memories. User contexts will
* be handled as they are created.
*/
rcd->flags = HFI1_CAP_KGET(MULTI_PKT_EGR) |
HFI1_CAP_KGET(NODROP_RHQ_FULL) |
HFI1_CAP_KGET(NODROP_EGR_FULL) |
HFI1_CAP_KGET(DMA_RTAIL);
/* Control context must use DMA_RTAIL */
if (rcd->ctxt == HFI1_CTRL_CTXT)
rcd->flags |= HFI1_CAP_DMA_RTAIL;
rcd->seq_cnt = 1;
rcd->sc = sc_alloc(dd, SC_ACK, rcd->rcvhdrqentsize, dd->node);
if (!rcd->sc) {
dd_dev_err(dd, "Kernel send context allocation failed\n");
return -ENOMEM;
}
hfi1_init_ctxt(rcd->sc);
return 0;
}
/*
* Create the receive context array and one or more kernel contexts
*/
int hfi1_create_kctxts(struct hfi1_devdata *dd)
{
u16 i;
int ret;
dd->rcd = kzalloc_node(dd->num_rcv_contexts * sizeof(*dd->rcd), dd->rcd = kzalloc_node(dd->num_rcv_contexts * sizeof(*dd->rcd),
GFP_KERNEL, dd->node); GFP_KERNEL, dd->node);
if (!dd->rcd) if (!dd->rcd)
goto nomem; return -ENOMEM;
/* create one or more kernel contexts */
for (i = 0; i < dd->first_dyn_alloc_ctxt; ++i) { for (i = 0; i < dd->first_dyn_alloc_ctxt; ++i) {
struct hfi1_pportdata *ppd; ret = hfi1_create_kctxt(dd, dd->pport);
struct hfi1_ctxtdata *rcd; if (ret)
goto bail;
ppd = dd->pport + (i % dd->num_pports);
/* dd->rcd[i] gets assigned inside the callee */
rcd = hfi1_create_ctxtdata(ppd, i, dd->node);
if (!rcd) {
dd_dev_err(dd,
"Unable to allocate kernel receive context, failing\n");
goto nomem;
}
/*
* Set up the kernel context flags here and now because they
* use default values for all receive side memories. User
* contexts will be handled as they are created.
*/
rcd->flags = HFI1_CAP_KGET(MULTI_PKT_EGR) |
HFI1_CAP_KGET(NODROP_RHQ_FULL) |
HFI1_CAP_KGET(NODROP_EGR_FULL) |
HFI1_CAP_KGET(DMA_RTAIL);
/* Control context must use DMA_RTAIL */
if (rcd->ctxt == HFI1_CTRL_CTXT)
rcd->flags |= HFI1_CAP_DMA_RTAIL;
rcd->seq_cnt = 1;
rcd->sc = sc_alloc(dd, SC_ACK, rcd->rcvhdrqentsize, dd->node);
if (!rcd->sc) {
dd_dev_err(dd,
"Unable to allocate kernel send context, failing\n");
goto nomem;
}
hfi1_init_ctxt(rcd->sc);
} }
/*
* Initialize aspm, to be done after gen3 transition and setting up
* contexts and before enabling interrupts
*/
aspm_init(dd);
return 0; return 0;
nomem: bail:
ret = -ENOMEM;
for (i = 0; dd->rcd && i < dd->first_dyn_alloc_ctxt; ++i) for (i = 0; dd->rcd && i < dd->first_dyn_alloc_ctxt; ++i)
hfi1_rcd_put(dd->rcd[i]); hfi1_rcd_put(dd->rcd[i]);
...@@ -208,6 +204,11 @@ static void hfi1_rcd_init(struct hfi1_ctxtdata *rcd) ...@@ -208,6 +204,11 @@ static void hfi1_rcd_init(struct hfi1_ctxtdata *rcd)
kref_init(&rcd->kref); kref_init(&rcd->kref);
} }
/**
* hfi1_rcd_free - When reference is zero clean up.
* @kref: pointer to an initialized rcd data structure
*
*/
static void hfi1_rcd_free(struct kref *kref) static void hfi1_rcd_free(struct kref *kref)
{ {
struct hfi1_ctxtdata *rcd = struct hfi1_ctxtdata *rcd =
...@@ -217,6 +218,12 @@ static void hfi1_rcd_free(struct kref *kref) ...@@ -217,6 +218,12 @@ static void hfi1_rcd_free(struct kref *kref)
kfree(rcd); kfree(rcd);
} }
/**
* hfi1_rcd_put - decrement reference for rcd
* @rcd: pointer to an initialized rcd data structure
*
* Use this to put a reference after the init.
*/
int hfi1_rcd_put(struct hfi1_ctxtdata *rcd) int hfi1_rcd_put(struct hfi1_ctxtdata *rcd)
{ {
if (rcd) if (rcd)
...@@ -225,16 +232,58 @@ int hfi1_rcd_put(struct hfi1_ctxtdata *rcd) ...@@ -225,16 +232,58 @@ int hfi1_rcd_put(struct hfi1_ctxtdata *rcd)
return 0; return 0;
} }
/**
* hfi1_rcd_get - increment reference for rcd
* @rcd: pointer to an initialized rcd data structure
*
* Use this to get a reference after the init.
*/
void hfi1_rcd_get(struct hfi1_ctxtdata *rcd) void hfi1_rcd_get(struct hfi1_ctxtdata *rcd)
{ {
kref_get(&rcd->kref); kref_get(&rcd->kref);
} }
/**
* allocate_rcd_index - allocate an rcd index from the rcd array
* @dd: pointer to a valid devdata structure
* @rcd: rcd data structure to assign
* @index: pointer to index that is allocated
*
* Find an empty index in the rcd array, and assign the given rcd to it.
* If the array is full, we are EBUSY.
*
*/
static u16 allocate_rcd_index(struct hfi1_devdata *dd,
struct hfi1_ctxtdata *rcd, u16 *index)
{
unsigned long flags;
u16 ctxt;
spin_lock_irqsave(&dd->uctxt_lock, flags);
for (ctxt = 0; ctxt < dd->num_rcv_contexts; ctxt++)
if (!dd->rcd[ctxt])
break;
if (ctxt < dd->num_rcv_contexts) {
rcd->ctxt = ctxt;
dd->rcd[ctxt] = rcd;
hfi1_rcd_init(rcd);
}
spin_unlock_irqrestore(&dd->uctxt_lock, flags);
if (ctxt >= dd->num_rcv_contexts)
return -EBUSY;
*index = ctxt;
return 0;
}
/* /*
* Common code for user and kernel context setup. * Common code for user and kernel context setup.
*/ */
struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt, int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa,
int numa) struct hfi1_ctxtdata **context)
{ {
struct hfi1_devdata *dd = ppd->dd; struct hfi1_devdata *dd = ppd->dd;
struct hfi1_ctxtdata *rcd; struct hfi1_ctxtdata *rcd;
...@@ -248,9 +297,18 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt, ...@@ -248,9 +297,18 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt,
rcd = kzalloc_node(sizeof(*rcd), GFP_KERNEL, numa); rcd = kzalloc_node(sizeof(*rcd), GFP_KERNEL, numa);
if (rcd) { if (rcd) {
u32 rcvtids, max_entries; u32 rcvtids, max_entries;
u16 ctxt;
int ret;
hfi1_cdbg(PROC, "setting up context %u\n", ctxt); hfi1_cdbg(PROC, "setting up context %u\n", ctxt);
ret = allocate_rcd_index(dd, rcd, &ctxt);
if (ret) {
*context = NULL;
kfree(rcd);
return ret;
}
INIT_LIST_HEAD(&rcd->qp_wait_list); INIT_LIST_HEAD(&rcd->qp_wait_list);
hfi1_exp_tid_group_init(&rcd->tid_group_list); hfi1_exp_tid_group_init(&rcd->tid_group_list);
hfi1_exp_tid_group_init(&rcd->tid_used_list); hfi1_exp_tid_group_init(&rcd->tid_used_list);
...@@ -258,8 +316,6 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt, ...@@ -258,8 +316,6 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt,
rcd->ppd = ppd; rcd->ppd = ppd;
rcd->dd = dd; rcd->dd = dd;
__set_bit(0, rcd->in_use_ctxts); __set_bit(0, rcd->in_use_ctxts);
rcd->ctxt = ctxt;
dd->rcd[ctxt] = rcd;
rcd->numa_id = numa; rcd->numa_id = numa;
rcd->rcv_array_groups = dd->rcv_entries.ngroups; rcd->rcv_array_groups = dd->rcv_entries.ngroups;
...@@ -363,15 +419,34 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt, ...@@ -363,15 +419,34 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u16 ctxt,
goto bail; goto bail;
} }
hfi1_rcd_init(rcd); *context = rcd;
return 0;
} }
return rcd;
bail: bail:
dd->rcd[ctxt] = NULL; *context = NULL;
kfree(rcd->egrbufs.rcvtids); hfi1_free_ctxt(dd, rcd);
kfree(rcd->egrbufs.buffers); return -ENOMEM;
kfree(rcd); }
return NULL;
/**
* hfi1_free_ctxt
* @dd: Pointer to a valid device
* @rcd: pointer to an initialized rcd data structure
*
* This is the "free" to match the _create_ctxtdata (alloc) function.
* This is the final "put" for the kref.
*/
void hfi1_free_ctxt(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd)
{
unsigned long flags;
if (rcd) {
spin_lock_irqsave(&dd->uctxt_lock, flags);
dd->rcd[rcd->ctxt] = NULL;
spin_unlock_irqrestore(&dd->uctxt_lock, flags);
hfi1_rcd_put(rcd);
}
} }
/* /*
......
...@@ -106,22 +106,13 @@ static int allocate_vnic_ctxt(struct hfi1_devdata *dd, ...@@ -106,22 +106,13 @@ static int allocate_vnic_ctxt(struct hfi1_devdata *dd,
struct hfi1_ctxtdata **vnic_ctxt) struct hfi1_ctxtdata **vnic_ctxt)
{ {
struct hfi1_ctxtdata *uctxt; struct hfi1_ctxtdata *uctxt;
u16 ctxt;
int ret; int ret;
if (dd->flags & HFI1_FROZEN) if (dd->flags & HFI1_FROZEN)
return -EIO; return -EIO;
for (ctxt = dd->first_dyn_alloc_ctxt; ret = hfi1_create_ctxtdata(dd->pport, dd->node, &uctxt);
ctxt < dd->num_rcv_contexts; ctxt++) if (ret < 0) {
if (!dd->rcd[ctxt])
break;
if (ctxt == dd->num_rcv_contexts)
return -EBUSY;
uctxt = hfi1_create_ctxtdata(dd->pport, ctxt, dd->node);
if (!uctxt) {
dd_dev_err(dd, "Unable to create ctxtdata, failing open\n"); dd_dev_err(dd, "Unable to create ctxtdata, failing open\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -156,11 +147,10 @@ static int allocate_vnic_ctxt(struct hfi1_devdata *dd, ...@@ -156,11 +147,10 @@ static int allocate_vnic_ctxt(struct hfi1_devdata *dd,
return ret; return ret;
bail: bail:
/* /*
* hfi1_rcd_put() will call hfi1_free_ctxtdata(), which will * hfi1_free_ctxt() will call hfi1_free_ctxtdata(), which will
* release send_context structure if uctxt->sc is not null * release send_context structure if uctxt->sc is not null
*/ */
dd->rcd[uctxt->ctxt] = NULL; hfi1_free_ctxt(dd, uctxt);
hfi1_rcd_put(uctxt);
dd_dev_dbg(dd, "vnic allocation failed. rc %d\n", ret); dd_dev_dbg(dd, "vnic allocation failed. rc %d\n", ret);
return ret; return ret;
} }
...@@ -201,14 +191,14 @@ static void deallocate_vnic_ctxt(struct hfi1_devdata *dd, ...@@ -201,14 +191,14 @@ static void deallocate_vnic_ctxt(struct hfi1_devdata *dd,
dd->send_contexts[uctxt->sc->sw_index].type = SC_USER; dd->send_contexts[uctxt->sc->sw_index].type = SC_USER;
spin_unlock_irqrestore(&dd->uctxt_lock, flags); spin_unlock_irqrestore(&dd->uctxt_lock, flags);
dd->rcd[uctxt->ctxt] = NULL;
uctxt->event_flags = 0; uctxt->event_flags = 0;
hfi1_clear_tids(uctxt); hfi1_clear_tids(uctxt);
hfi1_clear_ctxt_pkey(dd, uctxt); hfi1_clear_ctxt_pkey(dd, uctxt);
hfi1_stats.sps_ctxts--; hfi1_stats.sps_ctxts--;
hfi1_rcd_put(uctxt);
hfi1_free_ctxt(dd, uctxt);
} }
void hfi1_vnic_setup(struct hfi1_devdata *dd) void hfi1_vnic_setup(struct hfi1_devdata *dd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment