2018-07-06 19:38:39 +02:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/vmalloc.h>
# include "null_blk.h"
/* zone_size in MBs to sectors. */
# define ZONE_SIZE_SHIFT 11
static inline unsigned int null_zone_no ( struct nullb_device * dev , sector_t sect )
{
return sect > > ilog2 ( dev - > zone_size_sects ) ;
}
int null_zone_init ( struct nullb_device * dev )
{
sector_t dev_size = ( sector_t ) dev - > size * 1024 * 1024 ;
sector_t sector = 0 ;
unsigned int i ;
if ( ! is_power_of_2 ( dev - > zone_size ) ) {
pr_err ( " null_blk: zone_size must be power-of-two \n " ) ;
return - EINVAL ;
}
dev - > zone_size_sects = dev - > zone_size < < ZONE_SIZE_SHIFT ;
dev - > nr_zones = dev_size > >
( SECTOR_SHIFT + ilog2 ( dev - > zone_size_sects ) ) ;
dev - > zones = kvmalloc_array ( dev - > nr_zones , sizeof ( struct blk_zone ) ,
GFP_KERNEL | __GFP_ZERO ) ;
if ( ! dev - > zones )
return - ENOMEM ;
2018-10-30 16:14:05 +09:00
if ( dev - > zone_nr_conv > = dev - > nr_zones ) {
dev - > zone_nr_conv = dev - > nr_zones - 1 ;
pr_info ( " null_blk: changed the number of conventional zones to %u " ,
dev - > zone_nr_conv ) ;
}
for ( i = 0 ; i < dev - > zone_nr_conv ; i + + ) {
struct blk_zone * zone = & dev - > zones [ i ] ;
zone - > start = sector ;
zone - > len = dev - > zone_size_sects ;
zone - > wp = zone - > start + zone - > len ;
zone - > type = BLK_ZONE_TYPE_CONVENTIONAL ;
zone - > cond = BLK_ZONE_COND_NOT_WP ;
sector + = dev - > zone_size_sects ;
}
for ( i = dev - > zone_nr_conv ; i < dev - > nr_zones ; i + + ) {
2018-07-06 19:38:39 +02:00
struct blk_zone * zone = & dev - > zones [ i ] ;
zone - > start = zone - > wp = sector ;
zone - > len = dev - > zone_size_sects ;
zone - > type = BLK_ZONE_TYPE_SEQWRITE_REQ ;
zone - > cond = BLK_ZONE_COND_EMPTY ;
sector + = dev - > zone_size_sects ;
}
return 0 ;
}
void null_zone_exit ( struct nullb_device * dev )
{
kvfree ( dev - > zones ) ;
}
2018-10-12 19:08:49 +09:00
int null_zone_report ( struct gendisk * disk , sector_t sector ,
struct blk_zone * zones , unsigned int * nr_zones ,
gfp_t gfp_mask )
2018-07-06 19:38:39 +02:00
{
2018-10-12 19:08:49 +09:00
struct nullb * nullb = disk - > private_data ;
struct nullb_device * dev = nullb - > dev ;
unsigned int zno , nrz = 0 ;
2018-07-06 19:38:39 +02:00
2018-10-12 19:08:49 +09:00
if ( ! dev - > zoned )
/* Not a zoned null device */
return - EOPNOTSUPP ;
2018-07-06 19:38:39 +02:00
2018-10-12 19:08:49 +09:00
zno = null_zone_no ( dev , sector ) ;
if ( zno < dev - > nr_zones ) {
nrz = min_t ( unsigned int , * nr_zones , dev - > nr_zones - zno ) ;
memcpy ( zones , & dev - > zones [ zno ] , nrz * sizeof ( struct blk_zone ) ) ;
2018-07-06 19:38:39 +02:00
}
2018-10-12 19:08:49 +09:00
* nr_zones = nrz ;
2018-07-06 19:38:39 +02:00
2018-10-12 19:08:49 +09:00
return 0 ;
2018-07-06 19:38:39 +02:00
}
2018-09-12 18:21:11 -06:00
void null_zone_write ( struct nullb_cmd * cmd , sector_t sector ,
unsigned int nr_sectors )
2018-07-06 19:38:39 +02:00
{
struct nullb_device * dev = cmd - > nq - > dev ;
unsigned int zno = null_zone_no ( dev , sector ) ;
struct blk_zone * zone = & dev - > zones [ zno ] ;
switch ( zone - > cond ) {
case BLK_ZONE_COND_FULL :
/* Cannot write to a full zone */
cmd - > error = BLK_STS_IOERR ;
break ;
case BLK_ZONE_COND_EMPTY :
case BLK_ZONE_COND_IMP_OPEN :
/* Writes must be at the write pointer position */
2018-09-12 18:21:11 -06:00
if ( sector ! = zone - > wp ) {
2018-07-06 19:38:39 +02:00
cmd - > error = BLK_STS_IOERR ;
break ;
}
if ( zone - > cond = = BLK_ZONE_COND_EMPTY )
zone - > cond = BLK_ZONE_COND_IMP_OPEN ;
2018-09-12 18:21:11 -06:00
zone - > wp + = nr_sectors ;
2018-07-06 19:38:39 +02:00
if ( zone - > wp = = zone - > start + zone - > len )
zone - > cond = BLK_ZONE_COND_FULL ;
break ;
2018-10-30 16:14:05 +09:00
case BLK_ZONE_COND_NOT_WP :
break ;
2018-07-06 19:38:39 +02:00
default :
/* Invalid zone condition */
cmd - > error = BLK_STS_IOERR ;
break ;
}
}
2018-09-12 18:21:11 -06:00
void null_zone_reset ( struct nullb_cmd * cmd , sector_t sector )
2018-07-06 19:38:39 +02:00
{
struct nullb_device * dev = cmd - > nq - > dev ;
2018-09-12 18:21:11 -06:00
unsigned int zno = null_zone_no ( dev , sector ) ;
2018-07-06 19:38:39 +02:00
struct blk_zone * zone = & dev - > zones [ zno ] ;
2018-10-30 16:14:05 +09:00
if ( zone - > type = = BLK_ZONE_TYPE_CONVENTIONAL ) {
cmd - > error = BLK_STS_IOERR ;
return ;
}
2018-07-06 19:38:39 +02:00
zone - > cond = BLK_ZONE_COND_EMPTY ;
zone - > wp = zone - > start ;
}