mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
nvme-tcp: fix usage of page_frag_cache
nvme uses page_frag_cache to preallocate PDU for each preallocated request
of block device. Block devices are created in parallel threads,
consequently page_frag_cache is used in not thread-safe manner.
That leads to incorrect refcounting of backstore pages and premature free.
That can be catched by !sendpage_ok inside network stack:
WARNING: CPU: 7 PID: 467 at ../net/core/skbuff.c:6931 skb_splice_from_iter+0xfa/0x310.
tcp_sendmsg_locked+0x782/0xce0
tcp_sendmsg+0x27/0x40
sock_sendmsg+0x8b/0xa0
nvme_tcp_try_send_cmd_pdu+0x149/0x2a0
Then random panic may occur.
Fix that by serializing the usage of page_frag_cache.
Fixes: 4e893ca811 ("nvme_core: scan namespaces asynchronously")
Signed-off-by: Dmitry Bogdanov <d.bogdanov@yadro.com>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
Signed-off-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
parent
1161be71d1
commit
36ac05f7cf
|
|
@ -108,6 +108,7 @@ struct nvme_tcp_queue {
|
|||
|
||||
struct mutex queue_lock;
|
||||
struct mutex send_mutex;
|
||||
struct mutex pf_cache_lock;
|
||||
struct llist_head req_list;
|
||||
struct list_head send_list;
|
||||
|
||||
|
|
@ -552,9 +553,11 @@ static int nvme_tcp_init_request(struct blk_mq_tag_set *set,
|
|||
struct nvme_tcp_queue *queue = &ctrl->queues[queue_idx];
|
||||
u8 hdgst = nvme_tcp_hdgst_len(queue);
|
||||
|
||||
mutex_lock(&queue->pf_cache_lock);
|
||||
req->pdu = page_frag_alloc(&queue->pf_cache,
|
||||
sizeof(struct nvme_tcp_cmd_pdu) + hdgst,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
mutex_unlock(&queue->pf_cache_lock);
|
||||
if (!req->pdu)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1419,9 +1422,11 @@ static int nvme_tcp_alloc_async_req(struct nvme_tcp_ctrl *ctrl)
|
|||
struct nvme_tcp_request *async = &ctrl->async_req;
|
||||
u8 hdgst = nvme_tcp_hdgst_len(queue);
|
||||
|
||||
mutex_lock(&queue->pf_cache_lock);
|
||||
async->pdu = page_frag_alloc(&queue->pf_cache,
|
||||
sizeof(struct nvme_tcp_cmd_pdu) + hdgst,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
mutex_unlock(&queue->pf_cache_lock);
|
||||
if (!async->pdu)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1463,6 +1468,7 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid)
|
|||
kfree(queue->pdu);
|
||||
mutex_destroy(&queue->send_mutex);
|
||||
mutex_destroy(&queue->queue_lock);
|
||||
mutex_destroy(&queue->pf_cache_lock);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
lockdep_unregister_key(&queue->nvme_tcp_sk_key);
|
||||
|
|
@ -1790,6 +1796,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
|
|||
INIT_LIST_HEAD(&queue->send_list);
|
||||
mutex_init(&queue->send_mutex);
|
||||
INIT_WORK(&queue->io_work, nvme_tcp_io_work);
|
||||
mutex_init(&queue->pf_cache_lock);
|
||||
|
||||
if (qid > 0)
|
||||
queue->cmnd_capsule_len = nctrl->ioccsz * 16;
|
||||
|
|
@ -1930,6 +1937,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
|
|||
err_destroy_mutex:
|
||||
mutex_destroy(&queue->send_mutex);
|
||||
mutex_destroy(&queue->queue_lock);
|
||||
mutex_destroy(&queue->pf_cache_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user