178 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2019 Samsung Electronics Co., Ltd.4 */5 6#include <linux/list.h>7#include <linux/mm.h>8#include <linux/slab.h>9#include <linux/workqueue.h>10 11#include "server.h"12#include "connection.h"13#include "ksmbd_work.h"14#include "mgmt/ksmbd_ida.h"15 16static struct kmem_cache *work_cache;17static struct workqueue_struct *ksmbd_wq;18 19struct ksmbd_work *ksmbd_alloc_work_struct(void)20{21 struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL);22 23 if (work) {24 work->compound_fid = KSMBD_NO_FID;25 work->compound_pfid = KSMBD_NO_FID;26 INIT_LIST_HEAD(&work->request_entry);27 INIT_LIST_HEAD(&work->async_request_entry);28 INIT_LIST_HEAD(&work->fp_entry);29 INIT_LIST_HEAD(&work->interim_entry);30 INIT_LIST_HEAD(&work->aux_read_list);31 work->iov_alloc_cnt = 4;32 work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),33 GFP_KERNEL);34 if (!work->iov) {35 kmem_cache_free(work_cache, work);36 work = NULL;37 }38 }39 return work;40}41 42void ksmbd_free_work_struct(struct ksmbd_work *work)43{44 struct aux_read *ar, *tmp;45 46 WARN_ON(work->saved_cred != NULL);47 48 kvfree(work->response_buf);49 50 list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) {51 kvfree(ar->buf);52 list_del(&ar->entry);53 kfree(ar);54 }55 56 kfree(work->tr_buf);57 kvfree(work->request_buf);58 kfree(work->iov);59 if (!list_empty(&work->interim_entry))60 list_del(&work->interim_entry);61 62 if (work->async_id)63 ksmbd_release_id(&work->conn->async_ida, work->async_id);64 kmem_cache_free(work_cache, work);65}66 67void ksmbd_work_pool_destroy(void)68{69 kmem_cache_destroy(work_cache);70}71 72int ksmbd_work_pool_init(void)73{74 work_cache = kmem_cache_create("ksmbd_work_cache",75 sizeof(struct ksmbd_work), 0,76 SLAB_HWCACHE_ALIGN, NULL);77 if (!work_cache)78 return -ENOMEM;79 return 0;80}81 82int ksmbd_workqueue_init(void)83{84 ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0);85 if (!ksmbd_wq)86 return -ENOMEM;87 return 0;88}89 90void ksmbd_workqueue_destroy(void)91{92 destroy_workqueue(ksmbd_wq);93 ksmbd_wq = NULL;94}95 96bool ksmbd_queue_work(struct ksmbd_work *work)97{98 return queue_work(ksmbd_wq, &work->work);99}100 101static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib,102 unsigned int ib_len)103{104 work->iov[++work->iov_idx].iov_base = ib;105 work->iov[work->iov_idx].iov_len = ib_len;106 work->iov_cnt++;107}108 109static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,110 void *aux_buf, unsigned int aux_size)111{112 struct aux_read *ar = NULL;113 int need_iov_cnt = 1;114 115 if (aux_size) {116 need_iov_cnt++;117 ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);118 if (!ar)119 return -ENOMEM;120 }121 122 if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) {123 struct kvec *new;124 125 work->iov_alloc_cnt += 4;126 new = krealloc(work->iov,127 sizeof(struct kvec) * work->iov_alloc_cnt,128 GFP_KERNEL | __GFP_ZERO);129 if (!new) {130 kfree(ar);131 work->iov_alloc_cnt -= 4;132 return -ENOMEM;133 }134 work->iov = new;135 }136 137 /* Plus rfc_length size on first iov */138 if (!work->iov_idx) {139 work->iov[work->iov_idx].iov_base = work->response_buf;140 *(__be32 *)work->iov[0].iov_base = 0;141 work->iov[work->iov_idx].iov_len = 4;142 work->iov_cnt++;143 }144 145 __ksmbd_iov_pin(work, ib, len);146 inc_rfc1001_len(work->iov[0].iov_base, len);147 148 if (aux_size) {149 __ksmbd_iov_pin(work, aux_buf, aux_size);150 inc_rfc1001_len(work->iov[0].iov_base, aux_size);151 152 ar->buf = aux_buf;153 list_add(&ar->entry, &work->aux_read_list);154 }155 156 return 0;157}158 159int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len)160{161 return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0);162}163 164int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,165 void *aux_buf, unsigned int aux_size)166{167 return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);168}169 170int allocate_interim_rsp_buf(struct ksmbd_work *work)171{172 work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL);173 if (!work->response_buf)174 return -ENOMEM;175 work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;176 return 0;177}178