Skip to content

Instantly share code, notes, and snippets.

@dvdhrm
Created September 19, 2016 15:07
Show Gist options
  • Save dvdhrm/89826757073b28ae2533e7f41eae1b45 to your computer and use it in GitHub Desktop.
Save dvdhrm/89826757073b28ae2533e7f41eae1b45 to your computer and use it in GitHub Desktop.
static int bus1_peer_transfer(struct bus1_peer *src,
struct bus1_peer *dst,
struct bus1_cmd_handle_transfer *param)
{
struct bus1_handle *src_h, *dst_h;
int r = 0;
mutex_lock(&src->local.lock);
src_h = bus1_handle_ref_by_id(src, param.src_handle);
if (src_h) {
/*
* We know @src_h has a valid user-ref which is protected by
* local.lock. Hence, we can acquire the remote handle and know
* @src_h stays valid. If the remote-acquisition fails, we can
* safely bail out. If it succeeds, we must settle on the
* target node and make sure it is still live (returning
* EHOSTUNREACH without settling first would break causal
* order). If after settle the node is dead, we can safely bail
* out without triggering any release (dead nodes cannot
* trigger anything). But if it is life, we now have acquired a
* valid inflight reference and nothing prevents us from
* disclosing it.
*/
dst_h = bus1_handle_attach(src_h, dst);
if (IS_ERR(dst_h)) {
r = PTR_ERR(dst_h);
} else if (!bus1_handle_settle(src_h)) {
dst_h = bus1_handle_release(dst_h, NULL);
r = -EHOSTUNREACH;
}
} else if (param.src_handle & BUS1_HANDLE_FLAG_REMOTE) {
/*
* Cannot find any handle with the given ID, but the ID was
* marked as remote ID. This is a caller error, so tell them
* about it.
*/
r = -ENXIO;
} else {
/*
* No handle with the given ID exists, but it is a local ID.
* Hence, the caller wants us to create a new node with it. We
* allocate the node and attach a remote handle. If the attach
* fails, we can easily unref the unused node. If it succeeds,
* we have it properly pinned and nothing prevents us from
* disclosing it. We first disclose the source, though. This
* makes sure the newly created node is actually valid and
* usable by the peer.
* Note that there is no need to settle. The new node is
* completely unlinked, so the settle can be skipped.
*/
src_h = bus1_node_new(param.src_handle);
if (IS_ERR(src_h)) {
r = PTR_ERR(src_h);
src_h = NULL;
} else {
dst_h = bus1_handle_attach(src_h, dst);
if (IS_ERR(dst_h))
r = PTR_ERR(dst_h);
else
bus1_handle_disclose(src_h);
}
}
mutex_unlock(&src->local.lock);
bus1_handle_unref(src_h);
if (r < 0)
return r;
mutex_lock(&dst->local.lock);
bus1_handle_disclose(dst_h);
/* remote-user on @dst_h, so this cannot trigger release */
bus1_handle_release(dst_h, NULL);
mutex_unlock(&dst->local.lock);
return 0;
error:
mutex_unlock(&src->local.lock);
return r;
}
static int bus1_peer_ioctl_handle_transfer(struct bus1_peer *src,
unsigned long arg)
{
struct bus1_cmd_handle_transfer __user *uparam = (void __user *) arg;
struct bus1_cmd_handle_transfer param;
struct bus1_peer *dst = NULL;
struct fd dst_f;
int r;
BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_HANDLE_TRANSFER) != sizeof(param));
if (copy_from_user(&param, (void __user *)arg, sizeof(param)))
return -EFAULT;
if (unlikely(param.flags))
return -EINVAL;
if (param.dst_fd != -1) {
dst_f = fdget(param.dst_fd);
if (!dst_f.file)
return -EBADF;
if (dst_f.file->f_op != &bus1_fops) {
fdput(dst_f);
return -EOPNOTSUPP;
}
dst = bus1_peer_acquire(dst_f.file->private_data);
fdput(dst_f);
if (!dst)
return -ESHUTDOWN;
}
r = bus1_peer_transfer(src, dst ?: src, &param);
bus1_peer_release(dst);
if (r < 0)
return r;
return copy_to_user(uparam, &param, sizeof(param)) ? -EFAULT : 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment