in drbd/drbd_main.c [2697:2841]
enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsigned int minor)
{
struct drbd_resource *resource = adm_ctx->resource;
struct drbd_connection *connection;
struct drbd_device *device;
struct drbd_peer_device *peer_device, *tmp_peer_device;
struct gendisk *disk;
int id;
int vnr = adm_ctx->volume;
enum drbd_ret_code err = ERR_NOMEM;
device = minor_to_device(minor);
if (device)
return ERR_MINOR_OR_VOLUME_EXISTS;
/* GFP_KERNEL, we are outside of all write-out paths */
device = kzalloc(sizeof(struct drbd_device), GFP_KERNEL);
if (!device)
return ERR_NOMEM;
kref_init(&device->kref);
kref_get(&resource->kref);
device->resource = resource;
device->minor = minor;
device->vnr = vnr;
drbd_init_set_defaults(device);
disk = blk_alloc_disk(NUMA_NO_NODE);
if (!disk)
goto out_no_disk;
device->vdisk = disk;
device->rq_queue = disk->queue;
set_disk_ro(disk, true);
disk->major = DRBD_MAJOR;
disk->first_minor = minor;
disk->minors = 1;
disk->fops = &drbd_ops;
disk->flags |= GENHD_FL_NO_PART;
sprintf(disk->disk_name, "drbd%d", minor);
disk->private_data = device;
blk_queue_write_cache(disk->queue, true, true);
/* Setting the max_hw_sectors to an odd value of 8kibyte here
This triggers a max_bio_size message upon first attach or connect */
blk_queue_max_hw_sectors(disk->queue, DRBD_MAX_BIO_SIZE_SAFE >> 8);
device->md_io.page = alloc_page(GFP_KERNEL);
if (!device->md_io.page)
goto out_no_io_page;
if (drbd_bm_init(device))
goto out_no_bitmap;
device->read_requests = RB_ROOT;
device->write_requests = RB_ROOT;
id = idr_alloc(&drbd_devices, device, minor, minor + 1, GFP_KERNEL);
if (id < 0) {
if (id == -ENOSPC)
err = ERR_MINOR_OR_VOLUME_EXISTS;
goto out_no_minor_idr;
}
kref_get(&device->kref);
id = idr_alloc(&resource->devices, device, vnr, vnr + 1, GFP_KERNEL);
if (id < 0) {
if (id == -ENOSPC)
err = ERR_MINOR_OR_VOLUME_EXISTS;
goto out_idr_remove_minor;
}
kref_get(&device->kref);
INIT_LIST_HEAD(&device->peer_devices);
INIT_LIST_HEAD(&device->pending_bitmap_io);
for_each_connection(connection, resource) {
peer_device = kzalloc(sizeof(struct drbd_peer_device), GFP_KERNEL);
if (!peer_device)
goto out_idr_remove_from_resource;
peer_device->connection = connection;
peer_device->device = device;
list_add(&peer_device->peer_devices, &device->peer_devices);
kref_get(&device->kref);
id = idr_alloc(&connection->peer_devices, peer_device, vnr, vnr + 1, GFP_KERNEL);
if (id < 0) {
if (id == -ENOSPC)
err = ERR_INVALID_REQUEST;
goto out_idr_remove_from_resource;
}
kref_get(&connection->kref);
INIT_WORK(&peer_device->send_acks_work, drbd_send_acks_wf);
}
if (init_submitter(device)) {
err = ERR_NOMEM;
goto out_idr_remove_vol;
}
err = add_disk(disk);
if (err)
goto out_idr_remove_vol;
/* inherit the connection state */
device->state.conn = first_connection(resource)->cstate;
if (device->state.conn == C_WF_REPORT_PARAMS) {
for_each_peer_device(peer_device, device)
drbd_connected(peer_device);
}
/* move to create_peer_device() */
for_each_peer_device(peer_device, device)
drbd_debugfs_peer_device_add(peer_device);
drbd_debugfs_device_add(device);
return NO_ERROR;
out_idr_remove_vol:
idr_remove(&connection->peer_devices, vnr);
out_idr_remove_from_resource:
for_each_connection(connection, resource) {
peer_device = idr_remove(&connection->peer_devices, vnr);
if (peer_device)
kref_put(&connection->kref, drbd_destroy_connection);
}
for_each_peer_device_safe(peer_device, tmp_peer_device, device) {
list_del(&peer_device->peer_devices);
kfree(peer_device);
}
idr_remove(&resource->devices, vnr);
out_idr_remove_minor:
idr_remove(&drbd_devices, minor);
synchronize_rcu();
out_no_minor_idr:
drbd_bm_cleanup(device);
out_no_bitmap:
__free_page(device->md_io.page);
out_no_io_page:
blk_cleanup_disk(disk);
out_no_disk:
kref_put(&resource->kref, drbd_destroy_resource);
kfree(device);
return err;
}