Import of kernel-4.18.0-553.126.1.el8_10

This commit is contained in:
almalinux-bot-kernel 2026-05-29 05:33:04 +00:00
parent b6e320a30c
commit b7505e241b
76 changed files with 3171 additions and 1054 deletions

View File

@ -12,7 +12,7 @@ RHEL_MINOR = 10
#
# Use this spot to avoid future merge conflicts.
# Do not trim this comment.
RHEL_RELEASE = 553.125.1
RHEL_RELEASE = 553.126.1
#
# ZSTREAM

View File

@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/net.h>
#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/security.h>
@ -133,19 +134,15 @@ EXPORT_SYMBOL_GPL(af_alg_release);
void af_alg_release_parent(struct sock *sk)
{
struct alg_sock *ask = alg_sk(sk);
unsigned int nokey = ask->nokey_refcnt;
bool last = nokey && !ask->refcnt;
unsigned int nokey = atomic_read(&ask->nokey_refcnt);
sk = ask->parent;
ask = alg_sk(sk);
lock_sock(sk);
ask->nokey_refcnt -= nokey;
if (!last)
last = !--ask->refcnt;
release_sock(sk);
if (nokey)
atomic_dec(&ask->nokey_refcnt);
if (last)
if (atomic_dec_and_test(&ask->refcnt))
sock_put(sk);
}
EXPORT_SYMBOL_GPL(af_alg_release_parent);
@ -190,7 +187,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
err = -EBUSY;
lock_sock(sk);
if (ask->refcnt | ask->nokey_refcnt)
if (atomic_read(&ask->refcnt))
goto unlock;
swap(ask->type, type);
@ -239,7 +236,7 @@ static int alg_setsockopt(struct socket *sock, int level, int optname,
int err = -EBUSY;
lock_sock(sk);
if (ask->refcnt)
if (atomic_read(&ask->refcnt) != atomic_read(&ask->nokey_refcnt))
goto unlock;
type = ask->type;
@ -304,14 +301,14 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern)
if (err)
goto unlock;
sk2->sk_family = PF_ALG;
if (nokey || !ask->refcnt++)
if (atomic_inc_return_relaxed(&ask->refcnt) == 1)
sock_hold(sk);
ask->nokey_refcnt += nokey;
if (nokey) {
atomic_inc(&ask->nokey_refcnt);
atomic_set(&alg_sk(sk2)->nokey_refcnt, 1);
}
alg_sk(sk2)->parent = sk;
alg_sk(sk2)->type = type;
alg_sk(sk2)->nokey_refcnt = nokey;
newsock->ops = type->ops;
newsock->state = SS_CONNECTED;
@ -382,7 +379,6 @@ static int alg_create(struct net *net, struct socket *sock, int protocol,
sock->ops = &alg_proto_ops;
sock_init_data(sock, sk);
sk->sk_family = PF_ALG;
sk->sk_destruct = alg_sock_destruct;
return 0;
@ -621,6 +617,7 @@ void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst)
if (!ctx->used)
ctx->merge = 0;
ctx->init = ctx->more;
}
EXPORT_SYMBOL_GPL(af_alg_pull_tsgl);
@ -722,9 +719,10 @@ EXPORT_SYMBOL_GPL(af_alg_wmem_wakeup);
*
* @sk socket of connection to user space
* @flags If MSG_DONTWAIT is set, then only report if function would sleep
* @min Set to minimum request size if partial requests are allowed.
* @return 0 when writable memory is available, < 0 upon error
*/
int af_alg_wait_for_data(struct sock *sk, unsigned flags)
int af_alg_wait_for_data(struct sock *sk, unsigned flags, unsigned min)
{
DEFINE_WAIT_FUNC(wait, woken_wake_function);
struct alg_sock *ask = alg_sk(sk);
@ -742,7 +740,9 @@ int af_alg_wait_for_data(struct sock *sk, unsigned flags)
if (signal_pending(current))
break;
timeout = MAX_SCHEDULE_TIMEOUT;
if (sk_wait_event(sk, &timeout, (ctx->used || !ctx->more),
if (sk_wait_event(sk, &timeout,
ctx->init && (!ctx->more ||
(min && ctx->used >= min)),
&wait)) {
err = 0;
break;
@ -833,10 +833,17 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
}
lock_sock(sk);
if (!ctx->more && ctx->used) {
err = -EINVAL;
goto unlock;
if (ctx->init && !ctx->more) {
if (ctx->used) {
err = -EINVAL;
goto unlock;
}
pr_info_once(
"%s sent an empty control message without MSG_MORE.\n",
current->comm);
}
ctx->init = true;
if (init) {
ctx->enc = enc;

View File

@ -42,7 +42,7 @@
struct aead_tfm {
struct crypto_aead *aead;
struct crypto_skcipher *null_tfm;
struct crypto_sync_skcipher *null_tfm;
};
static inline bool aead_sufficient_data(struct sock *sk)
@ -75,14 +75,14 @@ static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
return af_alg_sendmsg(sock, msg, size, ivsize);
}
static int crypto_aead_copy_sgl(struct crypto_skcipher *null_tfm,
static int crypto_aead_copy_sgl(struct crypto_sync_skcipher *null_tfm,
struct scatterlist *src,
struct scatterlist *dst, unsigned int len)
{
SKCIPHER_REQUEST_ON_STACK(skreq, null_tfm);
SYNC_SKCIPHER_REQUEST_ON_STACK(skreq, null_tfm);
skcipher_request_set_tfm(skreq, null_tfm);
skcipher_request_set_callback(skreq, CRYPTO_TFM_REQ_MAY_BACKLOG,
skcipher_request_set_sync_tfm(skreq, null_tfm);
skcipher_request_set_callback(skreq, CRYPTO_TFM_REQ_MAY_SLEEP,
NULL, NULL);
skcipher_request_set_crypt(skreq, src, dst, len, NULL);
@ -99,7 +99,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
struct af_alg_ctx *ctx = ask->private;
struct aead_tfm *aeadc = pask->private;
struct crypto_aead *tfm = aeadc->aead;
struct crypto_skcipher *null_tfm = aeadc->null_tfm;
struct crypto_sync_skcipher *null_tfm = aeadc->null_tfm;
unsigned int as = crypto_aead_authsize(tfm);
unsigned int ivsize = crypto_aead_ivsize(tfm);
struct af_alg_async_req *areq;
@ -111,8 +111,8 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
size_t usedpages = 0; /* [in] RX bufs to be used from user */
size_t processed = 0; /* [in] TX bufs to be consumed */
if (!ctx->used) {
err = af_alg_wait_for_data(sk, flags);
if (!ctx->init || ctx->more) {
err = af_alg_wait_for_data(sk, flags, 0);
if (err)
return err;
}
@ -238,19 +238,20 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
areq->outlen = outlen;
aead_request_set_callback(&areq->cra_u.aead_req,
CRYPTO_TFM_REQ_MAY_BACKLOG,
CRYPTO_TFM_REQ_MAY_SLEEP,
af_alg_async_cb, areq);
err = ctx->enc ? crypto_aead_encrypt(&areq->cra_u.aead_req) :
crypto_aead_decrypt(&areq->cra_u.aead_req);
/* AIO operation in progress */
if (err == -EINPROGRESS || err == -EBUSY)
if (err == -EINPROGRESS)
return -EIOCBQUEUED;
sock_put(sk);
} else {
/* Synchronous operation */
aead_request_set_callback(&areq->cra_u.aead_req,
CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &ctx->wait);
err = crypto_wait_req(ctx->enc ?
@ -331,7 +332,7 @@ static int aead_check_key(struct socket *sock)
struct alg_sock *ask = alg_sk(sk);
lock_sock(sk);
if (ask->refcnt)
if (!atomic_read(&ask->nokey_refcnt))
goto unlock_child;
psk = ask->parent;
@ -343,11 +344,8 @@ static int aead_check_key(struct socket *sock)
if (crypto_aead_get_flags(tfm->aead) & CRYPTO_TFM_NEED_KEY)
goto unlock;
if (!pask->refcnt++)
sock_hold(psk);
ask->refcnt = 1;
sock_put(psk);
atomic_dec(&pask->nokey_refcnt);
atomic_set(&ask->nokey_refcnt, 0);
err = 0;
@ -421,7 +419,7 @@ static void *aead_bind(const char *name, u32 type, u32 mask)
{
struct aead_tfm *tfm;
struct crypto_aead *aead;
struct crypto_skcipher *null_tfm;
struct crypto_sync_skcipher *null_tfm;
tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
if (!tfm)
@ -508,12 +506,6 @@ static int aead_accept_parent_nokey(void *private, struct sock *sk)
INIT_LIST_HEAD(&ctx->tsgl_list);
ctx->len = len;
ctx->used = 0;
atomic_set(&ctx->rcvused, 0);
ctx->more = 0;
ctx->merge = 0;
ctx->enc = 0;
ctx->aead_assoclen = 0;
crypto_init_wait(&ctx->wait);
ask->private = ctx;

View File

@ -302,7 +302,7 @@ static int hash_check_key(struct socket *sock)
struct alg_sock *ask = alg_sk(sk);
lock_sock(sk);
if (ask->refcnt)
if (!atomic_read(&ask->nokey_refcnt))
goto unlock_child;
psk = ask->parent;
@ -314,11 +314,8 @@ static int hash_check_key(struct socket *sock)
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
goto unlock;
if (!pask->refcnt++)
sock_hold(psk);
ask->refcnt = 1;
sock_put(psk);
atomic_dec(&pask->nokey_refcnt);
atomic_set(&ask->nokey_refcnt, 0);
err = 0;

View File

@ -65,8 +65,8 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
int err = 0;
size_t len = 0;
if (!ctx->used) {
err = af_alg_wait_for_data(sk, flags);
if (!ctx->init || (ctx->more && ctx->used < bs)) {
err = af_alg_wait_for_data(sk, flags, bs);
if (err)
return err;
}
@ -225,7 +225,7 @@ static int skcipher_check_key(struct socket *sock)
struct alg_sock *ask = alg_sk(sk);
lock_sock(sk);
if (ask->refcnt)
if (!atomic_read(&ask->nokey_refcnt))
goto unlock_child;
psk = ask->parent;
@ -237,11 +237,8 @@ static int skcipher_check_key(struct socket *sock)
if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
goto unlock;
if (!pask->refcnt++)
sock_hold(psk);
ask->refcnt = 1;
sock_put(psk);
atomic_dec(&pask->nokey_refcnt);
atomic_set(&ask->nokey_refcnt, 0);
err = 0;
@ -350,6 +347,7 @@ static int skcipher_accept_parent_nokey(void *private, struct sock *sk)
ctx = sock_kmalloc(sk, len, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
memset(ctx, 0, len);
ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(tfm),
GFP_KERNEL);
@ -357,16 +355,10 @@ static int skcipher_accept_parent_nokey(void *private, struct sock *sk)
sock_kfree_s(sk, ctx, len);
return -ENOMEM;
}
memset(ctx->iv, 0, crypto_skcipher_ivsize(tfm));
INIT_LIST_HEAD(&ctx->tsgl_list);
ctx->len = len;
ctx->used = 0;
atomic_set(&ctx->rcvused, 0);
ctx->more = 0;
ctx->merge = 0;
ctx->enc = 0;
crypto_init_wait(&ctx->wait);
ask->private = ctx;

View File

@ -33,7 +33,7 @@ struct authenc_instance_ctx {
struct crypto_authenc_ctx {
struct crypto_ahash *auth;
struct crypto_skcipher *enc;
struct crypto_skcipher *null;
struct crypto_sync_skcipher *null;
};
struct authenc_request_ctx {
@ -193,9 +193,9 @@ static int crypto_authenc_copy_assoc(struct aead_request *req)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
SKCIPHER_REQUEST_ON_STACK(skreq, ctx->null);
SYNC_SKCIPHER_REQUEST_ON_STACK(skreq, ctx->null);
skcipher_request_set_tfm(skreq, ctx->null);
skcipher_request_set_sync_tfm(skreq, ctx->null);
skcipher_request_set_callback(skreq, aead_request_flags(req),
NULL, NULL);
skcipher_request_set_crypt(skreq, req->src, req->dst, req->assoclen,
@ -326,7 +326,7 @@ static int crypto_authenc_init_tfm(struct crypto_aead *tfm)
struct crypto_authenc_ctx *ctx = crypto_aead_ctx(tfm);
struct crypto_ahash *auth;
struct crypto_skcipher *enc;
struct crypto_skcipher *null;
struct crypto_sync_skcipher *null;
int err;
auth = crypto_spawn_ahash(&ictx->auth);

View File

@ -36,7 +36,7 @@ struct crypto_authenc_esn_ctx {
unsigned int reqoff;
struct crypto_ahash *auth;
struct crypto_skcipher *enc;
struct crypto_skcipher *null;
struct crypto_sync_skcipher *null;
};
struct authenc_esn_request_ctx {
@ -186,9 +186,9 @@ static int crypto_authenc_esn_copy_sg(struct aead_request *req,
{
struct crypto_aead *authenc_esn = crypto_aead_reqtfm(req);
struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);
SKCIPHER_REQUEST_ON_STACK(skreq, ctx->null);
SYNC_SKCIPHER_REQUEST_ON_STACK(skreq, ctx->null);
skcipher_request_set_tfm(skreq, ctx->null);
skcipher_request_set_sync_tfm(skreq, ctx->null);
skcipher_request_set_callback(skreq, aead_request_flags(req),
NULL, NULL);
skcipher_request_set_crypt(skreq, src, dst, len, NULL);
@ -371,7 +371,7 @@ static int crypto_authenc_esn_init_tfm(struct crypto_aead *tfm)
struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(tfm);
struct crypto_ahash *auth;
struct crypto_skcipher *enc;
struct crypto_skcipher *null;
struct crypto_sync_skcipher *null;
int err;
auth = crypto_spawn_ahash(&ictx->auth);

View File

@ -26,7 +26,7 @@
#include <linux/string.h>
static DEFINE_MUTEX(crypto_default_null_skcipher_lock);
static struct crypto_skcipher *crypto_default_null_skcipher;
static struct crypto_sync_skcipher *crypto_default_null_skcipher;
static int crypto_default_null_skcipher_refcnt;
static int null_compress(struct crypto_tfm *tfm, const u8 *src,
@ -153,16 +153,15 @@ MODULE_ALIAS_CRYPTO("compress_null");
MODULE_ALIAS_CRYPTO("digest_null");
MODULE_ALIAS_CRYPTO("cipher_null");
struct crypto_skcipher *crypto_get_default_null_skcipher(void)
struct crypto_sync_skcipher *crypto_get_default_null_skcipher(void)
{
struct crypto_skcipher *tfm;
struct crypto_sync_skcipher *tfm;
mutex_lock(&crypto_default_null_skcipher_lock);
tfm = crypto_default_null_skcipher;
if (!tfm) {
tfm = crypto_alloc_skcipher("ecb(cipher_null)",
0, CRYPTO_ALG_ASYNC);
tfm = crypto_alloc_sync_skcipher("ecb(cipher_null)", 0, 0);
if (IS_ERR(tfm))
goto unlock;
@ -182,7 +181,7 @@ void crypto_put_default_null_skcipher(void)
{
mutex_lock(&crypto_default_null_skcipher_lock);
if (!--crypto_default_null_skcipher_refcnt) {
crypto_free_skcipher(crypto_default_null_skcipher);
crypto_free_sync_skcipher(crypto_default_null_skcipher);
crypto_default_null_skcipher = NULL;
}
mutex_unlock(&crypto_default_null_skcipher_lock);

View File

@ -47,9 +47,9 @@ static int echainiv_encrypt(struct aead_request *req)
info = req->iv;
if (req->src != req->dst) {
SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
SYNC_SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
skcipher_request_set_tfm(nreq, ctx->sknull);
skcipher_request_set_sync_tfm(nreq, ctx->sknull);
skcipher_request_set_callback(nreq, req->base.flags,
NULL, NULL);
skcipher_request_set_crypt(nreq, req->src, req->dst,

View File

@ -50,7 +50,7 @@ struct crypto_rfc4543_instance_ctx {
struct crypto_rfc4543_ctx {
struct crypto_aead *child;
struct crypto_skcipher *null;
struct crypto_sync_skcipher *null;
u8 nonce[4];
};
@ -1068,9 +1068,9 @@ static int crypto_rfc4543_copy_src_to_dst(struct aead_request *req, bool enc)
unsigned int authsize = crypto_aead_authsize(aead);
unsigned int nbytes = req->assoclen + req->cryptlen -
(enc ? 0 : authsize);
SKCIPHER_REQUEST_ON_STACK(nreq, ctx->null);
SYNC_SKCIPHER_REQUEST_ON_STACK(nreq, ctx->null);
skcipher_request_set_tfm(nreq, ctx->null);
skcipher_request_set_sync_tfm(nreq, ctx->null);
skcipher_request_set_callback(nreq, req->base.flags, NULL, NULL);
skcipher_request_set_crypt(nreq, req->src, req->dst, nbytes, NULL);
@ -1094,7 +1094,7 @@ static int crypto_rfc4543_init_tfm(struct crypto_aead *tfm)
struct crypto_aead_spawn *spawn = &ictx->aead;
struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(tfm);
struct crypto_aead *aead;
struct crypto_skcipher *null;
struct crypto_sync_skcipher *null;
unsigned long align;
int err = 0;

View File

@ -73,9 +73,9 @@ static int seqiv_aead_encrypt(struct aead_request *req)
info = req->iv;
if (req->src != req->dst) {
SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
SYNC_SKCIPHER_REQUEST_ON_STACK(nreq, ctx->sknull);
skcipher_request_set_tfm(nreq, ctx->sknull);
skcipher_request_set_sync_tfm(nreq, ctx->sknull);
skcipher_request_set_callback(nreq, req->base.flags,
NULL, NULL);
skcipher_request_set_crypt(nreq, req->src, req->dst,

View File

@ -1206,10 +1206,20 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
switch (data[0]) {
case 0x04:
if (len < 32) {
dev_warn(wacom->pen_input->dev.parent,
"Report 0x04 too short: %zu bytes\n", len);
break;
}
wacom_intuos_bt_process_data(wacom, data + i);
i += 10;
/* fall through */
case 0x03:
if (i == 1 && len < 22) {
dev_warn(wacom->pen_input->dev.parent,
"Report 0x03 too short: %zu bytes\n", len);
break;
}
wacom_intuos_bt_process_data(wacom, data + i);
i += 10;
wacom_intuos_bt_process_data(wacom, data + i);

View File

@ -505,7 +505,8 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
struct rdma_ah_attr ah_attr;
struct ib_ah *ah;
__be64 *tid;
int ret, data_len, hdr_len, copy_offset, rmpp_active;
int ret, hdr_len, copy_offset, rmpp_active;
size_t data_len;
u8 base_version;
if (count < hdr_size(file) + IB_MGMT_RMPP_HDR)
@ -579,7 +580,10 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
}
base_version = ((struct ib_mad_hdr *)&packet->mad.data)->base_version;
data_len = count - hdr_size(file) - hdr_len;
if (check_sub_overflow((size_t)count, (size_t)hdr_size(file) + hdr_len, &data_len)) {
ret = -EINVAL;
goto err_ah;
}
packet->msg = ib_create_send_mad(agent,
be32_to_cpu(packet->mad.hdr.qpn),
packet->mad.hdr.pkey_index, rmpp_active,

View File

@ -2205,6 +2205,7 @@ int md_bitmap_resize(struct bitmap *bitmap, sector_t blocks,
memcpy(page_address(store.sb_page),
page_address(bitmap->storage.sb_page),
sizeof(bitmap_super_t));
mutex_lock(&bitmap->mddev->bitmap_info.mutex);
spin_lock_irq(&bitmap->counts.lock);
md_bitmap_file_unmap(&bitmap->storage);
bitmap->storage = store;
@ -2309,7 +2310,7 @@ int md_bitmap_resize(struct bitmap *bitmap, sector_t blocks,
set_page_attr(bitmap, i, BITMAP_PAGE_DIRTY);
}
spin_unlock_irq(&bitmap->counts.lock);
mutex_unlock(&bitmap->mddev->bitmap_info.mutex);
if (!init) {
md_bitmap_unplug(bitmap);
bitmap->mddev->pers->quiesce(bitmap->mddev, 0);

View File

@ -70,7 +70,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
return;
cifs_dbg(VFS, "Dump pending requests:\n");
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
mid_entry->mid_state,
@ -93,7 +93,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
mid_entry->resp_buf, 62);
}
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
#endif /* CONFIG_CIFS_DEBUG2 */
}
@ -526,7 +526,7 @@ skip_rdma:
seq_printf(m, "\n\t\t[NONE]");
seq_puts(m, "\n\n\tMIDs: ");
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
seq_printf(m, "\n\tState: %d com: %d pid:"
" %d cbdata: %p mid %llu\n",
@ -536,7 +536,7 @@ skip_rdma:
mid_entry->callback_data,
mid_entry->mid);
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
seq_printf(m, "\n--\n");
}
if (c == 0)

View File

@ -334,7 +334,6 @@ int
sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
struct cifs_fattr *fattr, uint sidtype)
{
int rc = 0;
struct key *sidkey;
char *sidstr;
const struct cred *saved_cred;
@ -441,12 +440,12 @@ out_revert_creds:
* fails then we just fall back to using the ctx->linux_uid/linux_gid.
*/
got_valid_id:
rc = 0;
if (sidtype == SIDOWNER)
fattr->cf_uid = fuid;
else
fattr->cf_gid = fgid;
return rc;
return 0;
}
int
@ -753,12 +752,83 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
}
#endif
static int validate_dacl(struct cifs_acl *pdacl, char *end_of_acl)
{
int i, ace_hdr_size, ace_size, min_ace_size;
u16 dacl_size, num_aces;
char *acl_base, *end_of_dacl;
struct cifs_ace *pace;
if (!pdacl)
return 0;
if (end_of_acl < (char *)pdacl + sizeof(struct cifs_acl)) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}
dacl_size = le16_to_cpu(pdacl->size);
if (dacl_size < sizeof(struct cifs_acl) ||
end_of_acl < (char *)pdacl + dacl_size) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}
num_aces = le16_to_cpu(pdacl->num_aces);
if (!num_aces)
return 0;
ace_hdr_size = offsetof(struct cifs_ace, sid) +
offsetof(struct cifs_sid, sub_auth);
min_ace_size = ace_hdr_size + sizeof(__le32);
if (num_aces > (dacl_size - sizeof(struct cifs_acl)) / min_ace_size) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}
end_of_dacl = (char *)pdacl + dacl_size;
acl_base = (char *)pdacl;
ace_size = sizeof(struct cifs_acl);
for (i = 0; i < num_aces; ++i) {
if (end_of_dacl - acl_base < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}
pace = (struct cifs_ace *)(acl_base + ace_size);
acl_base = (char *)pace;
if (end_of_dacl - acl_base < ace_hdr_size ||
pace->sid.num_subauth == 0 ||
pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}
ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
if (end_of_dacl - acl_base < ace_size ||
le16_to_cpu(pace->size) < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}
ace_size = le16_to_cpu(pace->size);
if (end_of_dacl - acl_base < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}
}
return 0;
}
static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
struct cifs_fattr *fattr, bool mode_from_special_sid)
{
int i;
int num_aces = 0;
u16 num_aces = 0;
int acl_size;
char *acl_base;
struct cifs_ace **ppace;
@ -772,15 +842,12 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
return;
}
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
if (validate_dacl(pdacl, end_of_acl))
return;
}
cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
le32_to_cpu(pdacl->num_aces));
le16_to_cpu(pdacl->num_aces));
/* reset rwx permissions for user/group/other.
Also, if num_aces is 0 i.e. DACL has no ACEs,
@ -790,12 +857,10 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
acl_base = (char *)pdacl;
acl_size = sizeof(struct cifs_acl);
num_aces = le32_to_cpu(pdacl->num_aces);
num_aces = le16_to_cpu(pdacl->num_aces);
if (num_aces > 0) {
umode_t denied_mode = 0;
if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *))
return;
ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *),
GFP_KERNEL);
if (!ppace)
@ -803,10 +868,13 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
for (i = 0; i < num_aces; ++i) {
ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
#ifdef CONFIG_CIFS_DEBUG2
dump_ace(ppace[i], end_of_acl);
dump_ace(ppace[i],
(char *)pdacl + le16_to_cpu(pdacl->size));
#endif
if (mode_from_special_sid &&
ppace[i]->sid.num_subauth >= 3 &&
(compare_sids(&(ppace[i]->sid),
&sid_unix_NFS_mode) == 0)) {
/*
@ -927,11 +995,11 @@ unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace)
static void populate_new_aces(char *nacl_base,
struct cifs_sid *pownersid,
struct cifs_sid *pgrpsid,
__u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
__u64 *pnmode, u16 *pnum_aces, u16 *pnsize,
bool modefromsid)
{
__u64 nmode;
u32 num_aces = 0;
u16 num_aces = 0;
u16 nsize = 0;
__u64 user_mode;
__u64 group_mode;
@ -1037,7 +1105,7 @@ static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl
u16 size = 0;
struct cifs_ace *pntace = NULL;
char *acl_base = NULL;
u32 src_num_aces = 0;
u16 src_num_aces = 0;
u16 nsize = 0;
struct cifs_ace *pnntace = NULL;
char *nacl_base = NULL;
@ -1045,7 +1113,7 @@ static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl
acl_base = (char *)pdacl;
size = sizeof(struct cifs_acl);
src_num_aces = le32_to_cpu(pdacl->num_aces);
src_num_aces = le16_to_cpu(pdacl->num_aces);
nacl_base = (char *)pndacl;
nsize = sizeof(struct cifs_acl);
@ -1077,11 +1145,11 @@ static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
u16 size = 0;
struct cifs_ace *pntace = NULL;
char *acl_base = NULL;
u32 src_num_aces = 0;
u16 src_num_aces = 0;
u16 nsize = 0;
struct cifs_ace *pnntace = NULL;
char *nacl_base = NULL;
u32 num_aces = 0;
u16 num_aces = 0;
__u64 nmode;
bool new_aces_set = false;
@ -1101,7 +1169,7 @@ static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
acl_base = (char *)pdacl;
size = sizeof(struct cifs_acl);
src_num_aces = le32_to_cpu(pdacl->num_aces);
src_num_aces = le16_to_cpu(pdacl->num_aces);
/* Retain old ACEs which we can retain */
for (i = 0; i < src_num_aces; ++i) {
@ -1147,7 +1215,7 @@ next_ace:
}
finalize_dacl:
pndacl->num_aces = cpu_to_le32(num_aces);
pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(nsize);
return 0;
@ -1185,6 +1253,17 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
return 0;
}
static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset)
{
if (acl_len < sizeof(struct cifs_acl))
return false;
if (dacloffset < sizeof(struct cifs_ntsd))
return false;
return dacloffset <= acl_len - sizeof(struct cifs_acl);
}
/* Convert CIFS ACL to POSIX form */
static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
@ -1205,7 +1284,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
le32_to_cpu(pntsd->gsidoffset),
@ -1236,11 +1314,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
return rc;
}
if (dacloffset)
if (dacloffset) {
if (!dacl_offset_valid(acl_len, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
group_sid_ptr, fattr, get_mode_from_special_sid);
else
} else {
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
}
return rc;
}
@ -1263,11 +1348,15 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
cifs_dbg(VFS, "Server returned illegal ACL size\n");
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, end_of_acl);
if (rc)
return rc;
}
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
@ -1282,7 +1371,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
ndacl_ptr->size = cpu_to_le16(0);
ndacl_ptr->num_aces = cpu_to_le32(0);
ndacl_ptr->num_aces = cpu_to_le16(0);
rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
pnmode, mode_from_sid);
@ -1628,10 +1717,22 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2);
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
rc = -EINVAL;
goto id_mode_to_cifs_acl_exit;
}
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
if (rc) {
kfree(pntsd);
cifs_put_tlink(tlink);
return rc;
}
if (mode_from_sid)
nsecdesclen +=
le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace);
le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace);
else /* cifsacl */
nsecdesclen += le16_to_cpu(dacl_ptr->size);
}
@ -1644,7 +1745,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
* descriptor parameters, and secuirty descriptor itself
*/
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
pnntsd = kzalloc(nsecdesclen, GFP_KERNEL);
if (!pnntsd) {
kfree(pntsd);
cifs_put_tlink(tlink);
@ -1664,6 +1765,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
}
id_mode_to_cifs_acl_exit:
cifs_put_tlink(tlink);
kfree(pnntsd);

View File

@ -77,7 +77,8 @@ struct cifs_sid {
struct cifs_acl {
__le16 revision; /* revision level */
__le16 size;
__le32 num_aces;
__le16 num_aces;
__le16 reserved;
} __attribute__((packed));
/* ACE types - see MS-DTYP 2.4.4.1 */

View File

@ -628,7 +628,8 @@ struct TCP_Server_Info {
#endif
wait_queue_head_t response_q;
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
spinlock_t mid_lock; /* protect mid queue and it's entries */
spinlock_t mid_queue_lock; /* protect mid queue */
spinlock_t mid_counter_lock;
struct list_head pending_mid_q;
bool noblocksnd; /* use blocking sendmsg */
bool noautotune; /* do not autotune send buf sizes */
@ -665,7 +666,7 @@ struct TCP_Server_Info {
/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
unsigned int capabilities; /* selective disabling of caps by smb sess */
int timeAdj; /* Adjust for difference in server time zone in sec */
__u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */
__u64 current_mid; /* multiplex id - rotating counter, protected by mid_counter_lock */
char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
@ -673,6 +674,7 @@ struct TCP_Server_Info {
__u32 reconnect_instance; /* incremented on each reconnect */
struct session_key session_key;
unsigned long lstrp; /* when we got last response from this server */
unsigned long neg_start; /* when negotiate started (jiffies) */
struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */
#define CIFS_NEGFLAVOR_LANMAN 0 /* wct == 13, LANMAN */
#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */
@ -1612,9 +1614,11 @@ struct mid_q_entry {
void *resp_buf; /* pointer to received SMB header */
unsigned int resp_buf_size;
int mid_state; /* wish this were enum but can not pass to wait_event */
unsigned int mid_flags;
__le16 command; /* smb command code */
unsigned int optype; /* operation type */
spinlock_t mid_lock;
bool wait_cancelled:1; /* Cancelled while waiting for response */
bool deleted_from_q:1; /* Whether Mid has been dequeued frem pending_mid_q */
bool large_buf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */
@ -1793,10 +1797,6 @@ static inline bool is_retryable_error(int error)
#define MID_SHUTDOWN 0x20
#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
/* Flags */
#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
#define MID_DELETED 2 /* Mid has been dequeued/deleted */
/* Types of response buffer returned from SendReceive2 */
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1
@ -1928,9 +1928,9 @@ require use of the stronger protocol */
* GlobalCurrentXid
* GlobalTotalActiveXid
* TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change)
* TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
* ->CurrentMid
* (any changes in mid_q_entry fields)
* TCP_Server_Info->mid_queue_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
* mid_q_entry->deleted_from_q
* TCP_Server_Info->mid_counter_lock TCP_Server_Info->current_mid cifs_get_tcp_session
* TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session
* ->credits
* ->echo_credits
@ -1955,6 +1955,9 @@ require use of the stronger protocol */
* ->invalidHandle initiate_cifs_search
* ->oplock_break_cancelled
* cifs_aio_ctx->aio_mutex cifs_aio_ctx cifs_aio_ctx_alloc
* mid_q_entry->mid_lock mid_q_entry->callback alloc_mid
* smb2_mid_entry_alloc
* (Any fields of mid_q_entry that will need protection)
****************************************************************************/
#ifdef DECLARE_GLOBALS_HERE
@ -2212,4 +2215,21 @@ static inline bool cifs_netbios_name(const char *name, size_t namelen)
return ret;
}
/*
* Execute mid callback atomically - ensures callback runs exactly once
* and prevents sleeping in atomic context.
*/
static inline void mid_execute_callback(struct mid_q_entry *mid)
{
void (*callback)(struct mid_q_entry *mid);
spin_lock(&mid->mid_lock);
callback = mid->callback;
mid->callback = NULL; /* Mark as executed, */
spin_unlock(&mid->mid_lock);
if (callback)
callback(mid);
}
#endif /* _CIFS_GLOB_H */

View File

@ -296,21 +296,21 @@ cifs_abort_connection(struct TCP_Server_Info *server)
/* mark submitted MIDs for retry and issue callback */
INIT_LIST_HEAD(&retry_list);
cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
kref_get(&mid->refcount);
if (mid->mid_state == MID_REQUEST_SUBMITTED)
mid->mid_state = MID_RETRY_NEEDED;
list_move(&mid->qhead, &retry_list);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
cifs_server_unlock(server);
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
list_for_each_entry_safe(mid, nmid, &retry_list, qhead) {
list_del_init(&mid->qhead);
mid->callback(mid);
mid_execute_callback(mid);
release_mid(mid);
}
@ -333,7 +333,7 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
}
cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
trace_smb3_reconnect(server->CurrentMid, server->conn_id,
trace_smb3_reconnect(server->current_mid, server->conn_id,
server->hostname);
server->tcpStatus = CifsNeedReconnect;
@ -626,6 +626,19 @@ allocate_buffers(struct TCP_Server_Info *server)
static bool
server_unresponsive(struct TCP_Server_Info *server)
{
/*
* If we're in the process of mounting a share or reconnecting a session
* and the server abruptly shut down (e.g. socket wasn't closed, packet
* had been ACK'ed but no SMB response), don't wait longer than 20s from
* when negotiate actually started.
*/
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsInNegotiate &&
time_after(jiffies, server->neg_start + 20 * HZ)) {
spin_unlock(&server->srv_lock);
cifs_reconnect(server, false);
return true;
}
/*
* We need to wait 3 echo intervals to make sure we handle such
* situations right:
@ -637,7 +650,6 @@ server_unresponsive(struct TCP_Server_Info *server)
* 65s kernel_recvmsg times out, and we see that we haven't gotten
* a response in >60s.
*/
spin_lock(&server->srv_lock);
if ((server->tcpStatus == CifsGood ||
server->tcpStatus == CifsNeedNegotiate) &&
(!server->ops->can_echo || server->ops->can_echo(server)) &&
@ -811,7 +823,7 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
#ifdef CONFIG_CIFS_STATS2
mid->when_received = jiffies;
#endif
spin_lock(&mid->server->mid_lock);
spin_lock(&mid->server->mid_queue_lock);
if (!malformed)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
@ -820,13 +832,13 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
* Trying to handle/dequeue a mid after the send_recv()
* function has finished processing it is a bug.
*/
if (mid->mid_flags & MID_DELETED) {
spin_unlock(&mid->server->mid_lock);
if (mid->deleted_from_q == true) {
spin_unlock(&mid->server->mid_queue_lock);
pr_warn_once("trying to dequeue a deleted mid\n");
} else {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
spin_unlock(&mid->server->mid_lock);
mid->deleted_from_q = true;
spin_unlock(&mid->server->mid_queue_lock);
}
}
@ -909,23 +921,23 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
struct list_head *tmp, *tmp2;
INIT_LIST_HEAD(&dispose_list);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid);
kref_get(&mid_entry->refcount);
mid_entry->mid_state = MID_SHUTDOWN;
list_move(&mid_entry->qhead, &dispose_list);
mid_entry->mid_flags |= MID_DELETED;
mid_entry->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
/* now walk dispose list and issue callbacks */
list_for_each_safe(tmp, tmp2, &dispose_list) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid);
list_del_init(&mid_entry->qhead);
mid_entry->callback(mid_entry);
mid_execute_callback(mid_entry);
release_mid(mid_entry);
}
/* 1/8th of sec is more than enough time for them to exit */
@ -1052,7 +1064,7 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_server_dbg(FYI, "%s: added %u credits total=%d\n",
@ -1195,7 +1207,7 @@ next_pdu:
server);
if (!mids[i]->multiRsp || mids[i]->multiEnd)
mids[i]->callback(mids[i]);
mid_execute_callback(mids[i]);
release_mid(mids[i]);
} else if (server->ops->is_oplock_break &&
@ -1618,7 +1630,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
spin_lock_init(&tcp_ses->req_lock);
spin_lock_init(&tcp_ses->srv_lock);
spin_lock_init(&tcp_ses->mid_lock);
spin_lock_init(&tcp_ses->mid_queue_lock);
spin_lock_init(&tcp_ses->mid_counter_lock);
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
@ -3768,6 +3781,7 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
}
server->tcpStatus = CifsInNegotiate;
server->neg_start = jiffies;
spin_unlock(&server->srv_lock);
rc = server->ops->negotiate(xid, ses, server);

View File

@ -103,17 +103,17 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
struct smb_hdr *buf = (struct smb_hdr *)buffer;
struct mid_q_entry *mid;
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if (compare_mid(mid->mid, buf) &&
mid->mid_state == MID_REQUEST_SUBMITTED &&
le16_to_cpu(mid->command) == buf->Command) {
kref_get(&mid->refcount);
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return mid;
}
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return NULL;
}
@ -177,10 +177,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
__u16 last_mid, cur_mid;
bool collision, reconnect = false;
spin_lock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
/* mid is 16 bit only for CIFS/SMB */
cur_mid = (__u16)((server->CurrentMid) & 0xffff);
cur_mid = (__u16)((server->current_mid) & 0xffff);
/* we do not want to loop forever */
last_mid = cur_mid;
cur_mid++;
@ -206,6 +205,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
cur_mid++;
num_mids = 0;
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
++num_mids;
if (mid_entry->mid == cur_mid &&
@ -215,6 +215,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
break;
}
}
spin_unlock(&server->mid_queue_lock);
/*
* if we have more than 32k mids in the list, then something
@ -231,12 +232,12 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
if (!collision) {
mid = (__u64)cur_mid;
server->CurrentMid = mid;
server->current_mid = mid;
break;
}
cur_mid++;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_counter_lock);
if (reconnect) {
cifs_signal_cifsd_for_reconnect(server, false);

View File

@ -121,7 +121,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
wake_up(&server->request_q);
if (reconnect_detected) {
trace_smb3_reconnect_detected(server->CurrentMid,
trace_smb3_reconnect_detected(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
@ -129,7 +129,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
}
if (reconnect_with_invalid_credits) {
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
trace_smb3_reconnect_with_invalid_credits(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n",
optype, scredits, add);
@ -161,7 +161,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
break;
}
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits);
}
@ -179,7 +179,7 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val)
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_set_credits(server->CurrentMid,
trace_smb3_set_credits(server->current_mid,
server->conn_id, server->hostname, scredits, val, in_flight);
cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
@ -264,7 +264,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -(credits->value), in_flight);
cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
__func__, credits->value, scredits);
@ -284,7 +284,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
return 0;
if (credits->value < new_val) {
trace_smb3_too_many_credits(server->CurrentMid,
trace_smb3_too_many_credits(server->current_mid,
server->conn_id, server->hostname, 0, credits->value - new_val, 0);
cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)",
credits->value, new_val);
@ -299,7 +299,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_reconnect_detected(server->CurrentMid,
trace_smb3_reconnect_detected(server->current_mid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
cifs_server_dbg(VFS, "trying to return %d credits to old session\n",
@ -313,7 +313,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
@ -329,19 +329,19 @@ smb2_get_next_mid(struct TCP_Server_Info *server)
{
__u64 mid;
/* for SMB2 we need the current value */
spin_lock(&server->mid_lock);
mid = server->CurrentMid++;
spin_unlock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
mid = server->current_mid++;
spin_unlock(&server->mid_counter_lock);
return mid;
}
static void
smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
{
spin_lock(&server->mid_lock);
if (server->CurrentMid >= val)
server->CurrentMid -= val;
spin_unlock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
if (server->current_mid >= val)
server->current_mid -= val;
spin_unlock(&server->mid_counter_lock);
}
static struct mid_q_entry *
@ -356,7 +356,7 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
return NULL;
}
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if ((mid->mid == wire_mid) &&
(mid->mid_state == MID_REQUEST_SUBMITTED) &&
@ -364,13 +364,13 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
kref_get(&mid->refcount);
if (dequeue) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return mid;
}
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return NULL;
}
@ -415,9 +415,9 @@ smb2_negotiate(const unsigned int xid,
{
int rc;
spin_lock(&server->mid_lock);
server->CurrentMid = 0;
spin_unlock(&server->mid_lock);
spin_lock(&server->mid_counter_lock);
server->current_mid = 0;
spin_unlock(&server->mid_counter_lock);
rc = SMB2_negotiate(xid, ses, server);
/* BB we probably don't need to retry with modern servers */
if (rc == -EAGAIN)
@ -2562,7 +2562,7 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
@ -5010,22 +5010,22 @@ static void smb2_decrypt_offload(struct work_struct *work)
dw->server->ops->is_network_name_deleted(dw->buf,
dw->server);
mid->callback(mid);
mid_execute_callback(mid);
} else {
spin_lock(&dw->server->srv_lock);
if (dw->server->tcpStatus == CifsNeedReconnect) {
spin_lock(&dw->server->mid_lock);
spin_lock(&dw->server->mid_queue_lock);
mid->mid_state = MID_RETRY_NEEDED;
spin_unlock(&dw->server->mid_lock);
spin_unlock(&dw->server->mid_queue_lock);
spin_unlock(&dw->server->srv_lock);
mid->callback(mid);
mid_execute_callback(mid);
} else {
spin_lock(&dw->server->mid_lock);
spin_lock(&dw->server->mid_queue_lock);
mid->mid_state = MID_REQUEST_SUBMITTED;
mid->mid_flags &= ~(MID_DELETED);
mid->deleted_from_q = false;
list_add_tail(&mid->qhead,
&dw->server->pending_mid_q);
spin_unlock(&dw->server->mid_lock);
spin_unlock(&dw->server->mid_queue_lock);
spin_unlock(&dw->server->srv_lock);
}
}

View File

@ -789,6 +789,7 @@ smb2_mid_entry_alloc(const struct smb2_hdr *shdr,
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
memset(temp, 0, sizeof(struct mid_q_entry));
kref_init(&temp->refcount);
spin_lock_init(&temp->mid_lock);
temp->mid = le64_to_cpu(shdr->MessageId);
temp->credits = credits > 0 ? credits : 1;
temp->pid = current->pid;
@ -858,9 +859,9 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
*mid = smb2_mid_entry_alloc(shdr, server);
if (*mid == NULL)
return -ENOMEM;
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return 0;
}

View File

@ -87,7 +87,7 @@ void __release_mid(struct kref *refcount)
#endif
struct TCP_Server_Info *server = midEntry->server;
if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
if (midEntry->resp_buf && (midEntry->wait_cancelled) &&
(midEntry->mid_state == MID_RESPONSE_RECEIVED ||
midEntry->mid_state == MID_RESPONSE_READY) &&
server->ops->handle_cancelled_mid)
@ -158,12 +158,12 @@ void __release_mid(struct kref *refcount)
void
delete_mid(struct mid_q_entry *mid)
{
spin_lock(&mid->server->mid_lock);
if (!(mid->mid_flags & MID_DELETED)) {
spin_lock(&mid->server->mid_queue_lock);
if (mid->deleted_from_q == false) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&mid->server->mid_lock);
spin_unlock(&mid->server->mid_queue_lock);
release_mid(mid);
}
@ -422,7 +422,7 @@ unmask:
* socket so the server throws away the partial SMB
*/
cifs_signal_cifsd_for_reconnect(server, false);
trace_smb3_partial_send_reconnect(server->CurrentMid,
trace_smb3_partial_send_reconnect(server->current_mid,
server->conn_id, server->hostname);
}
smbd_done:
@ -540,7 +540,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -1, in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
__func__, 1, scredits);
@ -573,7 +573,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(server->CurrentMid,
trace_smb3_credit_timeout(server->current_mid,
server->conn_id, server->hostname, scredits,
num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
@ -616,7 +616,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(
server->CurrentMid,
server->current_mid,
server->conn_id, server->hostname,
scredits, num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
@ -646,7 +646,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_add_credits(server->CurrentMid,
trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
-(num_credits), in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
@ -697,7 +697,7 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
*/
if (server->in_flight == 0) {
spin_unlock(&server->req_lock);
trace_smb3_insufficient_credits(server->CurrentMid,
trace_smb3_insufficient_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
num, in_flight);
cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
@ -747,9 +747,9 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
*ppmidQ = alloc_mid(in_buf, ses->server);
if (*ppmidQ == NULL)
return -ENOMEM;
spin_lock(&ses->server->mid_lock);
spin_lock(&ses->server->mid_queue_lock);
list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
spin_unlock(&ses->server->mid_lock);
spin_unlock(&ses->server->mid_queue_lock);
return 0;
}
@ -849,9 +849,9 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
mid->mid_state = MID_REQUEST_SUBMITTED;
/* put it on the pending_mid_q */
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
list_add_tail(&mid->qhead, &server->pending_mid_q);
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
/*
* Need to store the time in mid before calling I/O. For call_async,
@ -910,10 +910,10 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n",
__func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
switch (mid->mid_state) {
case MID_RESPONSE_READY:
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return rc;
case MID_RETRY_NEEDED:
rc = -EAGAIN;
@ -925,16 +925,19 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
rc = -EHOSTDOWN;
break;
default:
if (!(mid->mid_flags & MID_DELETED)) {
if (mid->deleted_from_q == false) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
mid->deleted_from_q = true;
}
spin_unlock(&server->mid_queue_lock);
cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
__func__, mid->mid, mid->mid_state);
rc = -EIO;
goto sync_mid_done;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
sync_mid_done:
release_mid(mid);
return rc;
}
@ -1234,15 +1237,14 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n",
midQ[i]->mid, le16_to_cpu(midQ[i]->command));
send_cancel(server, &rqst[i], midQ[i]);
spin_lock(&server->mid_lock);
midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED ||
midQ[i]->mid_state == MID_RESPONSE_RECEIVED) {
spin_lock(&midQ[i]->mid_lock);
midQ[i]->wait_cancelled = true;
if (midQ[i]->callback) {
midQ[i]->callback = cifs_cancelled_callback;
cancelled_mid[i] = true;
credits[i].value = 0;
}
spin_unlock(&server->mid_lock);
spin_unlock(&midQ[i]->mid_lock);
}
}
@ -1444,16 +1446,16 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
rc = wait_for_response(server, midQ);
if (rc != 0) {
send_cancel(server, &rqst, midQ);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) {
/* no longer considered to be "in-flight" */
midQ->callback = release_mid;
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
add_credits(server, &credits, 0);
return rc;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
}
rc = cifs_sync_mid_result(midQ, server);
@ -1626,15 +1628,15 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
rc = wait_for_response(server, midQ);
if (rc) {
send_cancel(server, &rqst, midQ);
spin_lock(&server->mid_lock);
spin_lock(&server->mid_queue_lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
midQ->mid_state == MID_RESPONSE_RECEIVED) {
/* no longer considered to be "in-flight" */
midQ->callback = release_mid;
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
return rc;
}
spin_unlock(&server->mid_lock);
spin_unlock(&server->mid_queue_lock);
}
/* We got the response - restart system call. */

View File

@ -1440,6 +1440,7 @@ xfs_attr3_leaf_add_work(
struct xfs_attr_leaf_name_local *name_loc;
struct xfs_attr_leaf_name_remote *name_rmt;
struct xfs_mount *mp;
int old_end, new_end;
int tmp;
int i;
@ -1531,17 +1532,49 @@ xfs_attr3_leaf_add_work(
if (be16_to_cpu(entry->nameidx) < ichdr->firstused)
ichdr->firstused = be16_to_cpu(entry->nameidx);
ASSERT(ichdr->firstused >= ichdr->count * sizeof(xfs_attr_leaf_entry_t)
+ xfs_attr3_leaf_hdr_size(leaf));
tmp = (ichdr->count - 1) * sizeof(xfs_attr_leaf_entry_t)
+ xfs_attr3_leaf_hdr_size(leaf);
new_end = ichdr->count * sizeof(struct xfs_attr_leaf_entry) +
xfs_attr3_leaf_hdr_size(leaf);
old_end = new_end - sizeof(struct xfs_attr_leaf_entry);
ASSERT(ichdr->firstused >= new_end);
for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
if (ichdr->freemap[i].base == tmp) {
ichdr->freemap[i].base += sizeof(xfs_attr_leaf_entry_t);
int diff = 0;
if (ichdr->freemap[i].base == old_end) {
/*
* This freemap entry starts at the old end of the
* leaf entry array, so we need to adjust its base
* upward to accomodate the larger array.
*/
diff = sizeof(struct xfs_attr_leaf_entry);
} else if (ichdr->freemap[i].size > 0 &&
ichdr->freemap[i].base < new_end) {
/*
* This freemap entry starts in the space claimed by
* the new leaf entry. Adjust its base upward to
* reflect that.
*/
diff = new_end - ichdr->freemap[i].base;
}
if (diff) {
ichdr->freemap[i].base += diff;
ichdr->freemap[i].size -=
min_t(uint16_t, ichdr->freemap[i].size,
sizeof(xfs_attr_leaf_entry_t));
min_t(uint16_t, ichdr->freemap[i].size, diff);
}
/*
* Don't leave zero-length freemaps with nonzero base lying
* around, because we don't want the code in _remove that
* matches on base address to get confused and create
* overlapping freemaps. If we end up with no freemap entries
* then the next _add will compact the leaf block and
* regenerate the freemaps.
*/
if (ichdr->freemap[i].size == 0 && ichdr->freemap[i].base > 0) {
ichdr->freemap[i].base = 0;
ichdr->holes = 1;
}
}
ichdr->usedbytes += xfs_attr_leaf_entsize(leaf, args->index);

View File

@ -34,8 +34,8 @@ struct alg_sock {
struct sock *parent;
unsigned int refcnt;
unsigned int nokey_refcnt;
atomic_t refcnt;
atomic_t nokey_refcnt;
const struct af_alg_type *type;
void *private;
@ -140,6 +140,7 @@ struct af_alg_async_req {
* SG?
* @enc: Cryptographic operation to be performed when
* recvmsg is invoked.
* @init: True if metadata has been sent.
* @len: Length of memory allocated for this data structure.
*/
struct af_alg_ctx {
@ -156,6 +157,7 @@ struct af_alg_ctx {
bool more;
bool merge;
bool enc;
bool init;
unsigned int len;
};
@ -236,7 +238,7 @@ void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst);
void af_alg_free_areq_sgls(struct af_alg_async_req *areq);
int af_alg_wait_for_wmem(struct sock *sk, unsigned int flags);
void af_alg_wmem_wakeup(struct sock *sk);
int af_alg_wait_for_data(struct sock *sk, unsigned flags);
int af_alg_wait_for_data(struct sock *sk, unsigned flags, unsigned min);
void af_alg_data_wakeup(struct sock *sk);
int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
unsigned int ivsize);

View File

@ -20,7 +20,7 @@
struct aead_geniv_ctx {
spinlock_t lock;
struct crypto_aead *child;
struct crypto_skcipher *sknull;
struct crypto_sync_skcipher *sknull;
u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
};

View File

@ -9,7 +9,7 @@
#define NULL_DIGEST_SIZE 0
#define NULL_IV_SIZE 0
struct crypto_skcipher *crypto_get_default_null_skcipher(void);
struct crypto_sync_skcipher *crypto_get_default_null_skcipher(void);
void crypto_put_default_null_skcipher(void);
#endif

View File

@ -88,7 +88,14 @@ struct bpf_map_ops {
/* funcs called by prog_array and perf_event_array map */
void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file,
int fd);
void (*map_fd_put_ptr)(void *ptr);
RH_KABI_REPLACE(
void (*map_fd_put_ptr)(void *ptr),
/* If need_defer is true, the implementation should guarantee that
* the to-be-put element is still alive before the bpf program, which
* may manipulate it, exists.
*/
void (*map_fd_put_ptr)(void *ptr, struct bpf_map *map, bool need_defer)
)
RH_KABI_BROKEN_REPLACE(
u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf),
int (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf)
@ -184,7 +191,10 @@ struct bpf_map {
RH_KABI_BROKEN_INSERT(u32 btf_vmlinux_value_type_id)
RH_KABI_REPLACE(bool unpriv_array, bool bypass_spec_v1)
RH_KABI_FILL_HOLE(bool frozen) /* write-once; write-protected by freeze_mutex*/
/* 22 bytes hole */
RH_KABI_FILL_HOLE(bool free_after_mult_rcu_gp)
RH_KABI_FILL_HOLE(bool free_after_rcu_gp)
RH_KABI_FILL_HOLE(atomic64_t sleepable_refcnt)
/* 16 bytes hole */
/* The 3rd and 4th cacheline with misc members to avoid false sharing
* particularly with refcounting.
@ -195,7 +205,11 @@ struct bpf_map {
) /* RH_KABI_BROKEN_REMOVE_BLOCK */
RH_KABI_BROKEN_INSERT(atomic64_t refcnt ____cacheline_aligned)
RH_KABI_BROKEN_REPLACE(atomic_t usercnt, atomic64_t usercnt)
struct work_struct work;
/* rcu is used before freeing and work is only used during freeing */
RH_KABI_REPLACE(struct work_struct work, union {
struct work_struct work;
struct rcu_head rcu;
})
RH_KABI_BROKEN_REMOVE(char name[BPF_OBJ_NAME_LEN])
RH_KABI_BROKEN_INSERT(struct mutex freeze_mutex)
RH_KABI_BROKEN_INSERT(u64 writecnt) /* writable mmap cnt; protected by freeze_mutex */

View File

@ -58,5 +58,6 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev,
extern int can_send(struct sk_buff *skb, int loop);
extern int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
void can_sock_destruct(struct sock *sk);
#endif /* !_CAN_CORE_H */

View File

@ -68,6 +68,7 @@ struct tc_action {
#define TCA_ACT_FLAGS_REPLACE (1U << (TCA_ACT_FLAGS_USER_BITS + 2))
#define TCA_ACT_FLAGS_NO_RTNL (1U << (TCA_ACT_FLAGS_USER_BITS + 3))
#define TCA_ACT_FLAGS_AT_INGRESS (1U << (TCA_ACT_FLAGS_USER_BITS + 4))
#define TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT (1U << (TCA_ACT_FLAGS_USER_BITS + 5))
/* Update lastuse only if needed, to avoid dirtying a cache line.
* We use a temp variable to avoid fetching jiffies twice.

View File

@ -55,6 +55,8 @@
#define BTPROTO_CMTP 5
#define BTPROTO_HIDP 6
#define BTPROTO_AVDTP 7
#define BTPROTO_ISO 8
#define BTPROTO_LAST BTPROTO_ISO
#define SOL_HCI 0
#define SOL_L2CAP 6
@ -149,10 +151,39 @@ struct bt_voice {
#define BT_MODE_LE_FLOWCTL 0x03
#define BT_MODE_EXT_FLOWCTL 0x04
#define BT_PKT_STATUS 16
#define BT_PKT_STATUS 16
#define BT_SCM_PKT_STATUS 0x03
#define BT_ISO_QOS 17
#define BT_ISO_QOS_CIG_UNSET 0xff
#define BT_ISO_QOS_CIS_UNSET 0xff
struct bt_iso_io_qos {
__u32 interval;
__u16 latency;
__u16 sdu;
__u8 phy;
__u8 rtn;
};
struct bt_iso_qos {
__u8 cig;
__u8 cis;
__u8 sca;
__u8 packing;
__u8 framing;
struct bt_iso_io_qos in;
struct bt_iso_io_qos out;
};
#define BT_ISO_PHY_1M 0x01
#define BT_ISO_PHY_2M 0x02
#define BT_ISO_PHY_CODED 0x04
#define BT_ISO_PHY_ANY (BT_ISO_PHY_1M | BT_ISO_PHY_2M | \
BT_ISO_PHY_CODED)
#define BT_CODEC 19
struct bt_codec_caps {
@ -336,6 +367,9 @@ int bt_sock_register(int proto, const struct net_proto_family *ops);
void bt_sock_unregister(int proto);
void bt_sock_link(struct bt_sock_list *l, struct sock *s);
void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
bool bt_sock_linked(struct bt_sock_list *l, struct sock *s);
struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
struct proto *prot, int proto, gfp_t prio, int kern);
int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
int flags);
int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,

View File

@ -1978,7 +1978,7 @@ struct hci_rp_le_read_iso_tx_sync {
struct hci_cis_params {
__u8 cis_id;
__le16 c_sdu;
__le16 p_pdu;
__le16 p_sdu;
__u8 c_phy;
__u8 p_phy;
__u8 c_rtn;
@ -1989,7 +1989,7 @@ struct hci_cp_le_set_cig_params {
__u8 cig_id;
__u8 c_interval[3];
__u8 p_interval[3];
__u8 wc_sca;
__u8 sca;
__u8 packing;
__u8 framing;
__le16 c_latency;
@ -2032,6 +2032,30 @@ struct hci_cp_le_reject_cis {
__u8 reason;
} __packed;
#define HCI_OP_LE_SETUP_ISO_PATH 0x206e
struct hci_cp_le_setup_iso_path {
__le16 handle;
__u8 direction;
__u8 path;
__u8 codec;
__le16 codec_cid;
__le16 codec_vid;
__u8 delay[3];
__u8 codec_cfg_len;
__u8 codec_cfg[0];
} __packed;
struct hci_rp_le_setup_iso_path {
__u8 status;
__le16 handle;
} __packed;
#define HCI_OP_LE_SET_HOST_FEATURE 0x2074
struct hci_cp_le_set_host_feature {
__u8 bit_number;
__u8 bit_value;
} __packed;
/* ---- HCI Events ---- */
struct hci_ev_status {
__u8 status;

View File

@ -124,6 +124,7 @@ struct hci_conn_hash {
unsigned int acl_num;
unsigned int amp_num;
unsigned int sco_num;
unsigned int iso_num;
unsigned int le_num;
unsigned int le_num_peripheral;
};
@ -259,6 +260,15 @@ struct adv_info {
#define HCI_ADV_TX_POWER_NO_PREFERENCE 0x7F
struct monitored_device {
struct list_head list;
bdaddr_t bdaddr;
__u8 addr_type;
__u16 handle;
bool notified;
};
struct adv_pattern {
struct list_head list;
__u8 ad_type;
@ -459,13 +469,16 @@ struct hci_dev {
unsigned int acl_cnt;
unsigned int sco_cnt;
unsigned int le_cnt;
unsigned int iso_cnt;
unsigned int acl_mtu;
unsigned int sco_mtu;
unsigned int le_mtu;
unsigned int iso_mtu;
unsigned int acl_pkts;
unsigned int sco_pkts;
unsigned int le_pkts;
unsigned int iso_pkts;
__u16 block_len;
__u16 block_mtu;
@ -541,6 +554,7 @@ struct hci_dev {
struct hci_conn_hash conn_hash;
struct mutex mgmt_pending_lock;
struct list_head mgmt_pending;
struct list_head reject_list;
struct list_head accept_list;
@ -600,6 +614,9 @@ struct hci_dev {
struct delayed_work interleave_scan;
struct list_head monitored_devices;
bool advmon_pend_notify;
#if IS_ENABLED(CONFIG_BT_LEDS)
struct led_trigger *power_led;
#endif
@ -641,6 +658,7 @@ enum conn_reasons {
CONN_REASON_PAIR_DEVICE,
CONN_REASON_L2CAP_CHAN,
CONN_REASON_SCO_CONNECT,
CONN_REASON_ISO_CONNECT,
};
struct hci_conn {
@ -693,6 +711,7 @@ struct hci_conn {
__s8 rssi;
__s8 tx_power;
__s8 max_tx_power;
struct bt_iso_qos iso_qos;
unsigned long flags;
enum conn_reasons conn_reason;
@ -723,6 +742,7 @@ struct hci_conn {
struct hci_dev *hdev;
void *l2cap_data;
void *sco_data;
void *iso_data;
struct amp_mgr *amp_mgr;
struct hci_conn *link;
@ -731,6 +751,8 @@ struct hci_conn {
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
void (*cleanup)(struct hci_conn *conn);
};
struct hci_chan {
@ -939,6 +961,9 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
case ESCO_LINK:
h->sco_num++;
break;
case ISO_LINK:
h->iso_num++;
break;
}
}
@ -965,6 +990,9 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
case ESCO_LINK:
h->sco_num--;
break;
case ISO_LINK:
h->iso_num--;
break;
}
}
@ -981,6 +1009,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
case SCO_LINK:
case ESCO_LINK:
return h->sco_num;
case ISO_LINK:
return h->iso_num;
default:
return 0;
}
@ -990,7 +1020,7 @@ static inline unsigned int hci_conn_count(struct hci_dev *hdev)
{
struct hci_conn_hash *c = &hdev->conn_hash;
return c->acl_num + c->amp_num + c->sco_num + c->le_num;
return c->acl_num + c->amp_num + c->sco_num + c->le_num + c->iso_num;
}
static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
@ -1076,6 +1106,53 @@ static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev,
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
bdaddr_t *ba,
__u8 ba_type)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK)
continue;
if (ba_type == c->dst_type && !bacmp(&c->dst, ba)) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
__u8 handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK)
continue;
if (handle == c->iso_qos.cig) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
__u8 type, __u16 state)
{
@ -1096,6 +1173,27 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
return NULL;
}
typedef void (*hci_conn_func_t)(struct hci_conn *conn, void *data);
static inline void hci_conn_hash_list_state(struct hci_dev *hdev,
hci_conn_func_t func, __u8 type,
__u16 state, void *data)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
if (!func)
return;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && c->state == state)
func(c, data);
}
rcu_read_unlock();
}
static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
@ -1119,6 +1217,8 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
int hci_disconnect(struct hci_conn *conn, __u8 reason);
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
void hci_sco_setup(struct hci_conn *conn, __u8 status);
bool hci_iso_setup_path(struct hci_conn *conn);
int hci_le_create_cis(struct hci_conn *conn);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role);
@ -1143,6 +1243,10 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
enum conn_reasons conn_reason);
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting, struct bt_codec *codec);
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos);
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos);
int hci_conn_check_link_mode(struct hci_conn *conn);
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
@ -1402,12 +1506,9 @@ bool hci_adv_instance_is_scannable(struct hci_dev *hdev, u8 instance);
void hci_adv_monitors_clear(struct hci_dev *hdev);
void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
int hci_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
int *err);
bool hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle, int *err);
bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err);
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
int hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle);
int hci_remove_all_adv_monitor(struct hci_dev *hdev);
bool hci_is_adv_monitoring(struct hci_dev *hdev);
int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);
@ -1507,6 +1608,15 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define use_enhanced_conn_complete(dev) (ll_privacy_capable(dev) || \
ext_adv_capable(dev))
/* CIS Master/Slave support */
#define iso_capable(dev) (cis_capable(dev))
#define cis_capable(dev) \
(cis_central_capable(dev) || cis_peripheral_capable(dev))
#define cis_central_capable(dev) \
((dev)->le_features[3] & HCI_LE_CIS_CENTRAL)
#define cis_peripheral_capable(dev) \
((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL)
/* ----- HCI protocols ----- */
#define HCI_PROTO_DEFER 0x01
@ -1521,6 +1631,10 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
case ESCO_LINK:
return sco_connect_ind(hdev, bdaddr, flags);
case ISO_LINK:
/* TODO: Handle connection indication */
return -EINVAL;
default:
BT_ERR("unknown link type %d", type);
return -EINVAL;
@ -1726,6 +1840,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param);
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
@ -1859,10 +1974,9 @@ void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
u8 instance);
void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
u8 instance);
void mgmt_adv_monitor_removed(struct hci_dev *hdev, u16 handle);
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
bdaddr_t *bdaddr, u8 addr_type);
int hci_abort_conn(struct hci_conn *conn, u8 reason);
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,

