Commit 15ff220a authored by Vinit Agnihotri's avatar Vinit Agnihotri Committed by Sasha Levin

IB/qib: Support creating qps with GFP_NOIO flag

[ Upstream commit fbbeb863 ]

The current code is problematic when the QP creation and ipoib is used to
support NFS and NFS desires to do IO for paging purposes. In that case, the
GFP_KERNEL allocation in qib_qp.c causes a deadlock in tight memory
situations.

This fix adds support to create queue pair with GFP_NOIO flag for connected
mode only to cleanly fail the create queue pair in those situations.

Cc: <stable@vger.kernel.org> # 3.16+
Reviewed-by: default avatarMike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: default avatarVinit Agnihotri <vinit.abhay.agnihotri@intel.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent 383de379
......@@ -100,9 +100,10 @@ static u32 credit_table[31] = {
32768 /* 1E */
};
static void get_map_page(struct qib_qpn_table *qpt, struct qpn_map *map)
static void get_map_page(struct qib_qpn_table *qpt, struct qpn_map *map,
gfp_t gfp)
{
unsigned long page = get_zeroed_page(GFP_KERNEL);
unsigned long page = get_zeroed_page(gfp);
/*
* Free the page if someone raced with us installing it.
......@@ -121,7 +122,7 @@ static void get_map_page(struct qib_qpn_table *qpt, struct qpn_map *map)
* zero/one for QP type IB_QPT_SMI/IB_QPT_GSI.
*/
static int alloc_qpn(struct qib_devdata *dd, struct qib_qpn_table *qpt,
enum ib_qp_type type, u8 port)
enum ib_qp_type type, u8 port, gfp_t gfp)
{
u32 i, offset, max_scan, qpn;
struct qpn_map *map;
......@@ -151,7 +152,7 @@ static int alloc_qpn(struct qib_devdata *dd, struct qib_qpn_table *qpt,
max_scan = qpt->nmaps - !offset;
for (i = 0;;) {
if (unlikely(!map->page)) {
get_map_page(qpt, map);
get_map_page(qpt, map, gfp);
if (unlikely(!map->page))
break;
}
......@@ -983,13 +984,21 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
size_t sz;
size_t sg_list_sz;
struct ib_qp *ret;
gfp_t gfp;
if (init_attr->cap.max_send_sge > ib_qib_max_sges ||
init_attr->cap.max_send_wr > ib_qib_max_qp_wrs ||
init_attr->create_flags) {
ret = ERR_PTR(-EINVAL);
goto bail;
}
init_attr->create_flags & ~(IB_QP_CREATE_USE_GFP_NOIO))
return ERR_PTR(-EINVAL);
/* GFP_NOIO is applicable in RC QPs only */
if (init_attr->create_flags & IB_QP_CREATE_USE_GFP_NOIO &&
init_attr->qp_type != IB_QPT_RC)
return ERR_PTR(-EINVAL);
gfp = init_attr->create_flags & IB_QP_CREATE_USE_GFP_NOIO ?
GFP_NOIO : GFP_KERNEL;
/* Check receive queue parameters if no SRQ is specified. */
if (!init_attr->srq) {
......@@ -1021,7 +1030,8 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
sz = sizeof(struct qib_sge) *
init_attr->cap.max_send_sge +
sizeof(struct qib_swqe);
swq = vmalloc((init_attr->cap.max_send_wr + 1) * sz);
swq = __vmalloc((init_attr->cap.max_send_wr + 1) * sz,
gfp, PAGE_KERNEL);
if (swq == NULL) {
ret = ERR_PTR(-ENOMEM);
goto bail;
......@@ -1037,13 +1047,13 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
} else if (init_attr->cap.max_recv_sge > 1)
sg_list_sz = sizeof(*qp->r_sg_list) *
(init_attr->cap.max_recv_sge - 1);
qp = kzalloc(sz + sg_list_sz, GFP_KERNEL);
qp = kzalloc(sz + sg_list_sz, gfp);
if (!qp) {
ret = ERR_PTR(-ENOMEM);
goto bail_swq;
}
RCU_INIT_POINTER(qp->next, NULL);
qp->s_hdr = kzalloc(sizeof(*qp->s_hdr), GFP_KERNEL);
qp->s_hdr = kzalloc(sizeof(*qp->s_hdr), gfp);
if (!qp->s_hdr) {
ret = ERR_PTR(-ENOMEM);
goto bail_qp;
......@@ -1058,8 +1068,16 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
qp->r_rq.max_sge = init_attr->cap.max_recv_sge;
sz = (sizeof(struct ib_sge) * qp->r_rq.max_sge) +
sizeof(struct qib_rwqe);
qp->r_rq.wq = vmalloc_user(sizeof(struct qib_rwq) +
qp->r_rq.size * sz);
if (gfp != GFP_NOIO)
qp->r_rq.wq = vmalloc_user(
sizeof(struct qib_rwq) +
qp->r_rq.size * sz);
else
qp->r_rq.wq = __vmalloc(
sizeof(struct qib_rwq) +
qp->r_rq.size * sz,
gfp, PAGE_KERNEL);
if (!qp->r_rq.wq) {
ret = ERR_PTR(-ENOMEM);
goto bail_qp;
......@@ -1090,7 +1108,7 @@ struct ib_qp *qib_create_qp(struct ib_pd *ibpd,
dev = to_idev(ibpd->device);
dd = dd_from_dev(dev);
err = alloc_qpn(dd, &dev->qpn_table, init_attr->qp_type,
init_attr->port_num);
init_attr->port_num, gfp);
if (err < 0) {
ret = ERR_PTR(err);
vfree(qp->r_rq.wq);
......
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