Path: blob/master/drivers/infiniband/core/uverbs_cmd.c
37212 views
/*1* Copyright (c) 2005 Topspin Communications. All rights reserved.2* Copyright (c) 2005, 2006, 2007 Cisco Systems. All rights reserved.3* Copyright (c) 2005 PathScale, Inc. All rights reserved.4* Copyright (c) 2006 Mellanox Technologies. All rights reserved.5*6* This software is available to you under a choice of one of two7* licenses. You may choose to be licensed under the terms of the GNU8* General Public License (GPL) Version 2, available from the file9* COPYING in the main directory of this source tree, or the10* OpenIB.org BSD license below:11*12* Redistribution and use in source and binary forms, with or13* without modification, are permitted provided that the following14* conditions are met:15*16* - Redistributions of source code must retain the above17* copyright notice, this list of conditions and the following18* disclaimer.19*20* - Redistributions in binary form must reproduce the above21* copyright notice, this list of conditions and the following22* disclaimer in the documentation and/or other materials23* provided with the distribution.24*25* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,26* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF27* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND28* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS29* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN30* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN31* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE32* SOFTWARE.33*/3435#include <linux/file.h>36#include <linux/fs.h>37#include <linux/slab.h>3839#include <asm/uaccess.h>4041#include "uverbs.h"4243static struct lock_class_key pd_lock_key;44static struct lock_class_key mr_lock_key;45static struct lock_class_key cq_lock_key;46static struct lock_class_key qp_lock_key;47static struct lock_class_key ah_lock_key;48static struct lock_class_key srq_lock_key;4950#define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \51do { \52(udata)->inbuf = (void __user *) (ibuf); \53(udata)->outbuf = (void __user *) (obuf); \54(udata)->inlen = (ilen); \55(udata)->outlen = (olen); \56} while (0)5758/*59* The ib_uobject locking scheme is as follows:60*61* - ib_uverbs_idr_lock protects the uverbs idrs themselves, so it62* needs to be held during all idr operations. When an object is63* looked up, a reference must be taken on the object's kref before64* dropping this lock.65*66* - Each object also has an rwsem. This rwsem must be held for67* reading while an operation that uses the object is performed.68* For example, while registering an MR, the associated PD's69* uobject.mutex must be held for reading. The rwsem must be held70* for writing while initializing or destroying an object.71*72* - In addition, each object has a "live" flag. If this flag is not73* set, then lookups of the object will fail even if it is found in74* the idr. This handles a reader that blocks and does not acquire75* the rwsem until after the object is destroyed. The destroy76* operation will set the live flag to 0 and then drop the rwsem;77* this will allow the reader to acquire the rwsem, see that the78* live flag is 0, and then drop the rwsem and its reference to79* object. The underlying storage will not be freed until the last80* reference to the object is dropped.81*/8283static void init_uobj(struct ib_uobject *uobj, u64 user_handle,84struct ib_ucontext *context, struct lock_class_key *key)85{86uobj->user_handle = user_handle;87uobj->context = context;88kref_init(&uobj->ref);89init_rwsem(&uobj->mutex);90lockdep_set_class(&uobj->mutex, key);91uobj->live = 0;92}9394static void release_uobj(struct kref *kref)95{96kfree(container_of(kref, struct ib_uobject, ref));97}9899static void put_uobj(struct ib_uobject *uobj)100{101kref_put(&uobj->ref, release_uobj);102}103104static void put_uobj_read(struct ib_uobject *uobj)105{106up_read(&uobj->mutex);107put_uobj(uobj);108}109110static void put_uobj_write(struct ib_uobject *uobj)111{112up_write(&uobj->mutex);113put_uobj(uobj);114}115116static int idr_add_uobj(struct idr *idr, struct ib_uobject *uobj)117{118int ret;119120retry:121if (!idr_pre_get(idr, GFP_KERNEL))122return -ENOMEM;123124spin_lock(&ib_uverbs_idr_lock);125ret = idr_get_new(idr, uobj, &uobj->id);126spin_unlock(&ib_uverbs_idr_lock);127128if (ret == -EAGAIN)129goto retry;130131return ret;132}133134void idr_remove_uobj(struct idr *idr, struct ib_uobject *uobj)135{136spin_lock(&ib_uverbs_idr_lock);137idr_remove(idr, uobj->id);138spin_unlock(&ib_uverbs_idr_lock);139}140141static struct ib_uobject *__idr_get_uobj(struct idr *idr, int id,142struct ib_ucontext *context)143{144struct ib_uobject *uobj;145146spin_lock(&ib_uverbs_idr_lock);147uobj = idr_find(idr, id);148if (uobj) {149if (uobj->context == context)150kref_get(&uobj->ref);151else152uobj = NULL;153}154spin_unlock(&ib_uverbs_idr_lock);155156return uobj;157}158159static struct ib_uobject *idr_read_uobj(struct idr *idr, int id,160struct ib_ucontext *context, int nested)161{162struct ib_uobject *uobj;163164uobj = __idr_get_uobj(idr, id, context);165if (!uobj)166return NULL;167168if (nested)169down_read_nested(&uobj->mutex, SINGLE_DEPTH_NESTING);170else171down_read(&uobj->mutex);172if (!uobj->live) {173put_uobj_read(uobj);174return NULL;175}176177return uobj;178}179180static struct ib_uobject *idr_write_uobj(struct idr *idr, int id,181struct ib_ucontext *context)182{183struct ib_uobject *uobj;184185uobj = __idr_get_uobj(idr, id, context);186if (!uobj)187return NULL;188189down_write(&uobj->mutex);190if (!uobj->live) {191put_uobj_write(uobj);192return NULL;193}194195return uobj;196}197198static void *idr_read_obj(struct idr *idr, int id, struct ib_ucontext *context,199int nested)200{201struct ib_uobject *uobj;202203uobj = idr_read_uobj(idr, id, context, nested);204return uobj ? uobj->object : NULL;205}206207static struct ib_pd *idr_read_pd(int pd_handle, struct ib_ucontext *context)208{209return idr_read_obj(&ib_uverbs_pd_idr, pd_handle, context, 0);210}211212static void put_pd_read(struct ib_pd *pd)213{214put_uobj_read(pd->uobject);215}216217static struct ib_cq *idr_read_cq(int cq_handle, struct ib_ucontext *context, int nested)218{219return idr_read_obj(&ib_uverbs_cq_idr, cq_handle, context, nested);220}221222static void put_cq_read(struct ib_cq *cq)223{224put_uobj_read(cq->uobject);225}226227static struct ib_ah *idr_read_ah(int ah_handle, struct ib_ucontext *context)228{229return idr_read_obj(&ib_uverbs_ah_idr, ah_handle, context, 0);230}231232static void put_ah_read(struct ib_ah *ah)233{234put_uobj_read(ah->uobject);235}236237static struct ib_qp *idr_read_qp(int qp_handle, struct ib_ucontext *context)238{239return idr_read_obj(&ib_uverbs_qp_idr, qp_handle, context, 0);240}241242static void put_qp_read(struct ib_qp *qp)243{244put_uobj_read(qp->uobject);245}246247static struct ib_srq *idr_read_srq(int srq_handle, struct ib_ucontext *context)248{249return idr_read_obj(&ib_uverbs_srq_idr, srq_handle, context, 0);250}251252static void put_srq_read(struct ib_srq *srq)253{254put_uobj_read(srq->uobject);255}256257ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,258const char __user *buf,259int in_len, int out_len)260{261struct ib_uverbs_get_context cmd;262struct ib_uverbs_get_context_resp resp;263struct ib_udata udata;264struct ib_device *ibdev = file->device->ib_dev;265struct ib_ucontext *ucontext;266struct file *filp;267int ret;268269if (out_len < sizeof resp)270return -ENOSPC;271272if (copy_from_user(&cmd, buf, sizeof cmd))273return -EFAULT;274275mutex_lock(&file->mutex);276277if (file->ucontext) {278ret = -EINVAL;279goto err;280}281282INIT_UDATA(&udata, buf + sizeof cmd,283(unsigned long) cmd.response + sizeof resp,284in_len - sizeof cmd, out_len - sizeof resp);285286ucontext = ibdev->alloc_ucontext(ibdev, &udata);287if (IS_ERR(ucontext)) {288ret = PTR_ERR(ucontext);289goto err;290}291292ucontext->device = ibdev;293INIT_LIST_HEAD(&ucontext->pd_list);294INIT_LIST_HEAD(&ucontext->mr_list);295INIT_LIST_HEAD(&ucontext->mw_list);296INIT_LIST_HEAD(&ucontext->cq_list);297INIT_LIST_HEAD(&ucontext->qp_list);298INIT_LIST_HEAD(&ucontext->srq_list);299INIT_LIST_HEAD(&ucontext->ah_list);300ucontext->closing = 0;301302resp.num_comp_vectors = file->device->num_comp_vectors;303304ret = get_unused_fd();305if (ret < 0)306goto err_free;307resp.async_fd = ret;308309filp = ib_uverbs_alloc_event_file(file, 1);310if (IS_ERR(filp)) {311ret = PTR_ERR(filp);312goto err_fd;313}314315if (copy_to_user((void __user *) (unsigned long) cmd.response,316&resp, sizeof resp)) {317ret = -EFAULT;318goto err_file;319}320321file->async_file = filp->private_data;322323INIT_IB_EVENT_HANDLER(&file->event_handler, file->device->ib_dev,324ib_uverbs_event_handler);325ret = ib_register_event_handler(&file->event_handler);326if (ret)327goto err_file;328329kref_get(&file->async_file->ref);330kref_get(&file->ref);331file->ucontext = ucontext;332333fd_install(resp.async_fd, filp);334335mutex_unlock(&file->mutex);336337return in_len;338339err_file:340fput(filp);341342err_fd:343put_unused_fd(resp.async_fd);344345err_free:346ibdev->dealloc_ucontext(ucontext);347348err:349mutex_unlock(&file->mutex);350return ret;351}352353ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file,354const char __user *buf,355int in_len, int out_len)356{357struct ib_uverbs_query_device cmd;358struct ib_uverbs_query_device_resp resp;359struct ib_device_attr attr;360int ret;361362if (out_len < sizeof resp)363return -ENOSPC;364365if (copy_from_user(&cmd, buf, sizeof cmd))366return -EFAULT;367368ret = ib_query_device(file->device->ib_dev, &attr);369if (ret)370return ret;371372memset(&resp, 0, sizeof resp);373374resp.fw_ver = attr.fw_ver;375resp.node_guid = file->device->ib_dev->node_guid;376resp.sys_image_guid = attr.sys_image_guid;377resp.max_mr_size = attr.max_mr_size;378resp.page_size_cap = attr.page_size_cap;379resp.vendor_id = attr.vendor_id;380resp.vendor_part_id = attr.vendor_part_id;381resp.hw_ver = attr.hw_ver;382resp.max_qp = attr.max_qp;383resp.max_qp_wr = attr.max_qp_wr;384resp.device_cap_flags = attr.device_cap_flags;385resp.max_sge = attr.max_sge;386resp.max_sge_rd = attr.max_sge_rd;387resp.max_cq = attr.max_cq;388resp.max_cqe = attr.max_cqe;389resp.max_mr = attr.max_mr;390resp.max_pd = attr.max_pd;391resp.max_qp_rd_atom = attr.max_qp_rd_atom;392resp.max_ee_rd_atom = attr.max_ee_rd_atom;393resp.max_res_rd_atom = attr.max_res_rd_atom;394resp.max_qp_init_rd_atom = attr.max_qp_init_rd_atom;395resp.max_ee_init_rd_atom = attr.max_ee_init_rd_atom;396resp.atomic_cap = attr.atomic_cap;397resp.max_ee = attr.max_ee;398resp.max_rdd = attr.max_rdd;399resp.max_mw = attr.max_mw;400resp.max_raw_ipv6_qp = attr.max_raw_ipv6_qp;401resp.max_raw_ethy_qp = attr.max_raw_ethy_qp;402resp.max_mcast_grp = attr.max_mcast_grp;403resp.max_mcast_qp_attach = attr.max_mcast_qp_attach;404resp.max_total_mcast_qp_attach = attr.max_total_mcast_qp_attach;405resp.max_ah = attr.max_ah;406resp.max_fmr = attr.max_fmr;407resp.max_map_per_fmr = attr.max_map_per_fmr;408resp.max_srq = attr.max_srq;409resp.max_srq_wr = attr.max_srq_wr;410resp.max_srq_sge = attr.max_srq_sge;411resp.max_pkeys = attr.max_pkeys;412resp.local_ca_ack_delay = attr.local_ca_ack_delay;413resp.phys_port_cnt = file->device->ib_dev->phys_port_cnt;414415if (copy_to_user((void __user *) (unsigned long) cmd.response,416&resp, sizeof resp))417return -EFAULT;418419return in_len;420}421422ssize_t ib_uverbs_query_port(struct ib_uverbs_file *file,423const char __user *buf,424int in_len, int out_len)425{426struct ib_uverbs_query_port cmd;427struct ib_uverbs_query_port_resp resp;428struct ib_port_attr attr;429int ret;430431if (out_len < sizeof resp)432return -ENOSPC;433434if (copy_from_user(&cmd, buf, sizeof cmd))435return -EFAULT;436437ret = ib_query_port(file->device->ib_dev, cmd.port_num, &attr);438if (ret)439return ret;440441memset(&resp, 0, sizeof resp);442443resp.state = attr.state;444resp.max_mtu = attr.max_mtu;445resp.active_mtu = attr.active_mtu;446resp.gid_tbl_len = attr.gid_tbl_len;447resp.port_cap_flags = attr.port_cap_flags;448resp.max_msg_sz = attr.max_msg_sz;449resp.bad_pkey_cntr = attr.bad_pkey_cntr;450resp.qkey_viol_cntr = attr.qkey_viol_cntr;451resp.pkey_tbl_len = attr.pkey_tbl_len;452resp.lid = attr.lid;453resp.sm_lid = attr.sm_lid;454resp.lmc = attr.lmc;455resp.max_vl_num = attr.max_vl_num;456resp.sm_sl = attr.sm_sl;457resp.subnet_timeout = attr.subnet_timeout;458resp.init_type_reply = attr.init_type_reply;459resp.active_width = attr.active_width;460resp.active_speed = attr.active_speed;461resp.phys_state = attr.phys_state;462resp.link_layer = rdma_port_get_link_layer(file->device->ib_dev,463cmd.port_num);464465if (copy_to_user((void __user *) (unsigned long) cmd.response,466&resp, sizeof resp))467return -EFAULT;468469return in_len;470}471472ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,473const char __user *buf,474int in_len, int out_len)475{476struct ib_uverbs_alloc_pd cmd;477struct ib_uverbs_alloc_pd_resp resp;478struct ib_udata udata;479struct ib_uobject *uobj;480struct ib_pd *pd;481int ret;482483if (out_len < sizeof resp)484return -ENOSPC;485486if (copy_from_user(&cmd, buf, sizeof cmd))487return -EFAULT;488489INIT_UDATA(&udata, buf + sizeof cmd,490(unsigned long) cmd.response + sizeof resp,491in_len - sizeof cmd, out_len - sizeof resp);492493uobj = kmalloc(sizeof *uobj, GFP_KERNEL);494if (!uobj)495return -ENOMEM;496497init_uobj(uobj, 0, file->ucontext, &pd_lock_key);498down_write(&uobj->mutex);499500pd = file->device->ib_dev->alloc_pd(file->device->ib_dev,501file->ucontext, &udata);502if (IS_ERR(pd)) {503ret = PTR_ERR(pd);504goto err;505}506507pd->device = file->device->ib_dev;508pd->uobject = uobj;509atomic_set(&pd->usecnt, 0);510511uobj->object = pd;512ret = idr_add_uobj(&ib_uverbs_pd_idr, uobj);513if (ret)514goto err_idr;515516memset(&resp, 0, sizeof resp);517resp.pd_handle = uobj->id;518519if (copy_to_user((void __user *) (unsigned long) cmd.response,520&resp, sizeof resp)) {521ret = -EFAULT;522goto err_copy;523}524525mutex_lock(&file->mutex);526list_add_tail(&uobj->list, &file->ucontext->pd_list);527mutex_unlock(&file->mutex);528529uobj->live = 1;530531up_write(&uobj->mutex);532533return in_len;534535err_copy:536idr_remove_uobj(&ib_uverbs_pd_idr, uobj);537538err_idr:539ib_dealloc_pd(pd);540541err:542put_uobj_write(uobj);543return ret;544}545546ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,547const char __user *buf,548int in_len, int out_len)549{550struct ib_uverbs_dealloc_pd cmd;551struct ib_uobject *uobj;552int ret;553554if (copy_from_user(&cmd, buf, sizeof cmd))555return -EFAULT;556557uobj = idr_write_uobj(&ib_uverbs_pd_idr, cmd.pd_handle, file->ucontext);558if (!uobj)559return -EINVAL;560561ret = ib_dealloc_pd(uobj->object);562if (!ret)563uobj->live = 0;564565put_uobj_write(uobj);566567if (ret)568return ret;569570idr_remove_uobj(&ib_uverbs_pd_idr, uobj);571572mutex_lock(&file->mutex);573list_del(&uobj->list);574mutex_unlock(&file->mutex);575576put_uobj(uobj);577578return in_len;579}580581ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,582const char __user *buf, int in_len,583int out_len)584{585struct ib_uverbs_reg_mr cmd;586struct ib_uverbs_reg_mr_resp resp;587struct ib_udata udata;588struct ib_uobject *uobj;589struct ib_pd *pd;590struct ib_mr *mr;591int ret;592593if (out_len < sizeof resp)594return -ENOSPC;595596if (copy_from_user(&cmd, buf, sizeof cmd))597return -EFAULT;598599INIT_UDATA(&udata, buf + sizeof cmd,600(unsigned long) cmd.response + sizeof resp,601in_len - sizeof cmd, out_len - sizeof resp);602603if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))604return -EINVAL;605606/*607* Local write permission is required if remote write or608* remote atomic permission is also requested.609*/610if (cmd.access_flags & (IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_REMOTE_WRITE) &&611!(cmd.access_flags & IB_ACCESS_LOCAL_WRITE))612return -EINVAL;613614uobj = kmalloc(sizeof *uobj, GFP_KERNEL);615if (!uobj)616return -ENOMEM;617618init_uobj(uobj, 0, file->ucontext, &mr_lock_key);619down_write(&uobj->mutex);620621pd = idr_read_pd(cmd.pd_handle, file->ucontext);622if (!pd) {623ret = -EINVAL;624goto err_free;625}626627mr = pd->device->reg_user_mr(pd, cmd.start, cmd.length, cmd.hca_va,628cmd.access_flags, &udata);629if (IS_ERR(mr)) {630ret = PTR_ERR(mr);631goto err_put;632}633634mr->device = pd->device;635mr->pd = pd;636mr->uobject = uobj;637atomic_inc(&pd->usecnt);638atomic_set(&mr->usecnt, 0);639640uobj->object = mr;641ret = idr_add_uobj(&ib_uverbs_mr_idr, uobj);642if (ret)643goto err_unreg;644645memset(&resp, 0, sizeof resp);646resp.lkey = mr->lkey;647resp.rkey = mr->rkey;648resp.mr_handle = uobj->id;649650if (copy_to_user((void __user *) (unsigned long) cmd.response,651&resp, sizeof resp)) {652ret = -EFAULT;653goto err_copy;654}655656put_pd_read(pd);657658mutex_lock(&file->mutex);659list_add_tail(&uobj->list, &file->ucontext->mr_list);660mutex_unlock(&file->mutex);661662uobj->live = 1;663664up_write(&uobj->mutex);665666return in_len;667668err_copy:669idr_remove_uobj(&ib_uverbs_mr_idr, uobj);670671err_unreg:672ib_dereg_mr(mr);673674err_put:675put_pd_read(pd);676677err_free:678put_uobj_write(uobj);679return ret;680}681682ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,683const char __user *buf, int in_len,684int out_len)685{686struct ib_uverbs_dereg_mr cmd;687struct ib_mr *mr;688struct ib_uobject *uobj;689int ret = -EINVAL;690691if (copy_from_user(&cmd, buf, sizeof cmd))692return -EFAULT;693694uobj = idr_write_uobj(&ib_uverbs_mr_idr, cmd.mr_handle, file->ucontext);695if (!uobj)696return -EINVAL;697698mr = uobj->object;699700ret = ib_dereg_mr(mr);701if (!ret)702uobj->live = 0;703704put_uobj_write(uobj);705706if (ret)707return ret;708709idr_remove_uobj(&ib_uverbs_mr_idr, uobj);710711mutex_lock(&file->mutex);712list_del(&uobj->list);713mutex_unlock(&file->mutex);714715put_uobj(uobj);716717return in_len;718}719720ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,721const char __user *buf, int in_len,722int out_len)723{724struct ib_uverbs_create_comp_channel cmd;725struct ib_uverbs_create_comp_channel_resp resp;726struct file *filp;727int ret;728729if (out_len < sizeof resp)730return -ENOSPC;731732if (copy_from_user(&cmd, buf, sizeof cmd))733return -EFAULT;734735ret = get_unused_fd();736if (ret < 0)737return ret;738resp.fd = ret;739740filp = ib_uverbs_alloc_event_file(file, 0);741if (IS_ERR(filp)) {742put_unused_fd(resp.fd);743return PTR_ERR(filp);744}745746if (copy_to_user((void __user *) (unsigned long) cmd.response,747&resp, sizeof resp)) {748put_unused_fd(resp.fd);749fput(filp);750return -EFAULT;751}752753fd_install(resp.fd, filp);754return in_len;755}756757ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,758const char __user *buf, int in_len,759int out_len)760{761struct ib_uverbs_create_cq cmd;762struct ib_uverbs_create_cq_resp resp;763struct ib_udata udata;764struct ib_ucq_object *obj;765struct ib_uverbs_event_file *ev_file = NULL;766struct ib_cq *cq;767int ret;768769if (out_len < sizeof resp)770return -ENOSPC;771772if (copy_from_user(&cmd, buf, sizeof cmd))773return -EFAULT;774775INIT_UDATA(&udata, buf + sizeof cmd,776(unsigned long) cmd.response + sizeof resp,777in_len - sizeof cmd, out_len - sizeof resp);778779if (cmd.comp_vector >= file->device->num_comp_vectors)780return -EINVAL;781782obj = kmalloc(sizeof *obj, GFP_KERNEL);783if (!obj)784return -ENOMEM;785786init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &cq_lock_key);787down_write(&obj->uobject.mutex);788789if (cmd.comp_channel >= 0) {790ev_file = ib_uverbs_lookup_comp_file(cmd.comp_channel);791if (!ev_file) {792ret = -EINVAL;793goto err;794}795}796797obj->uverbs_file = file;798obj->comp_events_reported = 0;799obj->async_events_reported = 0;800INIT_LIST_HEAD(&obj->comp_list);801INIT_LIST_HEAD(&obj->async_list);802803cq = file->device->ib_dev->create_cq(file->device->ib_dev, cmd.cqe,804cmd.comp_vector,805file->ucontext, &udata);806if (IS_ERR(cq)) {807ret = PTR_ERR(cq);808goto err_file;809}810811cq->device = file->device->ib_dev;812cq->uobject = &obj->uobject;813cq->comp_handler = ib_uverbs_comp_handler;814cq->event_handler = ib_uverbs_cq_event_handler;815cq->cq_context = ev_file;816atomic_set(&cq->usecnt, 0);817818obj->uobject.object = cq;819ret = idr_add_uobj(&ib_uverbs_cq_idr, &obj->uobject);820if (ret)821goto err_free;822823memset(&resp, 0, sizeof resp);824resp.cq_handle = obj->uobject.id;825resp.cqe = cq->cqe;826827if (copy_to_user((void __user *) (unsigned long) cmd.response,828&resp, sizeof resp)) {829ret = -EFAULT;830goto err_copy;831}832833mutex_lock(&file->mutex);834list_add_tail(&obj->uobject.list, &file->ucontext->cq_list);835mutex_unlock(&file->mutex);836837obj->uobject.live = 1;838839up_write(&obj->uobject.mutex);840841return in_len;842843err_copy:844idr_remove_uobj(&ib_uverbs_cq_idr, &obj->uobject);845846err_free:847ib_destroy_cq(cq);848849err_file:850if (ev_file)851ib_uverbs_release_ucq(file, ev_file, obj);852853err:854put_uobj_write(&obj->uobject);855return ret;856}857858ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,859const char __user *buf, int in_len,860int out_len)861{862struct ib_uverbs_resize_cq cmd;863struct ib_uverbs_resize_cq_resp resp;864struct ib_udata udata;865struct ib_cq *cq;866int ret = -EINVAL;867868if (copy_from_user(&cmd, buf, sizeof cmd))869return -EFAULT;870871INIT_UDATA(&udata, buf + sizeof cmd,872(unsigned long) cmd.response + sizeof resp,873in_len - sizeof cmd, out_len - sizeof resp);874875cq = idr_read_cq(cmd.cq_handle, file->ucontext, 0);876if (!cq)877return -EINVAL;878879ret = cq->device->resize_cq(cq, cmd.cqe, &udata);880if (ret)881goto out;882883resp.cqe = cq->cqe;884885if (copy_to_user((void __user *) (unsigned long) cmd.response,886&resp, sizeof resp.cqe))887ret = -EFAULT;888889out:890put_cq_read(cq);891892return ret ? ret : in_len;893}894895static int copy_wc_to_user(void __user *dest, struct ib_wc *wc)896{897struct ib_uverbs_wc tmp;898899tmp.wr_id = wc->wr_id;900tmp.status = wc->status;901tmp.opcode = wc->opcode;902tmp.vendor_err = wc->vendor_err;903tmp.byte_len = wc->byte_len;904tmp.ex.imm_data = (__u32 __force) wc->ex.imm_data;905tmp.qp_num = wc->qp->qp_num;906tmp.src_qp = wc->src_qp;907tmp.wc_flags = wc->wc_flags;908tmp.pkey_index = wc->pkey_index;909tmp.slid = wc->slid;910tmp.sl = wc->sl;911tmp.dlid_path_bits = wc->dlid_path_bits;912tmp.port_num = wc->port_num;913tmp.reserved = 0;914915if (copy_to_user(dest, &tmp, sizeof tmp))916return -EFAULT;917918return 0;919}920921ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,922const char __user *buf, int in_len,923int out_len)924{925struct ib_uverbs_poll_cq cmd;926struct ib_uverbs_poll_cq_resp resp;927u8 __user *header_ptr;928u8 __user *data_ptr;929struct ib_cq *cq;930struct ib_wc wc;931int ret;932933if (copy_from_user(&cmd, buf, sizeof cmd))934return -EFAULT;935936cq = idr_read_cq(cmd.cq_handle, file->ucontext, 0);937if (!cq)938return -EINVAL;939940/* we copy a struct ib_uverbs_poll_cq_resp to user space */941header_ptr = (void __user *)(unsigned long) cmd.response;942data_ptr = header_ptr + sizeof resp;943944memset(&resp, 0, sizeof resp);945while (resp.count < cmd.ne) {946ret = ib_poll_cq(cq, 1, &wc);947if (ret < 0)948goto out_put;949if (!ret)950break;951952ret = copy_wc_to_user(data_ptr, &wc);953if (ret)954goto out_put;955956data_ptr += sizeof(struct ib_uverbs_wc);957++resp.count;958}959960if (copy_to_user(header_ptr, &resp, sizeof resp)) {961ret = -EFAULT;962goto out_put;963}964965ret = in_len;966967out_put:968put_cq_read(cq);969return ret;970}971972ssize_t ib_uverbs_req_notify_cq(struct ib_uverbs_file *file,973const char __user *buf, int in_len,974int out_len)975{976struct ib_uverbs_req_notify_cq cmd;977struct ib_cq *cq;978979if (copy_from_user(&cmd, buf, sizeof cmd))980return -EFAULT;981982cq = idr_read_cq(cmd.cq_handle, file->ucontext, 0);983if (!cq)984return -EINVAL;985986ib_req_notify_cq(cq, cmd.solicited_only ?987IB_CQ_SOLICITED : IB_CQ_NEXT_COMP);988989put_cq_read(cq);990991return in_len;992}993994ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,995const char __user *buf, int in_len,996int out_len)997{998struct ib_uverbs_destroy_cq cmd;999struct ib_uverbs_destroy_cq_resp resp;1000struct ib_uobject *uobj;1001struct ib_cq *cq;1002struct ib_ucq_object *obj;1003struct ib_uverbs_event_file *ev_file;1004int ret = -EINVAL;10051006if (copy_from_user(&cmd, buf, sizeof cmd))1007return -EFAULT;10081009uobj = idr_write_uobj(&ib_uverbs_cq_idr, cmd.cq_handle, file->ucontext);1010if (!uobj)1011return -EINVAL;1012cq = uobj->object;1013ev_file = cq->cq_context;1014obj = container_of(cq->uobject, struct ib_ucq_object, uobject);10151016ret = ib_destroy_cq(cq);1017if (!ret)1018uobj->live = 0;10191020put_uobj_write(uobj);10211022if (ret)1023return ret;10241025idr_remove_uobj(&ib_uverbs_cq_idr, uobj);10261027mutex_lock(&file->mutex);1028list_del(&uobj->list);1029mutex_unlock(&file->mutex);10301031ib_uverbs_release_ucq(file, ev_file, obj);10321033memset(&resp, 0, sizeof resp);1034resp.comp_events_reported = obj->comp_events_reported;1035resp.async_events_reported = obj->async_events_reported;10361037put_uobj(uobj);10381039if (copy_to_user((void __user *) (unsigned long) cmd.response,1040&resp, sizeof resp))1041return -EFAULT;10421043return in_len;1044}10451046ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,1047const char __user *buf, int in_len,1048int out_len)1049{1050struct ib_uverbs_create_qp cmd;1051struct ib_uverbs_create_qp_resp resp;1052struct ib_udata udata;1053struct ib_uqp_object *obj;1054struct ib_pd *pd;1055struct ib_cq *scq, *rcq;1056struct ib_srq *srq;1057struct ib_qp *qp;1058struct ib_qp_init_attr attr;1059int ret;10601061if (out_len < sizeof resp)1062return -ENOSPC;10631064if (copy_from_user(&cmd, buf, sizeof cmd))1065return -EFAULT;10661067INIT_UDATA(&udata, buf + sizeof cmd,1068(unsigned long) cmd.response + sizeof resp,1069in_len - sizeof cmd, out_len - sizeof resp);10701071obj = kmalloc(sizeof *obj, GFP_KERNEL);1072if (!obj)1073return -ENOMEM;10741075init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_key);1076down_write(&obj->uevent.uobject.mutex);10771078srq = cmd.is_srq ? idr_read_srq(cmd.srq_handle, file->ucontext) : NULL;1079pd = idr_read_pd(cmd.pd_handle, file->ucontext);1080scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, 0);1081rcq = cmd.recv_cq_handle == cmd.send_cq_handle ?1082scq : idr_read_cq(cmd.recv_cq_handle, file->ucontext, 1);10831084if (!pd || !scq || !rcq || (cmd.is_srq && !srq)) {1085ret = -EINVAL;1086goto err_put;1087}10881089attr.event_handler = ib_uverbs_qp_event_handler;1090attr.qp_context = file;1091attr.send_cq = scq;1092attr.recv_cq = rcq;1093attr.srq = srq;1094attr.sq_sig_type = cmd.sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR;1095attr.qp_type = cmd.qp_type;1096attr.create_flags = 0;10971098attr.cap.max_send_wr = cmd.max_send_wr;1099attr.cap.max_recv_wr = cmd.max_recv_wr;1100attr.cap.max_send_sge = cmd.max_send_sge;1101attr.cap.max_recv_sge = cmd.max_recv_sge;1102attr.cap.max_inline_data = cmd.max_inline_data;11031104obj->uevent.events_reported = 0;1105INIT_LIST_HEAD(&obj->uevent.event_list);1106INIT_LIST_HEAD(&obj->mcast_list);11071108qp = pd->device->create_qp(pd, &attr, &udata);1109if (IS_ERR(qp)) {1110ret = PTR_ERR(qp);1111goto err_put;1112}11131114qp->device = pd->device;1115qp->pd = pd;1116qp->send_cq = attr.send_cq;1117qp->recv_cq = attr.recv_cq;1118qp->srq = attr.srq;1119qp->uobject = &obj->uevent.uobject;1120qp->event_handler = attr.event_handler;1121qp->qp_context = attr.qp_context;1122qp->qp_type = attr.qp_type;1123atomic_inc(&pd->usecnt);1124atomic_inc(&attr.send_cq->usecnt);1125atomic_inc(&attr.recv_cq->usecnt);1126if (attr.srq)1127atomic_inc(&attr.srq->usecnt);11281129obj->uevent.uobject.object = qp;1130ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);1131if (ret)1132goto err_destroy;11331134memset(&resp, 0, sizeof resp);1135resp.qpn = qp->qp_num;1136resp.qp_handle = obj->uevent.uobject.id;1137resp.max_recv_sge = attr.cap.max_recv_sge;1138resp.max_send_sge = attr.cap.max_send_sge;1139resp.max_recv_wr = attr.cap.max_recv_wr;1140resp.max_send_wr = attr.cap.max_send_wr;1141resp.max_inline_data = attr.cap.max_inline_data;11421143if (copy_to_user((void __user *) (unsigned long) cmd.response,1144&resp, sizeof resp)) {1145ret = -EFAULT;1146goto err_copy;1147}11481149put_pd_read(pd);1150put_cq_read(scq);1151if (rcq != scq)1152put_cq_read(rcq);1153if (srq)1154put_srq_read(srq);11551156mutex_lock(&file->mutex);1157list_add_tail(&obj->uevent.uobject.list, &file->ucontext->qp_list);1158mutex_unlock(&file->mutex);11591160obj->uevent.uobject.live = 1;11611162up_write(&obj->uevent.uobject.mutex);11631164return in_len;11651166err_copy:1167idr_remove_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);11681169err_destroy:1170ib_destroy_qp(qp);11711172err_put:1173if (pd)1174put_pd_read(pd);1175if (scq)1176put_cq_read(scq);1177if (rcq && rcq != scq)1178put_cq_read(rcq);1179if (srq)1180put_srq_read(srq);11811182put_uobj_write(&obj->uevent.uobject);1183return ret;1184}11851186ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,1187const char __user *buf, int in_len,1188int out_len)1189{1190struct ib_uverbs_query_qp cmd;1191struct ib_uverbs_query_qp_resp resp;1192struct ib_qp *qp;1193struct ib_qp_attr *attr;1194struct ib_qp_init_attr *init_attr;1195int ret;11961197if (copy_from_user(&cmd, buf, sizeof cmd))1198return -EFAULT;11991200attr = kmalloc(sizeof *attr, GFP_KERNEL);1201init_attr = kmalloc(sizeof *init_attr, GFP_KERNEL);1202if (!attr || !init_attr) {1203ret = -ENOMEM;1204goto out;1205}12061207qp = idr_read_qp(cmd.qp_handle, file->ucontext);1208if (!qp) {1209ret = -EINVAL;1210goto out;1211}12121213ret = ib_query_qp(qp, attr, cmd.attr_mask, init_attr);12141215put_qp_read(qp);12161217if (ret)1218goto out;12191220memset(&resp, 0, sizeof resp);12211222resp.qp_state = attr->qp_state;1223resp.cur_qp_state = attr->cur_qp_state;1224resp.path_mtu = attr->path_mtu;1225resp.path_mig_state = attr->path_mig_state;1226resp.qkey = attr->qkey;1227resp.rq_psn = attr->rq_psn;1228resp.sq_psn = attr->sq_psn;1229resp.dest_qp_num = attr->dest_qp_num;1230resp.qp_access_flags = attr->qp_access_flags;1231resp.pkey_index = attr->pkey_index;1232resp.alt_pkey_index = attr->alt_pkey_index;1233resp.sq_draining = attr->sq_draining;1234resp.max_rd_atomic = attr->max_rd_atomic;1235resp.max_dest_rd_atomic = attr->max_dest_rd_atomic;1236resp.min_rnr_timer = attr->min_rnr_timer;1237resp.port_num = attr->port_num;1238resp.timeout = attr->timeout;1239resp.retry_cnt = attr->retry_cnt;1240resp.rnr_retry = attr->rnr_retry;1241resp.alt_port_num = attr->alt_port_num;1242resp.alt_timeout = attr->alt_timeout;12431244memcpy(resp.dest.dgid, attr->ah_attr.grh.dgid.raw, 16);1245resp.dest.flow_label = attr->ah_attr.grh.flow_label;1246resp.dest.sgid_index = attr->ah_attr.grh.sgid_index;1247resp.dest.hop_limit = attr->ah_attr.grh.hop_limit;1248resp.dest.traffic_class = attr->ah_attr.grh.traffic_class;1249resp.dest.dlid = attr->ah_attr.dlid;1250resp.dest.sl = attr->ah_attr.sl;1251resp.dest.src_path_bits = attr->ah_attr.src_path_bits;1252resp.dest.static_rate = attr->ah_attr.static_rate;1253resp.dest.is_global = !!(attr->ah_attr.ah_flags & IB_AH_GRH);1254resp.dest.port_num = attr->ah_attr.port_num;12551256memcpy(resp.alt_dest.dgid, attr->alt_ah_attr.grh.dgid.raw, 16);1257resp.alt_dest.flow_label = attr->alt_ah_attr.grh.flow_label;1258resp.alt_dest.sgid_index = attr->alt_ah_attr.grh.sgid_index;1259resp.alt_dest.hop_limit = attr->alt_ah_attr.grh.hop_limit;1260resp.alt_dest.traffic_class = attr->alt_ah_attr.grh.traffic_class;1261resp.alt_dest.dlid = attr->alt_ah_attr.dlid;1262resp.alt_dest.sl = attr->alt_ah_attr.sl;1263resp.alt_dest.src_path_bits = attr->alt_ah_attr.src_path_bits;1264resp.alt_dest.static_rate = attr->alt_ah_attr.static_rate;1265resp.alt_dest.is_global = !!(attr->alt_ah_attr.ah_flags & IB_AH_GRH);1266resp.alt_dest.port_num = attr->alt_ah_attr.port_num;12671268resp.max_send_wr = init_attr->cap.max_send_wr;1269resp.max_recv_wr = init_attr->cap.max_recv_wr;1270resp.max_send_sge = init_attr->cap.max_send_sge;1271resp.max_recv_sge = init_attr->cap.max_recv_sge;1272resp.max_inline_data = init_attr->cap.max_inline_data;1273resp.sq_sig_all = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR;12741275if (copy_to_user((void __user *) (unsigned long) cmd.response,1276&resp, sizeof resp))1277ret = -EFAULT;12781279out:1280kfree(attr);1281kfree(init_attr);12821283return ret ? ret : in_len;1284}12851286ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,1287const char __user *buf, int in_len,1288int out_len)1289{1290struct ib_uverbs_modify_qp cmd;1291struct ib_udata udata;1292struct ib_qp *qp;1293struct ib_qp_attr *attr;1294int ret;12951296if (copy_from_user(&cmd, buf, sizeof cmd))1297return -EFAULT;12981299INIT_UDATA(&udata, buf + sizeof cmd, NULL, in_len - sizeof cmd,1300out_len);13011302attr = kmalloc(sizeof *attr, GFP_KERNEL);1303if (!attr)1304return -ENOMEM;13051306qp = idr_read_qp(cmd.qp_handle, file->ucontext);1307if (!qp) {1308ret = -EINVAL;1309goto out;1310}13111312attr->qp_state = cmd.qp_state;1313attr->cur_qp_state = cmd.cur_qp_state;1314attr->path_mtu = cmd.path_mtu;1315attr->path_mig_state = cmd.path_mig_state;1316attr->qkey = cmd.qkey;1317attr->rq_psn = cmd.rq_psn;1318attr->sq_psn = cmd.sq_psn;1319attr->dest_qp_num = cmd.dest_qp_num;1320attr->qp_access_flags = cmd.qp_access_flags;1321attr->pkey_index = cmd.pkey_index;1322attr->alt_pkey_index = cmd.alt_pkey_index;1323attr->en_sqd_async_notify = cmd.en_sqd_async_notify;1324attr->max_rd_atomic = cmd.max_rd_atomic;1325attr->max_dest_rd_atomic = cmd.max_dest_rd_atomic;1326attr->min_rnr_timer = cmd.min_rnr_timer;1327attr->port_num = cmd.port_num;1328attr->timeout = cmd.timeout;1329attr->retry_cnt = cmd.retry_cnt;1330attr->rnr_retry = cmd.rnr_retry;1331attr->alt_port_num = cmd.alt_port_num;1332attr->alt_timeout = cmd.alt_timeout;13331334memcpy(attr->ah_attr.grh.dgid.raw, cmd.dest.dgid, 16);1335attr->ah_attr.grh.flow_label = cmd.dest.flow_label;1336attr->ah_attr.grh.sgid_index = cmd.dest.sgid_index;1337attr->ah_attr.grh.hop_limit = cmd.dest.hop_limit;1338attr->ah_attr.grh.traffic_class = cmd.dest.traffic_class;1339attr->ah_attr.dlid = cmd.dest.dlid;1340attr->ah_attr.sl = cmd.dest.sl;1341attr->ah_attr.src_path_bits = cmd.dest.src_path_bits;1342attr->ah_attr.static_rate = cmd.dest.static_rate;1343attr->ah_attr.ah_flags = cmd.dest.is_global ? IB_AH_GRH : 0;1344attr->ah_attr.port_num = cmd.dest.port_num;13451346memcpy(attr->alt_ah_attr.grh.dgid.raw, cmd.alt_dest.dgid, 16);1347attr->alt_ah_attr.grh.flow_label = cmd.alt_dest.flow_label;1348attr->alt_ah_attr.grh.sgid_index = cmd.alt_dest.sgid_index;1349attr->alt_ah_attr.grh.hop_limit = cmd.alt_dest.hop_limit;1350attr->alt_ah_attr.grh.traffic_class = cmd.alt_dest.traffic_class;1351attr->alt_ah_attr.dlid = cmd.alt_dest.dlid;1352attr->alt_ah_attr.sl = cmd.alt_dest.sl;1353attr->alt_ah_attr.src_path_bits = cmd.alt_dest.src_path_bits;1354attr->alt_ah_attr.static_rate = cmd.alt_dest.static_rate;1355attr->alt_ah_attr.ah_flags = cmd.alt_dest.is_global ? IB_AH_GRH : 0;1356attr->alt_ah_attr.port_num = cmd.alt_dest.port_num;13571358ret = qp->device->modify_qp(qp, attr, cmd.attr_mask, &udata);13591360put_qp_read(qp);13611362if (ret)1363goto out;13641365ret = in_len;13661367out:1368kfree(attr);13691370return ret;1371}13721373ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,1374const char __user *buf, int in_len,1375int out_len)1376{1377struct ib_uverbs_destroy_qp cmd;1378struct ib_uverbs_destroy_qp_resp resp;1379struct ib_uobject *uobj;1380struct ib_qp *qp;1381struct ib_uqp_object *obj;1382int ret = -EINVAL;13831384if (copy_from_user(&cmd, buf, sizeof cmd))1385return -EFAULT;13861387memset(&resp, 0, sizeof resp);13881389uobj = idr_write_uobj(&ib_uverbs_qp_idr, cmd.qp_handle, file->ucontext);1390if (!uobj)1391return -EINVAL;1392qp = uobj->object;1393obj = container_of(uobj, struct ib_uqp_object, uevent.uobject);13941395if (!list_empty(&obj->mcast_list)) {1396put_uobj_write(uobj);1397return -EBUSY;1398}13991400ret = ib_destroy_qp(qp);1401if (!ret)1402uobj->live = 0;14031404put_uobj_write(uobj);14051406if (ret)1407return ret;14081409idr_remove_uobj(&ib_uverbs_qp_idr, uobj);14101411mutex_lock(&file->mutex);1412list_del(&uobj->list);1413mutex_unlock(&file->mutex);14141415ib_uverbs_release_uevent(file, &obj->uevent);14161417resp.events_reported = obj->uevent.events_reported;14181419put_uobj(uobj);14201421if (copy_to_user((void __user *) (unsigned long) cmd.response,1422&resp, sizeof resp))1423return -EFAULT;14241425return in_len;1426}14271428ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,1429const char __user *buf, int in_len,1430int out_len)1431{1432struct ib_uverbs_post_send cmd;1433struct ib_uverbs_post_send_resp resp;1434struct ib_uverbs_send_wr *user_wr;1435struct ib_send_wr *wr = NULL, *last, *next, *bad_wr;1436struct ib_qp *qp;1437int i, sg_ind;1438int is_ud;1439ssize_t ret = -EINVAL;14401441if (copy_from_user(&cmd, buf, sizeof cmd))1442return -EFAULT;14431444if (in_len < sizeof cmd + cmd.wqe_size * cmd.wr_count +1445cmd.sge_count * sizeof (struct ib_uverbs_sge))1446return -EINVAL;14471448if (cmd.wqe_size < sizeof (struct ib_uverbs_send_wr))1449return -EINVAL;14501451user_wr = kmalloc(cmd.wqe_size, GFP_KERNEL);1452if (!user_wr)1453return -ENOMEM;14541455qp = idr_read_qp(cmd.qp_handle, file->ucontext);1456if (!qp)1457goto out;14581459is_ud = qp->qp_type == IB_QPT_UD;1460sg_ind = 0;1461last = NULL;1462for (i = 0; i < cmd.wr_count; ++i) {1463if (copy_from_user(user_wr,1464buf + sizeof cmd + i * cmd.wqe_size,1465cmd.wqe_size)) {1466ret = -EFAULT;1467goto out_put;1468}14691470if (user_wr->num_sge + sg_ind > cmd.sge_count) {1471ret = -EINVAL;1472goto out_put;1473}14741475next = kmalloc(ALIGN(sizeof *next, sizeof (struct ib_sge)) +1476user_wr->num_sge * sizeof (struct ib_sge),1477GFP_KERNEL);1478if (!next) {1479ret = -ENOMEM;1480goto out_put;1481}14821483if (!last)1484wr = next;1485else1486last->next = next;1487last = next;14881489next->next = NULL;1490next->wr_id = user_wr->wr_id;1491next->num_sge = user_wr->num_sge;1492next->opcode = user_wr->opcode;1493next->send_flags = user_wr->send_flags;14941495if (is_ud) {1496next->wr.ud.ah = idr_read_ah(user_wr->wr.ud.ah,1497file->ucontext);1498if (!next->wr.ud.ah) {1499ret = -EINVAL;1500goto out_put;1501}1502next->wr.ud.remote_qpn = user_wr->wr.ud.remote_qpn;1503next->wr.ud.remote_qkey = user_wr->wr.ud.remote_qkey;1504} else {1505switch (next->opcode) {1506case IB_WR_RDMA_WRITE_WITH_IMM:1507next->ex.imm_data =1508(__be32 __force) user_wr->ex.imm_data;1509case IB_WR_RDMA_WRITE:1510case IB_WR_RDMA_READ:1511next->wr.rdma.remote_addr =1512user_wr->wr.rdma.remote_addr;1513next->wr.rdma.rkey =1514user_wr->wr.rdma.rkey;1515break;1516case IB_WR_SEND_WITH_IMM:1517next->ex.imm_data =1518(__be32 __force) user_wr->ex.imm_data;1519break;1520case IB_WR_SEND_WITH_INV:1521next->ex.invalidate_rkey =1522user_wr->ex.invalidate_rkey;1523break;1524case IB_WR_ATOMIC_CMP_AND_SWP:1525case IB_WR_ATOMIC_FETCH_AND_ADD:1526next->wr.atomic.remote_addr =1527user_wr->wr.atomic.remote_addr;1528next->wr.atomic.compare_add =1529user_wr->wr.atomic.compare_add;1530next->wr.atomic.swap = user_wr->wr.atomic.swap;1531next->wr.atomic.rkey = user_wr->wr.atomic.rkey;1532break;1533default:1534break;1535}1536}15371538if (next->num_sge) {1539next->sg_list = (void *) next +1540ALIGN(sizeof *next, sizeof (struct ib_sge));1541if (copy_from_user(next->sg_list,1542buf + sizeof cmd +1543cmd.wr_count * cmd.wqe_size +1544sg_ind * sizeof (struct ib_sge),1545next->num_sge * sizeof (struct ib_sge))) {1546ret = -EFAULT;1547goto out_put;1548}1549sg_ind += next->num_sge;1550} else1551next->sg_list = NULL;1552}15531554resp.bad_wr = 0;1555ret = qp->device->post_send(qp, wr, &bad_wr);1556if (ret)1557for (next = wr; next; next = next->next) {1558++resp.bad_wr;1559if (next == bad_wr)1560break;1561}15621563if (copy_to_user((void __user *) (unsigned long) cmd.response,1564&resp, sizeof resp))1565ret = -EFAULT;15661567out_put:1568put_qp_read(qp);15691570while (wr) {1571if (is_ud && wr->wr.ud.ah)1572put_ah_read(wr->wr.ud.ah);1573next = wr->next;1574kfree(wr);1575wr = next;1576}15771578out:1579kfree(user_wr);15801581return ret ? ret : in_len;1582}15831584static struct ib_recv_wr *ib_uverbs_unmarshall_recv(const char __user *buf,1585int in_len,1586u32 wr_count,1587u32 sge_count,1588u32 wqe_size)1589{1590struct ib_uverbs_recv_wr *user_wr;1591struct ib_recv_wr *wr = NULL, *last, *next;1592int sg_ind;1593int i;1594int ret;15951596if (in_len < wqe_size * wr_count +1597sge_count * sizeof (struct ib_uverbs_sge))1598return ERR_PTR(-EINVAL);15991600if (wqe_size < sizeof (struct ib_uverbs_recv_wr))1601return ERR_PTR(-EINVAL);16021603user_wr = kmalloc(wqe_size, GFP_KERNEL);1604if (!user_wr)1605return ERR_PTR(-ENOMEM);16061607sg_ind = 0;1608last = NULL;1609for (i = 0; i < wr_count; ++i) {1610if (copy_from_user(user_wr, buf + i * wqe_size,1611wqe_size)) {1612ret = -EFAULT;1613goto err;1614}16151616if (user_wr->num_sge + sg_ind > sge_count) {1617ret = -EINVAL;1618goto err;1619}16201621next = kmalloc(ALIGN(sizeof *next, sizeof (struct ib_sge)) +1622user_wr->num_sge * sizeof (struct ib_sge),1623GFP_KERNEL);1624if (!next) {1625ret = -ENOMEM;1626goto err;1627}16281629if (!last)1630wr = next;1631else1632last->next = next;1633last = next;16341635next->next = NULL;1636next->wr_id = user_wr->wr_id;1637next->num_sge = user_wr->num_sge;16381639if (next->num_sge) {1640next->sg_list = (void *) next +1641ALIGN(sizeof *next, sizeof (struct ib_sge));1642if (copy_from_user(next->sg_list,1643buf + wr_count * wqe_size +1644sg_ind * sizeof (struct ib_sge),1645next->num_sge * sizeof (struct ib_sge))) {1646ret = -EFAULT;1647goto err;1648}1649sg_ind += next->num_sge;1650} else1651next->sg_list = NULL;1652}16531654kfree(user_wr);1655return wr;16561657err:1658kfree(user_wr);16591660while (wr) {1661next = wr->next;1662kfree(wr);1663wr = next;1664}16651666return ERR_PTR(ret);1667}16681669ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,1670const char __user *buf, int in_len,1671int out_len)1672{1673struct ib_uverbs_post_recv cmd;1674struct ib_uverbs_post_recv_resp resp;1675struct ib_recv_wr *wr, *next, *bad_wr;1676struct ib_qp *qp;1677ssize_t ret = -EINVAL;16781679if (copy_from_user(&cmd, buf, sizeof cmd))1680return -EFAULT;16811682wr = ib_uverbs_unmarshall_recv(buf + sizeof cmd,1683in_len - sizeof cmd, cmd.wr_count,1684cmd.sge_count, cmd.wqe_size);1685if (IS_ERR(wr))1686return PTR_ERR(wr);16871688qp = idr_read_qp(cmd.qp_handle, file->ucontext);1689if (!qp)1690goto out;16911692resp.bad_wr = 0;1693ret = qp->device->post_recv(qp, wr, &bad_wr);16941695put_qp_read(qp);16961697if (ret)1698for (next = wr; next; next = next->next) {1699++resp.bad_wr;1700if (next == bad_wr)1701break;1702}17031704if (copy_to_user((void __user *) (unsigned long) cmd.response,1705&resp, sizeof resp))1706ret = -EFAULT;17071708out:1709while (wr) {1710next = wr->next;1711kfree(wr);1712wr = next;1713}17141715return ret ? ret : in_len;1716}17171718ssize_t ib_uverbs_post_srq_recv(struct ib_uverbs_file *file,1719const char __user *buf, int in_len,1720int out_len)1721{1722struct ib_uverbs_post_srq_recv cmd;1723struct ib_uverbs_post_srq_recv_resp resp;1724struct ib_recv_wr *wr, *next, *bad_wr;1725struct ib_srq *srq;1726ssize_t ret = -EINVAL;17271728if (copy_from_user(&cmd, buf, sizeof cmd))1729return -EFAULT;17301731wr = ib_uverbs_unmarshall_recv(buf + sizeof cmd,1732in_len - sizeof cmd, cmd.wr_count,1733cmd.sge_count, cmd.wqe_size);1734if (IS_ERR(wr))1735return PTR_ERR(wr);17361737srq = idr_read_srq(cmd.srq_handle, file->ucontext);1738if (!srq)1739goto out;17401741resp.bad_wr = 0;1742ret = srq->device->post_srq_recv(srq, wr, &bad_wr);17431744put_srq_read(srq);17451746if (ret)1747for (next = wr; next; next = next->next) {1748++resp.bad_wr;1749if (next == bad_wr)1750break;1751}17521753if (copy_to_user((void __user *) (unsigned long) cmd.response,1754&resp, sizeof resp))1755ret = -EFAULT;17561757out:1758while (wr) {1759next = wr->next;1760kfree(wr);1761wr = next;1762}17631764return ret ? ret : in_len;1765}17661767ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,1768const char __user *buf, int in_len,1769int out_len)1770{1771struct ib_uverbs_create_ah cmd;1772struct ib_uverbs_create_ah_resp resp;1773struct ib_uobject *uobj;1774struct ib_pd *pd;1775struct ib_ah *ah;1776struct ib_ah_attr attr;1777int ret;17781779if (out_len < sizeof resp)1780return -ENOSPC;17811782if (copy_from_user(&cmd, buf, sizeof cmd))1783return -EFAULT;17841785uobj = kmalloc(sizeof *uobj, GFP_KERNEL);1786if (!uobj)1787return -ENOMEM;17881789init_uobj(uobj, cmd.user_handle, file->ucontext, &ah_lock_key);1790down_write(&uobj->mutex);17911792pd = idr_read_pd(cmd.pd_handle, file->ucontext);1793if (!pd) {1794ret = -EINVAL;1795goto err;1796}17971798attr.dlid = cmd.attr.dlid;1799attr.sl = cmd.attr.sl;1800attr.src_path_bits = cmd.attr.src_path_bits;1801attr.static_rate = cmd.attr.static_rate;1802attr.ah_flags = cmd.attr.is_global ? IB_AH_GRH : 0;1803attr.port_num = cmd.attr.port_num;1804attr.grh.flow_label = cmd.attr.grh.flow_label;1805attr.grh.sgid_index = cmd.attr.grh.sgid_index;1806attr.grh.hop_limit = cmd.attr.grh.hop_limit;1807attr.grh.traffic_class = cmd.attr.grh.traffic_class;1808memcpy(attr.grh.dgid.raw, cmd.attr.grh.dgid, 16);18091810ah = ib_create_ah(pd, &attr);1811if (IS_ERR(ah)) {1812ret = PTR_ERR(ah);1813goto err_put;1814}18151816ah->uobject = uobj;1817uobj->object = ah;18181819ret = idr_add_uobj(&ib_uverbs_ah_idr, uobj);1820if (ret)1821goto err_destroy;18221823resp.ah_handle = uobj->id;18241825if (copy_to_user((void __user *) (unsigned long) cmd.response,1826&resp, sizeof resp)) {1827ret = -EFAULT;1828goto err_copy;1829}18301831put_pd_read(pd);18321833mutex_lock(&file->mutex);1834list_add_tail(&uobj->list, &file->ucontext->ah_list);1835mutex_unlock(&file->mutex);18361837uobj->live = 1;18381839up_write(&uobj->mutex);18401841return in_len;18421843err_copy:1844idr_remove_uobj(&ib_uverbs_ah_idr, uobj);18451846err_destroy:1847ib_destroy_ah(ah);18481849err_put:1850put_pd_read(pd);18511852err:1853put_uobj_write(uobj);1854return ret;1855}18561857ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file,1858const char __user *buf, int in_len, int out_len)1859{1860struct ib_uverbs_destroy_ah cmd;1861struct ib_ah *ah;1862struct ib_uobject *uobj;1863int ret;18641865if (copy_from_user(&cmd, buf, sizeof cmd))1866return -EFAULT;18671868uobj = idr_write_uobj(&ib_uverbs_ah_idr, cmd.ah_handle, file->ucontext);1869if (!uobj)1870return -EINVAL;1871ah = uobj->object;18721873ret = ib_destroy_ah(ah);1874if (!ret)1875uobj->live = 0;18761877put_uobj_write(uobj);18781879if (ret)1880return ret;18811882idr_remove_uobj(&ib_uverbs_ah_idr, uobj);18831884mutex_lock(&file->mutex);1885list_del(&uobj->list);1886mutex_unlock(&file->mutex);18871888put_uobj(uobj);18891890return in_len;1891}18921893ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,1894const char __user *buf, int in_len,1895int out_len)1896{1897struct ib_uverbs_attach_mcast cmd;1898struct ib_qp *qp;1899struct ib_uqp_object *obj;1900struct ib_uverbs_mcast_entry *mcast;1901int ret;19021903if (copy_from_user(&cmd, buf, sizeof cmd))1904return -EFAULT;19051906qp = idr_read_qp(cmd.qp_handle, file->ucontext);1907if (!qp)1908return -EINVAL;19091910obj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);19111912list_for_each_entry(mcast, &obj->mcast_list, list)1913if (cmd.mlid == mcast->lid &&1914!memcmp(cmd.gid, mcast->gid.raw, sizeof mcast->gid.raw)) {1915ret = 0;1916goto out_put;1917}19181919mcast = kmalloc(sizeof *mcast, GFP_KERNEL);1920if (!mcast) {1921ret = -ENOMEM;1922goto out_put;1923}19241925mcast->lid = cmd.mlid;1926memcpy(mcast->gid.raw, cmd.gid, sizeof mcast->gid.raw);19271928ret = ib_attach_mcast(qp, &mcast->gid, cmd.mlid);1929if (!ret)1930list_add_tail(&mcast->list, &obj->mcast_list);1931else1932kfree(mcast);19331934out_put:1935put_qp_read(qp);19361937return ret ? ret : in_len;1938}19391940ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,1941const char __user *buf, int in_len,1942int out_len)1943{1944struct ib_uverbs_detach_mcast cmd;1945struct ib_uqp_object *obj;1946struct ib_qp *qp;1947struct ib_uverbs_mcast_entry *mcast;1948int ret = -EINVAL;19491950if (copy_from_user(&cmd, buf, sizeof cmd))1951return -EFAULT;19521953qp = idr_read_qp(cmd.qp_handle, file->ucontext);1954if (!qp)1955return -EINVAL;19561957ret = ib_detach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid);1958if (ret)1959goto out_put;19601961obj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);19621963list_for_each_entry(mcast, &obj->mcast_list, list)1964if (cmd.mlid == mcast->lid &&1965!memcmp(cmd.gid, mcast->gid.raw, sizeof mcast->gid.raw)) {1966list_del(&mcast->list);1967kfree(mcast);1968break;1969}19701971out_put:1972put_qp_read(qp);19731974return ret ? ret : in_len;1975}19761977ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,1978const char __user *buf, int in_len,1979int out_len)1980{1981struct ib_uverbs_create_srq cmd;1982struct ib_uverbs_create_srq_resp resp;1983struct ib_udata udata;1984struct ib_uevent_object *obj;1985struct ib_pd *pd;1986struct ib_srq *srq;1987struct ib_srq_init_attr attr;1988int ret;19891990if (out_len < sizeof resp)1991return -ENOSPC;19921993if (copy_from_user(&cmd, buf, sizeof cmd))1994return -EFAULT;19951996INIT_UDATA(&udata, buf + sizeof cmd,1997(unsigned long) cmd.response + sizeof resp,1998in_len - sizeof cmd, out_len - sizeof resp);19992000obj = kmalloc(sizeof *obj, GFP_KERNEL);2001if (!obj)2002return -ENOMEM;20032004init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &srq_lock_key);2005down_write(&obj->uobject.mutex);20062007pd = idr_read_pd(cmd.pd_handle, file->ucontext);2008if (!pd) {2009ret = -EINVAL;2010goto err;2011}20122013attr.event_handler = ib_uverbs_srq_event_handler;2014attr.srq_context = file;2015attr.attr.max_wr = cmd.max_wr;2016attr.attr.max_sge = cmd.max_sge;2017attr.attr.srq_limit = cmd.srq_limit;20182019obj->events_reported = 0;2020INIT_LIST_HEAD(&obj->event_list);20212022srq = pd->device->create_srq(pd, &attr, &udata);2023if (IS_ERR(srq)) {2024ret = PTR_ERR(srq);2025goto err_put;2026}20272028srq->device = pd->device;2029srq->pd = pd;2030srq->uobject = &obj->uobject;2031srq->event_handler = attr.event_handler;2032srq->srq_context = attr.srq_context;2033atomic_inc(&pd->usecnt);2034atomic_set(&srq->usecnt, 0);20352036obj->uobject.object = srq;2037ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uobject);2038if (ret)2039goto err_destroy;20402041memset(&resp, 0, sizeof resp);2042resp.srq_handle = obj->uobject.id;2043resp.max_wr = attr.attr.max_wr;2044resp.max_sge = attr.attr.max_sge;20452046if (copy_to_user((void __user *) (unsigned long) cmd.response,2047&resp, sizeof resp)) {2048ret = -EFAULT;2049goto err_copy;2050}20512052put_pd_read(pd);20532054mutex_lock(&file->mutex);2055list_add_tail(&obj->uobject.list, &file->ucontext->srq_list);2056mutex_unlock(&file->mutex);20572058obj->uobject.live = 1;20592060up_write(&obj->uobject.mutex);20612062return in_len;20632064err_copy:2065idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uobject);20662067err_destroy:2068ib_destroy_srq(srq);20692070err_put:2071put_pd_read(pd);20722073err:2074put_uobj_write(&obj->uobject);2075return ret;2076}20772078ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,2079const char __user *buf, int in_len,2080int out_len)2081{2082struct ib_uverbs_modify_srq cmd;2083struct ib_udata udata;2084struct ib_srq *srq;2085struct ib_srq_attr attr;2086int ret;20872088if (copy_from_user(&cmd, buf, sizeof cmd))2089return -EFAULT;20902091INIT_UDATA(&udata, buf + sizeof cmd, NULL, in_len - sizeof cmd,2092out_len);20932094srq = idr_read_srq(cmd.srq_handle, file->ucontext);2095if (!srq)2096return -EINVAL;20972098attr.max_wr = cmd.max_wr;2099attr.srq_limit = cmd.srq_limit;21002101ret = srq->device->modify_srq(srq, &attr, cmd.attr_mask, &udata);21022103put_srq_read(srq);21042105return ret ? ret : in_len;2106}21072108ssize_t ib_uverbs_query_srq(struct ib_uverbs_file *file,2109const char __user *buf,2110int in_len, int out_len)2111{2112struct ib_uverbs_query_srq cmd;2113struct ib_uverbs_query_srq_resp resp;2114struct ib_srq_attr attr;2115struct ib_srq *srq;2116int ret;21172118if (out_len < sizeof resp)2119return -ENOSPC;21202121if (copy_from_user(&cmd, buf, sizeof cmd))2122return -EFAULT;21232124srq = idr_read_srq(cmd.srq_handle, file->ucontext);2125if (!srq)2126return -EINVAL;21272128ret = ib_query_srq(srq, &attr);21292130put_srq_read(srq);21312132if (ret)2133return ret;21342135memset(&resp, 0, sizeof resp);21362137resp.max_wr = attr.max_wr;2138resp.max_sge = attr.max_sge;2139resp.srq_limit = attr.srq_limit;21402141if (copy_to_user((void __user *) (unsigned long) cmd.response,2142&resp, sizeof resp))2143return -EFAULT;21442145return in_len;2146}21472148ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,2149const char __user *buf, int in_len,2150int out_len)2151{2152struct ib_uverbs_destroy_srq cmd;2153struct ib_uverbs_destroy_srq_resp resp;2154struct ib_uobject *uobj;2155struct ib_srq *srq;2156struct ib_uevent_object *obj;2157int ret = -EINVAL;21582159if (copy_from_user(&cmd, buf, sizeof cmd))2160return -EFAULT;21612162uobj = idr_write_uobj(&ib_uverbs_srq_idr, cmd.srq_handle, file->ucontext);2163if (!uobj)2164return -EINVAL;2165srq = uobj->object;2166obj = container_of(uobj, struct ib_uevent_object, uobject);21672168ret = ib_destroy_srq(srq);2169if (!ret)2170uobj->live = 0;21712172put_uobj_write(uobj);21732174if (ret)2175return ret;21762177idr_remove_uobj(&ib_uverbs_srq_idr, uobj);21782179mutex_lock(&file->mutex);2180list_del(&uobj->list);2181mutex_unlock(&file->mutex);21822183ib_uverbs_release_uevent(file, obj);21842185memset(&resp, 0, sizeof resp);2186resp.events_reported = obj->events_reported;21872188put_uobj(uobj);21892190if (copy_to_user((void __user *) (unsigned long) cmd.response,2191&resp, sizeof resp))2192ret = -EFAULT;21932194return ret ? ret : in_len;2195}219621972198