2021-08-02 10:30:05 -07:00
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2020 Intel Corporation. */
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/idr.h>
# include <linux/pci.h>
# include <cxlmem.h>
# include "core.h"
2021-09-08 22:12:32 -07:00
static DECLARE_RWSEM ( cxl_memdev_rwsem ) ;
2021-08-02 10:30:05 -07:00
/*
* An entire PCI topology full of devices should be enough for any
* config
*/
# define CXL_MEM_MAX_DEVS 65536
static int cxl_mem_major ;
static DEFINE_IDA ( cxl_memdev_ida ) ;
static void cxl_memdev_release ( struct device * dev )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
ida_free ( & cxl_memdev_ida , cxlmd - > id ) ;
kfree ( cxlmd ) ;
}
static char * cxl_memdev_devnode ( struct device * dev , umode_t * mode , kuid_t * uid ,
kgid_t * gid )
{
return kasprintf ( GFP_KERNEL , " cxl/%s " , dev_name ( dev ) ) ;
}
static ssize_t firmware_version_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
2021-11-02 13:29:01 -07:00
struct cxl_dev_state * cxlds = cxlmd - > cxlds ;
2021-08-02 10:30:05 -07:00
2021-11-02 13:29:01 -07:00
return sysfs_emit ( buf , " %.16s \n " , cxlds - > firmware_version ) ;
2021-08-02 10:30:05 -07:00
}
static DEVICE_ATTR_RO ( firmware_version ) ;
static ssize_t payload_max_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
2021-11-02 13:29:01 -07:00
struct cxl_dev_state * cxlds = cxlmd - > cxlds ;
2021-08-02 10:30:05 -07:00
2021-11-02 13:29:01 -07:00
return sysfs_emit ( buf , " %zu \n " , cxlds - > payload_size ) ;
2021-08-02 10:30:05 -07:00
}
static DEVICE_ATTR_RO ( payload_max ) ;
static ssize_t label_storage_size_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
2021-11-02 13:29:01 -07:00
struct cxl_dev_state * cxlds = cxlmd - > cxlds ;
2021-08-02 10:30:05 -07:00
2021-11-02 13:29:01 -07:00
return sysfs_emit ( buf , " %zu \n " , cxlds - > lsa_size ) ;
2021-08-02 10:30:05 -07:00
}
static DEVICE_ATTR_RO ( label_storage_size ) ;
static ssize_t ram_size_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
2021-11-02 13:29:01 -07:00
struct cxl_dev_state * cxlds = cxlmd - > cxlds ;
unsigned long long len = range_len ( & cxlds - > ram_range ) ;
2021-08-02 10:30:05 -07:00
return sysfs_emit ( buf , " %#llx \n " , len ) ;
}
static struct device_attribute dev_attr_ram_size =
__ATTR ( size , 0444 , ram_size_show , NULL ) ;
static ssize_t pmem_size_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
2021-11-02 13:29:01 -07:00
struct cxl_dev_state * cxlds = cxlmd - > cxlds ;
unsigned long long len = range_len ( & cxlds - > pmem_range ) ;
2021-08-02 10:30:05 -07:00
return sysfs_emit ( buf , " %#llx \n " , len ) ;
}
static struct device_attribute dev_attr_pmem_size =
__ATTR ( size , 0444 , pmem_size_show , NULL ) ;
2022-01-31 13:56:11 -08:00
static ssize_t serial_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
struct cxl_dev_state * cxlds = cxlmd - > cxlds ;
return sysfs_emit ( buf , " %#llx \n " , cxlds - > serial ) ;
}
static DEVICE_ATTR_RO ( serial ) ;
2021-08-02 10:30:05 -07:00
static struct attribute * cxl_memdev_attributes [ ] = {
2022-01-31 13:56:11 -08:00
& dev_attr_serial . attr ,
2021-08-02 10:30:05 -07:00
& dev_attr_firmware_version . attr ,
& dev_attr_payload_max . attr ,
& dev_attr_label_storage_size . attr ,
NULL ,
} ;
static struct attribute * cxl_memdev_pmem_attributes [ ] = {
& dev_attr_pmem_size . attr ,
NULL ,
} ;
static struct attribute * cxl_memdev_ram_attributes [ ] = {
& dev_attr_ram_size . attr ,
NULL ,
} ;
static struct attribute_group cxl_memdev_attribute_group = {
. attrs = cxl_memdev_attributes ,
} ;
static struct attribute_group cxl_memdev_ram_attribute_group = {
. name = " ram " ,
. attrs = cxl_memdev_ram_attributes ,
} ;
static struct attribute_group cxl_memdev_pmem_attribute_group = {
. name = " pmem " ,
. attrs = cxl_memdev_pmem_attributes ,
} ;
static const struct attribute_group * cxl_memdev_attribute_groups [ ] = {
& cxl_memdev_attribute_group ,
& cxl_memdev_ram_attribute_group ,
& cxl_memdev_pmem_attribute_group ,
NULL ,
} ;
static const struct device_type cxl_memdev_type = {
. name = " cxl_memdev " ,
. release = cxl_memdev_release ,
. devnode = cxl_memdev_devnode ,
. groups = cxl_memdev_attribute_groups ,
} ;
2021-09-14 12:03:04 -07:00
/**
* set_exclusive_cxl_commands ( ) - atomically disable user cxl commands
2021-11-02 13:29:01 -07:00
* @ cxlds : The device state to operate on
2021-09-14 12:03:04 -07:00
* @ cmds : bitmap of commands to mark exclusive
*
* Grab the cxl_memdev_rwsem in write mode to flush in - flight
* invocations of the ioctl path and then disable future execution of
* commands with the command ids set in @ cmds .
*/
2021-11-02 13:29:01 -07:00
void set_exclusive_cxl_commands ( struct cxl_dev_state * cxlds , unsigned long * cmds )
2021-09-14 12:03:04 -07:00
{
down_write ( & cxl_memdev_rwsem ) ;
2021-11-02 13:29:01 -07:00
bitmap_or ( cxlds - > exclusive_cmds , cxlds - > exclusive_cmds , cmds ,
2021-09-14 12:03:04 -07:00
CXL_MEM_COMMAND_ID_MAX ) ;
up_write ( & cxl_memdev_rwsem ) ;
}
2021-11-12 16:32:58 -08:00
EXPORT_SYMBOL_NS_GPL ( set_exclusive_cxl_commands , CXL ) ;
2021-09-14 12:03:04 -07:00
/**
* clear_exclusive_cxl_commands ( ) - atomically enable user cxl commands
2021-11-02 13:29:01 -07:00
* @ cxlds : The device state to modify
2021-09-14 12:03:04 -07:00
* @ cmds : bitmap of commands to mark available for userspace
*/
2021-11-02 13:29:01 -07:00
void clear_exclusive_cxl_commands ( struct cxl_dev_state * cxlds , unsigned long * cmds )
2021-09-14 12:03:04 -07:00
{
down_write ( & cxl_memdev_rwsem ) ;
2021-11-02 13:29:01 -07:00
bitmap_andnot ( cxlds - > exclusive_cmds , cxlds - > exclusive_cmds , cmds ,
2021-09-14 12:03:04 -07:00
CXL_MEM_COMMAND_ID_MAX ) ;
up_write ( & cxl_memdev_rwsem ) ;
}
2021-11-12 16:32:58 -08:00
EXPORT_SYMBOL_NS_GPL ( clear_exclusive_cxl_commands , CXL ) ;
2021-09-14 12:03:04 -07:00
2021-09-08 22:12:32 -07:00
static void cxl_memdev_shutdown ( struct device * dev )
{
struct cxl_memdev * cxlmd = to_cxl_memdev ( dev ) ;
down_write ( & cxl_memdev_rwsem ) ;
2021-11-02 13:29:01 -07:00
cxlmd - > cxlds = NULL ;
2021-09-08 22:12:32 -07:00
up_write ( & cxl_memdev_rwsem ) ;
}
2021-08-02 10:30:05 -07:00
static void cxl_memdev_unregister ( void * _cxlmd )
{
struct cxl_memdev * cxlmd = _cxlmd ;
struct device * dev = & cxlmd - > dev ;
2021-09-08 22:12:32 -07:00
cxl_memdev_shutdown ( dev ) ;
2021-08-02 10:30:05 -07:00
cdev_device_del ( & cxlmd - > cdev , dev ) ;
put_device ( dev ) ;
}
2021-11-02 13:29:01 -07:00
static struct cxl_memdev * cxl_memdev_alloc ( struct cxl_dev_state * cxlds ,
2021-08-02 10:30:05 -07:00
const struct file_operations * fops )
{
struct cxl_memdev * cxlmd ;
struct device * dev ;
struct cdev * cdev ;
int rc ;
cxlmd = kzalloc ( sizeof ( * cxlmd ) , GFP_KERNEL ) ;
if ( ! cxlmd )
return ERR_PTR ( - ENOMEM ) ;
rc = ida_alloc_range ( & cxl_memdev_ida , 0 , CXL_MEM_MAX_DEVS , GFP_KERNEL ) ;
if ( rc < 0 )
goto err ;
cxlmd - > id = rc ;
dev = & cxlmd - > dev ;
device_initialize ( dev ) ;
2021-11-02 13:29:01 -07:00
dev - > parent = cxlds - > dev ;
2021-08-02 10:30:05 -07:00
dev - > bus = & cxl_bus_type ;
dev - > devt = MKDEV ( cxl_mem_major , cxlmd - > id ) ;
dev - > type = & cxl_memdev_type ;
device_set_pm_not_required ( dev ) ;
cdev = & cxlmd - > cdev ;
cdev_init ( cdev , fops ) ;
return cxlmd ;
err :
kfree ( cxlmd ) ;
return ERR_PTR ( rc ) ;
}
2021-09-08 22:12:32 -07:00
static long __cxl_memdev_ioctl ( struct cxl_memdev * cxlmd , unsigned int cmd ,
unsigned long arg )
{
switch ( cmd ) {
case CXL_MEM_QUERY_COMMANDS :
return cxl_query_cmd ( cxlmd , ( void __user * ) arg ) ;
case CXL_MEM_SEND_COMMAND :
return cxl_send_cmd ( cxlmd , ( void __user * ) arg ) ;
default :
return - ENOTTY ;
}
}
static long cxl_memdev_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
struct cxl_memdev * cxlmd = file - > private_data ;
int rc = - ENXIO ;
down_read ( & cxl_memdev_rwsem ) ;
2021-11-02 13:29:01 -07:00
if ( cxlmd - > cxlds )
2021-09-08 22:12:32 -07:00
rc = __cxl_memdev_ioctl ( cxlmd , cmd , arg ) ;
up_read ( & cxl_memdev_rwsem ) ;
return rc ;
}
static int cxl_memdev_open ( struct inode * inode , struct file * file )
{
struct cxl_memdev * cxlmd =
container_of ( inode - > i_cdev , typeof ( * cxlmd ) , cdev ) ;
get_device ( & cxlmd - > dev ) ;
file - > private_data = cxlmd ;
return 0 ;
}
static int cxl_memdev_release_file ( struct inode * inode , struct file * file )
{
struct cxl_memdev * cxlmd =
container_of ( inode - > i_cdev , typeof ( * cxlmd ) , cdev ) ;
put_device ( & cxlmd - > dev ) ;
return 0 ;
}
static const struct file_operations cxl_memdev_fops = {
. owner = THIS_MODULE ,
. unlocked_ioctl = cxl_memdev_ioctl ,
. open = cxl_memdev_open ,
. release = cxl_memdev_release_file ,
. compat_ioctl = compat_ptr_ioctl ,
. llseek = noop_llseek ,
} ;
2021-11-02 13:29:01 -07:00
struct cxl_memdev * devm_cxl_add_memdev ( struct cxl_dev_state * cxlds )
2021-08-02 10:30:05 -07:00
{
struct cxl_memdev * cxlmd ;
struct device * dev ;
struct cdev * cdev ;
int rc ;
2021-11-02 13:29:01 -07:00
cxlmd = cxl_memdev_alloc ( cxlds , & cxl_memdev_fops ) ;
2021-08-02 10:30:05 -07:00
if ( IS_ERR ( cxlmd ) )
return cxlmd ;
dev = & cxlmd - > dev ;
rc = dev_set_name ( dev , " mem%d " , cxlmd - > id ) ;
if ( rc )
goto err ;
/*
* Activate ioctl operations , no cxl_memdev_rwsem manipulation
* needed as this is ordered with cdev_add ( ) publishing the device .
*/
2021-11-02 13:29:01 -07:00
cxlmd - > cxlds = cxlds ;
2021-08-02 10:30:05 -07:00
cdev = & cxlmd - > cdev ;
rc = cdev_device_add ( cdev , dev ) ;
if ( rc )
goto err ;
2021-11-02 13:29:01 -07:00
rc = devm_add_action_or_reset ( cxlds - > dev , cxl_memdev_unregister , cxlmd ) ;
2021-08-02 10:30:05 -07:00
if ( rc )
return ERR_PTR ( rc ) ;
return cxlmd ;
err :
/*
* The cdev was briefly live , shutdown any ioctl operations that
* saw that state .
*/
2021-09-08 22:12:32 -07:00
cxl_memdev_shutdown ( dev ) ;
2021-08-02 10:30:05 -07:00
put_device ( dev ) ;
return ERR_PTR ( rc ) ;
}
2021-11-12 16:32:58 -08:00
EXPORT_SYMBOL_NS_GPL ( devm_cxl_add_memdev , CXL ) ;
2021-08-02 10:30:05 -07:00
__init int cxl_memdev_init ( void )
{
dev_t devt ;
int rc ;
rc = alloc_chrdev_region ( & devt , 0 , CXL_MEM_MAX_DEVS , " cxl " ) ;
if ( rc )
return rc ;
cxl_mem_major = MAJOR ( devt ) ;
return 0 ;
}
void cxl_memdev_exit ( void )
{
unregister_chrdev_region ( MKDEV ( cxl_mem_major , 0 ) , CXL_MEM_MAX_DEVS ) ;
}