View File

@ -44,13 +44,25 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
struct hci_cmd_sync_work_entry *
hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
struct hci_cmd_sync_work_entry *entry);
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
hci_cmd_sync_work_func_t func, void *data,
hci_cmd_sync_work_destroy_t destroy);
int hci_update_eir_sync(struct hci_dev *hdev);
int hci_update_class_sync(struct hci_dev *hdev);
int hci_update_eir_sync(struct hci_dev *hdev);
int hci_update_class_sync(struct hci_dev *hdev);
int hci_update_name_sync(struct hci_dev *hdev);
int hci_update_name_sync(struct hci_dev *hdev, const u8 *name);
int hci_write_ssp_mode_sync(struct hci_dev *hdev, u8 mode);
int hci_update_random_address_sync(struct hci_dev *hdev, bool require_privacy,
@ -110,3 +122,6 @@ struct hci_conn;
int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
int hci_le_remove_cig(struct hci_dev *hdev, u8 handle);

View File

@ -1104,3 +1104,19 @@ struct mgmt_ev_controller_resume {
#define MGMT_WAKE_REASON_NON_BT_WAKE 0x0
#define MGMT_WAKE_REASON_UNEXPECTED 0x1
#define MGMT_WAKE_REASON_REMOTE_WAKE 0x2
#define MGMT_EV_ADV_MONITOR_DEVICE_FOUND 0x002f
struct mgmt_ev_adv_monitor_device_found {
__le16 monitor_handle;
struct mgmt_addr_info addr;
__s8 rssi;
__le32 flags;
__le16 eir_len;
__u8 eir[0];
} __packed;
#define MGMT_EV_ADV_MONITOR_DEVICE_LOST 0x0030
struct mgmt_ev_adv_monitor_device_lost {
__le16 monitor_handle;
struct mgmt_addr_info addr;
} __packed;

View File

@ -792,11 +792,11 @@ int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,
}
if (old_ptr)
map->ops->map_fd_put_ptr(old_ptr);
map->ops->map_fd_put_ptr(old_ptr, map, true);
return 0;
}
static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
static int __fd_array_map_delete_elem(struct bpf_map *map, void *key, bool need_defer)
{
struct bpf_array *array = container_of(map, struct bpf_array, map);
void *old_ptr;
@ -815,13 +815,18 @@ static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
}
if (old_ptr) {
map->ops->map_fd_put_ptr(old_ptr);
map->ops->map_fd_put_ptr(old_ptr, map, need_defer);
return 0;
} else {
return -ENOENT;
}
}
static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
{
return __fd_array_map_delete_elem(map, key, true);
}
static void *prog_fd_array_get_ptr(struct bpf_map *map,
struct file *map_file, int fd)
{
@ -839,8 +844,9 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
return prog;
}
static void prog_fd_array_put_ptr(void *ptr)
static void prog_fd_array_put_ptr(void *ptr, struct bpf_map *map, bool need_defer)
{
/* bpf_prog is freed after one RCU or tasks trace grace period */
bpf_prog_put(ptr);
}
@ -850,13 +856,13 @@ static u32 prog_fd_array_sys_lookup_elem(void *ptr)
}
/* decrement refcnt of all bpf_progs that are stored in this map */
static void bpf_fd_array_map_clear(struct bpf_map *map)
static void bpf_fd_array_map_clear(struct bpf_map *map, bool need_defer)
{
struct bpf_array *array = container_of(map, struct bpf_array, map);
int i;
for (i = 0; i < array->map.max_entries; i++)
fd_array_map_delete_elem(map, &i);
__fd_array_map_delete_elem(map, &i, need_defer);
}
static void prog_array_map_seq_show_elem(struct bpf_map *map, void *key,
@ -1035,7 +1041,7 @@ static void prog_array_map_clear_deferred(struct work_struct *work)
{
struct bpf_map *map = container_of(work, struct bpf_array_aux,
work)->map;
bpf_fd_array_map_clear(map);
bpf_fd_array_map_clear(map, true);
bpf_map_put(map);
}
@ -1167,8 +1173,9 @@ err_out:
return ee;
}
static void perf_event_fd_array_put_ptr(void *ptr)
static void perf_event_fd_array_put_ptr(void *ptr, struct bpf_map *map, bool need_defer)
{
/* bpf_perf_event is freed after one RCU grace period */
bpf_event_entry_free_rcu(ptr);
}
@ -1186,7 +1193,7 @@ static void perf_event_fd_array_release(struct bpf_map *map,
for (i = 0; i < array->map.max_entries; i++) {
ee = READ_ONCE(array->ptrs[i]);
if (ee && ee->map_file == map_file)
fd_array_map_delete_elem(map, &i);
__fd_array_map_delete_elem(map, &i, true);
}
rcu_read_unlock();
}
@ -1194,7 +1201,7 @@ static void perf_event_fd_array_release(struct bpf_map *map,
static void perf_event_fd_array_map_free(struct bpf_map *map)
{
if (map->map_flags & BPF_F_PRESERVE_ELEMS)
bpf_fd_array_map_clear(map);
bpf_fd_array_map_clear(map, false);
fd_array_map_free(map);
}
@ -1223,7 +1230,7 @@ static void *cgroup_fd_array_get_ptr(struct bpf_map *map,
return cgroup_get_from_fd(fd);
}
static void cgroup_fd_array_put_ptr(void *ptr)
static void cgroup_fd_array_put_ptr(void *ptr, struct bpf_map *map, bool need_defer)
{
/* cgroup_put free cgrp after a rcu grace period */
cgroup_put(ptr);
@ -1231,7 +1238,7 @@ static void cgroup_fd_array_put_ptr(void *ptr)
static void cgroup_fd_array_free(struct bpf_map *map)
{
bpf_fd_array_map_clear(map);
bpf_fd_array_map_clear(map, false);
fd_array_map_free(map);
}
@ -1277,7 +1284,7 @@ static void array_of_map_free(struct bpf_map *map)
* is protected by fdget/fdput.
*/
bpf_map_meta_free(map->inner_map_meta);
bpf_fd_array_map_clear(map);
bpf_fd_array_map_clear(map, false);
fd_array_map_free(map);
}

View File

@ -2224,12 +2224,16 @@ void __bpf_free_used_maps(struct bpf_prog_aux *aux,
struct bpf_map **used_maps, u32 len)
{
struct bpf_map *map;
bool sleepable;
u32 i;
sleepable = aux->sleepable;
for (i = 0; i < len; i++) {
map = used_maps[i];
if (map->ops->map_poke_untrack)
map->ops->map_poke_untrack(map, aux);
if (sleepable)
atomic64_dec(&map->sleepable_refcnt);
bpf_map_put(map);
}
}

View File

@ -816,7 +816,7 @@ static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l)
if (map->ops->map_fd_put_ptr) {
ptr = fd_htab_map_get_ptr(map, l);
map->ops->map_fd_put_ptr(ptr);
map->ops->map_fd_put_ptr(ptr, map, true);
}
}
@ -2241,7 +2241,7 @@ static void fd_htab_map_free(struct bpf_map *map)
hlist_nulls_for_each_entry_safe(l, n, head, hash_node) {
void *ptr = fd_htab_map_get_ptr(map, l);
map->ops->map_fd_put_ptr(ptr);
map->ops->map_fd_put_ptr(ptr, map, false);
}
}
@ -2282,7 +2282,7 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
ret = htab_map_update_elem(map, key, &ptr, map_flags);
if (ret)
map->ops->map_fd_put_ptr(ptr);
map->ops->map_fd_put_ptr(ptr, map, false);
return ret;
}

