@@ -42,22 +42,83 @@ static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *
4242}
4343static DEVICE_ATTR_RO (id );
4444
45+ static ssize_t dirty_shutdown_show (struct device * dev ,
46+ struct device_attribute * attr , char * buf )
47+ {
48+ struct nvdimm * nvdimm = to_nvdimm (dev );
49+ struct cxl_nvdimm * cxl_nvd = nvdimm_provider_data (nvdimm );
50+
51+ return sysfs_emit (buf , "%llu\n" , cxl_nvd -> dirty_shutdowns );
52+ }
53+ static DEVICE_ATTR_RO (dirty_shutdown );
54+
4555static struct attribute * cxl_dimm_attributes [] = {
4656 & dev_attr_id .attr ,
4757 & dev_attr_provider .attr ,
58+ & dev_attr_dirty_shutdown .attr ,
4859 NULL
4960};
5061
62+ #define CXL_INVALID_DIRTY_SHUTDOWN_COUNT ULLONG_MAX
63+ static umode_t cxl_dimm_visible (struct kobject * kobj ,
64+ struct attribute * a , int n )
65+ {
66+ if (a == & dev_attr_dirty_shutdown .attr ) {
67+ struct device * dev = kobj_to_dev (kobj );
68+ struct nvdimm * nvdimm = to_nvdimm (dev );
69+ struct cxl_nvdimm * cxl_nvd = nvdimm_provider_data (nvdimm );
70+
71+ if (cxl_nvd -> dirty_shutdowns ==
72+ CXL_INVALID_DIRTY_SHUTDOWN_COUNT )
73+ return 0 ;
74+ }
75+
76+ return a -> mode ;
77+ }
78+
5179static const struct attribute_group cxl_dimm_attribute_group = {
5280 .name = "cxl" ,
5381 .attrs = cxl_dimm_attributes ,
82+ .is_visible = cxl_dimm_visible
5483};
5584
5685static const struct attribute_group * cxl_dimm_attribute_groups [] = {
5786 & cxl_dimm_attribute_group ,
5887 NULL
5988};
6089
90+ static void cxl_nvdimm_arm_dirty_shutdown_tracking (struct cxl_nvdimm * cxl_nvd )
91+ {
92+ struct cxl_memdev * cxlmd = cxl_nvd -> cxlmd ;
93+ struct cxl_dev_state * cxlds = cxlmd -> cxlds ;
94+ struct cxl_memdev_state * mds = to_cxl_memdev_state (cxlds );
95+ struct device * dev = & cxl_nvd -> dev ;
96+ u32 count ;
97+
98+ /*
99+ * Dirty tracking is enabled and exposed to the user, only when:
100+ * - dirty shutdown on the device can be set, and,
101+ * - the device has a Device GPF DVSEC (albeit unused), and,
102+ * - the Get Health Info cmd can retrieve the device's dirty count.
103+ */
104+ cxl_nvd -> dirty_shutdowns = CXL_INVALID_DIRTY_SHUTDOWN_COUNT ;
105+
106+ if (cxl_arm_dirty_shutdown (mds )) {
107+ dev_warn (dev , "GPF: could not set dirty shutdown state\n" );
108+ return ;
109+ }
110+
111+ if (!cxl_gpf_get_dvsec (cxlds -> dev , false))
112+ return ;
113+
114+ if (cxl_get_dirty_count (mds , & count )) {
115+ dev_warn (dev , "GPF: could not retrieve dirty count\n" );
116+ return ;
117+ }
118+
119+ cxl_nvd -> dirty_shutdowns = count ;
120+ }
121+
61122static int cxl_nvdimm_probe (struct device * dev )
62123{
63124 struct cxl_nvdimm * cxl_nvd = to_cxl_nvdimm (dev );
@@ -78,6 +139,14 @@ static int cxl_nvdimm_probe(struct device *dev)
78139 set_bit (ND_CMD_GET_CONFIG_SIZE , & cmd_mask );
79140 set_bit (ND_CMD_GET_CONFIG_DATA , & cmd_mask );
80141 set_bit (ND_CMD_SET_CONFIG_DATA , & cmd_mask );
142+
143+ /*
144+ * Set dirty shutdown now, with the expectation that the device
145+ * clear it upon a successful GPF flow. The exception to this
146+ * is upon Viral detection, per CXL 3.2 section 12.4.2.
147+ */
148+ cxl_nvdimm_arm_dirty_shutdown_tracking (cxl_nvd );
149+
81150 nvdimm = __nvdimm_create (cxl_nvb -> nvdimm_bus , cxl_nvd ,
82151 cxl_dimm_attribute_groups , flags ,
83152 cmd_mask , 0 , NULL , cxl_nvd -> dev_id ,
0 commit comments