Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.5 183 lines 4.9 kB view raw
1/* 2 * Intel MIC Platform Software Stack (MPSS) 3 * 4 * Copyright(c) 2014 Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2, as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * Intel SCIF driver. 16 */ 17#include "scif_main.h" 18#include "../bus/scif_bus.h" 19#include "scif_peer_bus.h" 20 21static inline struct scif_peer_dev * 22dev_to_scif_peer(struct device *dev) 23{ 24 return container_of(dev, struct scif_peer_dev, dev); 25} 26 27struct bus_type scif_peer_bus = { 28 .name = "scif_peer_bus", 29}; 30 31static void scif_peer_release_dev(struct device *d) 32{ 33 struct scif_peer_dev *sdev = dev_to_scif_peer(d); 34 struct scif_dev *scifdev = &scif_dev[sdev->dnode]; 35 36 scif_cleanup_scifdev(scifdev); 37 kfree(sdev); 38} 39 40static int scif_peer_initialize_device(struct scif_dev *scifdev) 41{ 42 struct scif_peer_dev *spdev; 43 int ret; 44 45 spdev = kzalloc(sizeof(*spdev), GFP_KERNEL); 46 if (!spdev) { 47 ret = -ENOMEM; 48 goto err; 49 } 50 51 spdev->dev.parent = scifdev->sdev->dev.parent; 52 spdev->dev.release = scif_peer_release_dev; 53 spdev->dnode = scifdev->node; 54 spdev->dev.bus = &scif_peer_bus; 55 dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode); 56 57 device_initialize(&spdev->dev); 58 get_device(&spdev->dev); 59 rcu_assign_pointer(scifdev->spdev, spdev); 60 61 mutex_lock(&scif_info.conflock); 62 scif_info.total++; 63 scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid); 64 mutex_unlock(&scif_info.conflock); 65 return 0; 66err: 67 dev_err(&scifdev->sdev->dev, 68 "dnode %d: initialize_device rc %d\n", scifdev->node, ret); 69 return ret; 70} 71 72static int scif_peer_add_device(struct scif_dev *scifdev) 73{ 74 struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev); 75 char pool_name[16]; 76 int ret; 77 78 ret = device_add(&spdev->dev); 79 put_device(&spdev->dev); 80 if (ret) { 81 dev_err(&scifdev->sdev->dev, 82 "dnode %d: peer device_add failed\n", scifdev->node); 83 goto put_spdev; 84 } 85 86 scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode); 87 scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev, 88 sizeof(struct scif_status), 1, 89 0); 90 if (!scifdev->signal_pool) { 91 dev_err(&scifdev->sdev->dev, 92 "dnode %d: dmam_pool_create failed\n", scifdev->node); 93 ret = -ENOMEM; 94 goto del_spdev; 95 } 96 dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode); 97 return 0; 98del_spdev: 99 device_del(&spdev->dev); 100put_spdev: 101 RCU_INIT_POINTER(scifdev->spdev, NULL); 102 synchronize_rcu(); 103 put_device(&spdev->dev); 104 105 mutex_lock(&scif_info.conflock); 106 scif_info.total--; 107 mutex_unlock(&scif_info.conflock); 108 return ret; 109} 110 111void scif_add_peer_device(struct work_struct *work) 112{ 113 struct scif_dev *scifdev = container_of(work, struct scif_dev, 114 peer_add_work); 115 116 scif_peer_add_device(scifdev); 117} 118 119/* 120 * Peer device registration is split into a device_initialize and a device_add. 121 * The reason for doing this is as follows: First, peer device registration 122 * itself cannot be done in the message processing thread and must be delegated 123 * to another workqueue, otherwise if SCIF client probe, called during peer 124 * device registration, calls scif_connect(..), it will block the message 125 * processing thread causing a deadlock. Next, device_initialize is done in the 126 * "top-half" message processing thread and device_add in the "bottom-half" 127 * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing 128 * concurrently with SCIF_INIT message processing is unable to get a reference 129 * on the peer device, thereby failing the connect request. 130 */ 131void scif_peer_register_device(struct scif_dev *scifdev) 132{ 133 int ret; 134 135 mutex_lock(&scifdev->lock); 136 ret = scif_peer_initialize_device(scifdev); 137 if (ret) 138 goto exit; 139 schedule_work(&scifdev->peer_add_work); 140exit: 141 mutex_unlock(&scifdev->lock); 142} 143 144int scif_peer_unregister_device(struct scif_dev *scifdev) 145{ 146 struct scif_peer_dev *spdev; 147 148 mutex_lock(&scifdev->lock); 149 /* Flush work to ensure device register is complete */ 150 flush_work(&scifdev->peer_add_work); 151 152 /* 153 * Continue holding scifdev->lock since theoretically unregister_device 154 * can be called simultaneously from multiple threads 155 */ 156 spdev = rcu_dereference(scifdev->spdev); 157 if (!spdev) { 158 mutex_unlock(&scifdev->lock); 159 return -ENODEV; 160 } 161 162 RCU_INIT_POINTER(scifdev->spdev, NULL); 163 synchronize_rcu(); 164 mutex_unlock(&scifdev->lock); 165 166 dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode); 167 device_unregister(&spdev->dev); 168 169 mutex_lock(&scif_info.conflock); 170 scif_info.total--; 171 mutex_unlock(&scif_info.conflock); 172 return 0; 173} 174 175int scif_peer_bus_init(void) 176{ 177 return bus_register(&scif_peer_bus); 178} 179 180void scif_peer_bus_exit(void) 181{ 182 bus_unregister(&scif_peer_bus); 183}