View File

@ -11,6 +11,7 @@
*/
#include <linux/bpf.h>
#include <linux/rcupdate.h>
#include <linux/rcupdate_trace.h>
#include <linux/random.h>
#include <linux/smp.h>
#include <linux/topology.h>
@ -31,12 +32,13 @@
*
* Different map implementations will rely on rcu in map methods
* lookup/update/delete, therefore eBPF programs must run under rcu lock
* if program is allowed to access maps, so check rcu_read_lock_held in
* all three functions.
* if program is allowed to access maps, so check rcu_read_lock_held() or
* rcu_read_lock_trace_held() in all three functions.
*/
BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key)
{
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
!rcu_read_lock_bh_held());
return (unsigned long) map->ops->map_lookup_elem(map, key);
}
@ -52,7 +54,8 @@ const struct bpf_func_proto bpf_map_lookup_elem_proto = {
BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key,
void *, value, u64, flags)
{
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
!rcu_read_lock_bh_held());
return map->ops->map_update_elem(map, key, value, flags);
}
@ -69,7 +72,8 @@ const struct bpf_func_proto bpf_map_update_elem_proto = {
BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key)
{
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
!rcu_read_lock_bh_held());
return map->ops->map_delete_elem(map, key);
}

View File

@ -100,12 +100,21 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
return inner_map;
}
void bpf_map_fd_put_ptr(void *ptr)
void bpf_map_fd_put_ptr(void *ptr, struct bpf_map *map, bool need_defer)
{
/* ptr->ops->map_free() has to go through one
* rcu grace period by itself.
struct bpf_map *inner_map = ptr;
/* Defer the freeing of inner map according to the sleepable attribute
* of bpf program which owns the outer map, so unnecessary waiting for
* RCU tasks trace grace period can be avoided.
*/
bpf_map_put(ptr);
if (need_defer) {
if (atomic64_read(&map->sleepable_refcnt))
WRITE_ONCE(inner_map->free_after_mult_rcu_gp, true);
else
WRITE_ONCE(inner_map->free_after_rcu_gp, true);
}
bpf_map_put(inner_map);
}
u32 bpf_map_fd_sys_lookup_elem(void *ptr)

