xref: /linux/drivers/dibs/dibs_loopback.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1cb990a45SAlexandra Winter // SPDX-License-Identifier: GPL-2.0
2cb990a45SAlexandra Winter /*
3cb990a45SAlexandra Winter  *  Functions for dibs loopback/loopback-ism device.
4cb990a45SAlexandra Winter  *
5cb990a45SAlexandra Winter  *  Copyright (c) 2024, Alibaba Inc.
6cb990a45SAlexandra Winter  *
7cb990a45SAlexandra Winter  *  Author: Wen Gu <guwen@linux.alibaba.com>
8cb990a45SAlexandra Winter  *          Tony Lu <tonylu@linux.alibaba.com>
9cb990a45SAlexandra Winter  *
10cb990a45SAlexandra Winter  */
11cb990a45SAlexandra Winter 
12cc21191bSAlexandra Winter #include <linux/bitops.h>
13cc21191bSAlexandra Winter #include <linux/device.h>
14cb990a45SAlexandra Winter #include <linux/dibs.h>
15*203e3bebSJakub Kicinski #include <linux/mm.h>
16cb990a45SAlexandra Winter #include <linux/slab.h>
17cc21191bSAlexandra Winter #include <linux/spinlock.h>
18cb990a45SAlexandra Winter #include <linux/types.h>
19cb990a45SAlexandra Winter 
20cb990a45SAlexandra Winter #include "dibs_loopback.h"
21cb990a45SAlexandra Winter 
22cc21191bSAlexandra Winter #define DIBS_LO_SUPPORT_NOCOPY	0x1
23cc21191bSAlexandra Winter #define DIBS_DMA_ADDR_INVALID	(~(dma_addr_t)0)
24cc21191bSAlexandra Winter 
25845c334aSJulian Ruess static const char dibs_lo_dev_name[] = "lo";
26cb990a45SAlexandra Winter /* global loopback device */
27cb990a45SAlexandra Winter static struct dibs_lo_dev *lo_dev;
28cb990a45SAlexandra Winter 
2969baaac9SAlexandra Winter static u16 dibs_lo_get_fabric_id(struct dibs_dev *dibs)
3069baaac9SAlexandra Winter {
3169baaac9SAlexandra Winter 	return DIBS_LOOPBACK_FABRIC;
3269baaac9SAlexandra Winter }
3369baaac9SAlexandra Winter 
34719c3b67SAlexandra Winter static int dibs_lo_query_rgid(struct dibs_dev *dibs, const uuid_t *rgid,
35719c3b67SAlexandra Winter 			      u32 vid_valid, u32 vid)
36719c3b67SAlexandra Winter {
37719c3b67SAlexandra Winter 	/* rgid should be the same as lgid */
38719c3b67SAlexandra Winter 	if (!uuid_equal(rgid, &dibs->gid))
39719c3b67SAlexandra Winter 		return -ENETUNREACH;
40719c3b67SAlexandra Winter 	return 0;
41719c3b67SAlexandra Winter }
42719c3b67SAlexandra Winter 
43cc21191bSAlexandra Winter static int dibs_lo_max_dmbs(void)
44cc21191bSAlexandra Winter {
45cc21191bSAlexandra Winter 	return DIBS_LO_MAX_DMBS;
46cc21191bSAlexandra Winter }
47cc21191bSAlexandra Winter 
48cc21191bSAlexandra Winter static int dibs_lo_register_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb,
49cc21191bSAlexandra Winter 				struct dibs_client *client)
50cc21191bSAlexandra Winter {
51cc21191bSAlexandra Winter 	struct dibs_lo_dmb_node *dmb_node, *tmp_node;
52cc21191bSAlexandra Winter 	struct dibs_lo_dev *ldev;
53*203e3bebSJakub Kicinski 	struct folio *folio;
54cc21191bSAlexandra Winter 	unsigned long flags;
55cc21191bSAlexandra Winter 	int sba_idx, rc;
56cc21191bSAlexandra Winter 
57cc21191bSAlexandra Winter 	ldev = dibs->drv_priv;
58cc21191bSAlexandra Winter 	sba_idx = dmb->idx;
59cc21191bSAlexandra Winter 	/* check space for new dmb */
60cc21191bSAlexandra Winter 	for_each_clear_bit(sba_idx, ldev->sba_idx_mask, DIBS_LO_MAX_DMBS) {
61cc21191bSAlexandra Winter 		if (!test_and_set_bit(sba_idx, ldev->sba_idx_mask))
62cc21191bSAlexandra Winter 			break;
63cc21191bSAlexandra Winter 	}
64cc21191bSAlexandra Winter 	if (sba_idx == DIBS_LO_MAX_DMBS)
65cc21191bSAlexandra Winter 		return -ENOSPC;
66cc21191bSAlexandra Winter 
67cc21191bSAlexandra Winter 	dmb_node = kzalloc(sizeof(*dmb_node), GFP_KERNEL);
68cc21191bSAlexandra Winter 	if (!dmb_node) {
69cc21191bSAlexandra Winter 		rc = -ENOMEM;
70cc21191bSAlexandra Winter 		goto err_bit;
71cc21191bSAlexandra Winter 	}
72cc21191bSAlexandra Winter 
73cc21191bSAlexandra Winter 	dmb_node->sba_idx = sba_idx;
74cc21191bSAlexandra Winter 	dmb_node->len = dmb->dmb_len;
75*203e3bebSJakub Kicinski 
76*203e3bebSJakub Kicinski 	/* not critical; fail under memory pressure and fallback to TCP */
77*203e3bebSJakub Kicinski 	folio = folio_alloc(GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC |
78*203e3bebSJakub Kicinski 			    __GFP_NORETRY | __GFP_ZERO,
79*203e3bebSJakub Kicinski 			    get_order(dmb_node->len));
80*203e3bebSJakub Kicinski 	if (!folio) {
81cc21191bSAlexandra Winter 		rc = -ENOMEM;
82cc21191bSAlexandra Winter 		goto err_node;
83cc21191bSAlexandra Winter 	}
84*203e3bebSJakub Kicinski 	dmb_node->cpu_addr = folio_address(folio);
85cc21191bSAlexandra Winter 	dmb_node->dma_addr = DIBS_DMA_ADDR_INVALID;
86cc21191bSAlexandra Winter 	refcount_set(&dmb_node->refcnt, 1);
87cc21191bSAlexandra Winter 
88cc21191bSAlexandra Winter again:
89cc21191bSAlexandra Winter 	/* add new dmb into hash table */
90cc21191bSAlexandra Winter 	get_random_bytes(&dmb_node->token, sizeof(dmb_node->token));
91cc21191bSAlexandra Winter 	write_lock_bh(&ldev->dmb_ht_lock);
92cc21191bSAlexandra Winter 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_node->token) {
93cc21191bSAlexandra Winter 		if (tmp_node->token == dmb_node->token) {
94cc21191bSAlexandra Winter 			write_unlock_bh(&ldev->dmb_ht_lock);
95cc21191bSAlexandra Winter 			goto again;
96cc21191bSAlexandra Winter 		}
97cc21191bSAlexandra Winter 	}
98cc21191bSAlexandra Winter 	hash_add(ldev->dmb_ht, &dmb_node->list, dmb_node->token);
99cc21191bSAlexandra Winter 	write_unlock_bh(&ldev->dmb_ht_lock);
100cc21191bSAlexandra Winter 	atomic_inc(&ldev->dmb_cnt);
101cc21191bSAlexandra Winter 
102cc21191bSAlexandra Winter 	dmb->idx = dmb_node->sba_idx;
103cc21191bSAlexandra Winter 	dmb->dmb_tok = dmb_node->token;
104cc21191bSAlexandra Winter 	dmb->cpu_addr = dmb_node->cpu_addr;
105cc21191bSAlexandra Winter 	dmb->dma_addr = dmb_node->dma_addr;
106cc21191bSAlexandra Winter 	dmb->dmb_len = dmb_node->len;
107cc21191bSAlexandra Winter 
108cc21191bSAlexandra Winter 	spin_lock_irqsave(&dibs->lock, flags);
109cc21191bSAlexandra Winter 	dibs->dmb_clientid_arr[sba_idx] = client->id;
110cc21191bSAlexandra Winter 	spin_unlock_irqrestore(&dibs->lock, flags);
111cc21191bSAlexandra Winter 
112cc21191bSAlexandra Winter 	return 0;
113cc21191bSAlexandra Winter 
114cc21191bSAlexandra Winter err_node:
115cc21191bSAlexandra Winter 	kfree(dmb_node);
116cc21191bSAlexandra Winter err_bit:
117cc21191bSAlexandra Winter 	clear_bit(sba_idx, ldev->sba_idx_mask);
118cc21191bSAlexandra Winter 	return rc;
119cc21191bSAlexandra Winter }
120cc21191bSAlexandra Winter 
121cc21191bSAlexandra Winter static void __dibs_lo_unregister_dmb(struct dibs_lo_dev *ldev,
122cc21191bSAlexandra Winter 				     struct dibs_lo_dmb_node *dmb_node)
123cc21191bSAlexandra Winter {
124cc21191bSAlexandra Winter 	/* remove dmb from hash table */
125cc21191bSAlexandra Winter 	write_lock_bh(&ldev->dmb_ht_lock);
126cc21191bSAlexandra Winter 	hash_del(&dmb_node->list);
127cc21191bSAlexandra Winter 	write_unlock_bh(&ldev->dmb_ht_lock);
128cc21191bSAlexandra Winter 
129cc21191bSAlexandra Winter 	clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask);
130*203e3bebSJakub Kicinski 	folio_put(virt_to_folio(dmb_node->cpu_addr));
131cc21191bSAlexandra Winter 	kfree(dmb_node);
132cc21191bSAlexandra Winter 
133cc21191bSAlexandra Winter 	if (atomic_dec_and_test(&ldev->dmb_cnt))
134cc21191bSAlexandra Winter 		wake_up(&ldev->ldev_release);
135cc21191bSAlexandra Winter }
136cc21191bSAlexandra Winter 
137cc21191bSAlexandra Winter static int dibs_lo_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
138cc21191bSAlexandra Winter {
139cc21191bSAlexandra Winter 	struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
140cc21191bSAlexandra Winter 	struct dibs_lo_dev *ldev;
141cc21191bSAlexandra Winter 	unsigned long flags;
142cc21191bSAlexandra Winter 
143cc21191bSAlexandra Winter 	ldev = dibs->drv_priv;
144cc21191bSAlexandra Winter 
145cc21191bSAlexandra Winter 	/* find dmb from hash table */
146cc21191bSAlexandra Winter 	read_lock_bh(&ldev->dmb_ht_lock);
147cc21191bSAlexandra Winter 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
148cc21191bSAlexandra Winter 		if (tmp_node->token == dmb->dmb_tok) {
149cc21191bSAlexandra Winter 			dmb_node = tmp_node;
150cc21191bSAlexandra Winter 			break;
151cc21191bSAlexandra Winter 		}
152cc21191bSAlexandra Winter 	}
153cc21191bSAlexandra Winter 	read_unlock_bh(&ldev->dmb_ht_lock);
154cc21191bSAlexandra Winter 	if (!dmb_node)
155cc21191bSAlexandra Winter 		return -EINVAL;
156cc21191bSAlexandra Winter 
157cc21191bSAlexandra Winter 	if (refcount_dec_and_test(&dmb_node->refcnt)) {
158cc21191bSAlexandra Winter 		spin_lock_irqsave(&dibs->lock, flags);
159cc21191bSAlexandra Winter 		dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT;
160cc21191bSAlexandra Winter 		spin_unlock_irqrestore(&dibs->lock, flags);
161cc21191bSAlexandra Winter 
162cc21191bSAlexandra Winter 		__dibs_lo_unregister_dmb(ldev, dmb_node);
163cc21191bSAlexandra Winter 	}
164cc21191bSAlexandra Winter 	return 0;
165cc21191bSAlexandra Winter }
166cc21191bSAlexandra Winter 
167cc21191bSAlexandra Winter static int dibs_lo_support_dmb_nocopy(struct dibs_dev *dibs)
168cc21191bSAlexandra Winter {
169cc21191bSAlexandra Winter 	return DIBS_LO_SUPPORT_NOCOPY;
170cc21191bSAlexandra Winter }
171cc21191bSAlexandra Winter 
172cc21191bSAlexandra Winter static int dibs_lo_attach_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
173cc21191bSAlexandra Winter {
174cc21191bSAlexandra Winter 	struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
175cc21191bSAlexandra Winter 	struct dibs_lo_dev *ldev;
176cc21191bSAlexandra Winter 
177cc21191bSAlexandra Winter 	ldev = dibs->drv_priv;
178cc21191bSAlexandra Winter 
179cc21191bSAlexandra Winter 	/* find dmb_node according to dmb->dmb_tok */
180cc21191bSAlexandra Winter 	read_lock_bh(&ldev->dmb_ht_lock);
181cc21191bSAlexandra Winter 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
182cc21191bSAlexandra Winter 		if (tmp_node->token == dmb->dmb_tok) {
183cc21191bSAlexandra Winter 			dmb_node = tmp_node;
184cc21191bSAlexandra Winter 			break;
185cc21191bSAlexandra Winter 		}
186cc21191bSAlexandra Winter 	}
187cc21191bSAlexandra Winter 	if (!dmb_node) {
188cc21191bSAlexandra Winter 		read_unlock_bh(&ldev->dmb_ht_lock);
189cc21191bSAlexandra Winter 		return -EINVAL;
190cc21191bSAlexandra Winter 	}
191cc21191bSAlexandra Winter 	read_unlock_bh(&ldev->dmb_ht_lock);
192cc21191bSAlexandra Winter 
193cc21191bSAlexandra Winter 	if (!refcount_inc_not_zero(&dmb_node->refcnt))
194cc21191bSAlexandra Winter 		/* the dmb is being unregistered, but has
195cc21191bSAlexandra Winter 		 * not been removed from the hash table.
196cc21191bSAlexandra Winter 		 */
197cc21191bSAlexandra Winter 		return -EINVAL;
198cc21191bSAlexandra Winter 
199cc21191bSAlexandra Winter 	/* provide dmb information */
200cc21191bSAlexandra Winter 	dmb->idx = dmb_node->sba_idx;
201cc21191bSAlexandra Winter 	dmb->dmb_tok = dmb_node->token;
202cc21191bSAlexandra Winter 	dmb->cpu_addr = dmb_node->cpu_addr;
203cc21191bSAlexandra Winter 	dmb->dma_addr = dmb_node->dma_addr;
204cc21191bSAlexandra Winter 	dmb->dmb_len = dmb_node->len;
205cc21191bSAlexandra Winter 	return 0;
206cc21191bSAlexandra Winter }
207cc21191bSAlexandra Winter 
208cc21191bSAlexandra Winter static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token)
209cc21191bSAlexandra Winter {
210cc21191bSAlexandra Winter 	struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
211cc21191bSAlexandra Winter 	struct dibs_lo_dev *ldev;
212cc21191bSAlexandra Winter 
213cc21191bSAlexandra Winter 	ldev = dibs->drv_priv;
214cc21191bSAlexandra Winter 
215cc21191bSAlexandra Winter 	/* find dmb_node according to dmb->dmb_tok */
216cc21191bSAlexandra Winter 	read_lock_bh(&ldev->dmb_ht_lock);
217cc21191bSAlexandra Winter 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) {
218cc21191bSAlexandra Winter 		if (tmp_node->token == token) {
219cc21191bSAlexandra Winter 			dmb_node = tmp_node;
220cc21191bSAlexandra Winter 			break;
221cc21191bSAlexandra Winter 		}
222cc21191bSAlexandra Winter 	}
223cc21191bSAlexandra Winter 	if (!dmb_node) {
224cc21191bSAlexandra Winter 		read_unlock_bh(&ldev->dmb_ht_lock);
225cc21191bSAlexandra Winter 		return -EINVAL;
226cc21191bSAlexandra Winter 	}
227cc21191bSAlexandra Winter 	read_unlock_bh(&ldev->dmb_ht_lock);
228cc21191bSAlexandra Winter 
229cc21191bSAlexandra Winter 	if (refcount_dec_and_test(&dmb_node->refcnt))
230cc21191bSAlexandra Winter 		__dibs_lo_unregister_dmb(ldev, dmb_node);
231cc21191bSAlexandra Winter 	return 0;
232cc21191bSAlexandra Winter }
233cc21191bSAlexandra Winter 
234cc21191bSAlexandra Winter static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok,
235cc21191bSAlexandra Winter 			     unsigned int idx, bool sf, unsigned int offset,
236cc21191bSAlexandra Winter 			     void *data, unsigned int size)
237cc21191bSAlexandra Winter {
238cc21191bSAlexandra Winter 	struct dibs_lo_dmb_node *rmb_node = NULL, *tmp_node;
239cc21191bSAlexandra Winter 	struct dibs_lo_dev *ldev;
240cc21191bSAlexandra Winter 	u16 s_mask;
241cc21191bSAlexandra Winter 	u8 client_id;
242cc21191bSAlexandra Winter 	u32 sba_idx;
243cc21191bSAlexandra Winter 
244cc21191bSAlexandra Winter 	ldev = dibs->drv_priv;
245cc21191bSAlexandra Winter 
246cc21191bSAlexandra Winter 	read_lock_bh(&ldev->dmb_ht_lock);
247cc21191bSAlexandra Winter 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_tok) {
248cc21191bSAlexandra Winter 		if (tmp_node->token == dmb_tok) {
249cc21191bSAlexandra Winter 			rmb_node = tmp_node;
250cc21191bSAlexandra Winter 			break;
251cc21191bSAlexandra Winter 		}
252cc21191bSAlexandra Winter 	}
253cc21191bSAlexandra Winter 	if (!rmb_node) {
254cc21191bSAlexandra Winter 		read_unlock_bh(&ldev->dmb_ht_lock);
255cc21191bSAlexandra Winter 		return -EINVAL;
256cc21191bSAlexandra Winter 	}
257cc21191bSAlexandra Winter 	memcpy((char *)rmb_node->cpu_addr + offset, data, size);
258cc21191bSAlexandra Winter 	sba_idx = rmb_node->sba_idx;
259cc21191bSAlexandra Winter 	read_unlock_bh(&ldev->dmb_ht_lock);
260cc21191bSAlexandra Winter 
261cc21191bSAlexandra Winter 	if (!sf)
262cc21191bSAlexandra Winter 		return 0;
263cc21191bSAlexandra Winter 
264cc21191bSAlexandra Winter 	spin_lock(&dibs->lock);
265cc21191bSAlexandra Winter 	client_id = dibs->dmb_clientid_arr[sba_idx];
266cc21191bSAlexandra Winter 	s_mask = ror16(0x1000, idx);
267cc21191bSAlexandra Winter 	if (likely(client_id != NO_DIBS_CLIENT && dibs->subs[client_id]))
268cc21191bSAlexandra Winter 		dibs->subs[client_id]->ops->handle_irq(dibs, sba_idx, s_mask);
269cc21191bSAlexandra Winter 	spin_unlock(&dibs->lock);
270cc21191bSAlexandra Winter 
271cc21191bSAlexandra Winter 	return 0;
272cc21191bSAlexandra Winter }
273cc21191bSAlexandra Winter 
27469baaac9SAlexandra Winter static const struct dibs_dev_ops dibs_lo_ops = {
27569baaac9SAlexandra Winter 	.get_fabric_id = dibs_lo_get_fabric_id,
276719c3b67SAlexandra Winter 	.query_remote_gid = dibs_lo_query_rgid,
277cc21191bSAlexandra Winter 	.max_dmbs = dibs_lo_max_dmbs,
278cc21191bSAlexandra Winter 	.register_dmb = dibs_lo_register_dmb,
279cc21191bSAlexandra Winter 	.unregister_dmb = dibs_lo_unregister_dmb,
280cc21191bSAlexandra Winter 	.move_data = dibs_lo_move_data,
281cc21191bSAlexandra Winter 	.support_mmapped_rdmb = dibs_lo_support_dmb_nocopy,
282cc21191bSAlexandra Winter 	.attach_dmb = dibs_lo_attach_dmb,
283cc21191bSAlexandra Winter 	.detach_dmb = dibs_lo_detach_dmb,
28469baaac9SAlexandra Winter };
28569baaac9SAlexandra Winter 
286cc21191bSAlexandra Winter static void dibs_lo_dev_init(struct dibs_lo_dev *ldev)
287cc21191bSAlexandra Winter {
288cc21191bSAlexandra Winter 	rwlock_init(&ldev->dmb_ht_lock);
289cc21191bSAlexandra Winter 	hash_init(ldev->dmb_ht);
290cc21191bSAlexandra Winter 	atomic_set(&ldev->dmb_cnt, 0);
291cc21191bSAlexandra Winter 	init_waitqueue_head(&ldev->ldev_release);
292cc21191bSAlexandra Winter }
293cc21191bSAlexandra Winter 
294cc21191bSAlexandra Winter static void dibs_lo_dev_exit(struct dibs_lo_dev *ldev)
295cc21191bSAlexandra Winter {
296cc21191bSAlexandra Winter 	if (atomic_read(&ldev->dmb_cnt))
297cc21191bSAlexandra Winter 		wait_event(ldev->ldev_release, !atomic_read(&ldev->dmb_cnt));
298cc21191bSAlexandra Winter }
299cc21191bSAlexandra Winter 
300cb990a45SAlexandra Winter static int dibs_lo_dev_probe(void)
301cb990a45SAlexandra Winter {
302cb990a45SAlexandra Winter 	struct dibs_lo_dev *ldev;
303cb990a45SAlexandra Winter 	struct dibs_dev *dibs;
304cb990a45SAlexandra Winter 	int ret;
305cb990a45SAlexandra Winter 
306cb990a45SAlexandra Winter 	ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
307cb990a45SAlexandra Winter 	if (!ldev)
308cb990a45SAlexandra Winter 		return -ENOMEM;
309cb990a45SAlexandra Winter 
310cb990a45SAlexandra Winter 	dibs = dibs_dev_alloc();
311cb990a45SAlexandra Winter 	if (!dibs) {
312cb990a45SAlexandra Winter 		kfree(ldev);
313cb990a45SAlexandra Winter 		return -ENOMEM;
314cb990a45SAlexandra Winter 	}
315cb990a45SAlexandra Winter 
316cb990a45SAlexandra Winter 	ldev->dibs = dibs;
31769baaac9SAlexandra Winter 	dibs->drv_priv = ldev;
318cc21191bSAlexandra Winter 	dibs_lo_dev_init(ldev);
31905e68d8dSAlexandra Winter 	uuid_gen(&dibs->gid);
32069baaac9SAlexandra Winter 	dibs->ops = &dibs_lo_ops;
321cb990a45SAlexandra Winter 
322845c334aSJulian Ruess 	dibs->dev.parent = NULL;
323845c334aSJulian Ruess 	dev_set_name(&dibs->dev, "%s", dibs_lo_dev_name);
324845c334aSJulian Ruess 
325cb990a45SAlexandra Winter 	ret = dibs_dev_add(dibs);
326cb990a45SAlexandra Winter 	if (ret)
327cb990a45SAlexandra Winter 		goto err_reg;
328cb990a45SAlexandra Winter 	lo_dev = ldev;
329cb990a45SAlexandra Winter 	return 0;
330cb990a45SAlexandra Winter 
331cb990a45SAlexandra Winter err_reg:
332cc21191bSAlexandra Winter 	kfree(dibs->dmb_clientid_arr);
333cb990a45SAlexandra Winter 	/* pairs with dibs_dev_alloc() */
334845c334aSJulian Ruess 	put_device(&dibs->dev);
335cb990a45SAlexandra Winter 	kfree(ldev);
336cb990a45SAlexandra Winter 
337cb990a45SAlexandra Winter 	return ret;
338cb990a45SAlexandra Winter }
339cb990a45SAlexandra Winter 
340cb990a45SAlexandra Winter static void dibs_lo_dev_remove(void)
341cb990a45SAlexandra Winter {
342cb990a45SAlexandra Winter 	if (!lo_dev)
343cb990a45SAlexandra Winter 		return;
344cb990a45SAlexandra Winter 
345845c334aSJulian Ruess 	dibs_dev_del(lo_dev->dibs);
346cc21191bSAlexandra Winter 	dibs_lo_dev_exit(lo_dev);
347cb990a45SAlexandra Winter 	/* pairs with dibs_dev_alloc() */
348845c334aSJulian Ruess 	put_device(&lo_dev->dibs->dev);
349cb990a45SAlexandra Winter 	kfree(lo_dev);
350cb990a45SAlexandra Winter 	lo_dev = NULL;
351cb990a45SAlexandra Winter }
352cb990a45SAlexandra Winter 
353cb990a45SAlexandra Winter int dibs_loopback_init(void)
354cb990a45SAlexandra Winter {
355cb990a45SAlexandra Winter 	return dibs_lo_dev_probe();
356cb990a45SAlexandra Winter }
357cb990a45SAlexandra Winter 
358cb990a45SAlexandra Winter void dibs_loopback_exit(void)
359cb990a45SAlexandra Winter {
360cb990a45SAlexandra Winter 	dibs_lo_dev_remove();
361cb990a45SAlexandra Winter }
362