Import of kernel-4.18.0-553.126.1.el8_10
This commit is contained in:
parent
b6e320a30c
commit
b7505e241b
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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))));
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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, "e))) {
|
||||
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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 */
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
144
tools/testing/selftests/bpf/prog_tests/map_in_map.c
Normal file
144
tools/testing/selftests/bpf/prog_tests/map_in_map.c
Normal 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");
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
91
tools/testing/selftests/bpf/progs/access_map_in_map.c
Normal file
91
tools/testing/selftests/bpf/progs/access_map_in_map.c
Normal 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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user