View File

@ -13,7 +13,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd);
void bpf_map_meta_free(struct bpf_map *map_meta);
void *bpf_map_fd_get_ptr(struct bpf_map *map, struct file *map_file,
int ufd);
void bpf_map_fd_put_ptr(void *ptr);
void bpf_map_fd_put_ptr(void *ptr, struct bpf_map *map, bool need_defer);
u32 bpf_map_fd_sys_lookup_elem(void *ptr);
#endif

View File

@ -498,6 +498,22 @@ static void bpf_map_put_uref(struct bpf_map *map)
}
}
static void bpf_map_free_in_work(struct bpf_map *map)
{
INIT_WORK(&map->work, bpf_map_free_deferred);
schedule_work(&map->work);
}
static void bpf_map_free_rcu_gp(struct rcu_head *rcu)
{
bpf_map_free_in_work(container_of(rcu, struct bpf_map, rcu));
}
static void bpf_map_free_mult_rcu_gp(struct rcu_head *rcu)
{
call_rcu(rcu, bpf_map_free_rcu_gp);
}
/* decrement map refcnt and schedule it for freeing via workqueue
* (unrelying map implementation ops->map_free() might sleep)
*/
@ -507,8 +523,14 @@ static void __bpf_map_put(struct bpf_map *map, bool do_idr_lock)
/* bpf_map_free_id() must be called first */
bpf_map_free_id(map, do_idr_lock);
btf_put(map->btf);
INIT_WORK(&map->work, bpf_map_free_deferred);
schedule_work(&map->work);
WARN_ON_ONCE(atomic64_read(&map->sleepable_refcnt));
if (READ_ONCE(map->free_after_mult_rcu_gp))
call_rcu_tasks_trace(&map->rcu, bpf_map_free_mult_rcu_gp);
else if (READ_ONCE(map->free_after_rcu_gp))
call_rcu(&map->rcu, bpf_map_free_rcu_gp);
else
bpf_map_free_in_work(map);
}
}
@ -1182,9 +1204,9 @@ err_put:
#define BPF_MAP_DELETE_ELEM_LAST_FIELD key
static int map_delete_elem(union bpf_attr *attr)
static int map_delete_elem(union bpf_attr *attr, bpfptr_t uattr)
{
void __user *ukey = u64_to_user_ptr(attr->key);
bpfptr_t ukey = make_bpfptr(attr->key, uattr.is_kernel);
int ufd = attr->map_fd;
struct bpf_map *map;
struct fd f;
@ -1203,7 +1225,7 @@ static int map_delete_elem(union bpf_attr *attr)
goto err_put;
}
key = __bpf_copy_key(ukey, map->key_size);
key = ___bpf_copy_key(ukey, map->key_size);
if (IS_ERR(key)) {
err = PTR_ERR(key);
goto err_put;
@ -4436,6 +4458,11 @@ static int bpf_prog_bind_map(union bpf_attr *attr)
goto out_unlock;
}
/* The bpf program will not access the bpf map, but for the sake of
* simplicity, increase sleepable_refcnt for sleepable program as well.
*/
if (prog->aux->sleepable)
atomic64_inc(&map->sleepable_refcnt);
memcpy(used_maps_new, used_maps_old,
sizeof(used_maps_old[0]) * prog->aux->used_map_cnt);
used_maps_new[prog->aux->used_map_cnt] = map;
@ -4488,7 +4515,7 @@ static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size)
err = map_update_elem(&attr, uattr);
break;
case BPF_MAP_DELETE_ELEM:
err = map_delete_elem(&attr);
err = map_delete_elem(&attr, uattr);
break;
case BPF_MAP_GET_NEXT_KEY:
err = map_get_next_key(&attr);
@ -4620,8 +4647,10 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, void *, attr, u32, attr_size)
{
switch (cmd) {
case BPF_MAP_CREATE:
case BPF_MAP_DELETE_ELEM:
case BPF_MAP_UPDATE_ELEM:
case BPF_MAP_FREEZE:
case BPF_MAP_GET_FD_BY_ID:
case BPF_PROG_LOAD:
case BPF_BTF_LOAD:
break;

View File

@ -11372,10 +11372,12 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
return -E2BIG;
}
if (env->prog->aux->sleepable)
atomic64_inc(&map->sleepable_refcnt);
/* hold the map. If the program is rejected by verifier,
* the map will be released by release_maps() or it
* will be used by the valid program until it's unloaded
* and all maps are released in free_used_maps()
* and all maps are released in bpf_free_used_maps()
*/
bpf_map_inc(map);

View File

@ -27,6 +27,7 @@ menuconfig BT
SCO audio links
L2CAP (Logical Link Control and Adaptation Protocol)
SMP (Security Manager Protocol) on LE (Low Energy) links
ISO isochronous links
HCI Device drivers (Interface to the hardware)
RFCOMM Module (RFCOMM Protocol)
BNEP Module (Bluetooth Network Encapsulation Protocol)

View File

@ -138,6 +138,35 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto,
return err;
}
struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
struct proto *prot, int proto, gfp_t prio, int kern)
{
struct sock *sk;
sk = sk_alloc(net, PF_BLUETOOTH, prio, prot, kern);
if (!sk)
return NULL;
sock_init_data(sock, sk);
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
/* Init peer information so it can be properly monitored */
if (!kern) {
spin_lock(&sk->sk_peer_lock);
sk->sk_peer_pid = get_pid(task_tgid(current));
sk->sk_peer_cred = get_current_cred();
spin_unlock(&sk->sk_peer_lock);
}
return sk;
}
EXPORT_SYMBOL(bt_sock_alloc);
void bt_sock_link(struct bt_sock_list *l, struct sock *sk)
{
write_lock(&l->lock);
@ -154,8 +183,33 @@ void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk)
}
EXPORT_SYMBOL(bt_sock_unlink);
bool bt_sock_linked(struct bt_sock_list *l, struct sock *s)
{
struct sock *sk;
if (!l || !s)
return false;
read_lock(&l->lock);
sk_for_each(sk, &l->head) {
if (s == sk) {
read_unlock(&l->lock);
return true;
}
}
read_unlock(&l->lock);
return false;
}
EXPORT_SYMBOL(bt_sock_linked);
void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
{
const struct cred *old_cred;
struct pid *old_pid;
BT_DBG("parent %p, sk %p", parent, sk);
sock_hold(sk);
@ -168,6 +222,19 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q);
bt_sk(sk)->parent = parent;
/* Copy credentials from parent since for incoming connections the
* socket is allocated by the kernel.
*/
spin_lock(&sk->sk_peer_lock);
old_pid = sk->sk_peer_pid;
old_cred = sk->sk_peer_cred;
sk->sk_peer_pid = get_pid(parent->sk_peer_pid);
sk->sk_peer_cred = get_cred(parent->sk_peer_cred);
spin_unlock(&sk->sk_peer_lock);
put_pid(old_pid);
put_cred(old_cred);
if (bh)
bh_unlock_sock(sk);
else

View File

@ -206,21 +206,13 @@ static int bnep_sock_create(struct net *net, struct socket *sock, int protocol,
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto, kern);
sk = bt_sock_alloc(net, sock, &bnep_proto, protocol, GFP_ATOMIC, kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &bnep_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = BT_OPEN;
bt_sock_link(&bnep_sk_list, sk);
return 0;
}

View File

