enum drbd_ret_code drbd_create_device()

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;
}