0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/vmw_vmci_defs.h>
0009 #include <linux/vmw_vmci_api.h>
0010 #include <linux/completion.h>
0011 #include <linux/hash.h>
0012 #include <linux/kernel.h>
0013 #include <linux/list.h>
0014 #include <linux/module.h>
0015 #include <linux/sched.h>
0016 #include <linux/slab.h>
0017
0018 #include "vmci_datagram.h"
0019 #include "vmci_doorbell.h"
0020 #include "vmci_resource.h"
0021 #include "vmci_driver.h"
0022 #include "vmci_route.h"
0023
0024
0025 #define VMCI_DOORBELL_INDEX_BITS 6
0026 #define VMCI_DOORBELL_INDEX_TABLE_SIZE (1 << VMCI_DOORBELL_INDEX_BITS)
0027 #define VMCI_DOORBELL_HASH(_idx) hash_32(_idx, VMCI_DOORBELL_INDEX_BITS)
0028
0029
0030
0031
0032
0033 struct dbell_entry {
0034 struct vmci_resource resource;
0035 struct hlist_node node;
0036 struct work_struct work;
0037 vmci_callback notify_cb;
0038 void *client_data;
0039 u32 idx;
0040 u32 priv_flags;
0041 bool run_delayed;
0042 atomic_t active;
0043 };
0044
0045
0046 struct dbell_index_table {
0047 spinlock_t lock;
0048 struct hlist_head entries[VMCI_DOORBELL_INDEX_TABLE_SIZE];
0049 };
0050
0051 static struct dbell_index_table vmci_doorbell_it = {
0052 .lock = __SPIN_LOCK_UNLOCKED(vmci_doorbell_it.lock),
0053 };
0054
0055
0056
0057
0058
0059 static u32 max_notify_idx;
0060
0061
0062
0063
0064
0065 static u32 notify_idx_count;
0066
0067
0068
0069
0070
0071
0072 static u32 last_notify_idx_reserved;
0073
0074
0075 static u32 last_notify_idx_released = PAGE_SIZE;
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085 int vmci_dbell_get_priv_flags(struct vmci_handle handle, u32 *priv_flags)
0086 {
0087 if (priv_flags == NULL || handle.context == VMCI_INVALID_ID)
0088 return VMCI_ERROR_INVALID_ARGS;
0089
0090 if (handle.context == VMCI_HOST_CONTEXT_ID) {
0091 struct dbell_entry *entry;
0092 struct vmci_resource *resource;
0093
0094 resource = vmci_resource_by_handle(handle,
0095 VMCI_RESOURCE_TYPE_DOORBELL);
0096 if (!resource)
0097 return VMCI_ERROR_NOT_FOUND;
0098
0099 entry = container_of(resource, struct dbell_entry, resource);
0100 *priv_flags = entry->priv_flags;
0101 vmci_resource_put(resource);
0102 } else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) {
0103
0104
0105
0106
0107 return VMCI_ERROR_INVALID_ARGS;
0108 } else {
0109 *priv_flags = vmci_context_get_priv_flags(handle.context);
0110 }
0111
0112 return VMCI_SUCCESS;
0113 }
0114
0115
0116
0117
0118 static struct dbell_entry *dbell_index_table_find(u32 idx)
0119 {
0120 u32 bucket = VMCI_DOORBELL_HASH(idx);
0121 struct dbell_entry *dbell;
0122
0123 hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket],
0124 node) {
0125 if (idx == dbell->idx)
0126 return dbell;
0127 }
0128
0129 return NULL;
0130 }
0131
0132
0133
0134
0135
0136
0137 static void dbell_index_table_add(struct dbell_entry *entry)
0138 {
0139 u32 bucket;
0140 u32 new_notify_idx;
0141
0142 vmci_resource_get(&entry->resource);
0143
0144 spin_lock_bh(&vmci_doorbell_it.lock);
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154 if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) {
0155 if (last_notify_idx_released < max_notify_idx &&
0156 !dbell_index_table_find(last_notify_idx_released)) {
0157 new_notify_idx = last_notify_idx_released;
0158 last_notify_idx_released = PAGE_SIZE;
0159 } else {
0160 bool reused = false;
0161 new_notify_idx = last_notify_idx_reserved;
0162 if (notify_idx_count + 1 < max_notify_idx) {
0163 do {
0164 if (!dbell_index_table_find
0165 (new_notify_idx)) {
0166 reused = true;
0167 break;
0168 }
0169 new_notify_idx = (new_notify_idx + 1) %
0170 max_notify_idx;
0171 } while (new_notify_idx !=
0172 last_notify_idx_released);
0173 }
0174 if (!reused) {
0175 new_notify_idx = max_notify_idx;
0176 max_notify_idx++;
0177 }
0178 }
0179 } else {
0180 new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE;
0181 }
0182
0183 last_notify_idx_reserved = new_notify_idx;
0184 notify_idx_count++;
0185
0186 entry->idx = new_notify_idx;
0187 bucket = VMCI_DOORBELL_HASH(entry->idx);
0188 hlist_add_head(&entry->node, &vmci_doorbell_it.entries[bucket]);
0189
0190 spin_unlock_bh(&vmci_doorbell_it.lock);
0191 }
0192
0193
0194
0195
0196
0197 static void dbell_index_table_remove(struct dbell_entry *entry)
0198 {
0199 spin_lock_bh(&vmci_doorbell_it.lock);
0200
0201 hlist_del_init(&entry->node);
0202
0203 notify_idx_count--;
0204 if (entry->idx == max_notify_idx - 1) {
0205
0206
0207
0208
0209
0210
0211
0212 while (max_notify_idx > 0 &&
0213 !dbell_index_table_find(max_notify_idx - 1))
0214 max_notify_idx--;
0215 }
0216
0217 last_notify_idx_released = entry->idx;
0218
0219 spin_unlock_bh(&vmci_doorbell_it.lock);
0220
0221 vmci_resource_put(&entry->resource);
0222 }
0223
0224
0225
0226
0227
0228
0229 static int dbell_link(struct vmci_handle handle, u32 notify_idx)
0230 {
0231 struct vmci_doorbell_link_msg link_msg;
0232
0233 link_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
0234 VMCI_DOORBELL_LINK);
0235 link_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
0236 link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE;
0237 link_msg.handle = handle;
0238 link_msg.notify_idx = notify_idx;
0239
0240 return vmci_send_datagram(&link_msg.hdr);
0241 }
0242
0243
0244
0245
0246
0247 static int dbell_unlink(struct vmci_handle handle)
0248 {
0249 struct vmci_doorbell_unlink_msg unlink_msg;
0250
0251 unlink_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
0252 VMCI_DOORBELL_UNLINK);
0253 unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
0254 unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE;
0255 unlink_msg.handle = handle;
0256
0257 return vmci_send_datagram(&unlink_msg.hdr);
0258 }
0259
0260
0261
0262
0263
0264 static int dbell_notify_as_guest(struct vmci_handle handle, u32 priv_flags)
0265 {
0266 struct vmci_doorbell_notify_msg notify_msg;
0267
0268 notify_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
0269 VMCI_DOORBELL_NOTIFY);
0270 notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
0271 notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE;
0272 notify_msg.handle = handle;
0273
0274 return vmci_send_datagram(¬ify_msg.hdr);
0275 }
0276
0277
0278
0279
0280 static void dbell_delayed_dispatch(struct work_struct *work)
0281 {
0282 struct dbell_entry *entry = container_of(work,
0283 struct dbell_entry, work);
0284
0285 entry->notify_cb(entry->client_data);
0286 vmci_resource_put(&entry->resource);
0287 }
0288
0289
0290
0291
0292 int vmci_dbell_host_context_notify(u32 src_cid, struct vmci_handle handle)
0293 {
0294 struct dbell_entry *entry;
0295 struct vmci_resource *resource;
0296
0297 if (vmci_handle_is_invalid(handle)) {
0298 pr_devel("Notifying an invalid doorbell (handle=0x%x:0x%x)\n",
0299 handle.context, handle.resource);
0300 return VMCI_ERROR_INVALID_ARGS;
0301 }
0302
0303 resource = vmci_resource_by_handle(handle,
0304 VMCI_RESOURCE_TYPE_DOORBELL);
0305 if (!resource) {
0306 pr_devel("Notifying an unknown doorbell (handle=0x%x:0x%x)\n",
0307 handle.context, handle.resource);
0308 return VMCI_ERROR_NOT_FOUND;
0309 }
0310
0311 entry = container_of(resource, struct dbell_entry, resource);
0312 if (entry->run_delayed) {
0313 if (!schedule_work(&entry->work))
0314 vmci_resource_put(resource);
0315 } else {
0316 entry->notify_cb(entry->client_data);
0317 vmci_resource_put(resource);
0318 }
0319
0320 return VMCI_SUCCESS;
0321 }
0322
0323
0324
0325
0326 bool vmci_dbell_register_notification_bitmap(u64 bitmap_ppn)
0327 {
0328 int result;
0329 struct vmci_notify_bm_set_msg bitmap_set_msg = { };
0330
0331 bitmap_set_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
0332 VMCI_SET_NOTIFY_BITMAP);
0333 bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
0334 bitmap_set_msg.hdr.payload_size = sizeof(bitmap_set_msg) -
0335 VMCI_DG_HEADERSIZE;
0336 if (vmci_use_ppn64())
0337 bitmap_set_msg.bitmap_ppn64 = bitmap_ppn;
0338 else
0339 bitmap_set_msg.bitmap_ppn32 = (u32) bitmap_ppn;
0340
0341 result = vmci_send_datagram(&bitmap_set_msg.hdr);
0342 if (result != VMCI_SUCCESS) {
0343 pr_devel("Failed to register (PPN=%llu) as notification bitmap (error=%d)\n",
0344 bitmap_ppn, result);
0345 return false;
0346 }
0347 return true;
0348 }
0349
0350
0351
0352
0353 static void dbell_fire_entries(u32 notify_idx)
0354 {
0355 u32 bucket = VMCI_DOORBELL_HASH(notify_idx);
0356 struct dbell_entry *dbell;
0357
0358 spin_lock_bh(&vmci_doorbell_it.lock);
0359
0360 hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) {
0361 if (dbell->idx == notify_idx &&
0362 atomic_read(&dbell->active) == 1) {
0363 if (dbell->run_delayed) {
0364 vmci_resource_get(&dbell->resource);
0365 if (!schedule_work(&dbell->work))
0366 vmci_resource_put(&dbell->resource);
0367 } else {
0368 dbell->notify_cb(dbell->client_data);
0369 }
0370 }
0371 }
0372
0373 spin_unlock_bh(&vmci_doorbell_it.lock);
0374 }
0375
0376
0377
0378
0379
0380 void vmci_dbell_scan_notification_entries(u8 *bitmap)
0381 {
0382 u32 idx;
0383
0384 for (idx = 0; idx < max_notify_idx; idx++) {
0385 if (bitmap[idx] & 0x1) {
0386 bitmap[idx] &= ~1;
0387 dbell_fire_entries(idx);
0388 }
0389 }
0390 }
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409 int vmci_doorbell_create(struct vmci_handle *handle,
0410 u32 flags,
0411 u32 priv_flags,
0412 vmci_callback notify_cb, void *client_data)
0413 {
0414 struct dbell_entry *entry;
0415 struct vmci_handle new_handle;
0416 int result;
0417
0418 if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB ||
0419 priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)
0420 return VMCI_ERROR_INVALID_ARGS;
0421
0422 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0423 if (entry == NULL) {
0424 pr_warn("Failed allocating memory for datagram entry\n");
0425 return VMCI_ERROR_NO_MEM;
0426 }
0427
0428 if (vmci_handle_is_invalid(*handle)) {
0429 u32 context_id = vmci_get_context_id();
0430
0431 if (context_id == VMCI_INVALID_ID) {
0432 pr_warn("Failed to get context ID\n");
0433 result = VMCI_ERROR_NO_RESOURCES;
0434 goto free_mem;
0435 }
0436
0437
0438 new_handle = vmci_make_handle(context_id, VMCI_INVALID_ID);
0439 } else {
0440 bool valid_context = false;
0441
0442
0443
0444
0445
0446
0447
0448
0449 if (handle->context == VMCI_HOST_CONTEXT_ID ||
0450 (vmci_guest_code_active() &&
0451 vmci_get_context_id() == handle->context)) {
0452 valid_context = true;
0453 }
0454
0455 if (!valid_context || handle->resource == VMCI_INVALID_ID) {
0456 pr_devel("Invalid argument (handle=0x%x:0x%x)\n",
0457 handle->context, handle->resource);
0458 result = VMCI_ERROR_INVALID_ARGS;
0459 goto free_mem;
0460 }
0461
0462 new_handle = *handle;
0463 }
0464
0465 entry->idx = 0;
0466 INIT_HLIST_NODE(&entry->node);
0467 entry->priv_flags = priv_flags;
0468 INIT_WORK(&entry->work, dbell_delayed_dispatch);
0469 entry->run_delayed = flags & VMCI_FLAG_DELAYED_CB;
0470 entry->notify_cb = notify_cb;
0471 entry->client_data = client_data;
0472 atomic_set(&entry->active, 0);
0473
0474 result = vmci_resource_add(&entry->resource,
0475 VMCI_RESOURCE_TYPE_DOORBELL,
0476 new_handle);
0477 if (result != VMCI_SUCCESS) {
0478 pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d\n",
0479 new_handle.context, new_handle.resource, result);
0480 goto free_mem;
0481 }
0482
0483 new_handle = vmci_resource_handle(&entry->resource);
0484 if (vmci_guest_code_active()) {
0485 dbell_index_table_add(entry);
0486 result = dbell_link(new_handle, entry->idx);
0487 if (VMCI_SUCCESS != result)
0488 goto destroy_resource;
0489
0490 atomic_set(&entry->active, 1);
0491 }
0492
0493 *handle = new_handle;
0494
0495 return result;
0496
0497 destroy_resource:
0498 dbell_index_table_remove(entry);
0499 vmci_resource_remove(&entry->resource);
0500 free_mem:
0501 kfree(entry);
0502 return result;
0503 }
0504 EXPORT_SYMBOL_GPL(vmci_doorbell_create);
0505
0506
0507
0508
0509
0510
0511
0512
0513 int vmci_doorbell_destroy(struct vmci_handle handle)
0514 {
0515 struct dbell_entry *entry;
0516 struct vmci_resource *resource;
0517
0518 if (vmci_handle_is_invalid(handle))
0519 return VMCI_ERROR_INVALID_ARGS;
0520
0521 resource = vmci_resource_by_handle(handle,
0522 VMCI_RESOURCE_TYPE_DOORBELL);
0523 if (!resource) {
0524 pr_devel("Failed to destroy doorbell (handle=0x%x:0x%x)\n",
0525 handle.context, handle.resource);
0526 return VMCI_ERROR_NOT_FOUND;
0527 }
0528
0529 entry = container_of(resource, struct dbell_entry, resource);
0530
0531 if (!hlist_unhashed(&entry->node)) {
0532 int result;
0533
0534 dbell_index_table_remove(entry);
0535
0536 result = dbell_unlink(handle);
0537 if (VMCI_SUCCESS != result) {
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552 pr_devel("Unlink of doorbell (handle=0x%x:0x%x) unknown by hypervisor (error=%d)\n",
0553 handle.context, handle.resource, result);
0554 }
0555 }
0556
0557
0558
0559
0560
0561 vmci_resource_put(&entry->resource);
0562 vmci_resource_remove(&entry->resource);
0563
0564 kfree(entry);
0565
0566 return VMCI_SUCCESS;
0567 }
0568 EXPORT_SYMBOL_GPL(vmci_doorbell_destroy);
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579 int vmci_doorbell_notify(struct vmci_handle dst, u32 priv_flags)
0580 {
0581 int retval;
0582 enum vmci_route route;
0583 struct vmci_handle src;
0584
0585 if (vmci_handle_is_invalid(dst) ||
0586 (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS))
0587 return VMCI_ERROR_INVALID_ARGS;
0588
0589 src = VMCI_INVALID_HANDLE;
0590 retval = vmci_route(&src, &dst, false, &route);
0591 if (retval < VMCI_SUCCESS)
0592 return retval;
0593
0594 if (VMCI_ROUTE_AS_HOST == route)
0595 return vmci_ctx_notify_dbell(VMCI_HOST_CONTEXT_ID,
0596 dst, priv_flags);
0597
0598 if (VMCI_ROUTE_AS_GUEST == route)
0599 return dbell_notify_as_guest(dst, priv_flags);
0600
0601 pr_warn("Unknown route (%d) for doorbell\n", route);
0602 return VMCI_ERROR_DST_UNREACHABLE;
0603 }
0604 EXPORT_SYMBOL_GPL(vmci_doorbell_notify);