@ -141,6 +141,9 @@ static void hci_conn_cleanup(struct hci_conn *conn)
hci_conn_hash_del(hdev, conn);
if (conn->cleanup)
conn->cleanup(conn);
if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
switch (conn->setting & SCO_AIRMODE_MASK) {
case SCO_AIRMODE_CVSD:
@ -678,6 +681,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
break;
case LE_LINK:
case ISO_LINK:
/* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
break;
@ -748,10 +752,21 @@ int hci_conn_del(struct hci_conn *conn)
hdev->acl_cnt += conn->sent;
} else {
struct hci_conn *acl = conn->link;
if (acl) {
acl->link = NULL;
hci_conn_drop(acl);
}
/* Unacked ISO frames */
if (conn->type == ISO_LINK) {
if (hdev->iso_pkts)
hdev->iso_cnt += conn->sent;
else if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
else
hdev->acl_cnt += conn->sent;
}
}
if (conn->amp_mgr)
@ -1173,6 +1188,437 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
return sco;
}
struct iso_list_data {
u8 cig;
u8 cis;
int count;
struct {
struct hci_cp_le_set_cig_params cp;
struct hci_cis_params cis[0x11];
} pdu;
};
static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos)
{
struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis];
cis->cis_id = qos->cis;
cis->c_sdu = cpu_to_le16(qos->out.sdu);
cis->p_sdu = cpu_to_le16(qos->in.sdu);
cis->c_phy = qos->out.phy ? qos->out.phy : qos->in.phy;
cis->p_phy = qos->in.phy ? qos->in.phy : qos->out.phy;
cis->c_rtn = qos->out.rtn;
cis->p_rtn = qos->in.rtn;
d->pdu.cp.num_cis++;
}
static void cis_list(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET ||
d->cis != conn->iso_qos.cis)
return;
d->count++;
if (d->pdu.cp.cig_id == BT_ISO_QOS_CIG_UNSET ||
d->count >= ARRAY_SIZE(d->pdu.cis))
return;
cis_add(d, &conn->iso_qos);
}
static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data data;
memset(&data, 0, sizeof(data));
/* Allocate a CIG if not set */
if (qos->cig == BT_ISO_QOS_CIG_UNSET) {
for (data.cig = 0x00; data.cig < 0xff; data.cig++) {
data.count = 0;
data.cis = 0xff;
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK,
BT_BOUND, &data);
if (data.count)
continue;
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK,
BT_CONNECTED, &data);
if (!data.count)
break;
}
if (data.cig == 0xff)
return false;
/* Update CIG */
qos->cig = data.cig;
}
data.pdu.cp.cig_id = qos->cig;
hci_cpu_to_le24(qos->out.interval, data.pdu.cp.c_interval);
hci_cpu_to_le24(qos->in.interval, data.pdu.cp.p_interval);
data.pdu.cp.sca = qos->sca;
data.pdu.cp.packing = qos->packing;
data.pdu.cp.framing = qos->framing;
data.pdu.cp.c_latency = cpu_to_le16(qos->out.latency);
data.pdu.cp.p_latency = cpu_to_le16(qos->in.latency);
if (qos->cis != BT_ISO_QOS_CIS_UNSET) {
data.count = 0;
data.cig = qos->cig;
data.cis = qos->cis;
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
&data);
if (data.count)
return false;
cis_add(&data, qos);
}
/* Reprogram all CIS(s) with the same CIG */
for (data.cig = qos->cig, data.cis = 0x00; data.cis < 0x11;
data.cis++) {
data.count = 0;
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
&data);
if (data.count)
continue;
/* Allocate a CIS if not set */
if (qos->cis == BT_ISO_QOS_CIS_UNSET) {
/* Update CIS */
qos->cis = data.cis;
cis_add(&data, qos);
}
}
if (qos->cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis)
return false;
if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS,
sizeof(data.pdu.cp) +
(data.pdu.cp.num_cis * sizeof(*data.pdu.cis)),
&data.pdu) < 0)
return false;
return true;
}
static void find_cis(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
/* Ignore broadcast */
if (!bacmp(&conn->dst, BDADDR_ANY))
return;
d->count++;
}
static int remove_cig_sync(struct hci_dev *hdev, void *data)
{
u8 handle = PTR_ERR(data);
return hci_le_remove_cig_sync(hdev, handle);
}
int hci_le_remove_cig(struct hci_dev *hdev, u8 handle)
{
bt_dev_dbg(hdev, "handle 0x%2.2x", handle);
return hci_cmd_sync_queue(hdev, remove_cig_sync, ERR_PTR(handle), NULL);
}
static void cis_cleanup(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data d;
memset(&d, 0, sizeof(d));
d.cig = conn->iso_qos.cig;
/* Check if ISO connection is a CIS and remove CIG if there are
* no other connections using it.
*/
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d);
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d);
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
if (d.count)
return;
hci_le_remove_cig(hdev, conn->iso_qos.cig);
}
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos)
{
struct hci_conn *cis;
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type);
if (!cis) {
cis = hci_conn_add(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
if (!cis)
return ERR_PTR(-ENOMEM);
cis->cleanup = cis_cleanup;
}
if (cis->state == BT_CONNECTED)
return cis;
/* Check if CIS has been set and the settings matches */
if (cis->state == BT_BOUND &&
!memcmp(&cis->iso_qos, qos, sizeof(*qos)))
return cis;
/* Update LINK PHYs according to QoS preference */
cis->le_tx_phy = qos->out.phy;
cis->le_rx_phy = qos->in.phy;
/* If output interval is not set use the input interval as it cannot be
* 0x000000.
*/
if (!qos->out.interval)
qos->out.interval = qos->in.interval;
/* If input interval is not set use the output interval as it cannot be
* 0x000000.
*/
if (!qos->in.interval)
qos->in.interval = qos->out.interval;
/* If output latency is not set use the input latency as it cannot be
* 0x0000.
*/
if (!qos->out.latency)
qos->out.latency = qos->in.latency;
/* If input latency is not set use the output latency as it cannot be
* 0x0000.
*/
if (!qos->in.latency)
qos->in.latency = qos->out.latency;
if (!hci_le_set_cig_params(cis, qos)) {
hci_conn_drop(cis);
return ERR_PTR(-EINVAL);
}
cis->iso_qos = *qos;
cis->state = BT_BOUND;
return cis;
}
bool hci_iso_setup_path(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_setup_iso_path cmd;
memset(&cmd, 0, sizeof(cmd));
if (conn->iso_qos.out.sdu) {
cmd.handle = cpu_to_le16(conn->handle);
cmd.direction = 0x00; /* Input (Host to Controller) */
cmd.path = 0x00; /* HCI path if enabled */
cmd.codec = 0x03; /* Transparent Data */
if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd),
&cmd) < 0)
return false;
}
if (conn->iso_qos.in.sdu) {
cmd.handle = cpu_to_le16(conn->handle);
cmd.direction = 0x01; /* Output (Controller to Host) */
cmd.path = 0x00; /* HCI path if enabled */
cmd.codec = 0x03; /* Transparent Data */
if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd),
&cmd) < 0)
return false;
}
return true;
}
static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
{
struct {
struct hci_cp_le_create_cis cp;
struct hci_cis cis[0x1f];
} cmd;
struct hci_conn *conn = data;
u8 cig;
memset(&cmd, 0, sizeof(cmd));
cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle);
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
cmd.cp.num_cis++;
cig = conn->iso_qos.cig;
hci_dev_lock(hdev);
rcu_read_lock();
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
if (conn == data || conn->type != ISO_LINK ||
conn->state == BT_CONNECTED || conn->iso_qos.cig != cig)
continue;
/* Check if all CIS(s) belonging to a CIG are ready */
if (!conn->link || conn->link->state != BT_CONNECTED ||
conn->state != BT_CONNECT) {
cmd.cp.num_cis = 0;
break;
}
/* Group all CIS with state BT_CONNECT since the spec don't
* allow to send them individually:
*
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
* page 2566:
*
* If the Host issues this command before all the
* HCI_LE_CIS_Established events from the previous use of the
* command have been generated, the Controller shall return the
* error code Command Disallowed (0x0C).
*/
cis->acl_handle = cpu_to_le16(conn->link->handle);
cis->cis_handle = cpu_to_le16(conn->handle);
cmd.cp.num_cis++;
}
rcu_read_unlock();
hci_dev_unlock(hdev);
if (!cmd.cp.num_cis)
return 0;
return hci_send_cmd(hdev, HCI_OP_LE_CREATE_CIS, sizeof(cmd.cp) +
sizeof(cmd.cis[0]) * cmd.cp.num_cis, &cmd);
}
int hci_le_create_cis(struct hci_conn *conn)
{
struct hci_conn *cis;
struct hci_dev *hdev = conn->hdev;
int err;
switch (conn->type) {
case LE_LINK:
if (!conn->link || conn->state != BT_CONNECTED)
return -EINVAL;
cis = conn->link;
break;
case ISO_LINK:
cis = conn;
break;
default:
return -EINVAL;
}
if (cis->state == BT_CONNECT)
return 0;
/* Queue Create CIS */
err = hci_cmd_sync_queue(hdev, hci_create_cis_sync, cis, NULL);
if (err)
return err;
cis->state = BT_CONNECT;
return 0;
}
static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
struct bt_iso_io_qos *qos, __u8 phy)
{
/* Only set MTU if PHY is enabled */
if (!qos->sdu && qos->phy) {
if (hdev->iso_mtu > 0)
qos->sdu = hdev->iso_mtu;
else if (hdev->le_mtu > 0)
qos->sdu = hdev->le_mtu;
else
qos->sdu = hdev->acl_mtu;
}
/* Use the same PHY as ACL if set to any */
if (qos->phy == BT_ISO_PHY_ANY)
qos->phy = phy;
/* Use LE ACL connection interval if not set */
if (!qos->interval)
/* ACL interval unit in 1.25 ms to us */
qos->interval = conn->le_conn_interval * 1250;
/* Use LE ACL connection latency if not set */
if (!qos->latency)
qos->latency = conn->le_conn_latency;
}
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos)
{
struct hci_conn *le;
struct hci_conn *cis;
/* Convert from ISO socket address type to HCI address type */
if (dst_type == BDADDR_LE_PUBLIC)
dst_type = ADDR_LE_DEV_PUBLIC;
else
dst_type = ADDR_LE_DEV_RANDOM;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
le = hci_connect_le(hdev, dst, dst_type, false,
BT_SECURITY_LOW,
HCI_LE_CONN_TIMEOUT,
HCI_ROLE_SLAVE);
else
le = hci_connect_le_scan(hdev, dst, dst_type,
BT_SECURITY_LOW,
HCI_LE_CONN_TIMEOUT,
CONN_REASON_ISO_CONNECT);
if (IS_ERR(le))
return le;
hci_iso_qos_setup(hdev, le, &qos->out,
le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys);
hci_iso_qos_setup(hdev, le, &qos->in,
le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys);
cis = hci_bind_cis(hdev, dst, dst_type, qos);
if (IS_ERR(cis)) {
hci_conn_drop(le);
return cis;
}
le->link = cis;
cis->link = le;
hci_conn_hold(cis);
/* If LE is already connected and CIS handle is already set proceed to
* Create CIS immediately.
*/
if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET)
hci_le_create_cis(le);
return cis;
}
/* Check link security requirement */
int hci_conn_check_link_mode(struct hci_conn *conn)
{

View File

@ -625,7 +625,10 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
hdev->flush(hdev);
atomic_set(&hdev->cmd_cnt, 1);
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
hdev->acl_cnt = 0;
hdev->sco_cnt = 0;
hdev->le_cnt = 0;
hdev->iso_cnt = 0;
ret = hci_reset_sync(hdev);
@ -905,7 +908,7 @@ int hci_get_dev_info(void __user *arg)
else
flags = hdev->flags;
strcpy(di.name, hdev->name);
strscpy(di.name, hdev->name, sizeof(di.name));
di.bdaddr = hdev->bdaddr;
di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4);
di.flags = flags;
@ -1884,159 +1887,128 @@ void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
if (monitor->handle)
idr_remove(&hdev->adv_monitors_idr, monitor->handle);
if (monitor->state != ADV_MONITOR_STATE_NOT_REGISTERED) {
if (monitor->state != ADV_MONITOR_STATE_NOT_REGISTERED)
hdev->adv_monitors_cnt--;
mgmt_adv_monitor_removed(hdev, monitor->handle);
}
kfree(monitor);
}
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
{
return mgmt_add_adv_patterns_monitor_complete(hdev, status);
}
int hci_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status)
{
return mgmt_remove_adv_monitor_complete(hdev, status);
}
/* Assigns handle to a monitor, and if offloading is supported and power is on,
* also attempts to forward the request to the controller.
* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
* This function requires the caller holds hci_req_sync_lock.
*/
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
int *err)
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
{
int min, max, handle;
int status = 0;
*err = 0;
if (!monitor)
return -EINVAL;
if (!monitor) {
*err = -EINVAL;
return false;
}
hci_dev_lock(hdev);
min = HCI_MIN_ADV_MONITOR_HANDLE;
max = HCI_MIN_ADV_MONITOR_HANDLE + HCI_MAX_ADV_MONITOR_NUM_HANDLES;
handle = idr_alloc(&hdev->adv_monitors_idr, monitor, min, max,
GFP_KERNEL);
if (handle < 0) {
*err = handle;
return false;
}
hci_dev_unlock(hdev);
if (handle < 0)
return handle;
monitor->handle = handle;
if (!hdev_is_powered(hdev))
return false;
return status;
switch (hci_get_adv_monitor_offload_ext(hdev)) {
case HCI_ADV_MONITOR_EXT_NONE:
hci_update_passive_scan(hdev);
bt_dev_dbg(hdev, "%s add monitor status %d", hdev->name, *err);
bt_dev_dbg(hdev, "add monitor %d status %d",
monitor->handle, status);
/* Message was not forwarded to controller - not an error */
return false;
break;
case HCI_ADV_MONITOR_EXT_MSFT:
*err = msft_add_monitor_pattern(hdev, monitor);
bt_dev_dbg(hdev, "%s add monitor msft status %d", hdev->name,
*err);
status = msft_add_monitor_pattern(hdev, monitor);
bt_dev_dbg(hdev, "add monitor %d msft status %d",
handle, status);
break;
}
return (*err == 0);
return status;
}
/* Attempts to tell the controller and free the monitor. If somehow the
* controller doesn't have a corresponding handle, remove anyway.
* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
* This function requires the caller holds hci_req_sync_lock.
*/
static bool hci_remove_adv_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor,
u16 handle, int *err)
static int hci_remove_adv_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor)
{
*err = 0;
int status = 0;
int handle;
switch (hci_get_adv_monitor_offload_ext(hdev)) {
case HCI_ADV_MONITOR_EXT_NONE: /* also goes here when powered off */
bt_dev_dbg(hdev, "remove monitor %d status %d",
monitor->handle, status);
goto free_monitor;
case HCI_ADV_MONITOR_EXT_MSFT:
*err = msft_remove_monitor(hdev, monitor, handle);
handle = monitor->handle;
status = msft_remove_monitor(hdev, monitor);
bt_dev_dbg(hdev, "remove monitor %d msft status %d",
handle, status);
break;
}
/* In case no matching handle registered, just free the monitor */
if (*err == -ENOENT)
if (status == -ENOENT)
goto free_monitor;
return (*err == 0);
return status;
free_monitor:
if (*err == -ENOENT)
if (status == -ENOENT)
bt_dev_warn(hdev, "Removing monitor with no matching handle %d",
monitor->handle);
hci_free_adv_monitor(hdev, monitor);
*err = 0;
return false;
return status;
}
/* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
*/
bool hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle, int *err)
/* This function requires the caller holds hci_req_sync_lock */
int hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle)
{
struct adv_monitor *monitor = idr_find(&hdev->adv_monitors_idr, handle);
bool pending;
if (!monitor) {
*err = -EINVAL;
return false;
}
if (!monitor)
return -EINVAL;
pending = hci_remove_adv_monitor(hdev, monitor, handle, err);
if (!*err && !pending)
hci_update_passive_scan(hdev);
bt_dev_dbg(hdev, "%s remove monitor handle %d, status %d, %spending",
hdev->name, handle, *err, pending ? "" : "not ");
return pending;
return hci_remove_adv_monitor(hdev, monitor);
}
/* Returns true if request is forwarded (result is pending), false otherwise.
* This function requires the caller holds hdev->lock.
*/
bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err)
/* This function requires the caller holds hci_req_sync_lock */
int hci_remove_all_adv_monitor(struct hci_dev *hdev)
{
struct adv_monitor *monitor;
int idr_next_id = 0;
bool pending = false;
bool update = false;
int status = 0;
*err = 0;
while (!*err && !pending) {
while (1) {
monitor = idr_get_next(&hdev->adv_monitors_idr, &idr_next_id);
if (!monitor)
break;
pending = hci_remove_adv_monitor(hdev, monitor, 0, err);
status = hci_remove_adv_monitor(hdev, monitor);
if (status)
return status;
if (!*err && !pending)
update = true;
idr_next_id++;
}
if (update)
hci_update_passive_scan(hdev);
bt_dev_dbg(hdev, "%s remove all monitors status %d, %spending",
hdev->name, *err, pending ? "" : "not ");
return pending;
return status;
}
/* This function requires the caller holds hdev->lock */
@ -2510,6 +2482,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
mutex_init(&hdev->lock);
mutex_init(&hdev->req_lock);
mutex_init(&hdev->mgmt_pending_lock);
INIT_LIST_HEAD(&hdev->mgmt_pending);
INIT_LIST_HEAD(&hdev->reject_list);
@ -2527,6 +2500,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
INIT_LIST_HEAD(&hdev->conn_hash.list);
INIT_LIST_HEAD(&hdev->adv_instances);
INIT_LIST_HEAD(&hdev->blocked_keys);
INIT_LIST_HEAD(&hdev->monitored_devices);
INIT_LIST_HEAD(&hdev->local_codecs);
INIT_WORK(&hdev->rx_work, hci_rx_work);
@ -3162,9 +3136,117 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
queue_work(hdev->workqueue, &hdev->tx_work);
}
/* Send ISO data */
static void hci_add_iso_hdr(struct sk_buff *skb, __u16 handle, __u8 flags)
{
struct hci_iso_hdr *hdr;
int len = skb->len;
skb_push(skb, HCI_ISO_HDR_SIZE);
skb_reset_transport_header(skb);
hdr = (struct hci_iso_hdr *)skb_transport_header(skb);
hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags));
hdr->dlen = cpu_to_le16(len);
}
static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
struct sk_buff *skb)
{
struct hci_dev *hdev = conn->hdev;
struct sk_buff *list;
__u16 flags;
skb->len = skb_headlen(skb);
skb->data_len = 0;
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
list = skb_shinfo(skb)->frag_list;
flags = hci_iso_flags_pack(list ? ISO_START : ISO_SINGLE, 0x00);
hci_add_iso_hdr(skb, conn->handle, flags);
if (!list) {
/* Non fragmented */
BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
skb_queue_tail(queue, skb);
} else {
/* Fragmented */
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
skb_shinfo(skb)->frag_list = NULL;
__skb_queue_tail(queue, skb);
do {
skb = list; list = list->next;
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
flags = hci_iso_flags_pack(list ? ISO_CONT : ISO_END,
0x00);
hci_add_iso_hdr(skb, conn->handle, flags);
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
__skb_queue_tail(queue, skb);
} while (list);
}
}
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("%s len %d", hdev->name, skb->len);
hci_queue_iso(conn, &conn->data_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
}
/* ---- HCI TX task (outgoing data) ---- */
/* HCI Connection scheduler */
static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
{
struct hci_dev *hdev;
int cnt, q;
if (!conn) {
*quote = 0;
return;
}
hdev = conn->hdev;
switch (conn->type) {
case ACL_LINK:
cnt = hdev->acl_cnt;
break;
case AMP_LINK:
cnt = hdev->block_cnt;
break;
case SCO_LINK:
case ESCO_LINK:
cnt = hdev->sco_cnt;
break;
case LE_LINK:
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
case ISO_LINK:
cnt = hdev->iso_mtu ? hdev->iso_cnt :
hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
default:
cnt = 0;
bt_dev_err(hdev, "unknown link type %d", conn->type);
}
q = cnt / num;
*quote = q ? q : 1;
}
static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
int *quote)
{
@ -3197,29 +3279,7 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
rcu_read_unlock();
if (conn) {
int cnt, q;
switch (conn->type) {
case ACL_LINK:
cnt = hdev->acl_cnt;
break;
case SCO_LINK:
case ESCO_LINK:
cnt = hdev->sco_cnt;
break;
case LE_LINK:
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
default:
cnt = 0;
bt_dev_err(hdev, "unknown link type %d", conn->type);
}
q = cnt / num;
*quote = q ? q : 1;
} else
*quote = 0;
hci_quote_sent(conn, num, quote);
BT_DBG("conn %p quote %d", conn, *quote);
return conn;
@ -3253,7 +3313,7 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
struct hci_chan *chan = NULL;
unsigned int num = 0, min = ~0, cur_prio = 0;
struct hci_conn *conn;
int cnt, q, conn_num = 0;
int conn_num = 0;
BT_DBG("%s", hdev->name);
@ -3303,27 +3363,8 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
if (!chan)
return NULL;
switch (chan->conn->type) {
case ACL_LINK:
cnt = hdev->acl_cnt;
break;
case AMP_LINK:
cnt = hdev->block_cnt;
break;
case SCO_LINK:
case ESCO_LINK:
cnt = hdev->sco_cnt;
break;
case LE_LINK:
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
default:
cnt = 0;
bt_dev_err(hdev, "unknown link type %d", chan->conn->type);
}
hci_quote_sent(chan->conn, num, quote);
q = cnt / num;
*quote = q ? q : 1;
BT_DBG("chan %p quote %d", chan, *quote);
return chan;
}
@ -3612,18 +3653,46 @@ static void hci_sched_le(struct hci_dev *hdev)
hci_prio_recalculate(hdev, LE_LINK);
}
/* Schedule CIS */
static void hci_sched_iso(struct hci_dev *hdev)
{
struct hci_conn *conn;
struct sk_buff *skb;
int quote, *cnt;
BT_DBG("%s", hdev->name);
if (!hci_conn_num(hdev, ISO_LINK))
return;
cnt = hdev->iso_pkts ? &hdev->iso_cnt :
hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(hdev, skb);
conn->sent++;
if (conn->sent == ~0)
conn->sent = 0;
(*cnt)--;
}
}
}
static void hci_tx_work(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work);
struct sk_buff *skb;
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
hdev->sco_cnt, hdev->le_cnt);
BT_DBG("%s acl %d sco %d le %d iso %d", hdev->name, hdev->acl_cnt,
hdev->sco_cnt, hdev->le_cnt, hdev->iso_cnt);
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
/* Schedule queues and send stuff to HCI driver */
hci_sched_sco(hdev);
hci_sched_esco(hdev);
hci_sched_iso(hdev);
hci_sched_acl(hdev);
hci_sched_le(hdev);
}
@ -3706,6 +3775,39 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(skb);
}
static void hci_isodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_iso_hdr *hdr;
struct hci_conn *conn;
__u16 handle, flags;
hdr = skb_pull_data(skb, sizeof(*hdr));
if (!hdr) {
bt_dev_err(hdev, "ISO packet too small");
goto drop;
}
handle = __le16_to_cpu(hdr->handle);
flags = hci_flags(handle);
handle = hci_handle(handle);
bt_dev_dbg(hdev, "len %d handle 0x%4.4x flags 0x%4.4x", skb->len,
handle, flags);
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, handle);
hci_dev_unlock(hdev);
/* TODO: Send to upper protocol */
if (!conn) {
bt_dev_err(hdev, "ISO packet for unknown connection handle %d",
handle);
}
drop:
kfree_skb(skb);
}
static bool hci_req_is_complete(struct hci_dev *hdev)
{
struct sk_buff *skb;
@ -3860,6 +3962,11 @@ static void hci_rx_work(struct work_struct *work)
hci_scodata_packet(hdev, skb);
break;
case HCI_ISODATA_PKT:
BT_DBG("%s ISO data packet", hdev->name);
hci_isodata_packet(hdev, skb);
break;
default:
kfree_skb(skb);
break;

View File

@ -3770,6 +3770,127 @@ static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
}
}
static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_rp_le_read_buffer_size_v2 *rp = data;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
if (rp->status)
return rp->status;
hdev->le_mtu = __le16_to_cpu(rp->acl_mtu);
hdev->le_pkts = rp->acl_max_pkt;
hdev->iso_mtu = __le16_to_cpu(rp->iso_mtu);
hdev->iso_pkts = rp->iso_max_pkt;
hdev->le_cnt = hdev->le_pkts;
hdev->iso_cnt = hdev->iso_pkts;
BT_DBG("%s acl mtu %d:%d iso mtu %d:%d", hdev->name, hdev->acl_mtu,
hdev->acl_pkts, hdev->iso_mtu, hdev->iso_pkts);
return rp->status;
}
static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_rp_le_set_cig_params *rp = data;
struct hci_conn *conn;
int i = 0;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
hci_dev_lock(hdev);
if (rp->status) {
while ((conn = hci_conn_hash_lookup_cig(hdev, rp->cig_id))) {
conn->state = BT_CLOSED;
hci_connect_cfm(conn, rp->status);
hci_conn_del(conn);
}
goto unlock;
}
rcu_read_lock();
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
if (conn->type != ISO_LINK || conn->iso_qos.cig != rp->cig_id ||
conn->state == BT_CONNECTED)
continue;
conn->handle = __le16_to_cpu(rp->handle[i++]);
bt_dev_dbg(hdev, "%p handle 0x%4.4x link %p", conn,
conn->handle, conn->link);
/* Create CIS if LE is already connected */
if (conn->link && conn->link->state == BT_CONNECTED) {
rcu_read_unlock();
hci_le_create_cis(conn->link);
rcu_read_lock();
}
if (i == rp->num_handles)
break;
}
rcu_read_unlock();
unlock:
hci_dev_unlock(hdev);
return rp->status;
}
static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_rp_le_setup_iso_path *rp = data;
struct hci_cp_le_setup_iso_path *cp;
struct hci_conn *conn;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SETUP_ISO_PATH);
if (!cp)
return rp->status;
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (!conn)
goto unlock;
if (rp->status) {
hci_connect_cfm(conn, rp->status);
hci_conn_del(conn);
goto unlock;
}
switch (cp->direction) {
/* Input (Host to Controller) */
case 0x00:
/* Only confirm connection if output only */
if (conn->iso_qos.out.sdu && !conn->iso_qos.in.sdu)
hci_connect_cfm(conn, rp->status);
break;
/* Output (Controller to Host) */
case 0x01:
/* Confirm connection since conn->iso_qos is always configured
* last.
*/
hci_connect_cfm(conn, rp->status);
break;
}
unlock:
hci_dev_unlock(hdev);
return rp->status;
}
#define HCI_CC_VL(_op, _func, _min, _max) \
{ \
.op = _op, \
@ -3947,7 +4068,13 @@ static const struct hci_cc {
HCI_CC_STATUS(HCI_OP_LE_CLEAR_ADV_SETS, hci_cc_le_clear_adv_sets),
HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER, hci_cc_le_read_transmit_power,
sizeof(struct hci_rp_le_read_transmit_power)),
HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode)
HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode),
HCI_CC(HCI_OP_LE_READ_BUFFER_SIZE_V2, hci_cc_le_read_buffer_size_v2,
sizeof(struct hci_rp_le_read_buffer_size_v2)),
HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS, hci_cc_le_set_cig_params,
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_EVENT_SIZE),
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
sizeof(struct hci_rp_le_setup_iso_path)),
};
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
@ -4010,6 +4137,40 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, void *data,
queue_work(hdev->workqueue, &hdev->cmd_work);
}
static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_create_cis *cp;
int i;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
if (!status)
return;
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CIS);
if (!cp)
return;
hci_dev_lock(hdev);
/* Remove connection if command failed */
for (i = 0; cp->num_cis; cp->num_cis--, i++) {
struct hci_conn *conn;
u16 handle;
handle = __le16_to_cpu(cp->cis[i].cis_handle);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (conn) {
conn->state = BT_CLOSED;
hci_connect_cfm(conn, status);
hci_conn_del(conn);
}
}
hci_dev_unlock(hdev);
}
#define HCI_CS(_op, _func) \
{ \
.op = _op, \
@ -4039,7 +4200,8 @@ static const struct hci_cs {
HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn),
HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features),
HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc),
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn)
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
};
static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
@ -4175,6 +4337,22 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
hdev->sco_cnt = hdev->sco_pkts;
break;
case ISO_LINK:
if (hdev->iso_pkts) {
hdev->iso_cnt += count;
if (hdev->iso_cnt > hdev->iso_pkts)
hdev->iso_cnt = hdev->iso_pkts;
} else if (hdev->le_pkts) {
hdev->le_cnt += count;
if (hdev->le_cnt > hdev->le_pkts)
hdev->le_cnt = hdev->le_pkts;
} else {
hdev->acl_cnt += count;
if (hdev->acl_cnt > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts;
}
break;
default:
bt_dev_err(hdev, "unknown type %d conn %p",
conn->type, conn);
@ -6481,6 +6659,127 @@ unlock:
hci_dev_unlock(hdev);
}
static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_evt_le_cis_established *ev = data;
struct hci_conn *conn;
u16 handle = __le16_to_cpu(ev->handle);
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn) {
bt_dev_err(hdev,
"Unable to find connection with handle 0x%4.4x",
handle);
goto unlock;
}
if (conn->role == HCI_ROLE_SLAVE) {
__le32 interval;
memset(&interval, 0, sizeof(interval));
memcpy(&interval, ev->c_latency, sizeof(ev->c_latency));
conn->iso_qos.in.interval = le32_to_cpu(interval);
memcpy(&interval, ev->p_latency, sizeof(ev->p_latency));
conn->iso_qos.out.interval = le32_to_cpu(interval);
conn->iso_qos.in.latency = le16_to_cpu(ev->interval);
conn->iso_qos.out.latency = le16_to_cpu(ev->interval);
conn->iso_qos.in.sdu = le16_to_cpu(ev->c_mtu);
conn->iso_qos.out.sdu = le16_to_cpu(ev->p_mtu);
conn->iso_qos.in.phy = ev->c_phy;
conn->iso_qos.out.phy = ev->p_phy;
}
if (!ev->status) {
conn->state = BT_CONNECTED;
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
hci_iso_setup_path(conn);
goto unlock;
}
hci_connect_cfm(conn, ev->status);
hci_conn_del(conn);
unlock:
hci_dev_unlock(hdev);
}
static void hci_le_reject_cis(struct hci_dev *hdev, __le16 handle)
{
struct hci_cp_le_reject_cis cp;
memset(&cp, 0, sizeof(cp));
cp.handle = handle;
cp.reason = HCI_ERROR_REJ_BAD_ADDR;
hci_send_cmd(hdev, HCI_OP_LE_REJECT_CIS, sizeof(cp), &cp);
}
static void hci_le_accept_cis(struct hci_dev *hdev, __le16 handle)
{
struct hci_cp_le_accept_cis cp;
memset(&cp, 0, sizeof(cp));
cp.handle = handle;
hci_send_cmd(hdev, HCI_OP_LE_ACCEPT_CIS, sizeof(cp), &cp);
}
static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
struct hci_evt_le_cis_req *ev = data;
u16 acl_handle, cis_handle;
struct hci_conn *acl, *cis;
int mask;
__u8 flags = 0;
acl_handle = __le16_to_cpu(ev->acl_handle);
cis_handle = __le16_to_cpu(ev->cis_handle);
bt_dev_dbg(hdev, "acl 0x%4.4x handle 0x%4.4x cig 0x%2.2x cis 0x%2.2x",
acl_handle, cis_handle, ev->cig_id, ev->cis_id);
hci_dev_lock(hdev);
acl = hci_conn_hash_lookup_handle(hdev, acl_handle);
if (!acl)
goto unlock;
mask = hci_proto_connect_ind(hdev, &acl->dst, ISO_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_reject_cis(hdev, ev->cis_handle);
goto unlock;
}
cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
if (!cis) {
cis = hci_conn_add(hdev, ISO_LINK, &acl->dst, HCI_ROLE_SLAVE);
if (!cis) {
hci_le_reject_cis(hdev, ev->cis_handle);
goto unlock;
}
cis->handle = cis_handle;
}
cis->iso_qos.cig = ev->cig_id;
cis->iso_qos.cis = ev->cis_id;
if (!(flags & HCI_PROTO_DEFER)) {
hci_le_accept_cis(hdev, ev->cis_handle);
} else {
cis->state = BT_CONNECT2;
hci_connect_cfm(cis, 0);
}
unlock:
hci_dev_unlock(hdev);
}
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
[_op] = { \
.func = _func, \
@ -6544,6 +6843,12 @@ static const struct hci_le_ev {
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
sizeof(struct hci_evt_le_ext_adv_set_term)),
/* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */
HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_estabilished_evt,
sizeof(struct hci_evt_le_cis_established)),
/* [0x1a = HCI_EVT_LE_CIS_REQ] */
HCI_LE_EV(HCI_EVT_LE_CIS_REQ, hci_le_cis_req_evt,
sizeof(struct hci_evt_le_cis_req)),
};
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
@ -6582,7 +6887,6 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
if (skb->len > subev->max_len)
bt_dev_warn(hdev, "unexpected subevent 0x%2.2x length: %u > %u",
ev->subevent, skb->len, subev->max_len);
data = hci_le_ev_skb_pull(hdev, skb, ev->subevent, subev->min_len);
if (!data)
return;

