18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IP Payload Compression Protocol (IPComp) - RFC3173. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2003 James Morris <jmorris@intercode.com.au> 68c2ecf20Sopenharmony_ci * Copyright (c) 2003-2008 Herbert Xu <herbert@gondor.apana.org.au> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Todo: 98c2ecf20Sopenharmony_ci * - Tunable compression parameters. 108c2ecf20Sopenharmony_ci * - Compression stats. 118c2ecf20Sopenharmony_ci * - Adaptive compression. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/crypto.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/percpu.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/smp.h> 228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 238c2ecf20Sopenharmony_ci#include <net/ip.h> 248c2ecf20Sopenharmony_ci#include <net/ipcomp.h> 258c2ecf20Sopenharmony_ci#include <net/xfrm.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct ipcomp_tfms { 288c2ecf20Sopenharmony_ci struct list_head list; 298c2ecf20Sopenharmony_ci struct crypto_comp * __percpu *tfms; 308c2ecf20Sopenharmony_ci int users; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipcomp_resource_mutex); 348c2ecf20Sopenharmony_cistatic void * __percpu *ipcomp_scratches; 358c2ecf20Sopenharmony_cistatic int ipcomp_scratch_users; 368c2ecf20Sopenharmony_cistatic LIST_HEAD(ipcomp_tfms_list); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct ipcomp_data *ipcd = x->data; 418c2ecf20Sopenharmony_ci const int plen = skb->len; 428c2ecf20Sopenharmony_ci int dlen = IPCOMP_SCRATCH_SIZE; 438c2ecf20Sopenharmony_ci const u8 *start = skb->data; 448c2ecf20Sopenharmony_ci const int cpu = get_cpu(); 458c2ecf20Sopenharmony_ci u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); 468c2ecf20Sopenharmony_ci struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); 478c2ecf20Sopenharmony_ci int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen); 488c2ecf20Sopenharmony_ci int len; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (err) 518c2ecf20Sopenharmony_ci goto out; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (dlen < (plen + sizeof(struct ip_comp_hdr))) { 548c2ecf20Sopenharmony_ci err = -EINVAL; 558c2ecf20Sopenharmony_ci goto out; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci len = dlen - plen; 598c2ecf20Sopenharmony_ci if (len > skb_tailroom(skb)) 608c2ecf20Sopenharmony_ci len = skb_tailroom(skb); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci __skb_put(skb, len); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci len += plen; 658c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, scratch, len); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci while ((scratch += len, dlen -= len) > 0) { 688c2ecf20Sopenharmony_ci skb_frag_t *frag; 698c2ecf20Sopenharmony_ci struct page *page; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci err = -EMSGSIZE; 728c2ecf20Sopenharmony_ci if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) 738c2ecf20Sopenharmony_ci goto out; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags; 768c2ecf20Sopenharmony_ci page = alloc_page(GFP_ATOMIC); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci err = -ENOMEM; 798c2ecf20Sopenharmony_ci if (!page) 808c2ecf20Sopenharmony_ci goto out; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci __skb_frag_set_page(frag, page); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci len = PAGE_SIZE; 858c2ecf20Sopenharmony_ci if (dlen < len) 868c2ecf20Sopenharmony_ci len = dlen; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci skb_frag_off_set(frag, 0); 898c2ecf20Sopenharmony_ci skb_frag_size_set(frag, len); 908c2ecf20Sopenharmony_ci memcpy(skb_frag_address(frag), scratch, len); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci skb->truesize += len; 938c2ecf20Sopenharmony_ci skb->data_len += len; 948c2ecf20Sopenharmony_ci skb->len += len; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci skb_shinfo(skb)->nr_frags++; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci err = 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciout: 1028c2ecf20Sopenharmony_ci put_cpu(); 1038c2ecf20Sopenharmony_ci return err; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciint ipcomp_input(struct xfrm_state *x, struct sk_buff *skb) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci int nexthdr; 1098c2ecf20Sopenharmony_ci int err = -ENOMEM; 1108c2ecf20Sopenharmony_ci struct ip_comp_hdr *ipch; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (skb_linearize_cow(skb)) 1138c2ecf20Sopenharmony_ci goto out; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Remove ipcomp header and decompress original payload */ 1188c2ecf20Sopenharmony_ci ipch = (void *)skb->data; 1198c2ecf20Sopenharmony_ci nexthdr = ipch->nexthdr; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci skb->transport_header = skb->network_header + sizeof(*ipch); 1228c2ecf20Sopenharmony_ci __skb_pull(skb, sizeof(*ipch)); 1238c2ecf20Sopenharmony_ci err = ipcomp_decompress(x, skb); 1248c2ecf20Sopenharmony_ci if (err) 1258c2ecf20Sopenharmony_ci goto out; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci err = nexthdr; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ciout: 1308c2ecf20Sopenharmony_ci return err; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipcomp_input); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct ipcomp_data *ipcd = x->data; 1378c2ecf20Sopenharmony_ci const int plen = skb->len; 1388c2ecf20Sopenharmony_ci int dlen = IPCOMP_SCRATCH_SIZE; 1398c2ecf20Sopenharmony_ci u8 *start = skb->data; 1408c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 1418c2ecf20Sopenharmony_ci u8 *scratch; 1428c2ecf20Sopenharmony_ci int err; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci local_bh_disable(); 1458c2ecf20Sopenharmony_ci scratch = *this_cpu_ptr(ipcomp_scratches); 1468c2ecf20Sopenharmony_ci tfm = *this_cpu_ptr(ipcd->tfms); 1478c2ecf20Sopenharmony_ci err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); 1488c2ecf20Sopenharmony_ci if (err) 1498c2ecf20Sopenharmony_ci goto out; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) { 1528c2ecf20Sopenharmony_ci err = -EMSGSIZE; 1538c2ecf20Sopenharmony_ci goto out; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen); 1578c2ecf20Sopenharmony_ci local_bh_enable(); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr)); 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciout: 1638c2ecf20Sopenharmony_ci local_bh_enable(); 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciint ipcomp_output(struct xfrm_state *x, struct sk_buff *skb) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int err; 1708c2ecf20Sopenharmony_ci struct ip_comp_hdr *ipch; 1718c2ecf20Sopenharmony_ci struct ipcomp_data *ipcd = x->data; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (skb->len < ipcd->threshold) { 1748c2ecf20Sopenharmony_ci /* Don't bother compressing */ 1758c2ecf20Sopenharmony_ci goto out_ok; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (skb_linearize_cow(skb)) 1798c2ecf20Sopenharmony_ci goto out_ok; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci err = ipcomp_compress(x, skb); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (err) { 1848c2ecf20Sopenharmony_ci goto out_ok; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Install ipcomp header, convert into ipcomp datagram. */ 1888c2ecf20Sopenharmony_ci ipch = ip_comp_hdr(skb); 1898c2ecf20Sopenharmony_ci ipch->nexthdr = *skb_mac_header(skb); 1908c2ecf20Sopenharmony_ci ipch->flags = 0; 1918c2ecf20Sopenharmony_ci ipch->cpi = htons((u16 )ntohl(x->id.spi)); 1928c2ecf20Sopenharmony_ci *skb_mac_header(skb) = IPPROTO_COMP; 1938c2ecf20Sopenharmony_ciout_ok: 1948c2ecf20Sopenharmony_ci skb_push(skb, -skb_network_offset(skb)); 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipcomp_output); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void ipcomp_free_scratches(void) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci int i; 2028c2ecf20Sopenharmony_ci void * __percpu *scratches; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (--ipcomp_scratch_users) 2058c2ecf20Sopenharmony_ci return; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci scratches = ipcomp_scratches; 2088c2ecf20Sopenharmony_ci if (!scratches) 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for_each_possible_cpu(i) 2128c2ecf20Sopenharmony_ci vfree(*per_cpu_ptr(scratches, i)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci free_percpu(scratches); 2158c2ecf20Sopenharmony_ci ipcomp_scratches = NULL; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void * __percpu *ipcomp_alloc_scratches(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci void * __percpu *scratches; 2218c2ecf20Sopenharmony_ci int i; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (ipcomp_scratch_users++) 2248c2ecf20Sopenharmony_ci return ipcomp_scratches; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci scratches = alloc_percpu(void *); 2278c2ecf20Sopenharmony_ci if (!scratches) 2288c2ecf20Sopenharmony_ci return NULL; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ipcomp_scratches = scratches; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 2338c2ecf20Sopenharmony_ci void *scratch; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci scratch = vmalloc_node(IPCOMP_SCRATCH_SIZE, cpu_to_node(i)); 2368c2ecf20Sopenharmony_ci if (!scratch) 2378c2ecf20Sopenharmony_ci return NULL; 2388c2ecf20Sopenharmony_ci *per_cpu_ptr(scratches, i) = scratch; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return scratches; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void ipcomp_free_tfms(struct crypto_comp * __percpu *tfms) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct ipcomp_tfms *pos; 2478c2ecf20Sopenharmony_ci int cpu; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci list_for_each_entry(pos, &ipcomp_tfms_list, list) { 2508c2ecf20Sopenharmony_ci if (pos->tfms == tfms) 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci WARN_ON(list_entry_is_head(pos, &ipcomp_tfms_list, list)); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (--pos->users) 2578c2ecf20Sopenharmony_ci return; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci list_del(&pos->list); 2608c2ecf20Sopenharmony_ci kfree(pos); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (!tfms) 2638c2ecf20Sopenharmony_ci return; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 2668c2ecf20Sopenharmony_ci struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu); 2678c2ecf20Sopenharmony_ci crypto_free_comp(tfm); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci free_percpu(tfms); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic struct crypto_comp * __percpu *ipcomp_alloc_tfms(const char *alg_name) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ipcomp_tfms *pos; 2758c2ecf20Sopenharmony_ci struct crypto_comp * __percpu *tfms; 2768c2ecf20Sopenharmony_ci int cpu; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci list_for_each_entry(pos, &ipcomp_tfms_list, list) { 2808c2ecf20Sopenharmony_ci struct crypto_comp *tfm; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* This can be any valid CPU ID so we don't need locking. */ 2838c2ecf20Sopenharmony_ci tfm = this_cpu_read(*pos->tfms); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!strcmp(crypto_comp_name(tfm), alg_name)) { 2868c2ecf20Sopenharmony_ci pos->users++; 2878c2ecf20Sopenharmony_ci return pos->tfms; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci pos = kmalloc(sizeof(*pos), GFP_KERNEL); 2928c2ecf20Sopenharmony_ci if (!pos) 2938c2ecf20Sopenharmony_ci return NULL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci pos->users = 1; 2968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pos->list); 2978c2ecf20Sopenharmony_ci list_add(&pos->list, &ipcomp_tfms_list); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci pos->tfms = tfms = alloc_percpu(struct crypto_comp *); 3008c2ecf20Sopenharmony_ci if (!tfms) 3018c2ecf20Sopenharmony_ci goto error; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 3048c2ecf20Sopenharmony_ci struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0, 3058c2ecf20Sopenharmony_ci CRYPTO_ALG_ASYNC); 3068c2ecf20Sopenharmony_ci if (IS_ERR(tfm)) 3078c2ecf20Sopenharmony_ci goto error; 3088c2ecf20Sopenharmony_ci *per_cpu_ptr(tfms, cpu) = tfm; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return tfms; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cierror: 3148c2ecf20Sopenharmony_ci ipcomp_free_tfms(tfms); 3158c2ecf20Sopenharmony_ci return NULL; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void ipcomp_free_data(struct ipcomp_data *ipcd) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci if (ipcd->tfms) 3218c2ecf20Sopenharmony_ci ipcomp_free_tfms(ipcd->tfms); 3228c2ecf20Sopenharmony_ci ipcomp_free_scratches(); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_civoid ipcomp_destroy(struct xfrm_state *x) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct ipcomp_data *ipcd = x->data; 3288c2ecf20Sopenharmony_ci if (!ipcd) 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci xfrm_state_delete_tunnel(x); 3318c2ecf20Sopenharmony_ci mutex_lock(&ipcomp_resource_mutex); 3328c2ecf20Sopenharmony_ci ipcomp_free_data(ipcd); 3338c2ecf20Sopenharmony_ci mutex_unlock(&ipcomp_resource_mutex); 3348c2ecf20Sopenharmony_ci kfree(ipcd); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipcomp_destroy); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ciint ipcomp_init_state(struct xfrm_state *x) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int err; 3418c2ecf20Sopenharmony_ci struct ipcomp_data *ipcd; 3428c2ecf20Sopenharmony_ci struct xfrm_algo_desc *calg_desc; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci err = -EINVAL; 3458c2ecf20Sopenharmony_ci if (!x->calg) 3468c2ecf20Sopenharmony_ci goto out; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (x->encap) 3498c2ecf20Sopenharmony_ci goto out; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = -ENOMEM; 3528c2ecf20Sopenharmony_ci ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL); 3538c2ecf20Sopenharmony_ci if (!ipcd) 3548c2ecf20Sopenharmony_ci goto out; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci mutex_lock(&ipcomp_resource_mutex); 3578c2ecf20Sopenharmony_ci if (!ipcomp_alloc_scratches()) 3588c2ecf20Sopenharmony_ci goto error; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name); 3618c2ecf20Sopenharmony_ci if (!ipcd->tfms) 3628c2ecf20Sopenharmony_ci goto error; 3638c2ecf20Sopenharmony_ci mutex_unlock(&ipcomp_resource_mutex); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0); 3668c2ecf20Sopenharmony_ci BUG_ON(!calg_desc); 3678c2ecf20Sopenharmony_ci ipcd->threshold = calg_desc->uinfo.comp.threshold; 3688c2ecf20Sopenharmony_ci x->data = ipcd; 3698c2ecf20Sopenharmony_ci err = 0; 3708c2ecf20Sopenharmony_ciout: 3718c2ecf20Sopenharmony_ci return err; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cierror: 3748c2ecf20Sopenharmony_ci ipcomp_free_data(ipcd); 3758c2ecf20Sopenharmony_ci mutex_unlock(&ipcomp_resource_mutex); 3768c2ecf20Sopenharmony_ci kfree(ipcd); 3778c2ecf20Sopenharmony_ci goto out; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipcomp_init_state); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173"); 3838c2ecf20Sopenharmony_ciMODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 384