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