View File

@ -1215,7 +1215,9 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done;
}
hci_dev_lock(hdev);
mgmt_index_removed(hdev);
hci_dev_unlock(hdev);
err = hci_dev_open(hdev->id);
if (err) {
@ -2108,18 +2110,12 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol,
sock->ops = &hci_sock_ops;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto, kern);
sk = bt_sock_alloc(net, sock, &hci_sk_proto, protocol, GFP_ATOMIC,
kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sock->state = SS_UNCONNECTED;
sk->sk_state = BT_OPEN;
sk->sk_destruct = hci_sock_destruct;
bt_sock_link(&hci_sk_list, sk);

View File

@ -332,19 +332,27 @@ void hci_cmd_sync_init(struct hci_dev *hdev)
INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
}
static void _hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
struct hci_cmd_sync_work_entry *entry,
int err)
{
if (entry->destroy)
entry->destroy(hdev, entry->data, err);
list_del(&entry->list);
kfree(entry);
}
void hci_cmd_sync_clear(struct hci_dev *hdev)
{
struct hci_cmd_sync_work_entry *entry, *tmp;
cancel_work_sync(&hdev->cmd_sync_work);
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
if (entry->destroy)
entry->destroy(hdev, entry->data, -ECANCELED);
list_del(&entry->list);
kfree(entry);
}
mutex_lock(&hdev->cmd_sync_work_lock);
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list)
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
mutex_unlock(&hdev->cmd_sync_work_lock);
}
void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
@ -430,6 +438,121 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
}
EXPORT_SYMBOL(hci_cmd_sync_queue);
static struct hci_cmd_sync_work_entry *
_hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
struct hci_cmd_sync_work_entry *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
if (func && entry->func != func)
continue;
if (data && entry->data != data)
continue;
if (destroy && entry->destroy != destroy)
continue;
return entry;
}
return NULL;
}
/* Queue HCI command entry once:
*
* - Lookup if an entry already exist and only if it doesn't creates a new entry
* and queue it.
*/
int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
return 0;
return hci_cmd_sync_queue(hdev, func, data, destroy);
}
EXPORT_SYMBOL(hci_cmd_sync_queue_once);
/* Lookup HCI command entry:
*
* - Return first entry that matches by function callback or data or
* destroy callback.
*/
struct hci_cmd_sync_work_entry *
hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
struct hci_cmd_sync_work_entry *entry;
mutex_lock(&hdev->cmd_sync_work_lock);
entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
mutex_unlock(&hdev->cmd_sync_work_lock);
return entry;
}
EXPORT_SYMBOL(hci_cmd_sync_lookup_entry);
/* Cancel HCI command entry */
void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
struct hci_cmd_sync_work_entry *entry)
{
mutex_lock(&hdev->cmd_sync_work_lock);
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
mutex_unlock(&hdev->cmd_sync_work_lock);
}
EXPORT_SYMBOL(hci_cmd_sync_cancel_entry);
/* Dequeue one HCI command entry:
*
* - Lookup and cancel first entry that matches.
*/
bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
struct hci_cmd_sync_work_entry *entry;
mutex_lock(&hdev->cmd_sync_work_lock);
entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
if (!entry) {
mutex_unlock(&hdev->cmd_sync_work_lock);
return false;
}
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
mutex_unlock(&hdev->cmd_sync_work_lock);
return true;
}
EXPORT_SYMBOL(hci_cmd_sync_dequeue_once);
/* Dequeue HCI command entry:
*
* - Lookup and cancel any entry that matches by function callback or data or
* destroy callback.
*/
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
struct hci_cmd_sync_work_entry *entry;
bool ret = false;
mutex_lock(&hdev->cmd_sync_work_lock);
while ((entry = _hci_cmd_sync_lookup_entry(hdev, func, data,
destroy))) {
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
ret = true;
}
mutex_unlock(&hdev->cmd_sync_work_lock);
return ret;
}
EXPORT_SYMBOL(hci_cmd_sync_dequeue);
int hci_update_eir_sync(struct hci_dev *hdev)
{
struct hci_cp_write_eir cp;
@ -2253,7 +2376,8 @@ int hci_update_passive_scan(struct hci_dev *hdev)
hci_dev_test_flag(hdev, HCI_UNREGISTER))
return 0;
return hci_cmd_sync_queue(hdev, update_passive_scan_sync, NULL, NULL);
return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
NULL);
}
int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val)
@ -2483,13 +2607,13 @@ int hci_update_scan_sync(struct hci_dev *hdev)
return hci_write_scan_enable_sync(hdev, scan);
}
int hci_update_name_sync(struct hci_dev *hdev)
int hci_update_name_sync(struct hci_dev *hdev, const u8 *name)
{
struct hci_cp_write_local_name cp;
memset(&cp, 0, sizeof(cp));
memcpy(cp.name, hdev->dev_name, sizeof(cp.name));
memcpy(cp.name, name, sizeof(cp.name));
return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_LOCAL_NAME,
sizeof(cp), &cp,
@ -2541,7 +2665,7 @@ int hci_powered_update_sync(struct hci_dev *hdev)
hci_write_fast_connectable_sync(hdev, false);
hci_update_scan_sync(hdev);
hci_update_class_sync(hdev);
hci_update_name_sync(hdev);
hci_update_name_sync(hdev, hdev->dev_name);
hci_update_eir_sync(hdev);
}
@ -2994,6 +3118,12 @@ static const struct hci_init_stage hci_init2[] = {
/* Read LE Buffer Size */
static int hci_le_read_buffer_size_sync(struct hci_dev *hdev)
{
/* Use Read LE Buffer Size V2 if supported */
if (iso_capable(hdev) && hdev->commands[41] & 0x20)
return __hci_cmd_sync_status(hdev,
HCI_OP_LE_READ_BUFFER_SIZE_V2,
0, NULL, HCI_CMD_TIMEOUT);
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_BUFFER_SIZE,
0, NULL, HCI_CMD_TIMEOUT);
}
@ -3014,10 +3144,10 @@ static int hci_le_read_supported_states_sync(struct hci_dev *hdev)
/* LE Controller init stage 2 command sequence */
static const struct hci_init_stage le_init2[] = {
/* HCI_OP_LE_READ_BUFFER_SIZE */
HCI_INIT(hci_le_read_buffer_size_sync),
/* HCI_OP_LE_READ_LOCAL_FEATURES */
HCI_INIT(hci_le_read_local_features_sync),
/* HCI_OP_LE_READ_BUFFER_SIZE */
HCI_INIT(hci_le_read_buffer_size_sync),
/* HCI_OP_LE_READ_SUPPORTED_STATES */
HCI_INIT(hci_le_read_supported_states_sync),
{}
@ -3032,6 +3162,10 @@ static int hci_init2_sync(struct hci_dev *hdev)
if (hdev->dev_type == HCI_AMP)
return hci_init_stage_sync(hdev, amp_init2);
err = hci_init_stage_sync(hdev, hci_init2);
if (err)
return err;
if (lmp_bredr_capable(hdev)) {
err = hci_init_stage_sync(hdev, br_init2);
if (err)
@ -3049,7 +3183,7 @@ static int hci_init2_sync(struct hci_dev *hdev)
hci_dev_set_flag(hdev, HCI_LE_ENABLED);
}
return hci_init_stage_sync(hdev, hci_init2);
return 0;
}
static int hci_set_event_mask_sync(struct hci_dev *hdev)
@ -3370,6 +3504,12 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
if (ext_adv_capable(hdev))
events[2] |= 0x02; /* LE Advertising Set Terminated */
if (cis_capable(hdev)) {
events[3] |= 0x01; /* LE CIS Established */
if (cis_peripheral_capable(hdev))
events[3] |= 0x02; /* LE CIS Request */
}
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK,
sizeof(events), events, HCI_CMD_TIMEOUT);
}
@ -3510,6 +3650,24 @@ static int hci_set_le_support_sync(struct hci_dev *hdev)
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
/* LE Set Host Feature */
static int hci_le_set_host_feature_sync(struct hci_dev *hdev)
{
struct hci_cp_le_set_host_feature cp;
if (!iso_capable(hdev))
return 0;
memset(&cp, 0, sizeof(cp));
/* Isochronous Channels (Host Support) */
cp.bit_number = 32;
cp.bit_value = 1;
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HOST_FEATURE,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
/* LE Controller init stage 3 command sequence */
static const struct hci_init_stage le_init3[] = {
/* HCI_OP_LE_SET_EVENT_MASK */
@ -3536,6 +3694,8 @@ static const struct hci_init_stage le_init3[] = {
HCI_INIT(hci_le_read_num_support_adv_sets_sync),
/* HCI_OP_WRITE_LE_HOST_SUPPORTED */
HCI_INIT(hci_set_le_support_sync),
/* HCI_OP_LE_SET_HOST_FEATURE */
HCI_INIT(hci_le_set_host_feature_sync),
{}
};
@ -5372,3 +5532,14 @@ done:
hci_resume_advertising_sync(hdev);
return err;
}
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle)
{
struct hci_cp_le_remove_cig cp;
memset(&cp, 0, sizeof(cp));
cp.cig_id = handle;
return __hci_cmd_sync_status(hdev, HCI_OP_LE_REMOVE_CIG, sizeof(cp),
&cp, HCI_CMD_TIMEOUT);
}

View File

@ -258,21 +258,13 @@ static int hidp_sock_create(struct net *net, struct socket *sock, int protocol,
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto, kern);
sk = bt_sock_alloc(net, sock, &hidp_proto, protocol, GFP_ATOMIC, kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &hidp_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = BT_OPEN;
bt_sock_link(&hidp_sk_list, sk);
return 0;

View File

@ -177,21 +177,6 @@ done:
return err;
}
static void l2cap_sock_init_pid(struct sock *sk)
{
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
/* Only L2CAP_MODE_EXT_FLOWCTL ever need to access the PID in order to
* group the channels being requested.
*/
if (chan->mode != L2CAP_MODE_EXT_FLOWCTL)
return;
spin_lock(&sk->sk_peer_lock);
sk->sk_peer_pid = get_pid(task_tgid(current));
spin_unlock(&sk->sk_peer_lock);
}
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
int alen, int flags)
{
@ -267,8 +252,6 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
chan->mode != L2CAP_MODE_EXT_FLOWCTL)
chan->mode = L2CAP_MODE_LE_FLOWCTL;
l2cap_sock_init_pid(sk);
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
&la.l2_bdaddr, la.l2_bdaddr_type);
if (err)
@ -324,8 +307,6 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
goto done;
}
l2cap_sock_init_pid(sk);
sk->sk_max_ack_backlog = backlog;
sk->sk_ack_backlog = 0;
@ -1849,21 +1830,13 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
struct sock *sk;
struct l2cap_chan *chan;
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto, kern);
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
if (!sk)
return NULL;
sock_init_data(sock, sk);
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = l2cap_sock_destruct;
sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
chan = l2cap_chan_create();
if (!chan) {
sk_free(sk);

File diff suppressed because it is too large Load Diff

View File

@ -217,47 +217,47 @@ int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
struct mgmt_pending_cmd *cmd, *tmp;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
mutex_lock(&hdev->mgmt_pending_lock);
list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
if (hci_sock_get_channel(cmd->sk) != channel)
continue;
if (cmd->opcode == opcode)
if (cmd->opcode == opcode) {
mutex_unlock(&hdev->mgmt_pending_lock);
return cmd;
}
}
mutex_unlock(&hdev->mgmt_pending_lock);
return NULL;
}
struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
u16 opcode,
struct hci_dev *hdev,
const void *data)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->user_data != data)
continue;
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
}
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, bool remove,
void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
void *data)
{
struct mgmt_pending_cmd *cmd, *tmp;
mutex_lock(&hdev->mgmt_pending_lock);
list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
if (opcode > 0 && cmd->opcode != opcode)
continue;
if (remove)
list_del(&cmd->list);
cb(cmd, data);
if (remove)
mgmt_pending_free(cmd);
}
mutex_unlock(&hdev->mgmt_pending_lock);
}
struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
@ -271,7 +271,7 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
return NULL;
cmd->opcode = opcode;
cmd->index = hdev->id;
cmd->hdev = hdev;
cmd->param = kmemdup(data, len, GFP_KERNEL);
if (!cmd->param) {
@ -297,7 +297,9 @@ struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
if (!cmd)
return NULL;
list_add(&cmd->list, &hdev->mgmt_pending);
mutex_lock(&hdev->mgmt_pending_lock);
list_add_tail(&cmd->list, &hdev->mgmt_pending);
mutex_unlock(&hdev->mgmt_pending_lock);
return cmd;
}
@ -311,6 +313,56 @@ void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
{
mutex_lock(&cmd->hdev->mgmt_pending_lock);
list_del(&cmd->list);
mutex_unlock(&cmd->hdev->mgmt_pending_lock);
mgmt_pending_free(cmd);
}
bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
{
struct mgmt_pending_cmd *tmp;
lockdep_assert_held(&hdev->mgmt_pending_lock);
if (!cmd)
return false;
list_for_each_entry(tmp, &hdev->mgmt_pending, list) {
if (cmd == tmp)
return true;
}
return false;
}
bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
{
bool listed;
mutex_lock(&hdev->mgmt_pending_lock);
listed = __mgmt_pending_listed(hdev, cmd);
mutex_unlock(&hdev->mgmt_pending_lock);
return listed;
}
bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
{
bool listed;
if (!cmd)
return false;
mutex_lock(&hdev->mgmt_pending_lock);
listed = __mgmt_pending_listed(hdev, cmd);
if (listed)
list_del(&cmd->list);
mutex_unlock(&hdev->mgmt_pending_lock);
return listed;
}

View File

@ -23,7 +23,7 @@
struct mgmt_pending_cmd {
struct list_head list;
u16 opcode;
int index;
struct hci_dev *hdev;
void *param;
size_t param_len;
struct sock *sk;
@ -44,11 +44,7 @@ int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
struct hci_dev *hdev);
struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
u16 opcode,
struct hci_dev *hdev,
const void *data);
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, bool remove,
void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
void *data);
struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
@ -59,3 +55,6 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
void *data, u16 len);
void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);

View File

@ -80,6 +80,14 @@ struct msft_rp_le_set_advertisement_filter_enable {
__u8 sub_opcode;
} __packed;
#define MSFT_EV_LE_MONITOR_DEVICE 0x02
struct msft_ev_le_monitor_device {
__u8 addr_type;
bdaddr_t bdaddr;
__u8 monitor_handle;
__u8 monitor_state;
} __packed;
struct msft_monitor_advertisement_handle_data {
__u8 msft_handle;
__u16 mgmt_handle;
@ -91,18 +99,11 @@ struct msft_data {
__u8 evt_prefix_len;
__u8 *evt_prefix;
struct list_head handle_map;
__u16 pending_add_handle;
__u16 pending_remove_handle;
__u8 resuming;
__u8 suspending;
__u8 filter_enabled;
};
static int __msft_add_monitor_pattern(struct hci_dev *hdev,
struct adv_monitor *monitor);
static int __msft_remove_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor, u16 handle);
bool msft_monitor_supported(struct hci_dev *hdev)
{
return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR);
@ -156,34 +157,6 @@ failed:
return false;
}
static void reregister_monitor(struct hci_dev *hdev, int handle)
{
struct adv_monitor *monitor;
struct msft_data *msft = hdev->msft_data;
int err;
while (1) {
monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
if (!monitor) {
/* All monitors have been resumed */
msft->resuming = false;
hci_update_passive_scan(hdev);
return;
}
msft->pending_add_handle = (u16)handle;
err = __msft_add_monitor_pattern(hdev, monitor);
/* If success, we return and wait for monitor added callback */
if (!err)
return;
/* Otherwise remove the monitor and keep registering */
hci_free_adv_monitor(hdev, monitor);
handle++;
}
}
/* is_mgmt = true matches the handle exposed to userspace via mgmt.
* is_mgmt = false matches the handle used by the msft controller.
* This function requires the caller holds hdev->lock
@ -204,27 +177,47 @@ static struct msft_monitor_advertisement_handle_data *msft_find_handle_data
return NULL;
}
static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
u8 status, u16 opcode,
struct sk_buff *skb)
/* This function requires the caller holds hdev->lock */
static int msft_monitor_device_del(struct hci_dev *hdev, __u16 mgmt_handle,
bdaddr_t *bdaddr, __u8 addr_type,
bool notify)
{
struct msft_rp_le_monitor_advertisement *rp;
struct adv_monitor *monitor;
struct msft_monitor_advertisement_handle_data *handle_data;
struct msft_data *msft = hdev->msft_data;
struct monitored_device *dev, *tmp;
int count = 0;
hci_dev_lock(hdev);
list_for_each_entry_safe(dev, tmp, &hdev->monitored_devices, list) {
/* mgmt_handle == 0 indicates remove all devices, whereas,
* bdaddr == NULL indicates remove all devices matching the
* mgmt_handle.
*/
if ((!mgmt_handle || dev->handle == mgmt_handle) &&
(!bdaddr || (!bacmp(bdaddr, &dev->bdaddr) &&
addr_type == dev->addr_type))) {
if (notify && dev->notified) {
mgmt_adv_monitor_device_lost(hdev, dev->handle,
&dev->bdaddr,
dev->addr_type);
}
monitor = idr_find(&hdev->adv_monitors_idr, msft->pending_add_handle);
if (!monitor) {
bt_dev_err(hdev, "msft add advmon: monitor %u is not found!",
msft->pending_add_handle);
status = HCI_ERROR_UNSPECIFIED;
goto unlock;
list_del(&dev->list);
kfree(dev);
count++;
}
}
if (status)
goto unlock;
return count;
}
static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
struct adv_monitor *monitor,
struct sk_buff *skb)
{
struct msft_rp_le_monitor_advertisement *rp;
struct msft_monitor_advertisement_handle_data *handle_data;
struct msft_data *msft = hdev->msft_data;
int status = 0;
hci_dev_lock(hdev);
rp = (struct msft_rp_le_monitor_advertisement *)skb->data;
if (skb->len < sizeof(*rp)) {
@ -232,6 +225,10 @@ static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
goto unlock;
}
status = rp->status;
if (status)
goto unlock;
handle_data = kmalloc(sizeof(*handle_data), GFP_KERNEL);
if (!handle_data) {
status = HCI_ERROR_UNSPECIFIED;
@ -246,29 +243,23 @@ static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
monitor->state = ADV_MONITOR_STATE_OFFLOADED;
unlock:
if (status && monitor)
if (status)
hci_free_adv_monitor(hdev, monitor);
hci_dev_unlock(hdev);
if (!msft->resuming)
hci_add_adv_patterns_monitor_complete(hdev, status);
return status;
}
static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
u8 status, u16 opcode,
struct sk_buff *skb)
static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
u16 opcode,
struct adv_monitor *monitor,
struct sk_buff *skb)
{
struct msft_cp_le_cancel_monitor_advertisement *cp;
struct msft_rp_le_cancel_monitor_advertisement *rp;
struct adv_monitor *monitor;
struct msft_monitor_advertisement_handle_data *handle_data;
struct msft_data *msft = hdev->msft_data;
int err;
bool pending;
if (status)
goto done;
int status = 0;
rp = (struct msft_rp_le_cancel_monitor_advertisement *)skb->data;
if (skb->len < sizeof(*rp)) {
@ -276,57 +267,46 @@ static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
goto done;
}
status = rp->status;
if (status)
goto done;
hci_dev_lock(hdev);
cp = hci_sent_cmd_data(hdev, hdev->msft_opcode);
handle_data = msft_find_handle_data(hdev, cp->handle, false);
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
if (handle_data) {
monitor = idr_find(&hdev->adv_monitors_idr,
handle_data->mgmt_handle);
if (monitor && monitor->state == ADV_MONITOR_STATE_OFFLOADED)
if (monitor->state == ADV_MONITOR_STATE_OFFLOADED)
monitor->state = ADV_MONITOR_STATE_REGISTERED;
/* Do not free the monitor if it is being removed due to
* suspend. It will be re-monitored on resume.
*/
if (monitor && !msft->suspending)
if (!msft->suspending) {
hci_free_adv_monitor(hdev, monitor);
/* Clear any monitored devices by this Adv Monitor */
msft_monitor_device_del(hdev, handle_data->mgmt_handle,
NULL, 0, false);
}
list_del(&handle_data->list);
kfree(handle_data);
}
/* If remove all monitors is required, we need to continue the process
* here because the earlier it was paused when waiting for the
* response from controller.
*/
if (msft->pending_remove_handle == 0) {
pending = hci_remove_all_adv_monitor(hdev, &err);
if (pending) {
hci_dev_unlock(hdev);
return;
}
if (err)
status = HCI_ERROR_UNSPECIFIED;
}
hci_dev_unlock(hdev);
done:
if (!msft->suspending)
hci_remove_adv_monitor_complete(hdev, status);
return status;
}
/* This function requires the caller holds hci_req_sync_lock */
static int msft_remove_monitor_sync(struct hci_dev *hdev,
struct adv_monitor *monitor)
{
struct msft_cp_le_cancel_monitor_advertisement cp;
struct msft_monitor_advertisement_handle_data *handle_data;
struct sk_buff *skb;
u8 status;
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
@ -342,13 +322,8 @@ static int msft_remove_monitor_sync(struct hci_dev *hdev,
if (IS_ERR(skb))
return PTR_ERR(skb);
status = skb->data[0];
skb_pull(skb, 1);
msft_le_cancel_monitor_advertisement_cb(hdev, status, hdev->msft_opcode,
skb);
return status;
return msft_le_cancel_monitor_advertisement_cb(hdev, hdev->msft_opcode,
monitor, skb);
}
/* This function requires the caller holds hci_req_sync_lock */
@ -419,7 +394,6 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
ptrdiff_t offset = 0;
u8 pattern_count = 0;
struct sk_buff *skb;
u8 status;
if (!msft_monitor_pattern_valid(monitor))
return -EINVAL;
@ -461,23 +435,19 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
if (IS_ERR(skb))
return PTR_ERR(skb);
status = skb->data[0];
skb_pull(skb, 1);
msft_le_monitor_advertisement_cb(hdev, status, hdev->msft_opcode, skb);
return status;
return msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode,
monitor, skb);
}
/* This function requires the caller holds hci_req_sync_lock */
int msft_resume_sync(struct hci_dev *hdev)
static void reregister_monitor(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
struct adv_monitor *monitor;
struct msft_data *msft = hdev->msft_data;
int handle = 0;
if (!msft || !msft_monitor_supported(hdev))
return 0;
if (!msft)
return;
msft->resuming = true;
@ -491,12 +461,34 @@ int msft_resume_sync(struct hci_dev *hdev)
handle++;
}
/* All monitors have been resumed */
/* All monitors have been reregistered */
msft->resuming = false;
}
/* This function requires the caller holds hci_req_sync_lock */
int msft_resume_sync(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
if (!msft || !msft_monitor_supported(hdev))
return 0;
hci_dev_lock(hdev);
/* Clear already tracked devices on resume. Once the monitors are
* reregistered, devices in range will be found again after resume.
*/
hdev->advmon_pend_notify = false;
msft_monitor_device_del(hdev, 0, NULL, 0, true);
hci_dev_unlock(hdev);
reregister_monitor(hdev);
return 0;
}
/* This function requires the caller holds hci_req_sync_lock */
void msft_do_open(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
@ -529,7 +521,7 @@ void msft_do_open(struct hci_dev *hdev)
/* Monitors get removed on power off, so we need to explicitly
* tell the controller to re-monitor.
*/
reregister_monitor(hdev, 0);
reregister_monitor(hdev);
}
}
@ -557,6 +549,14 @@ void msft_do_close(struct hci_dev *hdev)
list_del(&handle_data->list);
kfree(handle_data);
}
hci_dev_lock(hdev);
/* Clear any devices that are being monitored and notify device lost */
hdev->advmon_pend_notify = false;
msft_monitor_device_del(hdev, 0, NULL, 0, true);
hci_dev_unlock(hdev);
}
void msft_register(struct hci_dev *hdev)
@ -590,10 +590,99 @@ void msft_unregister(struct hci_dev *hdev)
kfree(msft);
}
/* This function requires the caller holds hdev->lock */
static void msft_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr,
__u8 addr_type, __u16 mgmt_handle)
{
struct monitored_device *dev;
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
bt_dev_err(hdev, "MSFT vendor event %u: no memory",
MSFT_EV_LE_MONITOR_DEVICE);
return;
}
bacpy(&dev->bdaddr, bdaddr);
dev->addr_type = addr_type;
dev->handle = mgmt_handle;
dev->notified = false;
INIT_LIST_HEAD(&dev->list);
list_add(&dev->list, &hdev->monitored_devices);
hdev->advmon_pend_notify = true;
}
/* This function requires the caller holds hdev->lock */
static void msft_device_lost(struct hci_dev *hdev, bdaddr_t *bdaddr,
__u8 addr_type, __u16 mgmt_handle)
{
if (!msft_monitor_device_del(hdev, mgmt_handle, bdaddr, addr_type,
true)) {
bt_dev_err(hdev, "MSFT vendor event %u: dev %pMR not in list",
MSFT_EV_LE_MONITOR_DEVICE, bdaddr);
}
}
static void *msft_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
u8 ev, size_t len)
{
void *data;
data = skb_pull_data(skb, len);
if (!data)
bt_dev_err(hdev, "Malformed MSFT vendor event: 0x%02x", ev);
return data;
}
/* This function requires the caller holds hdev->lock */
static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct msft_ev_le_monitor_device *ev;
struct msft_monitor_advertisement_handle_data *handle_data;
u8 addr_type;
ev = msft_skb_pull(hdev, skb, MSFT_EV_LE_MONITOR_DEVICE, sizeof(*ev));
if (!ev)
return;
bt_dev_dbg(hdev,
"MSFT vendor event 0x%02x: handle 0x%04x state %d addr %pMR",
MSFT_EV_LE_MONITOR_DEVICE, ev->monitor_handle,
ev->monitor_state, &ev->bdaddr);
handle_data = msft_find_handle_data(hdev, ev->monitor_handle, false);
switch (ev->addr_type) {
case ADDR_LE_DEV_PUBLIC:
addr_type = BDADDR_LE_PUBLIC;
break;
case ADDR_LE_DEV_RANDOM:
addr_type = BDADDR_LE_RANDOM;
break;
default:
bt_dev_err(hdev,
"MSFT vendor event 0x%02x: unknown addr type 0x%02x",
MSFT_EV_LE_MONITOR_DEVICE, ev->addr_type);
return;
}
if (ev->monitor_state)
msft_device_found(hdev, &ev->bdaddr, addr_type,
handle_data->mgmt_handle);
else
msft_device_lost(hdev, &ev->bdaddr, addr_type,
handle_data->mgmt_handle);
}
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
{
struct msft_data *msft = hdev->msft_data;
u8 event;
u8 *evt_prefix;
u8 *evt;
if (!msft)
return;
@ -602,13 +691,12 @@ void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
* matches, and otherwise just return.
*/
if (msft->evt_prefix_len > 0) {
if (skb->len < msft->evt_prefix_len)
evt_prefix = msft_skb_pull(hdev, skb, 0, msft->evt_prefix_len);
if (!evt_prefix)
return;
if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len))
if (memcmp(evt_prefix, msft->evt_prefix, msft->evt_prefix_len))
return;
skb_pull(skb, msft->evt_prefix_len);
}
/* Every event starts at least with an event code and the rest of
@ -617,10 +705,23 @@ void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
if (skb->len < 1)
return;
event = *skb->data;
skb_pull(skb, 1);
evt = msft_skb_pull(hdev, skb, 0, sizeof(*evt));
if (!evt)
return;
bt_dev_dbg(hdev, "MSFT vendor event %u", event);
hci_dev_lock(hdev);
switch (*evt) {
case MSFT_EV_LE_MONITOR_DEVICE:
msft_monitor_device_evt(hdev, skb);
break;
default:
bt_dev_dbg(hdev, "MSFT vendor event 0x%02x", *evt);
break;
}
hci_dev_unlock(hdev);
}
__u64 msft_get_features(struct hci_dev *hdev)
@ -664,71 +765,7 @@ static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
/* This function requires the caller holds hdev->lock */
static int __msft_add_monitor_pattern(struct hci_dev *hdev,
struct adv_monitor *monitor)
{
struct msft_cp_le_monitor_advertisement *cp;
struct msft_le_monitor_advertisement_pattern_data *pattern_data;
struct msft_le_monitor_advertisement_pattern *pattern;
struct adv_pattern *entry;
struct hci_request req;
struct msft_data *msft = hdev->msft_data;
size_t total_size = sizeof(*cp) + sizeof(*pattern_data);
ptrdiff_t offset = 0;
u8 pattern_count = 0;
int err = 0;
if (!msft_monitor_pattern_valid(monitor))
return -EINVAL;
list_for_each_entry(entry, &monitor->patterns, list) {
pattern_count++;
total_size += sizeof(*pattern) + entry->length;
}
cp = kmalloc(total_size, GFP_KERNEL);
if (!cp)
return -ENOMEM;
cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT;
cp->rssi_high = monitor->rssi.high_threshold;
cp->rssi_low = monitor->rssi.low_threshold;
cp->rssi_low_interval = (u8)monitor->rssi.low_threshold_timeout;
cp->rssi_sampling_period = monitor->rssi.sampling_period;
cp->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN;
pattern_data = (void *)cp->data;
pattern_data->count = pattern_count;
list_for_each_entry(entry, &monitor->patterns, list) {
pattern = (void *)(pattern_data->data + offset);
/* the length also includes data_type and offset */
pattern->length = entry->length + 2;
pattern->data_type = entry->ad_type;
pattern->start_byte = entry->offset;
memcpy(pattern->pattern, entry->value, entry->length);
offset += sizeof(*pattern) + entry->length;
}
hci_req_init(&req, hdev);
hci_req_add(&req, hdev->msft_opcode, total_size, cp);
err = hci_req_run_skb(&req, msft_le_monitor_advertisement_cb);
kfree(cp);
if (!err)
msft->pending_add_handle = monitor->handle;
return err;
}
bool msft_curve_validity(struct hci_dev *hdev)
{
return hdev->msft_curve_validity;
}
/* This function requires the caller holds hdev->lock */
/* This function requires the caller holds hci_req_sync_lock */
int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor)
{
struct msft_data *msft = hdev->msft_data;
@ -739,41 +776,11 @@ int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor)
if (msft->resuming || msft->suspending)
return -EBUSY;
return __msft_add_monitor_pattern(hdev, monitor);
return msft_add_monitor_sync(hdev, monitor);
}
/* This function requires the caller holds hdev->lock */
static int __msft_remove_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor, u16 handle)
{
struct msft_cp_le_cancel_monitor_advertisement cp;
struct msft_monitor_advertisement_handle_data *handle_data;
struct hci_request req;
struct msft_data *msft = hdev->msft_data;
int err = 0;
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
/* If no matched handle, just remove without telling controller */
if (!handle_data)
return -ENOENT;
cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
cp.handle = handle_data->msft_handle;
hci_req_init(&req, hdev);
hci_req_add(&req, hdev->msft_opcode, sizeof(cp), &cp);
err = hci_req_run_skb(&req, msft_le_cancel_monitor_advertisement_cb);
if (!err)
msft->pending_remove_handle = handle;
return err;
}
/* This function requires the caller holds hdev->lock */
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
u16 handle)
/* This function requires the caller holds hci_req_sync_lock */
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
{
struct msft_data *msft = hdev->msft_data;
@ -783,7 +790,7 @@ int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
if (msft->resuming || msft->suspending)
return -EBUSY;
return __msft_remove_monitor(hdev, monitor, handle);
return msft_remove_monitor_sync(hdev, monitor);
}
void msft_req_add_set_filter_enable(struct hci_request *req, bool enable)
@ -812,3 +819,8 @@ int msft_set_filter_enable(struct hci_dev *hdev, bool enable)
return err;
}
bool msft_curve_validity(struct hci_dev *hdev)
{
return hdev->msft_curve_validity;
}

View File

@ -20,8 +20,7 @@ void msft_do_close(struct hci_dev *hdev);
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb);
__u64 msft_get_features(struct hci_dev *hdev);
int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor);
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
u16 handle);
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
void msft_req_add_set_filter_enable(struct hci_request *req, bool enable);
int msft_set_filter_enable(struct hci_dev *hdev, bool enable);
int msft_suspend_sync(struct hci_dev *hdev);
@ -64,8 +63,7 @@ static inline bool msft_curve_validity(struct hci_dev *hdev)
}
static inline int msft_remove_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor,
u16 handle)
struct adv_monitor *monitor)
{
return -EOPNOTSUPP;
}

View File

@ -268,18 +268,16 @@ static struct proto rfcomm_proto = {
.obj_size = sizeof(struct rfcomm_pinfo)
};
static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio, int kern)
static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock,
int proto, gfp_t prio, int kern)
{
struct rfcomm_dlc *d;
struct sock *sk;
sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto, kern);
sk = bt_sock_alloc(net, sock, &rfcomm_proto, proto, prio, kern);
if (!sk)
return NULL;
sock_init_data(sock, sk);
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
d = rfcomm_dlc_alloc(prio);
if (!d) {
sk_free(sk);
@ -298,11 +296,6 @@ static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int
sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
bt_sock_link(&rfcomm_sk_list, sk);
BT_DBG("sk %p", sk);

View File

@ -77,6 +77,16 @@ struct sco_pinfo {
#define SCO_CONN_TIMEOUT (HZ * 40)
#define SCO_DISCONN_TIMEOUT (HZ * 2)
static struct sock *sco_sock_hold(struct sco_conn *conn)
{
if (!conn || !bt_sock_linked(&sco_sk_list, conn->sk))
return NULL;
sock_hold(conn->sk);
return conn->sk;
}
static void sco_sock_timeout(struct work_struct *work)
{
struct sco_conn *conn = container_of(work, struct sco_conn,
@ -84,9 +94,11 @@ static void sco_sock_timeout(struct work_struct *work)
struct sock *sk;
sco_conn_lock(conn);
sk = conn->sk;
if (sk)
sock_hold(sk);
if (!conn->hcon) {
sco_conn_unlock(conn);
return;
}
sk = sco_sock_hold(conn);
sco_conn_unlock(conn);
if (!sk)
@ -188,9 +200,7 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
/* Kill socket */
sco_conn_lock(conn);
sk = conn->sk;
if (sk)
sock_hold(sk);
sk = sco_sock_hold(conn);
sco_conn_unlock(conn);
if (sk) {
@ -302,7 +312,7 @@ static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
struct sock *sk;
sco_conn_lock(conn);
sk = conn->sk;
sk = sco_sock_hold(conn);
sco_conn_unlock(conn);
if (!sk)
@ -311,11 +321,15 @@ static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
BT_DBG("sk %p len %u", sk, skb->len);
if (sk->sk_state != BT_CONNECTED)
goto drop;
goto drop_put;
if (!sock_queue_rcv_skb(sk, skb))
if (!sock_queue_rcv_skb(sk, skb)) {
sock_put(sk);
return;
}
drop_put:
sock_put(sk);
drop:
kfree_skb(skb);
}
@ -480,21 +494,13 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
{
struct sock *sk;
sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto, kern);
sk = bt_sock_alloc(net, sock, &sco_proto, proto, prio, kern);
if (!sk)
return NULL;
sock_init_data(sock, sk);
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = sco_sock_destruct;
sk->sk_sndtimeo = SCO_CONN_TIMEOUT;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
sco_pi(sk)->codec.id = BT_CODEC_CVSD;
sco_pi(sk)->codec.cid = 0xffff;

View File

@ -102,10 +102,11 @@ int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
EXPORT_SYMBOL(can_ioctl);
static void can_sock_destruct(struct sock *sk)
void can_sock_destruct(struct sock *sk)
{
skb_queue_purge(&sk->sk_receive_queue);
}
EXPORT_SYMBOL(can_sock_destruct);
static const struct can_proto *can_get_proto(int protocol)
{

View File

@ -313,6 +313,14 @@ static int raw_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}
static void raw_sock_destruct(struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
free_percpu(ro->uniq);
can_sock_destruct(sk);
}
static int raw_init(struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
@ -337,6 +345,8 @@ static int raw_init(struct sock *sk)
if (unlikely(!ro->uniq))
return -ENOMEM;
sk->sk_destruct = raw_sock_destruct;
/* set notifier */
ro->notifier.notifier_call = raw_notifier;
@ -379,7 +389,6 @@ static int raw_release(struct socket *sock)
ro->ifindex = 0;
ro->bound = 0;
ro->count = 0;
free_percpu(ro->uniq);
sock_orphan(sk);
sock->sk = NULL;

View File

@ -790,51 +790,49 @@ static int decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
ceph_decode_need(p, end, len, bad);
pool_end = *p + len;
ceph_decode_need(p, end, 4 + 4 + 4, bad);
pi->type = ceph_decode_8(p);
pi->size = ceph_decode_8(p);
pi->crush_ruleset = ceph_decode_8(p);
pi->object_hash = ceph_decode_8(p);
pi->pg_num = ceph_decode_32(p);
pi->pgp_num = ceph_decode_32(p);
*p += 4 + 4; /* skip lpg* */
*p += 4; /* skip last_change */
*p += 8 + 4; /* skip snap_seq, snap_epoch */
/* lpg*, last_change, snap_seq, snap_epoch */
ceph_decode_skip_n(p, end, 8 + 4 + 8 + 4, bad);
/* skip snaps */
num = ceph_decode_32(p);
ceph_decode_32_safe(p, end, num, bad);
while (num--) {
*p += 8; /* snapid key */
*p += 1 + 1; /* versions */
len = ceph_decode_32(p);
*p += len;
/* snapid key, pool snap (with versions) */
ceph_decode_skip_n(p, end, 8 + 2, bad);
ceph_decode_skip_string(p, end, bad);
}
/* skip removed_snaps */
num = ceph_decode_32(p);
*p += num * (8 + 8);
/* removed_snaps */
ceph_decode_skip_map(p, end, 64, 64, bad);
ceph_decode_need(p, end, 8 + 8 + 4, bad);
*p += 8; /* skip auid */
pi->flags = ceph_decode_64(p);
*p += 4; /* skip crash_replay_interval */
if (ev >= 7)
pi->min_size = ceph_decode_8(p);
ceph_decode_8_safe(p, end, pi->min_size, bad);
else
pi->min_size = pi->size - pi->size / 2;
if (ev >= 8)
*p += 8 + 8; /* skip quota_max_* */
/* quota_max_* */
ceph_decode_skip_n(p, end, 8 + 8, bad);
if (ev >= 9) {
/* skip tiers */
num = ceph_decode_32(p);
*p += num * 8;
/* tiers */
ceph_decode_skip_set(p, end, 64, bad);
ceph_decode_need(p, end, 8 + 1 + 8 + 8, bad);
*p += 8; /* skip tier_of */
*p += 1; /* skip cache_mode */
pi->read_tier = ceph_decode_64(p);
pi->write_tier = ceph_decode_64(p);
} else {
@ -842,86 +840,76 @@ static int decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
pi->write_tier = -1;
}
if (ev >= 10) {
/* skip properties */
num = ceph_decode_32(p);
while (num--) {
len = ceph_decode_32(p);
*p += len; /* key */
len = ceph_decode_32(p);
*p += len; /* val */
}
}
if (ev >= 10)
/* properties */
ceph_decode_skip_map(p, end, string, string, bad);
if (ev >= 11) {
/* skip hit_set_params */
*p += 1 + 1; /* versions */
len = ceph_decode_32(p);
*p += len;
/* hit_set_params (with versions) */
ceph_decode_skip_n(p, end, 2, bad);
ceph_decode_skip_string(p, end, bad);
*p += 4; /* skip hit_set_period */
*p += 4; /* skip hit_set_count */
/* hit_set_period, hit_set_count */
ceph_decode_skip_n(p, end, 4 + 4, bad);
}
if (ev >= 12)
*p += 4; /* skip stripe_width */
/* stripe_width */
ceph_decode_skip_32(p, end, bad);
if (ev >= 13) {
*p += 8; /* skip target_max_bytes */
*p += 8; /* skip target_max_objects */
*p += 4; /* skip cache_target_dirty_ratio_micro */
*p += 4; /* skip cache_target_full_ratio_micro */
*p += 4; /* skip cache_min_flush_age */
*p += 4; /* skip cache_min_evict_age */
}
if (ev >= 13)
/* target_max_*, cache_target_*, cache_min_* */
ceph_decode_skip_n(p, end, 16 + 8 + 8, bad);
if (ev >= 14) {
/* skip erasure_code_profile */
len = ceph_decode_32(p);
*p += len;
}
if (ev >= 14)
/* erasure_code_profile */
ceph_decode_skip_string(p, end, bad);
/*
* last_force_op_resend_preluminous, will be overridden if the
* map was encoded with RESEND_ON_SPLIT
*/
if (ev >= 15)
pi->last_force_request_resend = ceph_decode_32(p);
ceph_decode_32_safe(p, end, pi->last_force_request_resend, bad);
else
pi->last_force_request_resend = 0;
if (ev >= 16)
*p += 4; /* skip min_read_recency_for_promote */
/* min_read_recency_for_promote */
ceph_decode_skip_32(p, end, bad);
if (ev >= 17)
*p += 8; /* skip expected_num_objects */
/* expected_num_objects */
ceph_decode_skip_64(p, end, bad);
if (ev >= 19)
*p += 4; /* skip cache_target_dirty_high_ratio_micro */
/* cache_target_dirty_high_ratio_micro */
ceph_decode_skip_32(p, end, bad);
if (ev >= 20)
*p += 4; /* skip min_write_recency_for_promote */
/* min_write_recency_for_promote */
ceph_decode_skip_32(p, end, bad);
if (ev >= 21)
*p += 1; /* skip use_gmt_hitset */
/* use_gmt_hitset */
ceph_decode_skip_8(p, end, bad);
if (ev >= 22)
*p += 1; /* skip fast_read */
/* fast_read */
ceph_decode_skip_8(p, end, bad);
if (ev >= 23) {
*p += 4; /* skip hit_set_grade_decay_rate */
*p += 4; /* skip hit_set_search_last_n */
}
if (ev >= 23)
/* hit_set_grade_decay_rate, hit_set_search_last_n */
ceph_decode_skip_n(p, end, 4 + 4, bad);
if (ev >= 24) {
/* skip opts */
*p += 1 + 1; /* versions */
len = ceph_decode_32(p);
*p += len;
/* opts (with versions) */
ceph_decode_skip_n(p, end, 2, bad);
ceph_decode_skip_string(p, end, bad);
}
if (ev >= 25)
pi->last_force_request_resend = ceph_decode_32(p);
ceph_decode_32_safe(p, end, pi->last_force_request_resend, bad);
/* ignore the rest */

View File

@ -25,8 +25,7 @@ eui64_mt6(const struct sk_buff *skb, struct xt_action_param *par)
unsigned char eui64[8];
if (!(skb_mac_header(skb) >= skb->head &&
skb_mac_header(skb) + ETH_HLEN <= skb->data) &&
par->fragoff != 0) {
skb_mac_header(skb) + ETH_HLEN <= skb->data)) {
par->hotdrop = true;
return false;
}

View File

@ -925,6 +925,8 @@ int DecodeQ931(unsigned char *buf, size_t sz, Q931 *q931)
break;
p++;
len--;
if (len <= 0)
break;
return DecodeH323_UserInformation(buf, p, len,
&q931->UUIE);
}

View File

@ -530,7 +530,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
*/
synchronize_rcu();
nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
nf_ct_expect_iterate_destroy(expect_iter_me, me);
nf_ct_iterate_destroy(unhelp, me);
/* Maybe someone has gotten the helper already when unhelp above.

View File

@ -64,7 +64,7 @@ tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par)
return (mssval >= info->mss_min &&
mssval <= info->mss_max) ^ info->invert;
}
if (op[i] < 2)
if (op[i] < 2 || i == optlen - 1)
i++;
else
i += op[i+1] ? : 1;

View File

@ -609,8 +609,12 @@ again:
protocol = skb->protocol;
orig_vlan_tag_present = true;
} else {
struct vlan_hdr *vlan = (struct vlan_hdr *)skb->data;
struct vlan_hdr *vlan;
if (!pskb_may_pull(skb, VLAN_HLEN))
goto drop;
vlan = (struct vlan_hdr *)skb->data;
protocol = vlan->h_vlan_encapsulated_proto;
skb_pull(skb, VLAN_HLEN);
skb_reset_network_header(skb);

View File

@ -1309,6 +1309,12 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla,
return -EINVAL;
}
if (bind && !(flags & TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT)) {
NL_SET_ERR_MSG_MOD(extack,
"Attaching ct to a non ingress/clsact qdisc is unsupported");
return -EOPNOTSUPP;
}
err = nla_parse_nested(tb, TCA_CT_MAX, nla, ct_policy, extack);
if (err < 0)
return err;

View File

@ -2128,6 +2128,11 @@ static bool is_qdisc_ingress(__u32 classid)
return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS));
}
static bool is_ingress_or_clsact(struct tcf_block *block, struct Qdisc *q)
{
return tcf_block_shared(block) || (q && !!(q->flags & TCQ_F_INGRESS));
}
static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
struct netlink_ext_ack *extack)
{
@ -2321,6 +2326,8 @@ replay:
flags |= TCA_ACT_FLAGS_NO_RTNL;
if (is_qdisc_ingress(parent))
flags |= TCA_ACT_FLAGS_AT_INGRESS;
if (is_ingress_or_clsact(block, q))
flags |= TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT;
err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
flags, extack);
if (err == 0) {

View File

@ -543,6 +543,15 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
return 0;
}
/*
* ima_reset_appraise_flags - reset ima_iint_cache flags
*
* @digsig: whether to clear/set IMA_DIGSIG flag, tristate values
* 0: clear IMA_DIGSIG
* 1: set IMA_DIGSIG
* -1: don't change IMA_DIGSIG
*
*/
static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
struct integrity_iint_cache *iint;
@ -555,9 +564,9 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
return;
iint->measured_pcrs = 0;
set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
if (digsig)
if (digsig == 1)
set_bit(IMA_DIGSIG, &iint->atomic_flags);
else
else if (digsig == 0)
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
}
@ -574,6 +583,8 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
} else {
digsig = -1;
}
if (result == 1 || evm_revalidate_status(xattr_name)) {
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
@ -585,11 +596,13 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
int result;
int result, digsig = -1;
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1 || evm_revalidate_status(xattr_name)) {
ima_reset_appraise_flags(d_backing_inode(dentry), 0);
if (!strcmp(xattr_name, XATTR_NAME_IMA))
digsig = 0;
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
if (result == 1)
result = 0;
}

View File

@ -83,10 +83,11 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
event.motu_register_dsp_change.count =
(consumed - sizeof(event.motu_register_dsp_change)) / 4;
if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
if (copy_to_user(buf, &event,
min_t(long, count, sizeof(event.motu_register_dsp_change))))
return -EFAULT;
count = consumed;
count = min_t(long, count, consumed);
} else {
spin_unlock_irq(&motu->lock);

View File

@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2023. Huawei Technologies Co., Ltd */
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <test_progs.h>
#include <bpf/btf.h>
#include "access_map_in_map.skel.h"
struct thread_ctx {
pthread_barrier_t barrier;
int outer_map_fd;
int start, abort;
int loop, err;
};
static int wait_for_start_or_abort(struct thread_ctx *ctx)
{
while (!ctx->start && !ctx->abort)
usleep(1);
return ctx->abort ? -1 : 0;
}
static void *update_map_fn(void *data)
{
struct thread_ctx *ctx = data;
int loop = ctx->loop, err = 0;
if (wait_for_start_or_abort(ctx) < 0)
return NULL;
pthread_barrier_wait(&ctx->barrier);
while (loop-- > 0) {
int fd, zero = 0;
fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 4, 1, 0);
if (fd < 0) {
err |= 1;
pthread_barrier_wait(&ctx->barrier);
continue;
}
/* Remove the old inner map */
if (bpf_map_update_elem(ctx->outer_map_fd, &zero, &fd, 0) < 0)
err |= 2;
close(fd);
pthread_barrier_wait(&ctx->barrier);
}
ctx->err = err;
return NULL;
}
static void *access_map_fn(void *data)
{
struct thread_ctx *ctx = data;
int loop = ctx->loop;
if (wait_for_start_or_abort(ctx) < 0)
return NULL;
pthread_barrier_wait(&ctx->barrier);
while (loop-- > 0) {
/* Access the old inner map */
syscall(SYS_getpgid);
pthread_barrier_wait(&ctx->barrier);
}
return NULL;
}
static void test_map_in_map_access(const char *prog_name, const char *map_name)
{
struct access_map_in_map *skel;
struct bpf_map *outer_map;
struct bpf_program *prog;
struct thread_ctx ctx;
pthread_t tid[2];
int err;
skel = access_map_in_map__open();
if (!ASSERT_OK_PTR(skel, "access_map_in_map open"))
return;
bpf_object__for_each_program(prog, skel->obj)
bpf_program__set_autoload(prog, false);
prog = bpf_object__find_program_by_name(skel->obj, prog_name);
if (!ASSERT_OK_PTR(prog, "find program"))
goto out;
bpf_program__set_autoload(prog, true);
outer_map = bpf_object__find_map_by_name(skel->obj, map_name);
if (!ASSERT_OK_PTR(outer_map, "find map"))
goto out;
err = access_map_in_map__load(skel);
if (!ASSERT_OK(err, "access_map_in_map load"))
goto out;
err = access_map_in_map__attach(skel);
if (!ASSERT_OK(err, "access_map_in_map attach"))
goto out;
skel->bss->tgid = getpid();
memset(&ctx, 0, sizeof(ctx));
pthread_barrier_init(&ctx.barrier, NULL, 2);
ctx.outer_map_fd = bpf_map__fd(outer_map);
ctx.loop = 4;
err = pthread_create(&tid[0], NULL, update_map_fn, &ctx);
if (!ASSERT_OK(err, "close_thread"))
goto out;
err = pthread_create(&tid[1], NULL, access_map_fn, &ctx);
if (!ASSERT_OK(err, "read_thread")) {
ctx.abort = 1;
pthread_join(tid[0], NULL);
goto out;
}
ctx.start = 1;
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
ASSERT_OK(ctx.err, "err");
out:
access_map_in_map__destroy(skel);
}
void test_map_in_map(void)
{
if (test__start_subtest("acc_map_in_array"))
test_map_in_map_access("access_map_in_array", "outer_array_map");
if (test__start_subtest("sleepable_acc_map_in_array"))
test_map_in_map_access("sleepable_access_map_in_array", "outer_array_map");
if (test__start_subtest("acc_map_in_htab"))
test_map_in_map_access("access_map_in_htab", "outer_htab_map");
if (test__start_subtest("sleepable_acc_map_in_htab"))
test_map_in_map_access("sleepable_access_map_in_htab", "outer_htab_map");
}

View File

@ -12,7 +12,7 @@ struct args {
int btf_fd;
};
void test_syscall(void)
static void test_syscall_load_prog(void)
{
static char verifier_log[8192];
struct args ctx = {
@ -32,7 +32,7 @@ void test_syscall(void)
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
tattr.prog_fd = bpf_program__fd(skel->progs.bpf_prog);
tattr.prog_fd = bpf_program__fd(skel->progs.load_prog);
err = bpf_prog_test_run_xattr(&tattr);
ASSERT_EQ(err, 0, "err");
ASSERT_EQ(tattr.retval, 1, "retval");
@ -53,3 +53,29 @@ cleanup:
if (ctx.btf_fd > 0)
close(ctx.btf_fd);
}
static void test_syscall_update_outer_map(void)
{
struct bpf_prog_test_run_attr tattr = {};
struct syscall *skel;
int err;
skel = syscall__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
tattr.prog_fd = bpf_program__fd(skel->progs.update_outer_map);
err = bpf_prog_test_run_xattr(&tattr);
ASSERT_EQ(err, 0, "err");
ASSERT_EQ(tattr.retval, 1, "retval");
cleanup:
syscall__destroy(skel);
}
void test_syscall(void)
{
if (test__start_subtest("load_prog"))
test_syscall_load_prog();
if (test__start_subtest("update_outer_map"))
test_syscall_update_outer_map();
}

View File

@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2023. Huawei Technologies Co., Ltd */
#include <linux/bpf.h>
#include <time.h>
#include <bpf/bpf_helpers.h>
struct inner_map_type {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(key_size, 4);
__uint(value_size, 4);
__uint(max_entries, 1);
} inner_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__type(key, int);
__type(value, int);
__uint(max_entries, 1);
__array(values, struct inner_map_type);
} outer_array_map SEC(".maps") = {
.values = {
[0] = &inner_map,
},
};
struct {
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
__type(key, int);
__type(value, int);
__uint(max_entries, 1);
__array(values, struct inner_map_type);
} outer_htab_map SEC(".maps") = {
.values = {
[0] = &inner_map,
},
};
char _license[] SEC("license") = "GPL";
int tgid = 0;
static int acc_map_in_map(void *outer_map)
{
int i, key, value = 0xdeadbeef;
void *inner_map;
if ((bpf_get_current_pid_tgid() >> 32) != tgid)
return 0;
/* Find nonexistent inner map */
key = 1;
inner_map = bpf_map_lookup_elem(outer_map, &key);
if (inner_map)
return 0;
/* Find the old inner map */
key = 0;
inner_map = bpf_map_lookup_elem(outer_map, &key);
if (!inner_map)
return 0;
/* Wait for the old inner map to be replaced */
for (i = 0; i < 2048; i++)
bpf_map_update_elem(inner_map, &key, &value, 0);
return 0;
}
SEC("kprobe/__x64_sys_getpgid")
int access_map_in_array(void *ctx)
{
return acc_map_in_map(&outer_array_map);
}
SEC("fentry.s/__x64_sys_getpgid")
int sleepable_access_map_in_array(void *ctx)
{
return acc_map_in_map(&outer_array_map);
}
SEC("kprobe/__x64_sys_getpgid")
int access_map_in_htab(void *ctx)
{
return acc_map_in_map(&outer_htab_map);
}
SEC("fentry.s/__x64_sys_getpgid")
int sleepable_access_map_in_htab(void *ctx)
{
return acc_map_in_map(&outer_htab_map);
}

View File

@ -6,9 +6,15 @@
#include <bpf/bpf_tracing.h>
#include <../../../tools/include/linux/filter.h>
#include <linux/btf.h>
#include <string.h>
#include <errno.h>
char _license[] SEC("license") = "GPL";
struct bpf_map {
int id;
} __attribute__((preserve_access_index));
struct args {
__u64 log_buf;
__u32 log_size;
@ -27,6 +33,37 @@ struct args {
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
BTF_INT_ENC(encoding, bits_offset, bits)
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, int);
__type(value, union bpf_attr);
__uint(max_entries, 1);
} bpf_attr_array SEC(".maps");
struct inner_map_type {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(key_size, 4);
__uint(value_size, 4);
__uint(max_entries, 1);
} inner_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__type(key, int);
__type(value, int);
__uint(max_entries, 1);
__array(values, struct inner_map_type);
} outer_array_map SEC(".maps") = {
.values = {
[0] = &inner_map,
},
};
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
}
static int btf_load(void)
{
struct btf_blob {
@ -58,7 +95,7 @@ static int btf_load(void)
}
SEC("syscall")
int bpf_prog(struct args *ctx)
int load_prog(struct args *ctx)
{
static char license[] = "GPL";
static struct bpf_insn insns[] = {
@ -94,8 +131,8 @@ int bpf_prog(struct args *ctx)
map_create_attr.max_entries = ctx->max_entries;
map_create_attr.btf_fd = ret;
prog_load_attr.license = (long) license;
prog_load_attr.insns = (long) insns;
prog_load_attr.license = ptr_to_u64(license);
prog_load_attr.insns = ptr_to_u64(insns);
prog_load_attr.log_buf = ctx->log_buf;
prog_load_attr.log_size = ctx->log_size;
prog_load_attr.log_level = 1;
@ -107,8 +144,8 @@ int bpf_prog(struct args *ctx)
insns[3].imm = ret;
map_update_attr.map_fd = ret;
map_update_attr.key = (long) &key;
map_update_attr.value = (long) &value;
map_update_attr.key = ptr_to_u64(&key);
map_update_attr.value = ptr_to_u64(&value);
ret = bpf_sys_bpf(BPF_MAP_UPDATE_ELEM, &map_update_attr, sizeof(map_update_attr));
if (ret < 0)
return ret;
@ -119,3 +156,52 @@ int bpf_prog(struct args *ctx)
ctx->prog_fd = ret;
return 1;
}
SEC("syscall")
int update_outer_map(void *ctx)
{
int zero = 0, ret = 0, outer_fd = -1, inner_fd = -1, err;
const int attr_sz = sizeof(union bpf_attr);
union bpf_attr *attr;
attr = bpf_map_lookup_elem((struct bpf_map *)&bpf_attr_array, &zero);
if (!attr)
goto out;
memset(attr, 0, attr_sz);
attr->map_id = ((struct bpf_map *)&outer_array_map)->id;
outer_fd = bpf_sys_bpf(BPF_MAP_GET_FD_BY_ID, attr, attr_sz);
if (outer_fd < 0)
goto out;
memset(attr, 0, attr_sz);
attr->map_type = BPF_MAP_TYPE_ARRAY;
attr->key_size = 4;
attr->value_size = 4;
attr->max_entries = 1;
inner_fd = bpf_sys_bpf(BPF_MAP_CREATE, attr, attr_sz);
if (inner_fd < 0)
goto out;
memset(attr, 0, attr_sz);
attr->map_fd = outer_fd;
attr->key = ptr_to_u64(&zero);
attr->value = ptr_to_u64(&inner_fd);
err = bpf_sys_bpf(BPF_MAP_UPDATE_ELEM, attr, attr_sz);
if (err)
goto out;
memset(attr, 0, attr_sz);
attr->map_fd = outer_fd;
attr->key = ptr_to_u64(&zero);
err = bpf_sys_bpf(BPF_MAP_DELETE_ELEM, attr, attr_sz);
if (err)
goto out;
ret = 1;
out:
if (inner_fd >= 0)
bpf_sys_close(inner_fd);
if (outer_fd >= 0)
bpf_sys_close(outer_fd);
return ret;
}